From 6778948f9de86c3cfaf36725a7c87dcff9ba247f Mon Sep 17 00:00:00 2001
From: hc <hc@nodka.com>
Date: Mon, 11 Dec 2023 08:20:59 +0000
Subject: [PATCH] kernel_5.10 no rt

---
 kernel/drivers/mfd/rkx110_x120/rkx120_combtxphy.c                                   |  416 
 kernel/kernel/watchdog.c                                                            |   10 
 kernel/drivers/media/i2c/maxim4c/maxim4c_mipi_txphy.c                               |  546 
 kernel/arch/powerpc/kernel/watchdog.c                                               |    5 
 kernel/drivers/media/platform/rockchip/cif/dev.h                                    |   10 
 kernel/arch/arm/include/asm/kmap_types.h                                            |   10 
 kernel/drivers/net/wireless/rockchip_wlan/infineon/bcmdhd/dhd_pcie_linux.c          |    8 
 kernel/drivers/media/platform/rockchip/cif/dev.c                                    |   40 
 kernel/drivers/video/rockchip/mpp/mpp_vepu2.c                                       |   38 
 kernel/arch/x86/include/asm/iomap.h                                                 |   13 
 kernel/kernel/trace/trace_output.c                                                  |   19 
 kernel/include/linux/mutex.h                                                        |   34 
 kernel/drivers/gpu/drm/drm_atomic_uapi.c                                            |   14 
 kernel/lib/dump_stack.c                                                             |    2 
 kernel/arch/arm/mm/highmem.c                                                        |  121 
 kernel/kernel/rcu/tree.c                                                            |    4 
 kernel/drivers/crypto/rockchip/rk_crypto_v3_ahash.c                                 |    4 
 kernel/arch/arm/configs/rv1106-tee.config                                           |  105 
 kernel/drivers/misc/rk628/rk628_post_process.h                                      |   15 
 kernel/sound/soc/codecs/tda7803.c                                                   |  171 
 kernel/drivers/soc/rockchip/rockchip_system_monitor.c                               |    2 
 kernel/drivers/usb/dwc3/core.c                                                      |    9 
 kernel/drivers/misc/rk628/rk628_post_process.c                                      |  268 
 kernel/sound/soc/codecs/tda7803.h                                                   |  138 
 kernel/drivers/media/i2c/maxim4c/maxim4c_mipi_txphy.h                               |   67 
 kernel/arch/x86/kernel/fpu/core.c                                                   |   12 
 kernel/arch/arm/include/asm/hardirq.h                                               |   11 
 kernel/kernel/irq_work.c                                                            |  135 
 kernel/include/dt-bindings/soc/rockchip-amp.h                                       |    7 
 kernel/include/linux/smp.h                                                          |    3 
 kernel/kernel/printk/printk.c                                                       | 1833 
 kernel/drivers/usb/dwc3/core.h                                                      |    6 
 kernel/arch/arm/configs/rv1106-trailcam.config                                      |  506 
 kernel/drivers/misc/rk628/rk628_dsi.h                                               |  158 
 kernel/drivers/mfd/rkx110_x120/rkx120_linkrx.c                                      |  797 
 kernel/drivers/misc/rk628/rk628_dsi.c                                               | 1310 
 kernel/include/drm/bridge/dw_mipi_dsi.h                                             |    1 
 kernel/kernel/sched/core.c                                                          | 1275 
 kernel/kernel/stop_machine.c                                                        |   27 
 kernel/drivers/mfd/display-serdes/novo/novo-nca9539.c                               |  370 
 kernel/drivers/media/i2c/sc2310.c                                                   |    3 
 kernel/drivers/gpu/drm/panel/Kconfig                                                |    8 
 kernel/drivers/video/rockchip/rga3/rga_job.c                                        |   38 
 kernel/sound/soc/codecs/Kconfig                                                     |    6 
 kernel/include/linux/nfs_xdr.h                                                      |    2 
 kernel/arch/arm/boot/dts/rv1106g-evb2-v12-nofastae-spi-nand.dts                     |  129 
 kernel/arch/x86/include/asm/thread_info.h                                           |   11 
 kernel/kernel/kexec_core.c                                                          |    1 
 kernel/drivers/tty/serial/8250/8250_core.c                                          |   17 
 kernel/arch/arm/boot/dts/rv1106g-evb2-v10.dts                                       |    3 
 kernel/arch/s390/kernel/vtime.c                                                     |   51 
 kernel/drivers/mtd/nand/spi/skyhigh.c                                               |    4 
 kernel/Documentation/devicetree/bindings/spi/spi-rockchip.yaml                      |   13 
 kernel/drivers/video/rockchip/vehicle/vehicle_cfg.h                                 |    1 
 kernel/kernel/fork.c                                                                |   28 
 kernel/kernel/workqueue.c                                                           |    4 
 kernel/drivers/gpu/drm/qxl/qxl_release.c                                            |    4 
 kernel/drivers/media/i2c/maxim4c/maxim4c_pattern.c                                  |  374 
 kernel/drivers/scsi/libfc/fc_exch.c                                                 |    4 
 kernel/arch/openrisc/mm/init.c                                                      |    1 
 kernel/drivers/media/i2c/otp_eeprom.c                                               |    7 
 kernel/drivers/tty/serial/8250/8250_fsl.c                                           |    9 
 kernel/include/drm/bridge/analogix_dp.h                                             |    5 
 kernel/arch/arm64/kernel/asm-offsets.c                                              |    1 
 kernel/include/linux/highmem.h                                                      |  306 
 kernel/drivers/mfd/max96755f.c                                                      |    2 
 kernel/kernel/trace/trace_irqsoff.c                                                 |   86 
 kernel/drivers/media/i2c/maxim4c/maxim4c_pattern.h                                  |   16 
 kernel/drivers/misc/nkio/nk_io_core.c                                               |   26 
 kernel/drivers/media/i2c/Kconfig                                                    |   55 
 kernel/drivers/video/rockchip/vehicle/vehicle_ad_nvp6324.c                          |  883 
 kernel/drivers/video/rockchip/vehicle/vehicle_flinger.c                             |  114 
 kernel/arch/arm/kernel/signal.c                                                     |    3 
 kernel/drivers/media/platform/rockchip/isp/isp_stats_v32.c                          |  195 
 kernel/include/soc/rockchip/rockchip_sip.h                                          |    1 
 kernel/arch/x86/include/asm/preempt.h                                               |   36 
 kernel/drivers/tty/serial/8250/8250.h                                               |   47 
 kernel/include/linux/shmem_fs.h                                                     |    2 
 kernel/arch/powerpc/mm/highmem.c                                                    |   67 
 kernel/drivers/gpio/Makefile                                                        |    1 
 kernel/drivers/net/wireless/rockchip_wlan/infineon/bcmdhd/dhd_linux.h               |   34 
 kernel/include/linux/spinlock.h                                                     |   12 
 kernel/drivers/mfd/rkx110_x120/hal/cru_core.h                                       |  450 
 kernel/drivers/misc/rk628/Kconfig                                                   |   27 
 kernel/drivers/mfd/rkx110_x120/hal/cru_core.c                                       |  904 
 kernel/drivers/rknpu/rknpu_gem.c                                                    |  114 
 kernel/drivers/media/i2c/sc401ai.c                                                  |    3 
 kernel/kernel/time/tick-sched.c                                                     |    2 
 kernel/arch/x86/include/asm/fixmap.h                                                |    5 
 kernel/drivers/net/ethernet/chelsio/cxgb/common.h                                   |    6 
 kernel/arch/x86/include/asm/fpu/api.h                                               |   24 
 kernel/include/linux/spinlock_types_up.h                                            |    2 
 kernel/fs/fscache/object.c                                                          |   13 
 kernel/arch/powerpc/include/asm/fixmap.h                                            |    4 
 kernel/drivers/gpu/drm/rockchip/dw-dp.c                                             |   28 
 kernel/arch/arm/boot/dts/rv1106g-smart-door-lock-rmsl-v10.dts                       |    8 
 kernel/include/asm-generic/hardirq.h                                                |    6 
 kernel/arch/alpha/include/asm/spinlock_types.h                                      |    4 
 kernel/drivers/misc/rk628/rk628_combrxphy.h                                         |   16 
 kernel/drivers/spi/spidev-rkslv.c                                                   |  382 
 kernel/drivers/misc/rk628/rk628_combrxphy.c                                         |  567 
 kernel/drivers/ata/libahci.c                                                        |    2 
 kernel/mm/zsmalloc.c                                                                |   85 
 kernel/drivers/media/i2c/maxim4c/maxim4c_drv.c                                      |  737 
 kernel/include/linux/console.h                                                      |   11 
 kernel/drivers/md/raid5.h                                                           |    1 
 kernel/net/rfkill/rfkill-wlan.c                                                     |    5 
 kernel/drivers/misc/rk628/rk628_csi.h                                               |   86 
 kernel/drivers/media/i2c/maxim4c/maxim4c_drv.h                                      |  109 
 kernel/drivers/video/rockchip/rga3/include/rga_mm.h                                 |    2 
 kernel/drivers/gpu/drm/rockchip/Kconfig                                             |    7 
 kernel/drivers/soc/rockchip/minidump/rk_minidump.c                                  |    1 
 kernel/arch/alpha/include/asm/kmap_types.h                                          |   15 
 kernel/drivers/misc/rk628/rk628_csi.c                                               |  434 
 kernel/drivers/mtd/nand/spi/gigadevice.c                                            |  200 
 kernel/Documentation/devicetree/bindings/gpio/novo,nca9539-gpio.yaml                |   80 
 kernel/drivers/video/rockchip/rga3/rga_common.c                                     |   53 
 kernel/net/sched/sch_generic.c                                                      |   10 
 kernel/drivers/net/wireless/rockchip_wlan/infineon/bcmdhd/dhd_linux_platdev.c       |   12 
 kernel/arch/arm64/include/asm/hardirq.h                                             |    7 
 kernel/include/asm-generic/preempt.h                                                |    3 
 kernel/drivers/mfd/display-serdes/serdes-bridge.c                                   |  354 
 kernel/arch/arm/boot/dts/rv1106-evb-dual-cam.dtsi                                   |   58 
 kernel/arch/arm64/include/asm/preempt.h                                             |   28 
 kernel/arch/arm/kernel/entry-armv.S                                                 |   19 
 kernel/kernel/locking/rtmutex_common.h                                              |   34 
 kernel/drivers/misc/Kconfig                                                         |    2 
 kernel/include/linux/rtmutex.h                                                      |   46 
 kernel/include/linux/sched/wake_q.h                                                 |   13 
 kernel/arch/powerpc/kernel/entry_32.S                                               |   23 
 kernel/arch/sh/include/asm/fixmap.h                                                 |    8 
 kernel/drivers/video/rockchip/rga3/rga2_reg_info.c                                  |  515 
 kernel/drivers/gpu/drm/rockchip/rockchip_drm_vop.h                                  |   37 
 kernel/fs/inode.c                                                                   |    2 
 kernel/arch/microblaze/mm/init.c                                                    |    6 
 kernel/mm/vmstat.c                                                                  |   12 
 kernel/drivers/gpu/drm/rockchip/rockchip_drm_vop.c                                  |   33 
 kernel/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.h                                 |   17 
 kernel/include/linux/io-mapping.h                                                   |   28 
 kernel/arch/x86/mm/highmem_32.c                                                     |   59 
 kernel/drivers/usb/dwc3/Makefile                                                    |    1 
 kernel/drivers/gpu/drm/panel/panel-maxim-max96772.c                                 |  543 
 kernel/arch/sparc/kernel/irq_64.c                                                   |    2 
 kernel/arch/arm/boot/dts/rv1106g-evb2-v10-dual-camera.dts                           |   61 
 kernel/drivers/mmc/host/sdhci-of-dwcmshc.c                                          |   22 
 kernel/drivers/media/platform/rockchip/hdmirx/rk_hdmirx_class.c                     |   37 
 kernel/net/xfrm/xfrm_state.c                                                        |    3 
 kernel/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c                              |   16 
 kernel/drivers/misc/lt7911d-fb-notifier.c                                           |  547 
 kernel/arch/powerpc/Kconfig                                                         |    4 
 kernel/drivers/md/raid5.c                                                           |    7 
 kernel/include/linux/sensor-dev.h                                                   |    2 
 kernel/arch/arm/boot/dts/rv1106-thunder-boot.dtsi                                   |    5 
 kernel/arch/powerpc/kernel/misc_32.S                                                |    2 
 kernel/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c                                 |  454 
 kernel/drivers/video/rockchip/mpp/mpp_rkvenc.c                                      |    2 
 kernel/fs/dcache.c                                                                  |   39 
 kernel/drivers/mfd/rkx110_x120/rkx110_reg.h                                         |  515 
 kernel/arch/arm64/include/asm/pgtable.h                                             |    3 
 kernel/include/linux/irqflags.h                                                     |   23 
 kernel/kernel/Kconfig.locks                                                         |    2 
 kernel/include/linux/irq_cpustat.h                                                  |   28 
 kernel/drivers/char/tpm/tpm-dev-common.c                                            |    1 
 kernel/kernel/sched/cpudeadline.c                                                   |    4 
 kernel/drivers/media/i2c/sc4210.c                                                   |    3 
 kernel/sound/soc/codecs/rt5640.c                                                    |    3 
 kernel/drivers/mfd/rkx110_x120/pattern_gen.c                                        |  141 
 kernel/fs/namespace.c                                                               |    8 
 kernel/drivers/net/ethernet/chelsio/cxgb/cxgb2.c                                    |   54 
 kernel/kernel/trace/trace_mmiotrace.c                                               |   14 
 kernel/drivers/mfd/rkx110_x120/Makefile                                             |   23 
 kernel/include/linux/mm_types.h                                                     |    4 
 kernel/arch/arm/configs/rv1106-smart-door.config                                    |    5 
 kernel/drivers/media/i2c/sc035gs.c                                                  |    3 
 kernel/arch/x86/include/asm/stackprotector.h                                        |    8 
 kernel/drivers/media/i2c/maxim4c/maxim4c_api.h                                      |  101 
 kernel/drivers/media/i2c/maxim4c/maxim4c_remote.h                                   |   36 
 kernel/drivers/media/i2c/maxim4c/maxim4c_remote.c                                   |  434 
 kernel/drivers/media/i2c/rk628/rk628_bt1120_v4l2.c                                  |   60 
 kernel/drivers/misc/rk628/rk628_rgb.h                                               |   17 
 kernel/drivers/media/i2c/sc501ai.c                                                  |    3 
 kernel/drivers/gpu/drm/i915/selftests/i915_gem.c                                    |    4 
 kernel/drivers/net/wireless/rockchip_wlan/infineon/bcmdhd/wl_cfgvendor.c            |    2 
 kernel/drivers/mfd/rkx110_x120/rkx110_x120_core.c                                   | 1100 
 kernel/arch/arm/mach-rockchip/Kconfig                                               |    6 
 kernel/arch/powerpc/include/asm/thread_info.h                                       |   16 
 kernel/drivers/gpu/drm/rockchip/rockchip_post_csc.h                                 |    1 
 kernel/arch/arm64/boot/dts/rockchip/rk3568-evb1-ddr4-v10.dtsi                       |  468 
 kernel/drivers/gpu/drm/rockchip/rockchip_post_csc.c                                 |    1 
 kernel/drivers/misc/rk628/rk628_rgb.c                                               |  166 
 kernel/include/linux/thread_info.h                                                  |   12 
 kernel/drivers/media/i2c/rk628/rk628_mipi_dphy.h                                    |   37 
 kernel/drivers/misc/rk628/rk628_grf.h                                               |  263 
 kernel/drivers/firmware/rockchip_sip.c                                              |   10 
 kernel/drivers/video/rockchip/vehicle/vehicle_main.h                                |    1 
 kernel/drivers/scsi/fcoe/fcoe.c                                                     |   16 
 kernel/arch/sh/include/asm/hardirq.h                                                |   14 
 kernel/drivers/video/rockchip/vehicle/vehicle_main.c                                |    9 
 kernel/drivers/video/rockchip/rga3/rga_dma_buf.c                                    |   21 
 kernel/arch/ia64/include/asm/kmap_types.h                                           |   13 
 kernel/arch/microblaze/mm/Makefile                                                  |    1 
 kernel/drivers/mfd/display-serdes/serdes-i2c.c                                      |  337 
 kernel/drivers/video/rockchip/mpp/mpp_rkvdec2_link.c                                |   73 
 kernel/fs/proc/array.c                                                              |    4 
 kernel/drivers/soc/rockchip/fiq_debugger/fiq_debugger.c                             |    7 
 kernel/drivers/net/wireless/rockchip_wlan/infineon/bcmdhd/dhd_custom_msm.c          |    2 
 kernel/kernel/trace/trace_events_inject.c                                           |    6 
 kernel/drivers/phy/rockchip/phy-rockchip-typec.c                                    |  221 
 kernel/drivers/crypto/rockchip/rk_crypto_v2_skcipher.c                              |    4 
 kernel/drivers/misc/lt7911d-fw.h                                                    | 1109 
 kernel/drivers/soc/rockchip/mtd_vendor_storage.c                                    |   31 
 kernel/Documentation/RCU/whatisRCU.rst                                              |   10 
 kernel/fs/namei.c                                                                   |    4 
 kernel/kernel/debug/kdb/kdb_main.c                                                  |   10 
 kernel/drivers/video/rockchip/rga3/include/rga_drv.h                                |    8 
 kernel/arch/arm64/configs/rockchip_linux_defconfig                                  |  124 
 kernel/arch/xtensa/include/asm/spinlock_types.h                                     |    4 
 kernel/arch/powerpc/kernel/syscall_64.c                                             |   10 
 kernel/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c                                 |  526 
 kernel/kernel/locking/spinlock_debug.c                                              |    5 
 kernel/arch/arm/boot/dts/rv1106g-evb1-mcu-display-v20.dts                           |  314 
 kernel/lib/irq_poll.c                                                               |    5 
 kernel/drivers/devfreq/rockchip_dmc_common.c                                        |    7 
 kernel/include/soc/rockchip/rockchip_rockit.h                                       |    9 
 kernel/Documentation/devicetree/bindings/regulator/fan53555.txt                     |    2 
 kernel/arch/arm/boot/dts/rv1106g-evb1-v11-nofastae-spi-nand.dts                     |   29 
 kernel/drivers/media/i2c/sc500ai.c                                                  |    3 
 kernel/arch/um/include/asm/kmap_types.h                                             |   13 
 kernel/sound/soc/rockchip/rockchip_pdm.c                                            |  126 
 kernel/arch/sh/kernel/irq.c                                                         |    4 
 kernel/drivers/media/platform/rockchip/isp/isp_stats_v3x.c                          |   18 
 kernel/arch/powerpc/kexec/crash.c                                                   |    3 
 kernel/drivers/net/ethernet/chelsio/cxgb/subr.c                                     |   64 
 kernel/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c                               |    7 
 kernel/Makefile                                                                     |    1 
 kernel/drivers/media/platform/rockchip/cif/capture.c                                |  960 
 kernel/drivers/media/i2c/sc2232.c                                                   |    3 
 kernel/drivers/gpu/drm/rockchip/dw-mipi-dsi2-rockchip.c                             |  164 
 kernel/drivers/misc/rk628/rk628_config.c                                            |   52 
 kernel/drivers/misc/rk628/rk628_config.h                                            |   24 
 kernel/sound/soc/codecs/rk817_codec.c                                               |   23 
 kernel/arch/x86/kernel/irq_32.c                                                     |    2 
 kernel/drivers/media/platform/rockchip/isp/regs.c                                   |   62 
 kernel/Documentation/RCU/Design/Requirements/Requirements.rst                       |   26 
 kernel/arch/hexagon/include/asm/spinlock_types.h                                    |    4 
 kernel/drivers/media/platform/rockchip/isp/regs.h                                   |   14 
 kernel/arch/powerpc/xmon/xmon.c                                                     |    6 
 kernel/arch/s390/include/asm/vtime.h                                                |    1 
 kernel/drivers/phy/rockchip/phy-rockchip-inno-usb2.c                                |   29 
 kernel/include/uapi/linux/rkcif-config.h                                            |   12 
 kernel/drivers/mailbox/rockchip-mbox-demo.c                                         |  138 
 kernel/arch/arm/boot/dts/rv1106.dtsi                                                |   27 
 kernel/drivers/usb/gadget/function/uvc_v4l2.c                                       |    2 
 kernel/drivers/rtc/rtc-rockchip.c                                                   |    6 
 kernel/include/drm/drm_mode_config.h                                                |   13 
 kernel/kernel/printk/printk_safe.c                                                  |  422 
 kernel/drivers/media/platform/rockchip/isp/dev.h                                    |   48 
 kernel/drivers/rknpu/rknpu_iommu.c                                                  |    3 
 kernel/drivers/video/rockchip/rga3/rga_policy.c                                     |   54 
 kernel/arch/x86/kernel/crash_dump_32.c                                              |   48 
 kernel/arch/sparc/mm/Makefile                                                       |    3 
 kernel/arch/arm/mach-rockchip/rv1106_pm.h                                           |   21 
 kernel/drivers/media/platform/rockchip/hdmirx/Makefile                              |    2 
 kernel/drivers/soc/rockchip/rockchip_pm_config.c                                    |  214 
 kernel/drivers/rknpu/include/rknpu_drv.h                                            |    8 
 kernel/include/linux/rwlock_types.h                                                 |    4 
 kernel/drivers/spi/spi-rockchip.c                                                   |   31 
 kernel/arch/arm/mach-rockchip/rv1106_pm.c                                           |  117 
 kernel/arch/arm/boot/dts/rv1103g-evb-mcu-display-v11.dts                            |   62 
 kernel/arch/arm/boot/dts/rv1106-evb-cam.dtsi                                        |   29 
 kernel/arch/arm/boot/dts/rv1106-evb-ext-mcu-v10.dtsi                                |   62 
 kernel/drivers/media/platform/rockchip/isp/version.h                                |   32 
 kernel/kernel/notifier.c                                                            |   12 
 kernel/arch/powerpc/include/asm/simple_spinlock_types.h                             |    2 
 kernel/drivers/soc/rockchip/minidump/minidump_private.h                             |    3 
 kernel/arch/arm64/include/asm/thread_info.h                                         |   16 
 kernel/arch/microblaze/include/asm/fixmap.h                                         |    4 
 kernel/drivers/video/rockchip/rga3/include/rga.h                                    |  121 
 kernel/arch/arm/include/asm/fixmap.h                                                |    4 
 kernel/kernel/sched/swait.c                                                         |    1 
 kernel/arch/arm64/boot/dts/rockchip/rk3568-android.dtsi                             |    0 
 kernel/drivers/mfd/display-serdes/serdes-pinctrl.c                                  |  393 
 kernel/drivers/mmc/host/rk_sdmmc_ops.h                                              |    2 
 kernel/drivers/mmc/host/rk_sdmmc_ops.c                                              |   20 
 kernel/drivers/video/rockchip/vehicle/vehicle_generic_sensor.c                      |    6 
 kernel/drivers/mfd/Makefile                                                         |    2 
 kernel/drivers/media/i2c/Makefile                                                   |    6 
 kernel/arch/powerpc/kernel/traps.c                                                  |    8 
 kernel/drivers/mtd/nand/spi/jsc.c                                                   |    9 
 kernel/drivers/gpu/drm/rockchip/rockchip_drm_direct_show.h                          |    2 
 kernel/drivers/mfd/display-serdes/serdes-gpio.c                                     |  253 
 kernel/include/asm-generic/bug.h                                                    |    1 
 kernel/drivers/media/i2c/maxim4c/remote_max96715.c                                  |  400 
 kernel/drivers/gpu/drm/rockchip/rockchip_drm_direct_show.c                          |   15 
 kernel/include/linux/dcache.h                                                       |    4 
 kernel/drivers/misc/rk628/rk628_hdmirx.h                                            |  633 
 kernel/kernel/locking/rtmutex-debug.h                                               |   11 
 kernel/drivers/media/platform/rockchip/cif/subdev-itf.c                             |   62 
 kernel/drivers/crypto/rockchip/rk_crypto_utils.c                                    |    8 
 kernel/drivers/misc/rk628/rk628_hdmirx.c                                            |  777 
 kernel/kernel/locking/rtmutex-debug.c                                               |  102 
 kernel/drivers/media/platform/rockchip/isp/isp_stats.c                              |    1 
 kernel/drivers/misc/rk628/Makefile                                                  |   10 
 kernel/drivers/mtd/nand/spi/foresee.c                                               |   18 
 kernel/drivers/net/can/rockchip/rockchip_canfd.c                                    |  140 
 kernel/drivers/gpio/Kconfig                                                         |    8 
 kernel/arch/um/include/asm/hardirq.h                                                |   17 
 kernel/drivers/tty/serial/omap-serial.c                                             |   12 
 kernel/kernel/rcu/Kconfig                                                           |    4 
 kernel/drivers/atm/eni.c                                                            |    2 
 kernel/drivers/rknpu/include/rknpu_job.h                                            |    3 
 kernel/drivers/video/rockchip/mpp/mpp_rkvdec.c                                      |    6 
 kernel/kernel/sched/sched.h                                                         |   72 
 kernel/include/linux/cpuhotplug.h                                                   |    1 
 kernel/arch/sparc/include/asm/highmem.h                                             |    8 
 kernel/kernel/sched/cpupri.c                                                        |    4 
 kernel/drivers/soc/rockchip/rockchip_thunderboot_service.c                          |    7 
 kernel/arch/arm/mm/Makefile                                                         |    1 
 kernel/kernel/sched/rt.c                                                            |   81 
 kernel/drivers/mfd/display-serdes/rockchip/rockchip-rkx111.c                        |  189 
 kernel/drivers/media/platform/rockchip/isp/procfs.c                                 |    4 
 kernel/drivers/i2c/busses/i2c-rk3x.c                                                |    9 
 kernel/drivers/clk/rockchip/clk-pll.c                                               |  172 
 kernel/drivers/irqchip/irq-gic-common.c                                             |   21 
 kernel/Documentation/RCU/Design/Expedited-Grace-Periods/Expedited-Grace-Periods.rst |    4 
 kernel/kernel/time/timer.c                                                          |    9 
 kernel/drivers/firewire/ohci.c                                                      |    4 
 kernel/drivers/video/rockchip/rga3/rga_hw_config.c                                  |    9 
 kernel/drivers/misc/rk628/panel.h                                                   |   18 
 kernel/drivers/misc/rk628/panel.c                                                   |  245 
 kernel/arch/s390/Kconfig                                                            |    1 
 kernel/drivers/media/i2c/sc132gs.c                                                  |    3 
 kernel/drivers/usb/typec/tcpm/tcpm.c                                                |   18 
 kernel/Documentation/RCU/stallwarn.rst                                              |    4 
 kernel/include/linux/sched/rt.h                                                     |    8 
 kernel/kernel/trace/trace_events.c                                                  |   20 
 kernel/drivers/tty/serial/8250/8250_port.c                                          |   94 
 kernel/arch/sparc/mm/highmem.c                                                      |  115 
 kernel/arch/nds32/Kconfig.cpu                                                       |    1 
 kernel/drivers/edac/Kconfig                                                         |    7 
 kernel/arch/parisc/include/asm/kmap_types.h                                         |   13 
 kernel/drivers/mfd/rkx110_x120/serdes_combphy.c                                     |   87 
 kernel/drivers/rknpu/rknpu_debugger.c                                               |    6 
 kernel/arch/arm/kernel/asm-offsets.c                                                |    1 
 kernel/arch/arm/boot/dts/rk3308-voice-module-mainboard-v10-aarch32.dtsi             |    8 
 kernel/arch/arm64/configs/rockchip_rt.config                                        |   22 
 kernel/arch/arm/boot/dts/rv1106-tb-nofastae-spi-nor.dtsi                            |   19 
 kernel/drivers/clk/rockchip/clk-rk3399.c                                            |    4 
 kernel/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c                                |   16 
 kernel/drivers/media/i2c/maxim4c/remote_max96717.c                                  |  316 
 kernel/drivers/media/platform/rockchip/isp/regs_v3x.h                               |   16 
 kernel/drivers/media/platform/rockchip/isp/dmarx.c                                  |   20 
 kernel/include/linux/sched/mm.h                                                     |   11 
 kernel/drivers/mfd/rkx110_x120/serdes_combphy.h                                     |   28 
 kernel/drivers/misc/rk628/rk628_cru.c                                               |  472 
 kernel/drivers/mfd/display-serdes/gpio.h                                            |  248 
 kernel/drivers/rpmsg/rockchip_rpmsg_test.c                                          |   27 
 kernel/drivers/media/i2c/maxim4c/Kconfig                                            |   46 
 kernel/drivers/mfd/rkx110_x120/hal/cru_rkx121.h                                     |   48 
 kernel/drivers/usb/dwc3/Kconfig                                                     |    9 
 kernel/drivers/misc/rk628/rk628_cru.h                                               |  159 
 kernel/include/linux/rfkill-wlan.h                                                  |    7 
 kernel/drivers/mfd/rkx110_x120/rkx110_linktx.c                                      |  738 
 kernel/drivers/mfd/display-serdes/novo/Kconfig                                      |   20 
 kernel/kernel/locking/rtmutex.h                                                     |    7 
 kernel/drivers/mfd/Kconfig                                                          |    3 
 kernel/sound/soc/rockchip/Kconfig                                                   |   13 
 kernel/drivers/soc/rockchip/sdmmc_vendor_storage.c                                  |   29 
 kernel/kernel/locking/rtmutex.c                                                     |  965 
 kernel/include/linux/interrupt.h                                                    |   34 
 kernel/net/sunrpc/svc_xprt.c                                                        |    4 
 kernel/drivers/rpmsg/rockchip_rpmsg.c                                               |   11 
 kernel/drivers/gpu/drm/qxl/qxl_ioctl.c                                              |   27 
 kernel/drivers/devfreq/rockchip_dmc.c                                               |    4 
 kernel/drivers/mfd/rkx110_x120/hal/pinctrl_rkx110_x120.c                            |  612 
 kernel/drivers/mfd/display-serdes/rohm/rohm-bu18rl82.c                              |  460 
 kernel/arch/openrisc/mm/ioremap.c                                                   |    1 
 kernel/drivers/gpu/drm/panel/panel-simple.c                                         |  192 
 kernel/drivers/mfd/display-serdes/rohm/rohm-bu18rl82.h                              |  376 
 kernel/drivers/input/touchscreen/gt1x/gt1x.c                                        |   16 
 kernel/kernel/rcu/update.c                                                          |    4 
 kernel/drivers/clk/rockchip/clk-rk3568.c                                            |   12 
 kernel/arch/powerpc/include/asm/stackprotector.h                                    |    4 
 kernel/drivers/media/i2c/maxim4c/maxim4c_v4l2.c                                     |  999 
 kernel/drivers/gpu/drm/i915/selftests/i915_gem_gtt.c                                |    8 
 kernel/include/linux/trace_events.h                                                 |   76 
 kernel/arch/mips/include/asm/fixmap.h                                               |    4 
 kernel/drivers/mmc/host/dw_mmc.c                                                    |   12 
 kernel/drivers/media/i2c/maxim4c/maxim4c_link.h                                     |   86 
 kernel/drivers/media/i2c/maxim4c/maxim4c_link.c                                     |  754 
 kernel/drivers/net/wireless/rockchip_wlan/infineon/bcmdhd/Makefile                  |    9 
 kernel/include/linux/notifier.h                                                     |    6 
 kernel/drivers/net/wireless/ath/ath9k/beacon.c                                      |    2 
 kernel/mm/slub.c                                                                    |  148 
 kernel/drivers/mfd/rkx110_x120/hal/cru_rkx110.c                                     |  612 
 kernel/net/core/gen_stats.c                                                         |   12 
 kernel/drivers/mfd/rkx110_x120/hal/pinctrl_rkx110_x120.h                            |  166 
 kernel/kernel/sched/cputime.c                                                       |   35 
 kernel/arch/arm64/include/asm/spinlock_types.h                                      |    4 
 kernel/drivers/net/ethernet/chelsio/cxgb/sge.h                                      |    3 
 kernel/drivers/net/ethernet/chelsio/cxgb/sge.c                                      |   53 
 kernel/drivers/gpu/drm/drm_color_mgmt.c                                             |   41 
 kernel/drivers/input/sensors/accel/iam20680_acc.c                                   |  239 
 kernel/include/linux/bottom_half.h                                                  |    8 
 kernel/net/core/gen_estimator.c                                                     |    6 
 kernel/arch/x86/Kconfig                                                             |    2 
 kernel/drivers/media/i2c/sc230ai.c                                                  |   50 
 kernel/drivers/media/i2c/gc8034.c                                                   |    1 
 kernel/drivers/mfd/rkx110_x120/hal/cru_rkx110.h                                     |  358 
 kernel/drivers/tty/serial/8250/8250_ingenic.c                                       |    7 
 kernel/drivers/spi/spidev-rkmst.c                                                   |  636 
 kernel/arch/arm/boot/dts/rv1106g-evb2-v12-nofastae-emmc.dts                         |  134 
 kernel/drivers/media/i2c/sc031gs.c                                                  |    3 
 kernel/drivers/mfd/rkx110_x120/hal/cru_rkx121.c                                     |  626 
 kernel/kernel/Kconfig.preempt                                                       |    7 
 kernel/drivers/dma/pl330.c                                                          |    8 
 kernel/arch/xtensa/mm/highmem.c                                                     |   50 
 kernel/Documentation/admin-guide/kernel-parameters.txt                              |   11 
 kernel/block/blk-mq.c                                                               |  135 
 kernel/kernel/ksysfs.c                                                              |   12 
 kernel/drivers/mfd/rkx110_x120/hal/hal_os_def.h                                     |   33 
 kernel/drivers/mfd/display-serdes/maxim/maxim-max96755.c                            |  719 
 kernel/Documentation/RCU/rcubarrier.rst                                             |    6 
 kernel/drivers/media/i2c/max96712.c                                                 |  738 
 kernel/arch/arm/mm/fault.c                                                          |    6 
 kernel/drivers/gpu/drm/bridge/maxim-max96755f.c                                     |   14 
 kernel/include/linux/kmsg_dump.h                                                    |   52 
 kernel/drivers/mfd/rkx110_x120/rkx110_x120.h                                        |  366 
 kernel/drivers/mfd/display-serdes/maxim/maxim-max96755.h                            |  189 
 kernel/drivers/mfd/rkx110_x120/rkx120.c                                             |  309 
 kernel/kernel/sched/features.h                                                      |    8 
 kernel/net/core/dev.c                                                               |   33 
 kernel/include/trace/hooks/dtask.h                                                  |    2 
 kernel/fs/cifs/readdir.c                                                            |    2 
 kernel/arch/arc/include/asm/kmap_types.h                                            |   14 
 kernel/drivers/soc/rockchip/rockchip_opp_select.c                                   |  101 
 kernel/arch/nds32/mm/highmem.c                                                      |   48 
 kernel/drivers/misc/rk628/rk628_hdmitx.h                                            |  350 
 kernel/kernel/trace/trace_functions.c                                               |   28 
 kernel/arch/parisc/include/asm/hardirq.h                                            |    1 
 kernel/drivers/misc/rk628/rk628_hdmitx.c                                            | 1198 
 kernel/kernel/printk/internal.h                                                     |   74 
 kernel/drivers/mtd/nand/spi/esmt.c                                                  |   67 
 kernel/include/drm/bridge/dw_hdmi.h                                                 |    4 
 kernel/drivers/media/platform/rockchip/cif/procfs.c                                 |   10 
 kernel/drivers/video/rockchip/vehicle/vehicle_cif.h                                 |   10 
 kernel/drivers/media/i2c/sc850sl.c                                                  |    3 
 kernel/drivers/misc/rk628/rk628_lvds.h                                              |   15 
 kernel/drivers/pci/controller/dwc/pcie-dw-ep-rockchip.c                             |   60 
 kernel/arch/powerpc/include/asm/highmem.h                                           |    7 
 kernel/drivers/misc/rk628/rk628_lvds.c                                              |  126 
 kernel/drivers/media/i2c/gc2093.c                                                   |  386 
 kernel/arch/nds32/include/asm/highmem.h                                             |   22 
 kernel/arch/mips/include/asm/kmap_types.h                                           |   13 
 kernel/arch/arm/boot/dts/rv1106g-evb2-v11-trailcam-emmc.dts                         |  622 
 kernel/drivers/video/rockchip/vehicle/vehicle_cif.c                                 |  189 
 kernel/lib/locking-selftest.c                                                       |   51 
 kernel/arch/x86/crypto/aesni-intel_glue.c                                           |   22 
 kernel/drivers/mfd/display-serdes/novo/Makefile                                     |    6 
 kernel/arch/arm64/kernel/fpsimd.c                                                   |   37 
 kernel/drivers/misc/nkmcu/nk_mcu.c                                                  |    2 
 kernel/drivers/rknpu/rknpu_mem.c                                                    |   16 
 kernel/mm/slab.c                                                                    |   90 
 kernel/drivers/edac/Makefile                                                        |    1 
 kernel/drivers/media/platform/rockchip/isp/dev.c                                    |   15 
 kernel/arch/x86/crypto/cast5_avx_glue.c                                             |   21 
 kernel/include/soc/rockchip/rockchip_dmc.h                                          |    2 
 kernel/drivers/video/rockchip/rga3/rga3_reg_info.c                                  |  498 
 kernel/drivers/mfd/display-serdes/core.h                                            |  353 
 kernel/mm/z3fold.c                                                                  |   17 
 kernel/drivers/clk/rockchip/clk-rk3588.c                                            |    6 
 kernel/drivers/gpu/drm/drm_atomic_state_helper.c                                    |    7 
 kernel/sound/soc/codecs/Makefile                                                    |    2 
 kernel/mm/slab.h                                                                    |    2 
 kernel/arch/arm64/kernel/signal.c                                                   |    2 
 kernel/include/soc/rockchip/rockchip_opp_select.h                                   |   22 
 kernel/drivers/video/rockchip/mpp/mpp_rkvdec2.c                                     |    2 
 kernel/include/linux/rcupdate.h                                                     |   10 
 kernel/drivers/misc/rk628/rk628_combtxphy.h                                         |   82 
 kernel/drivers/media/platform/rockchip/isp/capture_v21.c                            |  170 
 kernel/drivers/pci/controller/pci-hyperv.c                                          |    2 
 kernel/drivers/mtd/nand/spi/fmsh.c                                                  |   40 
 kernel/arch/arm/boot/dts/rk3288-linux.dtsi                                          |    7 
 kernel/include/linux/vmstat.h                                                       |    4 
 kernel/include/dt-bindings/mfd/rockchip-serdes.h                                    |  151 
 kernel/drivers/gpu/drm/rockchip/cdn-dp-link-training.c                              |   76 
 kernel/include/linux/u64_stats_sync.h                                               |   42 
 kernel/drivers/mfd/display-serdes/maxim/Makefile                                    |    9 
 kernel/drivers/media/i2c/sc530ai.c                                                  |   62 
 kernel/arch/sh/include/asm/kmap_types.h                                             |   15 
 kernel/drivers/media/i2c/dw9763.c                                                   |  216 
 kernel/drivers/media/platform/rockchip/isp/rkisp.h                                  |    2 
 kernel/drivers/irqchip/irq-gic-v3-its.c                                             |   30 
 kernel/drivers/cpufreq/cpufreq_times.c                                              |   22 
 kernel/drivers/media/i2c/rk628/rk628_csi_v4l2.c                                     |  215 
 kernel/drivers/input/sensors/gyro/iam20680_gyro.c                                   |  206 
 kernel/drivers/media/platform/rockchip/isp/rkisp.c                                  |  449 
 kernel/include/linux/wait.h                                                         |    1 
 kernel/drivers/rkflash/sfc_nand.c                                                   |   20 
 kernel/arch/arm/Makefile                                                            |    1 
 kernel/fs/fscache/internal.h                                                        |    1 
 kernel/include/linux/preempt.h                                                      |  190 
 kernel/drivers/leds/trigger/Kconfig                                                 |    1 
 kernel/drivers/mfd/display-serdes/Makefile                                          |   13 
 kernel/drivers/mfd/display-serdes/Kconfig                                           |   31 
 kernel/drivers/video/rockchip/rga3/include/rga2_reg_info.h                          |   37 
 kernel/arch/sparc/include/asm/kmap_types.h                                          |   11 
 kernel/include/asm-generic/kmap_types.h                                             |   11 
 kernel/arch/x86/include/asm/highmem.h                                               |   13 
 kernel/arch/x86/include/asm/paravirt_types.h                                        |    1 
 kernel/drivers/spi/Kconfig                                                          |    6 
 kernel/drivers/media/platform/rockchip/isp/isp_params_v21.c                         |   29 
 kernel/mm/Kconfig                                                                   |    5 
 kernel/drivers/gpu/drm/bridge/synopsys/Makefile                                     |    2 
 kernel/drivers/mfd/display-serdes/serdes-core.c                                     |  391 
 kernel/arch/arm/boot/dts/Makefile                                                   |    8 
 kernel/kernel/ptrace.c                                                              |   32 
 kernel/drivers/media/i2c/imx415.c                                                   |    3 
 kernel/fs/pstore/platform.c                                                         |    5 
 kernel/drivers/media/i2c/sc5336.c                                                   | 1588 
 kernel/drivers/media/platform/rockchip/isp/isp_params_v21.h                         |    5 
 kernel/include/linux/iam20680.h                                                     |  238 
 kernel/arch/arm/boot/dts/rv1103g-battery-ipc-v11.dts                                |    5 
 kernel/arch/x86/mm/init_32.c                                                        |   15 
 kernel/sound/soc/rockchip/rockchip_sai.c                                            |  148 
 kernel/kernel/cpu.c                                                                 |    9 
 kernel/drivers/media/i2c/sc200ai.c                                                  |    3 
 kernel/kernel/entry/common.c                                                        |   14 
 kernel/drivers/media/platform/rockchip/isp/isp_params_v32.c                         | 2368 
 kernel/drivers/media/platform/rockchip/isp/isp_rockit.c                             |   34 
 kernel/drivers/gpu/drm/i915/display/intel_sprite.c                                  |   15 
 kernel/drivers/media/platform/rockchip/hdmirx/Kconfig                               |   12 
 kernel/kernel/cgroup/cpuset.c                                                       |   82 
 kernel/arch/arm/configs/rk3308bs_aarch32_mipi_display.config                        |  313 
 kernel/arch/x86/include/asm/signal.h                                                |   13 
 kernel/drivers/mfd/rkx110_x120/rkx110_combrxphy.c                                   |  280 
 kernel/kernel/softirq.c                                                             |  445 
 kernel/drivers/media/platform/rockchip/isp/isp_params_v32.h                         |  163 
 kernel/drivers/misc/rk628/rk628_combtxphy.c                                         |  309 
 kernel/drivers/net/ethernet/jme.c                                                   |   10 
 kernel/drivers/mfd/rkx110_x120/hal/cru_api.h                                        |  210 
 kernel/kernel/irq/spurious.c                                                        |    8 
 kernel/Documentation/RCU/checklist.rst                                              |    2 
 kernel/drivers/net/ethernet/jme.h                                                   |    2 
 kernel/drivers/rknpu/include/rknpu_ioctl.h                                          |    6 
 kernel/mm/workingset.c                                                              |    5 
 kernel/drivers/hv/vmbus_drv.c                                                       |    5 
 kernel/drivers/net/ethernet/dlink/sundance.c                                        |    2 
 kernel/include/linux/mtd/spinand.h                                                  |    1 
 kernel/kernel/trace/trace_syscalls.c                                                |   20 
 kernel/drivers/thermal/rockchip_thermal.c                                           |   80 
 kernel/arch/arm/boot/dts/rv1106-tb-nofastae.dtsi                                    |   35 
 kernel/drivers/gpu/drm/drm_atomic_helper.c                                          |    3 
 kernel/kernel/smp.c                                                                 |   14 
 kernel/kernel/signal.c                                                              |  105 
 kernel/arch/sh/kernel/traps.c                                                       |    2 
 kernel/arch/um/include/asm/fixmap.h                                                 |    1 
 kernel/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c                            |    6 
 kernel/kernel/trace/trace_event_perf.c                                              |    5 
 kernel/drivers/gpu/drm/i915/i915_gem.c                                              |   40 
 kernel/drivers/rknpu/include/rknpu_mm.h                                             |    1 
 kernel/drivers/rknpu/rknpu_drv.c                                                    |  140 
 kernel/drivers/video/rockchip/mpp/mpp_iommu.c                                       |    3 
 kernel/drivers/irqchip/irq-gic.c                                                    |   61 
 kernel/fs/eventfd.c                                                                 |   12 
 kernel/kernel/trace/trace_hwlat.c                                                   |    7 
 kernel/drivers/mfd/rkx110_x120/rkx120_dsi_tx.c                                      | 1182 
 kernel/drivers/crypto/rockchip/rk_crypto_core.c                                     |   23 
 kernel/arch/csky/include/asm/fixmap.h                                               |    4 
 kernel/drivers/mfd/rkx110_x120/rkx120_dsi_tx.h                                      |   23 
 kernel/drivers/soc/rockchip/fiq_debugger/rk_fiq_debugger.c                          |   20 
 kernel/kernel/trace/trace_kprobe.c                                                  |   10 
 kernel/include/linux/rwsem.h                                                        |   12 
 kernel/arch/sparc/Kconfig                                                           |    1 
 kernel/drivers/media/i2c/rk628/Kconfig                                              |    1 
 kernel/arch/sparc/include/asm/vaddrs.h                                              |    4 
 kernel/fs/proc/proc_sysctl.c                                                        |    2 
 kernel/arch/arm/boot/dts/rk3288-evb.dtsi                                            |   72 
 kernel/drivers/media/i2c/lt6911uxc.c                                                |   38 
 kernel/drivers/media/platform/rockchip/isp/capture.c                                |   25 
 kernel/drivers/edac/rockchip_edac.c                                                 |  358 
 kernel/drivers/gpio/gpio-nca9539.c                                                  |  332 
 kernel/drivers/media/i2c/sc430cs.c                                                  |    3 
 kernel/include/net/netns/xfrm.h                                                     |    2 
 kernel/arch/powerpc/kernel/nvram_64.c                                               |   12 
 kernel/mm/vmalloc.c                                                                 |   13 
 kernel/arch/csky/mm/highmem.c                                                       |   75 
 kernel/drivers/cpufreq/rockchip-cpufreq.c                                           |    2 
 kernel/drivers/pwm/pwm-rockchip.c                                                   |   26 
 kernel/drivers/media/i2c/maxim4c/remote_max9295.c                                   |  337 
 kernel/drivers/nvme/host/pci.c                                                      |    5 
 kernel/drivers/net/wireless/rockchip_wlan/infineon/bcmdhd/dhd_gpio.c                |  437 
 kernel/kernel/trace/trace_functions_graph.c                                         |   32 
 kernel/arch/arm/configs/rv1106-wakeup.config                                        |  127 
 kernel/kernel/trace/trace_sched_wakeup.c                                            |   71 
 kernel/arch/arm/boot/dts/rk3308-dot-rk816-v10-aarch32.dts                           |    6 
 kernel/drivers/media/i2c/gc1084.c                                                   |   81 
 kernel/arch/mips/kernel/crash_dump.c                                                |   42 
 kernel/drivers/mfd/rkx110_x120/rkx120_reg.h                                         |  468 
 kernel/kernel/cgroup/rstat.c                                                        |    5 
 kernel/include/linux/irq_work.h                                                     |   25 
 kernel/kernel/kthread.c                                                             |   16 
 kernel/drivers/media/i2c/it6616.c                                                   |   69 
 kernel/drivers/media/i2c/maxim4c/maxim4c_video_pipe.c                               |  356 
 kernel/fs/nfs/dir.c                                                                 |    4 
 kernel/include/soc/rockchip/rockchip_amp.h                                          |   39 
 kernel/drivers/media/i2c/sc1346.c                                                   | 1505 
 kernel/drivers/misc/rk628/rk628.h                                                   |  484 
 kernel/arch/mips/Kconfig                                                            |    1 
 kernel/drivers/gpu/drm/rockchip/cdn-dp-reg.c                                        |  100 
 kernel/drivers/gpu/drm/i915/gt/intel_breadcrumbs.c                                  |    5 
 kernel/drivers/media/i2c/sc301iot.c                                                 |  776 
 kernel/drivers/gpu/drm/i915/i915_irq.c                                              |    2 
 kernel/include/asm-generic/Kbuild                                                   |    2 
 kernel/include/linux/hardirq.h                                                      |    7 
 kernel/arch/microblaze/Kconfig                                                      |    1 
 kernel/drivers/gpu/drm/rockchip/cdn-dp-reg.h                                        |   53 
 kernel/drivers/misc/rk628/rk628.c                                                   | 1310 
 kernel/arch/arm64/kernel/traps.c                                                    |    7 
 kernel/drivers/mfd/display-serdes/rockchip/Makefile                                 |    6 
 kernel/kernel/sched/topology.c                                                      |    1 
 kernel/arch/microblaze/mm/highmem.c                                                 |   78 
 kernel/mm/highmem.c                                                                 |  262 
 kernel/include/uapi/linux/media-bus-format.h                                        |    1 
 kernel/drivers/misc/rk628/rk628_gvi.c                                               |  230 
 kernel/fs/afs/dir_silly.c                                                           |    2 
 kernel/include/net/sch_generic.h                                                    |   27 
 kernel/drivers/misc/rk628/rk628_gvi.h                                               |  218 
 kernel/arch/nds32/include/asm/fixmap.h                                              |    4 
 kernel/drivers/media/platform/rockchip/cif/cif-scale.c                              |    4 
 kernel/drivers/crypto/rockchip/rk_crypto_v3_skcipher.c                              |    4 
 kernel/fs/fuse/readdir.c                                                            |    2 
 kernel/drivers/misc/rk628/rk628_gpio.h                                              |  296 
 kernel/include/linux/serial_8250.h                                                  |    5 
 kernel/arch/powerpc/include/asm/spinlock_types.h                                    |    4 
 kernel/arch/x86/kernel/irq_64.c                                                     |    2 
 kernel/Documentation/devicetree/bindings/mfd/rkx110_x120.yaml                       |  564 
 kernel/drivers/media/i2c/lt6911uxe.c                                                |   11 
 kernel/sound/soc/rockchip/rockchip_i2s_tdm.c                                        |  700 
 kernel/drivers/clk/rockchip/clk-rk3328.c                                            |   13 
 kernel/drivers/tty/serial/amba-pl011.c                                              |   17 
 kernel/include/linux/pid.h                                                          |    1 
 kernel/include/linux/sched.h                                                        |  122 
 kernel/drivers/media/i2c/gc2053.c                                                   |    8 
 kernel/arch/ia64/include/asm/spinlock_types.h                                       |    4 
 kernel/arch/csky/include/asm/highmem.h                                              |    6 
 kernel/drivers/gpu/drm/rockchip/rockchip_vop_reg.h                                  |    2 
 kernel/drivers/mfd/display-serdes/rockchip/Kconfig                                  |   25 
 kernel/drivers/media/i2c/maxim4c/maxim4c_video_pipe.h                               |   50 
 kernel/arch/arm/boot/dts/rv1106g-evb2-v12-wakeup.dts                                |  327 
 kernel/drivers/phy/rockchip/phy-rockchip-samsung-dcphy.c                            |   14 
 kernel/drivers/video/rockchip/rga3/include/rga_common.h                             |    4 
 kernel/drivers/spi/Makefile                                                         |    2 
 kernel/drivers/gpu/drm/panel/Makefile                                               |    1 
 kernel/drivers/media/platform/Makefile                                              |    2 
 kernel/drivers/gpu/drm/radeon/radeon_display.c                                      |    2 
 kernel/include/linux/skbuff.h                                                       |    7 
 kernel/include/drm/drm_crtc.h                                                       |   11 
 kernel/drivers/soc/rockchip/rockchip-cpuinfo.c                                      |   13 
 kernel/kernel/irq/manage.c                                                          |    6 
 kernel/drivers/mfd/display-serdes/rohm/Kconfig                                      |   25 
 kernel/drivers/scsi/fcoe/fcoe_ctlr.c                                                |    4 
 kernel/drivers/mfd/display-serdes/serdes-irq.c                                      |  109 
 kernel/arch/powerpc/kernel/irq.c                                                    |    2 
 kernel/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c                                    |    6 
 kernel/drivers/media/i2c/ov50c40.c                                                  |    1 
 kernel/drivers/mfd/display-serdes/serdes-panel.c                                    |  246 
 kernel/arch/arm/include/asm/highmem.h                                               |   34 
 kernel/arch/arm/mach-rockchip/rv1106_sleep.S                                        |    8 
 kernel/kernel/trace/trace.h                                                         |   57 
 kernel/include/net/gen_stats.h                                                      |   11 
 kernel/kernel/trace/trace.c                                                         |  231 
 kernel/drivers/rknpu/include/rknpu_gem.h                                            |    7 
 kernel/arch/arm/boot/dts/rv1126.dtsi                                                |   11 
 kernel/arch/powerpc/kernel/asm-offsets.c                                            |    1 
 kernel/drivers/mfd/rkx110_x120/Kconfig                                              |   18 
 kernel/arch/arm/boot/dts/rv1106-evb.dtsi                                            |    4 
 kernel/drivers/media/platform/rockchip/cif/hw.h                                     |    1 
 kernel/drivers/tty/serial/8250/8250_mtk.c                                           |   29 
 kernel/fs/aio.c                                                                     |    3 
 kernel/drivers/rknpu/include/rknpu_iommu.h                                          |    6 
 kernel/drivers/media/platform/rockchip/cif/hw.c                                     |   56 
 kernel/drivers/gpu/drm/i915/gt/intel_engine_pm.c                                    |    8 
 kernel/include/linux/fs.h                                                           |    2 
 kernel/arch/sh/mm/init.c                                                            |    8 
 kernel/lib/test_lockup.c                                                            |   16 
 kernel/lib/bug.c                                                                    |    1 
 kernel/kernel/locking/rwsem.c                                                       |    8 
 kernel/drivers/mfd/display-serdes/rohm/Makefile                                     |    6 
 kernel/mm/page_alloc.c                                                              |  180 
 kernel/drivers/media/platform/rockchip/isp/common.h                                 |   26 
 kernel/net/Kconfig                                                                  |    2 
 kernel/include/linux/kernel.h                                                       |   26 
 kernel/lib/smp_processor_id.c                                                       |    5 
 kernel/drivers/video/rockchip/vehicle/vehicle_ad.h                                  |    1 
 kernel/include/linux/rockchip/rockchip_sip.h                                        |   32 
 kernel/arch/arm64/kernel/entry.S                                                    |   13 
 kernel/drivers/gpu/drm/qxl/qxl_image.c                                              |   18 
 kernel/kernel/printk/Makefile                                                       |    1 
 kernel/drivers/media/platform/rockchip/isp/isp_params.c                             |   41 
 kernel/include/linux/blkdev.h                                                       |    2 
 kernel/drivers/mtd/mtdoops.c                                                        |    5 
 kernel/arch/powerpc/mm/Makefile                                                     |    1 
 kernel/arch/powerpc/kernel/exceptions-64e.S                                         |   16 
 kernel/include/uapi/linux/rk-isp2-config.h                                          |    2 
 kernel/arch/mips/include/asm/highmem.h                                              |    6 
 kernel/drivers/media/platform/rockchip/isp/common.c                                 |    8 
 kernel/drivers/video/rockchip/vehicle/vehicle_dev.c                                 |    3 
 kernel/drivers/mfd/rkx110_x120/rkx110_dsi_rx.c                                      |  124 
 kernel/drivers/mfd/display-serdes/maxim/maxim-max96772.h                            |   14 
 kernel/drivers/mfd/rkx110_x120/rkx110_dsi_rx.h                                      |   14 
 kernel/include/linux/stop_machine.h                                                 |    5 
 kernel/drivers/media/i2c/lt7911uxc.c                                                |    5 
 kernel/drivers/mfd/display-serdes/maxim/maxim-max96772.c                            |  328 
 kernel/drivers/gpu/drm/i915/i915_trace.h                                            |    6 
 kernel/drivers/media/i2c/os02k10.c                                                  | 2262 +
 kernel/drivers/video/rockchip/mpp/mpp_rkvenc2.c                                     |  171 
 kernel/drivers/media/i2c/imx586.c                                                   |    1 
 kernel/drivers/mfd/display-serdes/rockchip/rockchip-rkx121.c                        |  189 
 kernel/arch/arm/boot/dts/rk3308-dot-v10-aarch32.dts                                 |    6 
 kernel/drivers/media/platform/rockchip/isp/hw.c                                     |   86 
 kernel/net/rfkill/rfkill-bt.c                                                       |   25 
 kernel/kernel/sched/fair.c                                                          |   16 
 kernel/drivers/media/i2c/s5kjn1.c                                                   |    1 
 kernel/drivers/media/platform/rockchip/isp/hw.h                                     |    3 
 kernel/drivers/media/i2c/max96722.c                                                 | 1617 
 kernel/sound/soc/codecs/rv1106_codec.c                                              |  111 
 kernel/kernel/locking/lockdep.c                                                     |    2 
 kernel/include/linux/spinlock_types.h                                               |   94 
 kernel/drivers/input/sensors/accel/Makefile                                         |    1 
 kernel/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/fbmem.h                          |    8 
 kernel/arch/arc/mm/highmem.c                                                        |   54 
 kernel/net/core/sock.c                                                              |    6 
 kernel/kernel/panic.c                                                               |   33 
 kernel/arch/powerpc/kernel/misc_64.S                                                |    2 
 kernel/include/net/sock.h                                                           |    4 
 kernel/arch/arm/include/asm/spinlock_types.h                                        |    4 
 kernel/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c                                  |  185 
 kernel/lib/Kconfig.debug                                                            |    2 
 kernel/arch/arm/boot/dts/rv1106g-evb1-v11-facial-gate.dts                           |   29 
 kernel/arch/xtensa/include/asm/highmem.h                                            |   12 
 kernel/drivers/mailbox/Kconfig                                                      |    6 
 kernel/arch/arm64/Kconfig                                                           |    3 
 kernel/init/Kconfig                                                                 |    7 
 kernel/drivers/media/i2c/sc3338.c                                                   |    7 
 kernel/arch/x86/kvm/x86.c                                                           |    8 
 kernel/drivers/gpu/drm/vmwgfx/vmwgfx_blit.c                                         |   30 
 kernel/drivers/mfd/rkx110_x120/rkx110_x120_panel.c                                  |  642 
 kernel/arch/arm/include/asm/irq.h                                                   |    2 
 kernel/drivers/media/i2c/sc223a.c                                                   | 1608 
 kernel/include/linux/signal.h                                                       |    1 
 kernel/kernel/trace/trace_uprobe.c                                                  |    4 
 kernel/drivers/media/i2c/sc4238.c                                                   |    3 
 kernel/drivers/media/i2c/maxim4c/maxim4c_i2c.h                                      |   55 
 kernel/drivers/mtd/nand/spi/core.c                                                  |   16 
 kernel/drivers/gpu/drm/rockchip/rockchip_drm_drv.c                                  |   40 
 kernel/drivers/media/i2c/maxim4c/maxim4c_i2c.c                                      |  407 
 kernel/drivers/gpu/drm/rockchip/rockchip_drm_drv.h                                  |   45 
 kernel/include/trace/events/sched.h                                                 |   12 
 kernel/drivers/media/i2c/sc3336.c                                                   |    3 
 kernel/drivers/media/platform/rockchip/cif/version.h                                |    2 
 kernel/arch/arm/kernel/smp.c                                                        |    2 
 kernel/drivers/rtc/rtc-rk808.c                                                      |    7 
 kernel/arch/arm/configs/rockchip_linux_defconfig                                    |   13 
 kernel/arch/mips/mm/init.c                                                          |    4 
 kernel/Documentation/driver-api/io-mapping.rst                                      |   92 
 kernel/arch/powerpc/mm/mem.c                                                        |    7 
 kernel/include/uapi/linux/rk-camera-module.h                                        |   43 
 kernel/drivers/phy/rockchip/phy-rockchip-usbdp.c                                    |    4 
 kernel/arch/arm/configs/rv1106-tb-nofastae.config                                   |  421 
 kernel/drivers/rkflash/sfc.c                                                        |    6 
 kernel/arch/powerpc/kvm/Kconfig                                                     |    1 
 kernel/lib/scatterlist.c                                                            |    2 
 kernel/drivers/gpu/drm/rockchip/Makefile                                            |    3 
 kernel/drivers/gpu/drm/rockchip/rockchip_lvds.c                                     |  164 
 kernel/drivers/media/i2c/sc4336.c                                                   |    3 
 kernel/arch/xtensa/Kconfig                                                          |    1 
 kernel/arch/powerpc/kernel/time.c                                                   |   56 
 kernel/drivers/gpu/drm/rockchip/rockchip_drm_fb.c                                   |    1 
 kernel/arch/arm/boot/dts/rk3288-evb-rk808-linux.dts                                 |   60 
 kernel/sound/soc/rockchip/rockchip_multi_dais_pcm.c                                 |    9 
 kernel/arch/arm/kernel/entry-common.S                                               |    1 
 kernel/fs/proc/base.c                                                               |    3 
 kernel/drivers/mfd/display-serdes/maxim/maxim-max96752.c                            |  319 
 kernel/arch/arm64/boot/dts/rockchip/rk3568.dtsi                                     |    0 
 kernel/arch/arm/Kconfig                                                             |    6 
 kernel/drivers/mfd/rkx110_x120/hal/cru_rkx120.h                                     |  305 
 kernel/arch/mips/mm/highmem.c                                                       |   77 
 kernel/arch/microblaze/include/asm/highmem.h                                        |    6 
 kernel/include/linux/rk_hdmirx_class.h                                              |   13 
 kernel/arch/arm/mm/cache-xsc3l2.c                                                   |    4 
 kernel/sound/soc/rockchip/rockchip_i2s.c                                            |  244 
 kernel/drivers/mfd/display-serdes/maxim/maxim-max96752.h                            |   14 
 kernel/fs/btrfs/ctree.h                                                             |    1 
 kernel/drivers/cpufreq/cpufreq_interactive.c                                        |    3 
 kernel/drivers/media/platform/rockchip/isp/csi.c                                    |   16 
 kernel/kernel/trace/trace_branch.c                                                  |    6 
 kernel/drivers/misc/rk628/rk628_pinctrl.c                                           |  326 
 kernel/drivers/misc/rk628/rk628_pinctrl.h                                           |   19 
 kernel/kernel/locking/spinlock.c                                                    |    7 
 kernel/sound/soc/rockchip/rockchip_multi_dais.c                                     |   34 
 kernel/drivers/mfd/display-serdes/rohm/rohm-bu18tl82.c                              |  461 
 kernel/arch/arm64/kvm/arm.c                                                         |    6 
 kernel/drivers/mfd/rkx110_x120/rkx110.c                                             |  304 
 kernel/kernel/exit.c                                                                |    2 
 kernel/arch/powerpc/include/asm/cmpxchg.h                                           |    2 
 kernel/drivers/mfd/display-serdes/rohm/rohm-bu18tl82.h                              |  438 
 kernel/arch/sparc/mm/srmmu.c                                                        |    2 
 kernel/drivers/gpu/drm/qxl/qxl_object.h                                             |    4 
 kernel/drivers/gpu/drm/qxl/qxl_object.c                                             |   12 
 kernel/include/linux/rockchip/cpu.h                                                 |   16 
 kernel/drivers/crypto/rockchip/rk_crypto_v2_ahash.c                                 |    4 
 kernel/arch/sh/include/asm/spinlock_types.h                                         |    4 
 kernel/drivers/mtd/spi-nor/xmc.c                                                    |    2 
 kernel/drivers/mmc/host/dw_mmc-rockchip.c                                           |   15 
 kernel/mm/memcontrol.c                                                              |   66 
 kernel/drivers/mfd/rkx110_x120/hal/pinctrl_api.h                                    |   45 
 kernel/drivers/media/i2c/ar0822.c                                                   | 5484 +++
 kernel/drivers/mtd/nand/spi/dosilicon.c                                             |   11 
 kernel/include/uapi/drm/rockchip_drm.h                                              |    1 
 kernel/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c                              |   13 
 kernel/drivers/mfd/rkx110_x120/hal/cru_rkx120.c                                     |  599 
 kernel/drivers/media/platform/rockchip/isp/isp_params_v3x.c                         |   84 
 kernel/arch/arc/include/asm/highmem.h                                               |   26 
 kernel/arch/csky/Kconfig                                                            |    1 
 kernel/kernel/futex.c                                                               |   10 
 kernel/drivers/mfd/rkx110_x120/hal/hal_def.h                                        |   58 
 kernel/include/linux/spinlock_api_smp.h                                             |    4 
 kernel/arch/um/kernel/kmsg_dump.c                                                   |   13 
 kernel/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c                                 |   10 
 kernel/arch/nds32/mm/Makefile                                                       |    1 
 kernel/include/linux/delay.h                                                        |    6 
 kernel/drivers/media/platform/rockchip/isp/isp_params_v3x.h                         |    5 
 kernel/drivers/video/rockchip/mpp/mpp_common.c                                      |   55 
 kernel/arch/arm64/configs/rockchip_defconfig                                        |   12 
 kernel/drivers/soc/rockchip/rockchip_amp.c                                          |  387 
 kernel/drivers/input/sensors/accel/Kconfig                                          |    7 
 kernel/drivers/media/platform/rockchip/isp/regs_v2x.h                               |    2 
 kernel/include/linux/rbtree.h                                                       |   27 
 kernel/arch/powerpc/platforms/pseries/iommu.c                                       |   31 
 kernel/include/linux/printk.h                                                       |   30 
 kernel/kernel/locking/Makefile                                                      |   10 
 kernel/arch/s390/include/asm/spinlock_types.h                                       |    4 
 kernel/drivers/input/sensors/gyro/Kconfig                                           |    7 
 kernel/drivers/pinctrl/pinctrl-rockchip.c                                           |   37 
 kernel/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h                           |    2 
 kernel/arch/arc/Kconfig                                                             |    1 
 kernel/drivers/media/platform/rockchip/isp/capture_v30.c                            |  127 
 kernel/arch/arm64/configs/rk3308bs_mipi_display.config                              |  128 
 kernel/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c                           |   52 
 kernel/drivers/phy/rockchip/phy-rockchip-csi2-dphy.c                                |  816 
 kernel/Documentation/devicetree/bindings/usb/snps,dwc3.yaml                         |    5 
 kernel/include/linux/entry-common.h                                                 |    6 
 kernel/arch/arm/include/asm/thread_info.h                                           |    4 
 kernel/kernel/trace/trace_events_trigger.c                                          |    8 
 kernel/kernel/trace/blktrace.c                                                      |   17 
 kernel/arch/arm/configs/rk3308-aarch32-ia.config                                    |  325 
 kernel/drivers/media/i2c/otp_eeprom.h                                               |    3 
 kernel/include/linux/sched/hotplug.h                                                |    2 
 kernel/drivers/media/platform/rockchip/cif/mipi-csi2.h                              |   35 
 kernel/arch/powerpc/include/asm/kmap_types.h                                        |   13 
 kernel/drivers/video/rockchip/rga3/include/rga3_reg_info.h                          |   26 
 kernel/arch/xtensa/include/asm/fixmap.h                                             |    4 
 kernel/drivers/media/platform/rockchip/cif/mipi-csi2.c                              |  403 
 kernel/drivers/mfd/display-serdes/maxim/maxim-max96789.h                            |   14 
 kernel/drivers/video/rockchip/rga3/rga_mm.c                                         |  107 
 kernel/drivers/mfd/display-serdes/maxim/maxim-max96789.c                            |  352 
 kernel/drivers/mtd/nand/spi/xtx.c                                                   |   51 
 kernel/drivers/mfd/display-serdes/maxim/Kconfig                                     |   44 
 kernel/lib/nmi_backtrace.c                                                          |    6 
 kernel/drivers/phy/rockchip/phy-rockchip-inno-dsidphy.c                             |    1 
 kernel/arch/x86/include/asm/kmap_types.h                                            |   13 
 kernel/kernel/sched/deadline.c                                                      |   47 
 kernel/arch/arm/boot/dts/rv1106g-evb2-v12-nofastae-spi-nor.dts                      |  134 
 kernel/arch/Kconfig                                                                 |    8 
 kernel/drivers/gpu/drm/rockchip/cdn-dp-core.h                                       |    3 
 kernel/fs/nfs/unlink.c                                                              |    4 
 kernel/drivers/gpu/drm/rockchip/cdn-dp-core.c                                       |   79 
 kernel/drivers/input/sensors/gyro/Makefile                                          |    1 
 kernel/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp-hdcp.c                            |  650 
 kernel/drivers/mailbox/Makefile                                                     |    2 
 kernel/drivers/media/i2c/sc2239.c                                                   |    3 
 kernel/arch/arm/boot/dts/rv1106-tb-nofastae-emmc.dtsi                               |   31 
 kernel/arch/ia64/kernel/time.c                                                      |   20 
 kernel/arch/x86/mm/iomap_32.c                                                       |   57 
 kernel/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c                           |   14 
 kernel/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp-hdcp.h                            |   55 
 kernel/drivers/pci/controller/dwc/pcie-dw-rockchip.c                                |   99 
 kernel/drivers/media/i2c/maxim4c/Makefile                                           |   14 
 kernel/include/linux/cpumask.h                                                      |    6 
 kernel/arch/powerpc/platforms/powernv/opal-kmsg.c                                   |    3 
 kernel/drivers/mfd/rkx110_x120/hal/cru_rkx111.h                                     |  117 
 kernel/drivers/mfd/rkx110_x120/hal/cru_rkx111.c                                     |  706 
 kernel/drivers/char/tpm/tpm_tis.c                                                   |   29 
 kernel/include/linux/eventfd.h                                                      |   11 
 kernel/drivers/gpu/drm/drm_mode_config.c                                            |   16 
 kernel/lib/cpumask.c                                                                |   18 
 kernel/drivers/gpu/drm/rockchip/rockchip_drm_logo.c                                 |   74 
 kernel/drivers/firmware/efi/efi.c                                                   |    5 
 kernel/drivers/gpu/drm/rockchip/rockchip_drm_logo.h                                 |    3 
 kernel/drivers/media/platform/rockchip/hdmirx/rk_hdmirx.h                           |    1 
 kernel/drivers/mtd/nand/spi/unim.c                                                  |    9 
 kernel/drivers/nvme/host/nvme.h                                                     |    5 
 kernel/drivers/block/zram/zram_drv.h                                                |    1 
 kernel/drivers/phy/rockchip/phy-rockchip-csi2-dphy-common.h                         |   15 
 kernel/kernel/time/hrtimer.c                                                        |   30 
 kernel/drivers/media/platform/rockchip/hdmirx/rk_hdmirx.c                           |   64 
 kernel/drivers/rknpu/rknpu_job.c                                                    |  221 
 kernel/drivers/media/i2c/sc210iot.c                                                 |    3 
 kernel/drivers/block/zram/zram_drv.c                                                |   36 
 kernel/drivers/media/platform/rockchip/isp/capture_v32.c                            |  141 
 kernel/include/linux/local_lock_internal.h                                          |  111 
 kernel/arch/arm/mm/cache-feroceon-l2.c                                              |    6 
 kernel/include/linux/ww_mutex.h                                                     |    8 
 kernel/net/sched/sch_api.c                                                          |    2 
 kernel/drivers/video/rockchip/rga3/rga_debugger.c                                   |   17 
 kernel/drivers/gpu/drm/rockchip/rockchip_rgb.c                                      |   98 
 kernel/include/linux/vtime.h                                                        |   42 
 kernel/include/soc/rockchip/rockchip_iommu.h                                        |    2 
 kernel/arch/arm/boot/dts/rv1103g-battery-ipc-v10.dts                                |    5 
 /dev/null                                                                           |  317 
 kernel/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c                          |   18 
 kernel/fs/fscache/main.c                                                            |    6 
 kernel/drivers/media/i2c/nvp6158_drv/nvp6158_v4l2.c                                 |   12 
 kernel/drivers/media/platform/rockchip/cif/cif-tools.c                              |  102 
 kernel/drivers/mfd/display-serdes/maxim/maxim-max96745.h                            |  139 
 kernel/arch/x86/crypto/glue_helper.c                                                |   26 
 kernel/mm/shmem.c                                                                   |   31 
 kernel/mm/zswap.c                                                                   |   43 
 kernel/drivers/cpufreq/cpufreq-dt-platdev.c                                         |    1 
 kernel/drivers/gpu/drm/ttm/ttm_bo_util.c                                            |   20 
 kernel/drivers/mfd/display-serdes/maxim/maxim-max96745.c                            |  781 
 932 files changed, 83,737 insertions(+), 14,479 deletions(-)

diff --git a/kernel/Documentation/RCU/Design/Expedited-Grace-Periods/Expedited-Grace-Periods.rst b/kernel/Documentation/RCU/Design/Expedited-Grace-Periods/Expedited-Grace-Periods.rst
index 6f89cf1..72f0f6f 100644
--- a/kernel/Documentation/RCU/Design/Expedited-Grace-Periods/Expedited-Grace-Periods.rst
+++ b/kernel/Documentation/RCU/Design/Expedited-Grace-Periods/Expedited-Grace-Periods.rst
@@ -38,7 +38,7 @@
 RCU-preempt Expedited Grace Periods
 ===================================
 
-``CONFIG_PREEMPTION=y`` kernels implement RCU-preempt.
+``CONFIG_PREEMPT=y`` kernels implement RCU-preempt.
 The overall flow of the handling of a given CPU by an RCU-preempt
 expedited grace period is shown in the following diagram:
 
@@ -112,7 +112,7 @@
 RCU-sched Expedited Grace Periods
 ---------------------------------
 
-``CONFIG_PREEMPTION=n`` kernels implement RCU-sched. The overall flow of
+``CONFIG_PREEMPT=n`` kernels implement RCU-sched. The overall flow of
 the handling of a given CPU by an RCU-sched expedited grace period is
 shown in the following diagram:
 
diff --git a/kernel/Documentation/RCU/Design/Requirements/Requirements.rst b/kernel/Documentation/RCU/Design/Requirements/Requirements.rst
index 17d3848..1ae79a1 100644
--- a/kernel/Documentation/RCU/Design/Requirements/Requirements.rst
+++ b/kernel/Documentation/RCU/Design/Requirements/Requirements.rst
@@ -78,7 +78,7 @@
 Production-quality implementations of ``rcu_read_lock()`` and
 ``rcu_read_unlock()`` are extremely lightweight, and in fact have
 exactly zero overhead in Linux kernels built for production use with
-``CONFIG_PREEMPTION=n``.
+``CONFIG_PREEMPT=n``.
 
 This guarantee allows ordering to be enforced with extremely low
 overhead to readers, for example:
@@ -1182,7 +1182,7 @@
 costs have plummeted. However, as I learned from Matt Mackall's
 `bloatwatch <http://elinux.org/Linux_Tiny-FAQ>`__ efforts, memory
 footprint is critically important on single-CPU systems with
-non-preemptible (``CONFIG_PREEMPTION=n``) kernels, and thus `tiny
+non-preemptible (``CONFIG_PREEMPT=n``) kernels, and thus `tiny
 RCU <https://lkml.kernel.org/g/20090113221724.GA15307@linux.vnet.ibm.com>`__
 was born. Josh Triplett has since taken over the small-memory banner
 with his `Linux kernel tinification <https://tiny.wiki.kernel.org/>`__
@@ -1498,7 +1498,7 @@
 
 Implementations of RCU for which ``rcu_read_lock()`` and
 ``rcu_read_unlock()`` generate no code, such as Linux-kernel RCU when
-``CONFIG_PREEMPTION=n``, can be nested arbitrarily deeply. After all, there
+``CONFIG_PREEMPT=n``, can be nested arbitrarily deeply. After all, there
 is no overhead. Except that if all these instances of
 ``rcu_read_lock()`` and ``rcu_read_unlock()`` are visible to the
 compiler, compilation will eventually fail due to exhausting memory,
@@ -1771,7 +1771,7 @@
 
 However, once the scheduler has spawned its first kthread, this early
 boot trick fails for ``synchronize_rcu()`` (as well as for
-``synchronize_rcu_expedited()``) in ``CONFIG_PREEMPTION=y`` kernels. The
+``synchronize_rcu_expedited()``) in ``CONFIG_PREEMPT=y`` kernels. The
 reason is that an RCU read-side critical section might be preempted,
 which means that a subsequent ``synchronize_rcu()`` really does have to
 wait for something, as opposed to simply returning immediately.
@@ -2010,7 +2010,7 @@
        5 rcu_read_unlock();
        6 do_something_with(v, user_v);
 
-If the compiler did make this transformation in a ``CONFIG_PREEMPTION=n`` kernel
+If the compiler did make this transformation in a ``CONFIG_PREEMPT=n`` kernel
 build, and if ``get_user()`` did page fault, the result would be a quiescent
 state in the middle of an RCU read-side critical section.  This misplaced
 quiescent state could result in line 4 being a use-after-free access,
@@ -2289,10 +2289,10 @@
 
 The Linux kernel is used for real-time workloads, especially in
 conjunction with the `-rt
-patchset <https://wiki.linuxfoundation.org/realtime/>`__. The
+patchset <https://rt.wiki.kernel.org/index.php/Main_Page>`__. The
 real-time-latency response requirements are such that the traditional
 approach of disabling preemption across RCU read-side critical sections
-is inappropriate. Kernels built with ``CONFIG_PREEMPTION=y`` therefore use
+is inappropriate. Kernels built with ``CONFIG_PREEMPT=y`` therefore use
 an RCU implementation that allows RCU read-side critical sections to be
 preempted. This requirement made its presence known after users made it
 clear that an earlier `real-time
@@ -2414,7 +2414,7 @@
 ``call_rcu_bh()``, ``rcu_barrier_bh()``, and
 ``rcu_read_lock_bh_held()``. However, the update-side APIs are now
 simple wrappers for other RCU flavors, namely RCU-sched in
-CONFIG_PREEMPTION=n kernels and RCU-preempt otherwise.
+CONFIG_PREEMPT=n kernels and RCU-preempt otherwise.
 
 Sched Flavor (Historical)
 ~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -2432,11 +2432,11 @@
 RCU read-side critical section can be a quiescent state. Therefore,
 *RCU-sched* was created, which follows “classic” RCU in that an
 RCU-sched grace period waits for pre-existing interrupt and NMI
-handlers. In kernels built with ``CONFIG_PREEMPTION=n``, the RCU and
+handlers. In kernels built with ``CONFIG_PREEMPT=n``, the RCU and
 RCU-sched APIs have identical implementations, while kernels built with
-``CONFIG_PREEMPTION=y`` provide a separate implementation for each.
+``CONFIG_PREEMPT=y`` provide a separate implementation for each.
 
-Note well that in ``CONFIG_PREEMPTION=y`` kernels,
+Note well that in ``CONFIG_PREEMPT=y`` kernels,
 ``rcu_read_lock_sched()`` and ``rcu_read_unlock_sched()`` disable and
 re-enable preemption, respectively. This means that if there was a
 preemption attempt during the RCU-sched read-side critical section,
@@ -2599,10 +2599,10 @@
 
 The tasks-RCU API is quite compact, consisting only of
 ``call_rcu_tasks()``, ``synchronize_rcu_tasks()``, and
-``rcu_barrier_tasks()``. In ``CONFIG_PREEMPTION=n`` kernels, trampolines
+``rcu_barrier_tasks()``. In ``CONFIG_PREEMPT=n`` kernels, trampolines
 cannot be preempted, so these APIs map to ``call_rcu()``,
 ``synchronize_rcu()``, and ``rcu_barrier()``, respectively. In
-``CONFIG_PREEMPTION=y`` kernels, trampolines can be preempted, and these
+``CONFIG_PREEMPT=y`` kernels, trampolines can be preempted, and these
 three APIs are therefore implemented by separate functions that check
 for voluntary context switches.
 
diff --git a/kernel/Documentation/RCU/checklist.rst b/kernel/Documentation/RCU/checklist.rst
index 7ed4956..2efed99 100644
--- a/kernel/Documentation/RCU/checklist.rst
+++ b/kernel/Documentation/RCU/checklist.rst
@@ -214,7 +214,7 @@
 	the rest of the system.
 
 7.	As of v4.20, a given kernel implements only one RCU flavor,
-	which is RCU-sched for PREEMPTION=n and RCU-preempt for PREEMPTION=y.
+	which is RCU-sched for PREEMPT=n and RCU-preempt for PREEMPT=y.
 	If the updater uses call_rcu() or synchronize_rcu(),
 	then the corresponding readers my use rcu_read_lock() and
 	rcu_read_unlock(), rcu_read_lock_bh() and rcu_read_unlock_bh(),
diff --git a/kernel/Documentation/RCU/rcubarrier.rst b/kernel/Documentation/RCU/rcubarrier.rst
index 3b4a248..f64f441 100644
--- a/kernel/Documentation/RCU/rcubarrier.rst
+++ b/kernel/Documentation/RCU/rcubarrier.rst
@@ -9,7 +9,7 @@
 of as a replacement for read-writer locking (among other things), but with
 very low-overhead readers that are immune to deadlock, priority inversion,
 and unbounded latency. RCU read-side critical sections are delimited
-by rcu_read_lock() and rcu_read_unlock(), which, in non-CONFIG_PREEMPTION
+by rcu_read_lock() and rcu_read_unlock(), which, in non-CONFIG_PREEMPT
 kernels, generate no code whatsoever.
 
 This means that RCU writers are unaware of the presence of concurrent
@@ -329,10 +329,10 @@
 	to smp_call_function() and further to smp_call_function_on_cpu(),
 	causing this latter to spin until the cross-CPU invocation of
 	rcu_barrier_func() has completed. This by itself would prevent
-	a grace period from completing on non-CONFIG_PREEMPTION kernels,
+	a grace period from completing on non-CONFIG_PREEMPT kernels,
 	since each CPU must undergo a context switch (or other quiescent
 	state) before the grace period can complete. However, this is
-	of no use in CONFIG_PREEMPTION kernels.
+	of no use in CONFIG_PREEMPT kernels.
 
 	Therefore, on_each_cpu() disables preemption across its call
 	to smp_call_function() and also across the local call to
diff --git a/kernel/Documentation/RCU/stallwarn.rst b/kernel/Documentation/RCU/stallwarn.rst
index e97d1b4..c9ab6af 100644
--- a/kernel/Documentation/RCU/stallwarn.rst
+++ b/kernel/Documentation/RCU/stallwarn.rst
@@ -25,7 +25,7 @@
 
 -	A CPU looping with bottom halves disabled.
 
--	For !CONFIG_PREEMPTION kernels, a CPU looping anywhere in the kernel
+-	For !CONFIG_PREEMPT kernels, a CPU looping anywhere in the kernel
 	without invoking schedule().  If the looping in the kernel is
 	really expected and desirable behavior, you might need to add
 	some calls to cond_resched().
@@ -44,7 +44,7 @@
 	result in the ``rcu_.*kthread starved for`` console-log message,
 	which will include additional debugging information.
 
--	A CPU-bound real-time task in a CONFIG_PREEMPTION kernel, which might
+-	A CPU-bound real-time task in a CONFIG_PREEMPT kernel, which might
 	happen to preempt a low-priority task in the middle of an RCU
 	read-side critical section.   This is especially damaging if
 	that low-priority task is not permitted to run on any other CPU,
diff --git a/kernel/Documentation/RCU/whatisRCU.rst b/kernel/Documentation/RCU/whatisRCU.rst
index 3b2b147..fb3ff76 100644
--- a/kernel/Documentation/RCU/whatisRCU.rst
+++ b/kernel/Documentation/RCU/whatisRCU.rst
@@ -684,7 +684,7 @@
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 This section presents a "toy" RCU implementation that is based on
 "classic RCU".  It is also short on performance (but only for updates) and
-on features such as hotplug CPU and the ability to run in CONFIG_PREEMPTION
+on features such as hotplug CPU and the ability to run in CONFIG_PREEMPT
 kernels.  The definitions of rcu_dereference() and rcu_assign_pointer()
 are the same as those shown in the preceding section, so they are omitted.
 ::
@@ -740,7 +740,7 @@
 Quick Quiz #3:
 		If it is illegal to block in an RCU read-side
 		critical section, what the heck do you do in
-		CONFIG_PREEMPT_RT, where normal spinlocks can block???
+		PREEMPT_RT, where normal spinlocks can block???
 
 :ref:`Answers to Quick Quiz <8_whatisRCU>`
 
@@ -1094,7 +1094,7 @@
 		overhead is **negative**.
 
 Answer:
-		Imagine a single-CPU system with a non-CONFIG_PREEMPTION
+		Imagine a single-CPU system with a non-CONFIG_PREEMPT
 		kernel where a routing table is used by process-context
 		code, but can be updated by irq-context code (for example,
 		by an "ICMP REDIRECT" packet).	The usual way of handling
@@ -1121,10 +1121,10 @@
 Quick Quiz #3:
 		If it is illegal to block in an RCU read-side
 		critical section, what the heck do you do in
-		CONFIG_PREEMPT_RT, where normal spinlocks can block???
+		PREEMPT_RT, where normal spinlocks can block???
 
 Answer:
-		Just as CONFIG_PREEMPT_RT permits preemption of spinlock
+		Just as PREEMPT_RT permits preemption of spinlock
 		critical sections, it permits preemption of RCU
 		read-side critical sections.  It also permits
 		spinlocks blocking while in RCU read-side critical
diff --git a/kernel/Documentation/admin-guide/kernel-parameters.txt b/kernel/Documentation/admin-guide/kernel-parameters.txt
index c6a21cf..c841b52 100644
--- a/kernel/Documentation/admin-guide/kernel-parameters.txt
+++ b/kernel/Documentation/admin-guide/kernel-parameters.txt
@@ -4241,10 +4241,6 @@
 			value, meaning that RCU_SOFTIRQ is used by default.
 			Specify rcutree.use_softirq=0 to use rcuc kthreads.
 
-			But note that CONFIG_PREEMPT_RT=y kernels disable
-			this kernel boot parameter, forcibly setting it
-			to zero.
-
 	rcutree.rcu_fanout_exact= [KNL]
 			Disable autobalancing of the rcu_node combining
 			tree.  This is used by rcutorture, and might
@@ -4622,13 +4618,6 @@
 			rcu_end_inkernel_boot() has been invoked), use
 			only normal grace-period primitives.  No effect
 			on CONFIG_TINY_RCU kernels.
-
-			But note that CONFIG_PREEMPT_RT=y kernels enables
-			this kernel boot parameter, forcibly setting
-			it to the value one, that is, converting any
-			post-boot attempt at an expedited RCU grace
-			period to instead use normal non-expedited
-			grace-period processing.
 
 	rcupdate.rcu_task_ipi_delay= [KNL]
 			Set time in jiffies during which RCU tasks will
diff --git a/kernel/Documentation/devicetree/bindings/gpio/novo,nca9539-gpio.yaml b/kernel/Documentation/devicetree/bindings/gpio/novo,nca9539-gpio.yaml
new file mode 100644
index 0000000..316c5df
--- /dev/null
+++ b/kernel/Documentation/devicetree/bindings/gpio/novo,nca9539-gpio.yaml
@@ -0,0 +1,80 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/gpio/novo,nca9539-gpio.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Novosense I2C GPIO controller
+
+maintainers:
+  - Cody Xie <cody.xie@rock-chips.com>
+
+description: |
+  This controller is A GPIO expander with I2C interface and one interrupt pin.
+
+properties:
+  compatible:
+    const: novo,nca9539-gpio
+
+  reg:
+    items:
+      - description: the I2C address containing the GPIO controller registers.
+
+  gpio-controller: true
+
+  '#gpio-cells':
+    const: 2
+
+  ngpios:
+    minimum: 0
+    maximum: 32
+
+  interrupt-controller: true
+
+  '#interrupt-cells':
+    const: 2
+
+  interrupts:
+    maxItems: 1
+
+  vdd-supply:
+    - description: the regulator for the VDD supplier.
+
+required:
+  - compatible
+  - reg
+  - "#gpio-cells"
+  - gpio-controller
+  - vdd-supply
+
+additionalProperties: false
+
+dependencies:
+  interrupt-controller: [ interrupts ]
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/irq.h>
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+    / {
+	  nca9539_vdd: nca9539-vdd {
+		compatible = "regulator-fixed";
+		regulator-name = "nca9539_vdd";
+		enable-active-high;
+		regulator-boot-on;
+		regulator-always-on;
+	  };
+	};
+
+    nca9539_gpio: gpio@74 {
+		compatible = "novo,nca9539-gpio";
+        reg = <0x74>;
+        gpio-controller;
+        #gpio-cells = <2>;
+		interrupt-controller;
+		#interrupt-cells = <2>;
+		vdd-supply = <&nca9539_vdd>;
+    };
+
+
+...
diff --git a/kernel/Documentation/devicetree/bindings/mfd/rkx110_x120.yaml b/kernel/Documentation/devicetree/bindings/mfd/rkx110_x120.yaml
new file mode 100644
index 0000000..8f00d4c
--- /dev/null
+++ b/kernel/Documentation/devicetree/bindings/mfd/rkx110_x120.yaml
@@ -0,0 +1,564 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/rockchip/rockchip-vop.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Rockchip SerDes MFD driver
+
+description:
+  Rockchip SerDes MFD driver is a pair chip for long distance transmitting
+  display image.
+
+maintainers:
+  - Zhang Yubing <yubing.zhang@rock-chips.com>
+
+properties:
+  compatible:
+    enum:
+      - "rockchip,rkx110", "rockchip,rkx120"
+
+  interrupts:
+    maxItems: 1
+    description:
+      The Serdes interrupt is shared by RKX110/RKX120.
+
+  clocks:
+    items:
+      - description:
+
+  clock-names:
+    items:
+      - const:
+
+  resets:
+    maxItems: 1
+
+  reset-names:
+    items:
+      - const:
+
+  route:
+    type: object
+    description:
+      A route node descriptor serdes topology
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - clocks
+  - clock-names
+  - resets
+  - reset-names
+  - route
+
+additionalProperties: false
+
+# example0:
+# 1 video source input, 1 channel, 1 lane, 1 remote, 1 video output:
+# disp in can select follow interface:
+# dsi0_rx, dsi1_rx, lvds0_rx, lvds1_rx, dual-lvds_rx, rgb_rx
+# disp out can select follow interface:
+# dsi_tx, lvds0_tx, lvds1_tx, dual-lvds_tx, rgb_tx
++-------+          +---------+            +---------+         +--------+
+|       | disp in  |         |   cable0   |         | disp out|        |
+|  soc  |--------->|  RK110  +----------->|  RK120  +-------->| screen |
+|       |          |         |            |         |         |        |
++-------+          +---------+            +---------+         +--------+
+
+examples0:
+  - |
+     #include <dt-bindings/mfd/rockchip-serdes.h>
+    &i2c1 {
+      status = "okay";
+
+      rkx110_x120: rkx110_x120@55 {
+        compatible = "rockchip,rkx110";
+        enable-gpios = <&gpio0 RK_PD3 GPIO_ACTIVE_HIGH>;
+        reset-gpios = <&gpio0 RK_PB7 GPIO_ACTIVE_LOW>;
+        status = "okay";
+
+        remote0-addr = <0x35>;
+
+        panel {
+          compatible = "rockchip,serdes_panel";
+          status = "okay";
+
+          local-port0 = <RK_SERDES_LVDS_RX0>;
+          remote0-port0 = <RK_SERDES_LVDS_TX0>;
+
+          backlight0 = <&backlight>;
+          power0-supply = <&vcc3v3_lcd_n>;
+          reset0-gpios = <&gpio0 RK_PC4 GPIO_ACTIVE_LOW>;
+          reset0-delay-ms = <20>;
+          enable0-delay-ms = <20>;
+          prepare0-delay-ms = <20>;
+          unprepare0-delay-ms = <20>;
+          disable0-delay-ms = <20>;
+          bus-format0 = <MEDIA_BUS_FMT_RGB888_1X7X4_SPWG>;
+
+          display-timings {
+            native-mode = <&timing0>;
+
+            timing0: timing0 {
+              clock-frequency = <27000000>;
+              hactive = <1024>;
+              vactive = <600>;
+              hback-porch = <160>;
+              hfront-porch = <160>;
+              vback-porch = <20>;
+              vfront-porch = <15>;
+              hsync-len = <6>;
+              vsync-len = <5>;
+              hsync-active = <0>;
+              vsync-active = <0>;
+              de-active = <0>;
+              pixelclk-active = <0>;
+            };
+          };
+        };
+      };
+    };
+
+# example1:
+# 1 video source input, 2 channel, 1 lane, 1 remote, 1 video output:
+# disp in can select follow interface:
+# dsi0_rx, dsi1_rx, lvds0_rx, lvds1_rx, dual-lvds_rx, rgb_rx
+# disp out can select follow interface:
+# dsi_tx, lvds0_tx, lvds1_tx, dual-lvds_tx, rgb_tx
++-------+          +---------+   cable0   +---------+         +--------+
+|       | disp in  |         +----------->|         | disp out|        |
+|  soc  |--------->|  RK110  |   cable1   |  RK120  +-------->| screen |
+|       |          |         +----------->|         |         |        |
++-------+          +---------+            +---------+         +--------+
+
+examples1:
+  - |
+     #include <dt-bindings/mfd/rockchip-serdes.h>
+    &i2c1 {
+      status = "okay";
+
+      rkx110_x120: rkx110_x120@55 {
+        compatible = "rockchip,rkx110";
+        enable-gpios = <&gpio0 RK_PD3 GPIO_ACTIVE_HIGH>;
+        reset-gpios = <&gpio0 RK_PB7 GPIO_ACTIVE_LOW>;
+        status = "okay";
+
+        remote0-addr = <0x35>;
+
+        panel {
+          compatible = "rockchip,serdes_panel";
+          status = "okay";
+
+          local-port0 = <RK_SERDES_LVDS_RX0>;
+          remote0-port0 = <RK_SERDES_LVDS_TX0>;
+          num-lanes = <2>;
+
+          backlight0 = <&backlight>;
+          power0-supply = <&vcc3v3_lcd_n>;
+          reset0-gpios = <&gpio0 RK_PC4 GPIO_ACTIVE_LOW>;
+          reset0-delay-ms = <20>;
+          enable0-delay-ms = <20>;
+          prepare0-delay-ms = <20>;
+          unprepare0-delay-ms = <20>;
+          disable0-delay-ms = <20>;
+          bus-format0 = <MEDIA_BUS_FMT_RGB888_1X7X4_SPWG>;
+
+          display-timings {
+            native-mode = <&timing0>;
+
+            timing0: timing0 {
+              clock-frequency = <27000000>;
+              hactive = <1024>;
+              vactive = <600>;
+              hback-porch = <160>;
+              hfront-porch = <160>;
+              vback-porch = <20>;
+              vfront-porch = <15>;
+              hsync-len = <6>;
+              vsync-len = <5>;
+              hsync-active = <0>;
+              vsync-active = <0>;
+              de-active = <0>;
+              pixelclk-active = <0>;
+            };
+          };
+        };
+      };
+    };
+
+# example2:
+# 1 video source input, 2 channel, 2 lane, 2 remote, 2 video output:
+# disp in can select follow interface:
+# dsi0_rx, dsi1_rx, lvds0_rx, lvds1_rx, dual-lvds_rx, rgb_rx
+# disp out can select follow interface:
+# dsi_tx, lvds0_tx, lvds1_tx, dual-lvds_tx, rgb_tx
+                                          +---------+         +--------+
+                                  cable0  |         |disp0 out|        |
+                                 +------->|  RK120  +-------->| screen |
+                                 |        |         |         |        |
++-------+          +---------+   |        +---------+         +--------+
+|       | disp in  |         +---+
+|  soc  |--------->|  RK110  |
+|       |          |         +---+
++-------+          +---------+   |        +---------+         +--------+
+                                 |cable1  |         |disp1 out|        |
+                                 +------->|  RK120  +-------->| screen |
+                                          |         |         |        |
+                                          +---------+         +--------+
+
+examples2:
+  - |
+     #include <dt-bindings/mfd/rockchip-serdes.h>
+    &i2c1 {
+      status = "okay";
+
+      rkx110_x120: rkx110_x120@55 {
+        compatible = "rockchip,rkx110";
+        enable-gpios = <&gpio0 RK_PD3 GPIO_ACTIVE_HIGH>;
+        reset-gpios = <&gpio0 RK_PB7 GPIO_ACTIVE_LOW>;
+        status = "okay";
+
+        remote0-addr = <0x35>;
+        remote1-addr = <0x36>;
+
+        panel {
+          compatible = "rockchip,serdes_panel";
+          status = "okay";
+
+          local-port0 = <RK_SERDES_LVDS_RX0>;
+          remote0-port0 = <RK_SERDES_LVDS_TX0>;
+          remote1-port0 = <RK_SERDES_LVDS_TX0>;
+
+          backlight0 = <&backlight>;
+          power0-supply = <&vcc3v3_lcd_n>;
+          reset0-gpios = <&gpio0 RK_PC4 GPIO_ACTIVE_LOW>;
+          reset0-delay-ms = <20>;
+          enable0-delay-ms = <20>;
+          prepare0-delay-ms = <20>;
+          unprepare0-delay-ms = <20>;
+          disable0-delay-ms = <20>;
+          bus-format0 = <MEDIA_BUS_FMT_RGB888_1X7X4_SPWG>;
+
+          display-timings {
+            native-mode = <&timing0>;
+
+            timing0: timing0 {
+              clock-frequency = <27000000>;
+              hactive = <1024>;
+              vactive = <600>;
+              hback-porch = <160>;
+              hfront-porch = <160>;
+              vback-porch = <20>;
+              vfront-porch = <15>;
+              hsync-len = <6>;
+              vsync-len = <5>;
+              hsync-active = <0>;
+              vsync-active = <0>;
+              de-active = <0>;
+              pixelclk-active = <0>;
+            };
+          };
+        };
+      };
+    };
+
+# example3:
+# 1 video source input, 2 channel, 1 lane, 1 remote, 2 video output:
+# disp in can select follow interface:
+# dsi0_rx, dsi1_rx, lvds0_rx, lvds1_rx, dual-lvds_rx, rgb_rx
+# disp out can select follow interface:
+# lvds0_tx and lvds1_tx
+                                                              +--------+
+                                                     lvds0_tx |        |
+                                                         +--->| screen |
++-------+          +---------+            +---------+    |    |        |
+|       | disp in  |         |   cable0   |         |----+    +--------+
+|  soc  |--------->|  RK110  +----------->|  RK120  |
+|       |          |         |            |         |----+    +--------+
++-------+          +---------+            +---------+    |    |        |
+                                                         +--->| screen |
+                                                     lvds1_tx |        |
+                                                              +--------+
+
+examples3:
+  - |
+     #include <dt-bindings/mfd/rockchip-serdes.h>
+    &i2c1 {
+      status = "okay";
+
+      rkx110_x120: rkx110_x120@55 {
+        compatible = "rockchip,rkx110";
+        enable-gpios = <&gpio0 RK_PD3 GPIO_ACTIVE_HIGH>;
+        reset-gpios = <&gpio0 RK_PB7 GPIO_ACTIVE_LOW>;
+        status = "okay";
+
+        remote0-addr = <0x35>;
+
+        panel {
+          compatible = "rockchip,serdes_panel";
+          status = "okay";
+
+          local-port0 = <RK_SERDES_LVDS_RX0>;
+          remote0-port0 = <RK_SERDES_LVDS_TX0>;
+          remote0-port1 = <RK_SERDES_LVDS_TX1>;
+
+          backlight0 = <&backlight>;
+          power0-supply = <&vcc3v3_lcd_n>;
+          reset0-gpios = <&gpio0 RK_PC4 GPIO_ACTIVE_LOW>;
+          reset0-delay-ms = <20>;
+          enable0-delay-ms = <20>;
+          prepare0-delay-ms = <20>;
+          unprepare0-delay-ms = <20>;
+          disable0-delay-ms = <20>;
+          bus-format0 = <MEDIA_BUS_FMT_RGB888_1X7X4_SPWG>;
+
+          display-timings {
+            native-mode = <&timing0>;
+
+            timing0: timing0 {
+              clock-frequency = <27000000>;
+              hactive = <1024>;
+              vactive = <600>;
+              hback-porch = <160>;
+              hfront-porch = <160>;
+              vback-porch = <20>;
+              vfront-porch = <15>;
+              hsync-len = <6>;
+              vsync-len = <5>;
+              hsync-active = <0>;
+              vsync-active = <0>;
+              de-active = <0>;
+              pixelclk-active = <0>;
+            };
+          };
+        };
+      };
+    };
+
+# example4:
+# 2 video source input, 2 channel, 1 lane, 1 remote, 2 video output:
+# disp in can select follow interface:
+# dsi0_rx and dsi1_rx, or lvds0_rx and lvds1_rx
+# disp out can select follow interface:
+# lvds0_tx and lvds1_tx
+                                                              +--------+
+                                                     lvds0_tx |        |
+                                                         +--->| screen |
++-------+ disp0_rx +---------+            +---------+    |    |        |
+|       |--------->|         |   cable0   |         |----+    +--------+
+|  soc  | disp1_rx |  RK110  +----------->|  RK120  |
+|       |--------->|         |            |         |----+    +--------+
++-------+          +---------+            +---------+    |    |        |
+                                                         +--->| screen |
+                                                     lvds1_tx |        |
+                                                              +--------+
+
+examples4:
+  - |
+     #include <dt-bindings/mfd/rockchip-serdes.h>
+    &i2c1 {
+      status = "okay";
+
+      rkx110_x120: rkx110_x120@55 {
+        compatible = "rockchip,rkx110";
+        enable-gpios = <&gpio0 RK_PD3 GPIO_ACTIVE_HIGH>;
+        reset-gpios = <&gpio0 RK_PB7 GPIO_ACTIVE_LOW>;
+        status = "okay";
+
+        remote0-addr = <0x35>;
+
+        panel {
+          compatible = "rockchip,serdes_panel";
+          status = "okay";
+
+          local-port0 = <RK_SERDES_LVDS_RX0>;
+          local-port1 = <RK_SERDES_LVDS_RX1>;
+          remote0-port0 = <RK_SERDES_LVDS_TX0>;
+          remote0-port1 = <RK_SERDES_LVDS_TX1>;
+
+          backlight0 = <&backlight>;
+          power0-supply = <&vcc3v3_lcd_n>;
+          reset0-gpios = <&gpio0 RK_PC4 GPIO_ACTIVE_LOW>;
+          reset0-delay-ms = <20>;
+          enable0-delay-ms = <20>;
+          prepare0-delay-ms = <20>;
+          unprepare0-delay-ms = <20>;
+          disable0-delay-ms = <20>;
+          bus-format0 = <MEDIA_BUS_FMT_RGB888_1X7X4_SPWG>;
+
+          display-timings {
+            native-mode = <&timing0>;
+
+            timing0: timing0 {
+              clock-frequency = <27000000>;
+              hactive = <1024>;
+              vactive = <600>;
+              hback-porch = <160>;
+              hfront-porch = <160>;
+              vback-porch = <20>;
+              vfront-porch = <15>;
+              hsync-len = <6>;
+              vsync-len = <5>;
+              hsync-active = <0>;
+              vsync-active = <0>;
+              de-active = <0>;
+              pixelclk-active = <0>;
+            };
+          };
+        };
+      };
+    };
+
+# example5:
+# 2 video source input, 2 channel, 2 lane, 1 remote, 2 video output:
+# disp in can select follow interface:
+# dsi0_rx and dsi1_rx, or lvds0_rx and lvds1_rx
+# disp out can select follow interface:
+# lvds0_tx and lvds1_tx
+                                                              +--------+
+                                                     lvds0_tx |        |
+                                                         +--->| screen |
++-------+ disp0_rx +---------+   cable0   +---------+    |    |        |
+|       |--------->|         +----------->|         |----+    +--------+
+|  soc  | disp1_rx |  RK110  |   cable1   |  RK120  |
+|       |--------->|         +----------->|         |----+    +--------+
++-------+          +---------+            +---------+    |    |        |
+                                                         +--->| screen |
+                                                     lvds1_tx |        |
+                                                              +--------+
+
+examples5:
+  - |
+     #include <dt-bindings/mfd/rockchip-serdes.h>
+    &i2c1 {
+      status = "okay";
+
+      rkx110_x120: rkx110_x120@55 {
+        compatible = "rockchip,rkx110";
+        enable-gpios = <&gpio0 RK_PD3 GPIO_ACTIVE_HIGH>;
+        reset-gpios = <&gpio0 RK_PB7 GPIO_ACTIVE_LOW>;
+        status = "okay";
+
+        remote0-addr = <0x35>;
+
+        panel {
+          compatible = "rockchip,serdes_panel";
+          status = "okay";
+
+          local-port0 = <RK_SERDES_LVDS_RX0>;
+          local-port1 = <RK_SERDES_LVDS_RX1>;
+          remote0-port0 = <RK_SERDES_LVDS_TX0>;
+          remote0-port1 = <RK_SERDES_LVDS_TX1>;
+          num-lanes = <2>;
+
+          backlight0 = <&backlight>;
+          power0-supply = <&vcc3v3_lcd_n>;
+          reset0-gpios = <&gpio0 RK_PC4 GPIO_ACTIVE_LOW>;
+          reset0-delay-ms = <20>;
+          enable0-delay-ms = <20>;
+          prepare0-delay-ms = <20>;
+          unprepare0-delay-ms = <20>;
+          disable0-delay-ms = <20>;
+          bus-format0 = <MEDIA_BUS_FMT_RGB888_1X7X4_SPWG>;
+
+          display-timings {
+            native-mode = <&timing0>;
+
+            timing0: timing0 {
+              clock-frequency = <27000000>;
+              hactive = <1024>;
+              vactive = <600>;
+              hback-porch = <160>;
+              hfront-porch = <160>;
+              vback-porch = <20>;
+              vfront-porch = <15>;
+              hsync-len = <6>;
+              vsync-len = <5>;
+              hsync-active = <0>;
+              vsync-active = <0>;
+              de-active = <0>;
+              pixelclk-active = <0>;
+            };
+          };
+        };
+      };
+    };
+
+# example6:
+# 2 video source input, 2 channel, 2 lane, 2 remote, 2 video output:
+# disp in can select follow interface:
+# dsi0_rx and dsi1_rx, or lvds0_rx and lvds1_rx
+# disp out can select follow interface:
+# dsi_tx, lvds0_tx, lvds1_tx, dual-lvds_tx, rgb_tx
+                                          +---------+         +--------+
+                                  cable0  |         |disp0 out|        |
+                                 +------->|  RK120  +-------->| screen |
+                                 |        |         |         |        |
++-------+ disp0_rx +---------+   |        +---------+         +--------+
+|       |--------->|         +---+
+|  soc  | disp1_rx |  RK110  |
+|       |--------->|         +---+
++-------+          +---------+   |        +---------+         +--------+
+                                 |cable1  |         |disp1 out|        |
+                                 +------->|  RK120  +-------->| screen |
+                                          |         |         |        |
+                                          +---------+         +--------+
+
+examples6:
+  - |
+     #include <dt-bindings/mfd/rockchip-serdes.h>
+    &i2c1 {
+      status = "okay";
+
+      rkx110_x120: rkx110_x120@55 {
+        compatible = "rockchip,rkx110";
+        enable-gpios = <&gpio0 RK_PD3 GPIO_ACTIVE_HIGH>;
+        reset-gpios = <&gpio0 RK_PB7 GPIO_ACTIVE_LOW>;
+        status = "okay";
+
+        remote0-addr = <0x35>;
+        remote1-addr = <0x36>;
+
+        panel {
+          compatible = "rockchip,serdes_panel";
+          status = "okay";
+
+          local-port0 = <RK_SERDES_LVDS_RX0>;
+          local-port1 = <RK_SERDES_LVDS_RX1>;
+          remote0-port0 = <RK_SERDES_LVDS_TX0>;
+          remote1-port0 = <RK_SERDES_LVDS_TX0>;
+
+          backlight0 = <&backlight>;
+          power0-supply = <&vcc3v3_lcd_n>;
+          reset0-gpios = <&gpio0 RK_PC4 GPIO_ACTIVE_LOW>;
+          reset0-delay-ms = <20>;
+          enable0-delay-ms = <20>;
+          prepare0-delay-ms = <20>;
+          unprepare0-delay-ms = <20>;
+          disable0-delay-ms = <20>;
+          bus-format0 = <MEDIA_BUS_FMT_RGB888_1X7X4_SPWG>;
+
+          display-timings {
+            native-mode = <&timing0>;
+
+            timing0: timing0 {
+              clock-frequency = <27000000>;
+              hactive = <1024>;
+              vactive = <600>;
+              hback-porch = <160>;
+              hfront-porch = <160>;
+              vback-porch = <20>;
+              vfront-porch = <15>;
+              hsync-len = <6>;
+              vsync-len = <5>;
+              hsync-active = <0>;
+              vsync-active = <0>;
+              de-active = <0>;
+              pixelclk-active = <0>;
+            };
+          };
+        };
+      };
+    };
\ No newline at end of file
diff --git a/kernel/Documentation/devicetree/bindings/regulator/fan53555.txt b/kernel/Documentation/devicetree/bindings/regulator/fan53555.txt
index 3a4a64d..e7fc045 100644
--- a/kernel/Documentation/devicetree/bindings/regulator/fan53555.txt
+++ b/kernel/Documentation/devicetree/bindings/regulator/fan53555.txt
@@ -2,7 +2,7 @@
 
 Required properties:
   - compatible: one of "fcs,fan53555", "fcs,fan53526", "silergy,syr827" or
-		"silergy,syr828", "rockchip,rk8603", "rockchip,rk8604"
+		"silergy,syr828"
   - reg: I2C address
 
 Optional properties:
diff --git a/kernel/Documentation/devicetree/bindings/spi/spi-rockchip.yaml b/kernel/Documentation/devicetree/bindings/spi/spi-rockchip.yaml
index 358f2e3..d0fbfa3 100644
--- a/kernel/Documentation/devicetree/bindings/spi/spi-rockchip.yaml
+++ b/kernel/Documentation/devicetree/bindings/spi/spi-rockchip.yaml
@@ -94,6 +94,19 @@
     description: Add this property to set the transmission method as CPU polling.
     type: boolean
 
+  rockchip,cs-inactive-disable:
+    description: Add this property to disable the cs inactive interrupt for spi
+      slave.
+    type: boolean
+
+  ready-gpios:
+    description: GPIO spec for the spi slave ready signal.
+    maxItems: 1
+
+  rockchip,autosuspend-delay-ms:
+    default: 0
+    description: Set pm runtime autosuspend value in milliseconds.
+
 required:
   - compatible
   - reg
diff --git a/kernel/Documentation/devicetree/bindings/usb/snps,dwc3.yaml b/kernel/Documentation/devicetree/bindings/usb/snps,dwc3.yaml
index 3091a43..86c730a 100644
--- a/kernel/Documentation/devicetree/bindings/usb/snps,dwc3.yaml
+++ b/kernel/Documentation/devicetree/bindings/usb/snps,dwc3.yaml
@@ -190,6 +190,11 @@
       When set, all SuperSpeed bus instances in park mode are disabled.
     type: boolean
 
+  snps,parkmode-disable-hs-quirk:
+    description:
+      When set, all HighSpeed bus instances in park mode are disabled.
+    type: boolean
+
   snps,dis_metastability_quirk:
     description:
       When set, disable metastability workaround. CAUTION! Use only if you are
diff --git a/kernel/Documentation/driver-api/io-mapping.rst b/kernel/Documentation/driver-api/io-mapping.rst
index a7830c5..a966239 100644
--- a/kernel/Documentation/driver-api/io-mapping.rst
+++ b/kernel/Documentation/driver-api/io-mapping.rst
@@ -20,64 +20,78 @@
 mappable, while 'size' indicates how large a mapping region to
 enable. Both are in bytes.
 
-This _wc variant provides a mapping which may only be used with
-io_mapping_map_local_wc() or io_mapping_map_wc().
+This _wc variant provides a mapping which may only be used
+with the io_mapping_map_atomic_wc or io_mapping_map_wc.
 
-With this mapping object, individual pages can be mapped either temporarily
-or long term, depending on the requirements. Of course, temporary maps are
-more efficient.
+With this mapping object, individual pages can be mapped either atomically
+or not, depending on the necessary scheduling environment. Of course, atomic
+maps are more efficient::
 
-	void *io_mapping_map_local_wc(struct io_mapping *mapping,
-				      unsigned long offset)
+	void *io_mapping_map_atomic_wc(struct io_mapping *mapping,
+				       unsigned long offset)
 
-'offset' is the offset within the defined mapping region.  Accessing
-addresses beyond the region specified in the creation function yields
-undefined results. Using an offset which is not page aligned yields an
-undefined result. The return value points to a single page in CPU address
-space.
+'offset' is the offset within the defined mapping region.
+Accessing addresses beyond the region specified in the
+creation function yields undefined results. Using an offset
+which is not page aligned yields an undefined result. The
+return value points to a single page in CPU address space.
 
-This _wc variant returns a write-combining map to the page and may only be
-used with mappings created by io_mapping_create_wc()
+This _wc variant returns a write-combining map to the
+page and may only be used with mappings created by
+io_mapping_create_wc
 
-Temporary mappings are only valid in the context of the caller. The mapping
-is not guaranteed to be globaly visible.
+Note that the task may not sleep while holding this page
+mapped.
 
-io_mapping_map_local_wc() has a side effect on X86 32bit as it disables
-migration to make the mapping code work. No caller can rely on this side
-effect.
+::
 
-Nested mappings need to be undone in reverse order because the mapping
-code uses a stack for keeping track of them::
+	void io_mapping_unmap_atomic(void *vaddr)
 
- addr1 = io_mapping_map_local_wc(map1, offset1);
- addr2 = io_mapping_map_local_wc(map2, offset2);
- ...
- io_mapping_unmap_local(addr2);
- io_mapping_unmap_local(addr1);
+'vaddr' must be the value returned by the last
+io_mapping_map_atomic_wc call. This unmaps the specified
+page and allows the task to sleep once again.
 
-The mappings are released with::
+If you need to sleep while holding the lock, you can use the non-atomic
+variant, although they may be significantly slower.
 
-	void io_mapping_unmap_local(void *vaddr)
-
-'vaddr' must be the value returned by the last io_mapping_map_local_wc()
-call. This unmaps the specified mapping and undoes eventual side effects of
-the mapping function.
-
-If you need to sleep while holding a mapping, you can use the regular
-variant, although this may be significantly slower::
+::
 
 	void *io_mapping_map_wc(struct io_mapping *mapping,
 				unsigned long offset)
 
-This works like io_mapping_map_local_wc() except it has no side effects and
-the pointer is globaly visible.
+This works like io_mapping_map_atomic_wc except it allows
+the task to sleep while holding the page mapped.
 
-The mappings are released with::
+
+::
 
 	void io_mapping_unmap(void *vaddr)
 
-Use for pages mapped with io_mapping_map_wc().
+This works like io_mapping_unmap_atomic, except it is used
+for pages mapped with io_mapping_map_wc.
 
 At driver close time, the io_mapping object must be freed::
 
 	void io_mapping_free(struct io_mapping *mapping)
+
+Current Implementation
+======================
+
+The initial implementation of these functions uses existing mapping
+mechanisms and so provides only an abstraction layer and no new
+functionality.
+
+On 64-bit processors, io_mapping_create_wc calls ioremap_wc for the whole
+range, creating a permanent kernel-visible mapping to the resource. The
+map_atomic and map functions add the requested offset to the base of the
+virtual address returned by ioremap_wc.
+
+On 32-bit processors with HIGHMEM defined, io_mapping_map_atomic_wc uses
+kmap_atomic_pfn to map the specified page in an atomic fashion;
+kmap_atomic_pfn isn't really supposed to be used with device pages, but it
+provides an efficient mapping for this usage.
+
+On 32-bit processors without HIGHMEM defined, io_mapping_map_atomic_wc and
+io_mapping_map_wc both use ioremap_wc, a terribly inefficient function which
+performs an IPI to inform all processors about the new mapping. This results
+in a significant performance penalty.
diff --git a/kernel/Makefile b/kernel/Makefile
index 8eecea3..49d1d03 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -522,7 +522,6 @@
 		   -fno-strict-aliasing -fno-common -fshort-wchar -fno-PIE \
 		   -Werror=implicit-function-declaration -Werror=implicit-int \
 		   -Werror=return-type -Wno-format-security \
-		   -w \
 		   -std=gnu89
 KBUILD_CPPFLAGS := -D__KERNEL__
 KBUILD_AFLAGS_KERNEL :=
diff --git a/kernel/arch/Kconfig b/kernel/arch/Kconfig
index 3d427a6..c3a40c5 100644
--- a/kernel/arch/Kconfig
+++ b/kernel/arch/Kconfig
@@ -37,7 +37,6 @@
 	tristate "OProfile system profiling"
 	depends on PROFILING
 	depends on HAVE_OPROFILE
-	depends on !PREEMPT_RT
 	select RING_BUFFER
 	select RING_BUFFER_ALLOW_SWAP
 	help
@@ -757,12 +756,6 @@
 config HAVE_VIRT_CPU_ACCOUNTING
 	bool
 
-config HAVE_VIRT_CPU_ACCOUNTING_IDLE
-	bool
-	help
-	  Architecture has its own way to account idle CPU time and therefore
-	  doesn't implement vtime_account_idle().
-
 config ARCH_HAS_SCALED_CPUTIME
 	bool
 
@@ -777,6 +770,7 @@
 	  some 32-bit arches may require multiple accesses, so proper
 	  locking is needed to protect against concurrent accesses.
 
+
 config HAVE_IRQ_TIME_ACCOUNTING
 	bool
 	help
diff --git a/kernel/arch/alpha/include/asm/kmap_types.h b/kernel/arch/alpha/include/asm/kmap_types.h
new file mode 100644
index 0000000..651714b
--- /dev/null
+++ b/kernel/arch/alpha/include/asm/kmap_types.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _ASM_KMAP_TYPES_H
+#define _ASM_KMAP_TYPES_H
+
+/* Dummy header just to define km_type. */
+
+#ifdef CONFIG_DEBUG_HIGHMEM
+#define  __WITH_KM_FENCE
+#endif
+
+#include <asm-generic/kmap_types.h>
+
+#undef __WITH_KM_FENCE
+
+#endif
diff --git a/kernel/arch/alpha/include/asm/spinlock_types.h b/kernel/arch/alpha/include/asm/spinlock_types.h
index 6883bc9..1d5716b 100644
--- a/kernel/arch/alpha/include/asm/spinlock_types.h
+++ b/kernel/arch/alpha/include/asm/spinlock_types.h
@@ -2,6 +2,10 @@
 #ifndef _ALPHA_SPINLOCK_TYPES_H
 #define _ALPHA_SPINLOCK_TYPES_H
 
+#ifndef __LINUX_SPINLOCK_TYPES_H
+# error "please don't include this file directly"
+#endif
+
 typedef struct {
 	volatile unsigned int lock;
 } arch_spinlock_t;
diff --git a/kernel/arch/arc/Kconfig b/kernel/arch/arc/Kconfig
index d880400..0a89cc9 100644
--- a/kernel/arch/arc/Kconfig
+++ b/kernel/arch/arc/Kconfig
@@ -507,7 +507,6 @@
 config HIGHMEM
 	bool "High Memory Support"
 	select ARCH_DISCONTIGMEM_ENABLE
-	select KMAP_LOCAL
 	help
 	  With ARC 2G:2G address split, only upper 2G is directly addressable by
 	  kernel. Enable this to potentially allow access to rest of 2G and PAE
diff --git a/kernel/arch/arc/include/asm/highmem.h b/kernel/arch/arc/include/asm/highmem.h
index a6b8e2c..6e5eafb 100644
--- a/kernel/arch/arc/include/asm/highmem.h
+++ b/kernel/arch/arc/include/asm/highmem.h
@@ -9,29 +9,17 @@
 #ifdef CONFIG_HIGHMEM
 
 #include <uapi/asm/page.h>
-#include <asm/kmap_size.h>
-
-#define FIXMAP_SIZE		PGDIR_SIZE
-#define PKMAP_SIZE		PGDIR_SIZE
+#include <asm/kmap_types.h>
 
 /* start after vmalloc area */
 #define FIXMAP_BASE		(PAGE_OFFSET - FIXMAP_SIZE - PKMAP_SIZE)
-
-#define FIX_KMAP_SLOTS		(KM_MAX_IDX * NR_CPUS)
-#define FIX_KMAP_BEGIN		(0UL)
-#define FIX_KMAP_END		((FIX_KMAP_BEGIN + FIX_KMAP_SLOTS) - 1)
-
-#define FIXADDR_TOP		(FIXMAP_BASE + (FIX_KMAP_END << PAGE_SHIFT))
-
-/*
- * This should be converted to the asm-generic version, but of course this
- * is needlessly different from all other architectures. Sigh - tglx
- */
-#define __fix_to_virt(x)	(FIXADDR_TOP - ((x) << PAGE_SHIFT))
-#define __virt_to_fix(x)	(((FIXADDR_TOP - ((x) & PAGE_MASK))) >> PAGE_SHIFT)
+#define FIXMAP_SIZE		PGDIR_SIZE	/* only 1 PGD worth */
+#define KM_TYPE_NR		((FIXMAP_SIZE >> PAGE_SHIFT)/NR_CPUS)
+#define FIXMAP_ADDR(nr)		(FIXMAP_BASE + ((nr) << PAGE_SHIFT))
 
 /* start after fixmap area */
 #define PKMAP_BASE		(FIXMAP_BASE + FIXMAP_SIZE)
+#define PKMAP_SIZE		PGDIR_SIZE
 #define LAST_PKMAP		(PKMAP_SIZE >> PAGE_SHIFT)
 #define LAST_PKMAP_MASK		(LAST_PKMAP - 1)
 #define PKMAP_ADDR(nr)		(PKMAP_BASE + ((nr) << PAGE_SHIFT))
@@ -41,13 +29,11 @@
 
 extern void kmap_init(void);
 
-#define arch_kmap_local_post_unmap(vaddr)			\
-	local_flush_tlb_kernel_range(vaddr, vaddr + PAGE_SIZE)
-
 static inline void flush_cache_kmaps(void)
 {
 	flush_cache_all();
 }
+
 #endif
 
 #endif
diff --git a/kernel/arch/arc/include/asm/kmap_types.h b/kernel/arch/arc/include/asm/kmap_types.h
new file mode 100644
index 0000000..fecf785
--- /dev/null
+++ b/kernel/arch/arc/include/asm/kmap_types.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2015 Synopsys, Inc. (www.synopsys.com)
+ */
+
+#ifndef _ASM_KMAP_TYPES_H
+#define _ASM_KMAP_TYPES_H
+
+/*
+ * We primarily need to define KM_TYPE_NR here but that in turn
+ * is a function of PGDIR_SIZE etc.
+ * To avoid circular deps issue, put everything in asm/highmem.h
+ */
+#endif
diff --git a/kernel/arch/arc/mm/highmem.c b/kernel/arch/arc/mm/highmem.c
index c79912a..1b9f473 100644
--- a/kernel/arch/arc/mm/highmem.c
+++ b/kernel/arch/arc/mm/highmem.c
@@ -36,8 +36,9 @@
  *   This means each only has 1 PGDIR_SIZE worth of kvaddr mappings, which means
  *   2M of kvaddr space for typical config (8K page and 11:8:13 traversal split)
  *
- * - The fixed KMAP slots for kmap_local/atomic() require KM_MAX_IDX slots per
- *   CPU. So the number of CPUs sharing a single PTE page is limited.
+ * - fixmap anyhow needs a limited number of mappings. So 2M kvaddr == 256 PTE
+ *   slots across NR_CPUS would be more than sufficient (generic code defines
+ *   KM_TYPE_NR as 20).
  *
  * - pkmap being preemptible, in theory could do with more than 256 concurrent
  *   mappings. However, generic pkmap code: map_new_virtual(), doesn't traverse
@@ -46,6 +47,48 @@
  */
 
 extern pte_t * pkmap_page_table;
+static pte_t * fixmap_page_table;
+
+void *kmap_atomic_high_prot(struct page *page, pgprot_t prot)
+{
+	int idx, cpu_idx;
+	unsigned long vaddr;
+
+	cpu_idx = kmap_atomic_idx_push();
+	idx = cpu_idx + KM_TYPE_NR * smp_processor_id();
+	vaddr = FIXMAP_ADDR(idx);
+
+	set_pte_at(&init_mm, vaddr, fixmap_page_table + idx,
+		   mk_pte(page, prot));
+
+	return (void *)vaddr;
+}
+EXPORT_SYMBOL(kmap_atomic_high_prot);
+
+void kunmap_atomic_high(void *kv)
+{
+	unsigned long kvaddr = (unsigned long)kv;
+
+	if (kvaddr >= FIXMAP_BASE && kvaddr < (FIXMAP_BASE + FIXMAP_SIZE)) {
+
+		/*
+		 * Because preemption is disabled, this vaddr can be associated
+		 * with the current allocated index.
+		 * But in case of multiple live kmap_atomic(), it still relies on
+		 * callers to unmap in right order.
+		 */
+		int cpu_idx = kmap_atomic_idx();
+		int idx = cpu_idx + KM_TYPE_NR * smp_processor_id();
+
+		WARN_ON(kvaddr != FIXMAP_ADDR(idx));
+
+		pte_clear(&init_mm, kvaddr, fixmap_page_table + idx);
+		local_flush_tlb_kernel_range(kvaddr, kvaddr + PAGE_SIZE);
+
+		kmap_atomic_idx_pop();
+	}
+}
+EXPORT_SYMBOL(kunmap_atomic_high);
 
 static noinline pte_t * __init alloc_kmap_pgtable(unsigned long kvaddr)
 {
@@ -65,9 +108,10 @@
 {
 	/* Due to recursive include hell, we can't do this in processor.h */
 	BUILD_BUG_ON(PAGE_OFFSET < (VMALLOC_END + FIXMAP_SIZE + PKMAP_SIZE));
-	BUILD_BUG_ON(LAST_PKMAP > PTRS_PER_PTE);
-	BUILD_BUG_ON(FIX_KMAP_SLOTS > PTRS_PER_PTE);
 
+	BUILD_BUG_ON(KM_TYPE_NR > PTRS_PER_PTE);
 	pkmap_page_table = alloc_kmap_pgtable(PKMAP_BASE);
-	alloc_kmap_pgtable(FIXMAP_BASE);
+
+	BUILD_BUG_ON(LAST_PKMAP > PTRS_PER_PTE);
+	fixmap_page_table = alloc_kmap_pgtable(FIXMAP_BASE);
 }
diff --git a/kernel/arch/arm/Kconfig b/kernel/arch/arm/Kconfig
index 357a88f..f6ce22c 100644
--- a/kernel/arch/arm/Kconfig
+++ b/kernel/arch/arm/Kconfig
@@ -31,7 +31,6 @@
 	select ARCH_OPTIONAL_KERNEL_RWX if ARCH_HAS_STRICT_KERNEL_RWX
 	select ARCH_OPTIONAL_KERNEL_RWX_DEFAULT if CPU_V7
 	select ARCH_SUPPORTS_ATOMIC_RMW
-	select ARCH_SUPPORTS_RT if HAVE_POSIX_CPU_TIMERS_TASK_WORK
 	select ARCH_USE_BUILTIN_BSWAP
 	select ARCH_USE_CMPXCHG_LOCKREF
 	select ARCH_WANT_DEFAULT_TOPDOWN_MMAP_LAYOUT if MMU
@@ -68,7 +67,7 @@
 	select HARDIRQS_SW_RESEND
 	select HAVE_ARCH_AUDITSYSCALL if AEABI && !OABI_COMPAT
 	select HAVE_ARCH_BITREVERSE if (CPU_32v7M || CPU_32v7) && !CPU_32v6
-	select HAVE_ARCH_JUMP_LABEL if !XIP_KERNEL && !CPU_ENDIAN_BE32 && MMU && !PREEMPT_RT
+	select HAVE_ARCH_JUMP_LABEL if !XIP_KERNEL && !CPU_ENDIAN_BE32 && MMU
 	select HAVE_ARCH_KGDB if !CPU_ENDIAN_BE32 && MMU
 	select HAVE_ARCH_MMAP_RND_BITS if MMU
 	select HAVE_ARCH_SECCOMP
@@ -108,7 +107,6 @@
 	select HAVE_PERF_EVENTS
 	select HAVE_PERF_REGS
 	select HAVE_PERF_USER_STACK_DUMP
-	select HAVE_PREEMPT_LAZY
 	select MMU_GATHER_RCU_TABLE_FREE if SMP && ARM_LPAE
 	select HAVE_REGS_AND_STACK_ACCESS_API
 	select HAVE_RSEQ
@@ -124,7 +122,6 @@
 	select OLD_SIGSUSPEND3
 	select PCI_SYSCALL if PCI
 	select PERF_USE_VMALLOC
-	select HAVE_POSIX_CPU_TIMERS_TASK_WORK if !KVM
 	select RTC_LIB
 	select SET_FS
 	select SYS_SUPPORTS_APM_EMULATION
@@ -1500,7 +1497,6 @@
 config HIGHMEM
 	bool "High Memory Support"
 	depends on MMU
-	select KMAP_LOCAL
 	help
 	  The address space of ARM processors is only 4 Gigabytes large
 	  and it has to accommodate user address space, kernel address
diff --git a/kernel/arch/arm/Makefile b/kernel/arch/arm/Makefile
index 6db15e9..bad2ea2 100644
--- a/kernel/arch/arm/Makefile
+++ b/kernel/arch/arm/Makefile
@@ -157,6 +157,7 @@
 textofs-$(CONFIG_CPU_RV1106) := 0x00208000
 textofs-$(CONFIG_CPU_RV1126) := 0x00608000
 endif
+textofs-$(CONFIG_RV1106_HPMCU_FAST_WAKEUP) := 0x00208000
 
 # Machine directory name.  This list is sorted alphanumerically
 # by CONFIG_* macro name.
diff --git a/kernel/arch/arm/boot/dts/Makefile b/kernel/arch/arm/boot/dts/Makefile
index 694c927..36464dc 100644
--- a/kernel/arch/arm/boot/dts/Makefile
+++ b/kernel/arch/arm/boot/dts/Makefile
@@ -984,6 +984,7 @@
 	rv1106g-38x38-ipc-v10.dtb \
 	rv1106g-38x38-ipc-v10-spi-nand.dtb \
 	rv1106g-evb1-mcu-display-v11.dtb \
+	rv1106g-evb1-mcu-display-v20.dtb \
 	rv1106g-evb1-rgb-display-v11.dtb \
 	rv1106g-evb1-v10.dtb \
 	rv1106g-evb1-v11.dtb \
@@ -995,11 +996,18 @@
 	rv1106g-evb1-v10-dual-cam.dtb \
 	rv1106g-evb1-v11-dual-cam.dtb \
 	rv1106g-evb1-v10-facial-gate.dtb \
+	rv1106g-evb1-v11-facial-gate.dtb \
 	rv1106g-evb1-v10-spi-nand.dtb \
 	rv1106g-evb1-v10-spi-nor.dtb \
+	rv1106g-evb1-v11-nofastae-spi-nand.dtb \
 	rv1106g-evb2-v10.dtb \
 	rv1106g-evb2-v10-dual-camera.dtb \
 	rv1106g-evb2-v11-emmc.dtb \
+	rv1106g-evb2-v11-trailcam-emmc.dtb \
+	rv1106g-evb2-v12-nofastae-emmc.dtb \
+	rv1106g-evb2-v12-nofastae-spi-nand.dtb \
+	rv1106g-evb2-v12-nofastae-spi-nor.dtb \
+	rv1106g-evb2-v12-wakeup.dtb \
 	rv1106g-smart-door-lock-rmsl-v10.dtb \
 	rv1106g-smart-door-lock-rmsl-v12.dtb \
 	rv1106g-uvc-demo-v10.dtb \
diff --git a/kernel/arch/arm/boot/dts/rk3288-evb-rk808-linux.dts b/kernel/arch/arm/boot/dts/rk3288-evb-rk808-linux.dts
index 50f1e81..69a5008 100644
--- a/kernel/arch/arm/boot/dts/rk3288-evb-rk808-linux.dts
+++ b/kernel/arch/arm/boot/dts/rk3288-evb-rk808-linux.dts
@@ -1,42 +1,4 @@
-/*
- * This file is dual-licensed: you can use it either under the terms
- * of the GPL or the X11 license, at your option. Note that this dual
- * licensing only applies to this file, and not this project as a
- * whole.
- *
- *  a) This file 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 file 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.
- *
- * Or, alternatively,
- *
- *  b) Permission is hereby granted, free of charge, to any person
- *     obtaining a copy of this software and associated documentation
- *     files (the "Software"), to deal in the Software without
- *     restriction, including without limitation the rights to use,
- *     copy, modify, merge, publish, distribute, sublicense, and/or
- *     sell copies of the Software, and to permit persons to whom the
- *     Software is furnished to do so, subject to the following
- *     conditions:
- *
- *     The above copyright notice and this permission notice shall be
- *     included in all copies or substantial portions of the Software.
- *
- *     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- *     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
- *     OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- *     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
- *     HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
- *     WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- *     FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- *     OTHER DEALINGS IN THE SOFTWARE.
- */
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
 
 /dts-v1/;
 #include "rk3288-evb.dtsi"
@@ -174,6 +136,14 @@
 	pinctrl-0 = <&hdmi_ddc>, <&hdmi_cec_c0>;
 };
 
+&hdmi_in_vopb {
+	status = "okay";
+};
+
+&hdmi_in_vopl {
+	status = "disabled";
+};
+
 &i2c0 {
 	clock-frequency = <400000>;
 
@@ -262,7 +232,8 @@
 				regulator-max-microvolt = <3300000>;
 				regulator-name = "vcca_codec";
 				regulator-state-mem {
-					regulator-off-in-suspend;
+					regulator-on-in-suspend;
+					regulator-suspend-microvolt = <3300000>;
 				};
 			};
 
@@ -282,10 +253,10 @@
 				regulator-always-on;
 				regulator-boot-on;
 				regulator-min-microvolt = <1800000>;
-				regulator-max-microvolt = <3300000>;
+				regulator-max-microvolt = <1800000>;
 				regulator-name = "vcc_wl";
 				regulator-state-mem {
-					regulator-off-in-suspend;
+					regulator-on-in-suspend;
 				};
 			};
 
@@ -296,8 +267,7 @@
 				regulator-max-microvolt = <3300000>;
 				regulator-name = "vccio_sd";
 				regulator-state-mem {
-					regulator-on-in-suspend;
-					regulator-suspend-microvolt = <3300000>;
+					regulator-off-in-suspend;
 				};
 			};
 
@@ -340,7 +310,7 @@
 				regulator-boot-on;
 				regulator-name = "vcc_sd";
 				regulator-state-mem {
-					regulator-on-in-suspend;
+					regulator-off-in-suspend;
 				};
 			};
 
diff --git a/kernel/arch/arm/boot/dts/rk3288-evb.dtsi b/kernel/arch/arm/boot/dts/rk3288-evb.dtsi
index 0b1cda7..f3b2325 100644
--- a/kernel/arch/arm/boot/dts/rk3288-evb.dtsi
+++ b/kernel/arch/arm/boot/dts/rk3288-evb.dtsi
@@ -47,43 +47,49 @@
 		};
 	};
 
-	sound: sound {
-		compatible = "simple-audio-card";
-		simple-audio-card,format = "i2s";
-		simple-audio-card,name = "rockchip,rt5640-codec";
-		simple-audio-card,mclk-fs = <512>;
+	rt5640_sound: rt5640-sound {
 		status = "okay";
-
-		simple-audio-card,dai-link@0 {
-			format = "i2s";
-			cpu {
-				sound-dai = <&i2s>;
-			};
-
-			codec {
-				sound-dai = <&rt5640>;
-			};
-		};
-
-		simple-audio-card,dai-link@1 {
-			format = "i2s";
-			cpu {
-				sound-dai = <&i2s>;
-			};
-
-			codec {
-				sound-dai = <&hdmi>;
-			};
+		compatible = "rockchip,multicodecs-card";
+		rockchip,card-name = "rockchip-rt5640";
+		hp-det-gpio = <&gpio7 RK_PA7 GPIO_ACTIVE_HIGH>;
+		io-channels = <&saradc 2>;
+		io-channel-names = "adc-detect";
+		keyup-threshold-microvolt = <1800000>;
+		poll-interval = <100>;
+		rockchip,format = "i2s";
+		rockchip,mclk-fs = <512>;
+		rockchip,cpu = <&i2s>;
+		rockchip,codec = <&rt5640>;
+		rockchip,audio-routing =
+			"Headphone", "HPOL",
+			"Headphone", "HPOR",
+			"Speaker", "SPOLP",
+			"Speaker", "SPOLN",
+			"Speaker", "SPORP",
+			"Speaker", "SPORN",
+			"Headphone", "Headphone Power",
+			"Headphone", "Headphone Power",
+			"Speaker", "Speaker Power",
+			"Speaker", "Speaker Power",
+			"DMIC L1", "Main Mic",
+			"DMIC R1", "Main Mic",
+			"IN3P", "Headset Mic",
+			"Headset Mic", "MICBIAS1";
+		play-pause-key {
+			label = "playpause";
+			linux,code = <KEY_PLAYPAUSE>;
+			press-threshold-microvolt = <2000>;
 		};
 	};
 
-	hdmi_analog_sound: hdmi-analog-sound {
-		compatible = "rockchip,rk3288-hdmi-analog",
-			     "rockchip,rk3368-hdmi-analog";
-		rockchip,model = "rockchip,rt5640-codec";
-		rockchip,cpu = <&i2s>;
-		rockchip,codec = <&rt5640>, <&hdmi>;
+	hdmi_sound: hdmi-sound {
 		status = "disabled";
+		compatible = "rockchip,hdmi";
+		rockchip,mclk-fs = <256>;
+		rockchip,card-name = "rockchip-hdmi0";
+		rockchip,cpu = <&i2s>;
+		rockchip,codec = <&hdmi>;
+		rockchip,jack-det;
 	};
 
 	backlight: backlight {
@@ -368,8 +374,6 @@
 		reg = <0x1c>;
 		clocks = <&cru SCLK_I2S0_OUT>;
 		clock-names = "mclk";
-		interrupt-parent = <&gpio7>;
-		interrupts = <7 IRQ_TYPE_EDGE_FALLING>;
 		pinctrl-names = "default";
 		pinctrl-0 = <&i2s0_mclk>;
 	};
diff --git a/kernel/arch/arm/boot/dts/rk3288-linux.dtsi b/kernel/arch/arm/boot/dts/rk3288-linux.dtsi
index 8262a19..1ea066a 100644
--- a/kernel/arch/arm/boot/dts/rk3288-linux.dtsi
+++ b/kernel/arch/arm/boot/dts/rk3288-linux.dtsi
@@ -7,6 +7,13 @@
 #include "rk3288-dram-default-timing.dtsi"
 
 / {
+	aliases {
+		mshc0 = &emmc;
+		mshc1 = &sdmmc;
+		mshc2 = &sdio0;
+		mshc3 = &sdio1;
+	};
+
 	chosen {
 		bootargs = "earlycon=uart8250,mmio32,0xff690000 console=ttyFIQ0 vmalloc=496M rw root=PARTUUID=614e0000-0000 rootfstype=ext4 rootwait";
 	};
diff --git a/kernel/arch/arm/boot/dts/rk3308-dot-rk816-v10-aarch32.dts b/kernel/arch/arm/boot/dts/rk3308-dot-rk816-v10-aarch32.dts
index 5c9f695..09c653c 100644
--- a/kernel/arch/arm/boot/dts/rk3308-dot-rk816-v10-aarch32.dts
+++ b/kernel/arch/arm/boot/dts/rk3308-dot-rk816-v10-aarch32.dts
@@ -406,6 +406,12 @@
 		};
 	};
 
+	wireless-bluetooth {
+		uart4_gpios: uart4-gpios {
+			rockchip,pins = <4 RK_PA7 RK_FUNC_GPIO &pcfg_pull_none>;
+		};
+	};
+
 	wireless-wlan {
 		wifi_wake_host: wifi-wake-host {
 			rockchip,pins = <0 RK_PA0 RK_FUNC_GPIO &pcfg_pull_up>;
diff --git a/kernel/arch/arm/boot/dts/rk3308-dot-v10-aarch32.dts b/kernel/arch/arm/boot/dts/rk3308-dot-v10-aarch32.dts
index 384dd9e..970d67d 100644
--- a/kernel/arch/arm/boot/dts/rk3308-dot-v10-aarch32.dts
+++ b/kernel/arch/arm/boot/dts/rk3308-dot-v10-aarch32.dts
@@ -225,6 +225,12 @@
 		};
 	};
 
+	wireless-bluetooth {
+		uart4_gpios: uart4-gpios {
+			rockchip,pins = <4 RK_PA7 RK_FUNC_GPIO &pcfg_pull_none>;
+		};
+	};
+
 	wireless-wlan {
 		wifi_wake_host: wifi-wake-host {
 			rockchip,pins = <0 RK_PA0 RK_FUNC_GPIO &pcfg_pull_up>;
diff --git a/kernel/arch/arm/boot/dts/rk3308-voice-module-mainboard-v10-aarch32.dtsi b/kernel/arch/arm/boot/dts/rk3308-voice-module-mainboard-v10-aarch32.dtsi
index 635d38f..f1b8ebc 100644
--- a/kernel/arch/arm/boot/dts/rk3308-voice-module-mainboard-v10-aarch32.dtsi
+++ b/kernel/arch/arm/boot/dts/rk3308-voice-module-mainboard-v10-aarch32.dtsi
@@ -390,6 +390,14 @@
 	status = "okay";
 };
 
+&pinctrl {
+	wireless-bluetooth {
+		uart4_gpios: uart4-gpios {
+			rockchip,pins = <4 RK_PA7 RK_FUNC_GPIO &pcfg_pull_none>;
+		};
+	};
+};
+
 &pwm3 {
 	status = "okay";
 	/* Used for IR */
diff --git a/kernel/arch/arm/boot/dts/rv1103g-battery-ipc-v10.dts b/kernel/arch/arm/boot/dts/rv1103g-battery-ipc-v10.dts
index ee95a18..6f66231 100644
--- a/kernel/arch/arm/boot/dts/rv1103g-battery-ipc-v10.dts
+++ b/kernel/arch/arm/boot/dts/rv1103g-battery-ipc-v10.dts
@@ -106,6 +106,11 @@
 	status = "okay";
 };
 
+&cpu0 {
+	/delete-property/ clocks;
+	/delete-property/ operating-points-v2;
+};
+
 &csi2_dphy_hw {
 	status = "okay";
 };
diff --git a/kernel/arch/arm/boot/dts/rv1103g-battery-ipc-v11.dts b/kernel/arch/arm/boot/dts/rv1103g-battery-ipc-v11.dts
index e8a6f79..3a09607 100644
--- a/kernel/arch/arm/boot/dts/rv1103g-battery-ipc-v11.dts
+++ b/kernel/arch/arm/boot/dts/rv1103g-battery-ipc-v11.dts
@@ -106,6 +106,11 @@
 	status = "okay";
 };
 
+&cpu0 {
+	/delete-property/ clocks;
+	/delete-property/ operating-points-v2;
+};
+
 &csi2_dphy_hw {
 	status = "okay";
 };
diff --git a/kernel/arch/arm/boot/dts/rv1103g-evb-mcu-display-v11.dts b/kernel/arch/arm/boot/dts/rv1103g-evb-mcu-display-v11.dts
index 5d1c51b..e62f08d 100644
--- a/kernel/arch/arm/boot/dts/rv1103g-evb-mcu-display-v11.dts
+++ b/kernel/arch/arm/boot/dts/rv1103g-evb-mcu-display-v11.dts
@@ -91,22 +91,30 @@
 	status = "okay";
 	rockchip,data-sync-bypass;
 	pinctrl-names = "default";
+	/*
+	 * rgb3x8_pins for RGB3x8(8bit)
+	 * rgb565_pins for RGB565(16bit)
+	 */
 	pinctrl-0 = <&rgb3x8_pins>;
 
 	/*
 	 * 320x480 RGB/MCU screen K350C4516T
 	 */
 	mcu_panel: mcu-panel {
+		/*
+		 * MEDIA_BUS_FMT_RGB888_3X8  for RGB3x8(8bit)
+		 * MEDIA_BUS_FMT_RGB565_1X16 for RGB565(16bit)
+		 */
 		bus-format = <MEDIA_BUS_FMT_RGB888_3X8>;
 		backlight = <&backlight>;
 		enable-gpios = <&gpio3 RK_PC7 GPIO_ACTIVE_LOW>;
 		enable-delay-ms = <20>;
 		reset-gpios = <&gpio3 RK_PD0 GPIO_ACTIVE_LOW>;
-		reset-value = <0>;
 		reset-delay-ms = <10>;
 		prepare-delay-ms = <20>;
 		unprepare-delay-ms = <20>;
 		disable-delay-ms = <20>;
+		init-delay-ms = <10>;
 		width-mm = <217>;
 		height-mm = <136>;
 
@@ -161,20 +169,31 @@
 			  00   00  01  36
 			  01   00  01  48
 
-			  00   00  01  3a  //interface mode control
-			  01   00  01  77  //spi rgb:66(r1 r4 r5) mcu parallel: 55(r2 r3 r6)
-					   //                     mcu serial:   77(r1 r3 r6)
+			  00   00  01  3a
+			  01   00  01  66 /*
+					   * interface pixel format:
+					   * 66 for RGB3x8(8bit)
+					   * 55 for RGB565(16bit)
+					   */
 
-			  00   00  01  b0  //interface mode control
+			  00   00  01  b0
 			  01   00  01  00
 
-			  00   00  01  b1  //frame rate 70hz
-			  01   00  01  b0
+			  00   00  01  b1
+			  01   00  01  70 /*
+					   * frame rate control:
+					   * 70 (45hz) for RGB3x8(8bit)
+					   * a0 (60hz) for RGB565(16bit)
+					   */
 			  01   00  01  11
 			  00   00  01  b4
 			  01   00  01  02
-			  00   00  01  B6  //RGB/MCU Interface Control
-			  01   00  01  02  //02 mcu, 32 rgb
+			  00   00  01  B6
+			  01   00  01  02 /*
+					   * display function control:
+					   * 32 for RGB
+					   * 02 for MCU
+					   */
 			  01   00  01  02
 
 			  00   00  01  b7
@@ -208,7 +227,11 @@
 			native-mode = <&kd050fwfba002_timing>;
 
 			kd050fwfba002_timing: timing0 {
-				clock-frequency = <20000000>;
+				/*
+				 * 7840125  for frame rate 45Hz
+				 * 10453500 for frame rate 60Hz
+				 */
+				clock-frequency = <7840125>;
 				hactive = <320>;
 				vactive = <480>;
 				hback-porch = <10>;
@@ -267,12 +290,25 @@
 &vop {
 	status = "okay";
 
+	/*
+	 * Default config is as follows:
+	 *
+	 * mcu-pix-total = <9>;
+	 * mcu-cs-pst = <1>;
+	 * mcu-cs-pend = <8>;
+	 * mcu-rw-pst = <2>;
+	 * mcu-rw-pend = <5>;
+	 * mcu-hold-mode = <0>; // default set to 0
+	 *
+	 * To increase the frame rate, reduce all parameters because
+	 * the max dclk rate of mcu is 150M in rv1103/rv1106.
+	 */
 	mcu-timing {
-		mcu-pix-total = <9>;
+		mcu-pix-total = <5>;
 		mcu-cs-pst = <1>;
-		mcu-cs-pend = <8>;
+		mcu-cs-pend = <4>;
 		mcu-rw-pst = <2>;
-		mcu-rw-pend = <5>;
+		mcu-rw-pend = <3>;
 
 		mcu-hold-mode = <0>; // default set to 0
 	};
diff --git a/kernel/arch/arm/boot/dts/rv1106-evb-cam.dtsi b/kernel/arch/arm/boot/dts/rv1106-evb-cam.dtsi
index 2e4967f..9bc4ef9 100644
--- a/kernel/arch/arm/boot/dts/rv1106-evb-cam.dtsi
+++ b/kernel/arch/arm/boot/dts/rv1106-evb-cam.dtsi
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: (GPL-2.0+ OR MIT)
 /*
- * Copyright (c) 2022 Rockchip Electronics Co., Ltd.
+ * Copyright (c) 2023 Rockchip Electronics Co., Ltd.
  *
  */
 
@@ -43,6 +43,11 @@
 			csi_dphy_input4: endpoint@4 {
 				reg = <4>;
 				remote-endpoint = <&jx_k17_out>;
+				data-lanes = <1 2>;
+			};
+			csi_dphy_input5: endpoint@5 {
+				reg = <5>;
+				remote-endpoint = <&sc3338_out>;
 				data-lanes = <1 2>;
 			};
 		};
@@ -110,6 +115,28 @@
 		};
 	};
 
+	sc3338: sc3338@30 {
+		compatible = "smartsens,sc3338";
+		status = "okay";
+		reg = <0x30>;
+		clocks = <&cru MCLK_REF_MIPI0>;
+		clock-names = "xvclk";
+		reset-gpios = <&gpio3 RK_PC5 GPIO_ACTIVE_HIGH>;
+		pwdn-gpios = <&gpio3 RK_PD2 GPIO_ACTIVE_HIGH>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&mipi_refclk_out0>;
+		rockchip,camera-module-index = <0>;
+		rockchip,camera-module-facing = "back";
+		rockchip,camera-module-name = "FKO1";
+		rockchip,camera-module-lens-name = "30IRC-F16";
+		port {
+			sc3338_out: endpoint {
+				remote-endpoint = <&csi_dphy_input5>;
+				data-lanes = <1 2>;
+			};
+		};
+	};
+
 	sc4336: sc4336@30 {
 		compatible = "smartsens,sc4336";
 		status = "okay";
diff --git a/kernel/arch/arm/boot/dts/rv1106-evb-dual-cam.dtsi b/kernel/arch/arm/boot/dts/rv1106-evb-dual-cam.dtsi
index 2fbddb1..bd3d8fe 100644
--- a/kernel/arch/arm/boot/dts/rv1106-evb-dual-cam.dtsi
+++ b/kernel/arch/arm/boot/dts/rv1106-evb-dual-cam.dtsi
@@ -7,6 +7,8 @@
  *          sc4336    0x30    lane2~3(dphy2)
  * v1.1.0   gc2053    0x37    lane0~1(dphy1)
  *          gc2053    0x3f    lane2~3(dphy2)
+ * v1.2.0   sc301iot  0x30    lane0~1(dphy1)
+ *          sc301iot  0x32    lane2~3(dphy2)
  */
 
 &csi2_dphy_hw {
@@ -34,6 +36,12 @@
 			csi_dphy_input2: endpoint@2 {
 				reg = <2>;
 				remote-endpoint = <&gc2053_out>;
+				data-lanes = <1 2>;
+			};
+
+			csi_dphy_input4: endpoint@3 {
+				reg = <3>;
+				remote-endpoint = <&sc301iot_out>;
 				data-lanes = <1 2>;
 			};
 		};
@@ -72,6 +80,12 @@
 			csi_dphy_input3: endpoint@2 {
 				reg = <2>;
 				remote-endpoint = <&gc2053_1_out>;
+				data-lanes = <1 2>;
+			};
+
+			csi_dphy_input5: endpoint@3 {
+				reg = <3>;
+				remote-endpoint = <&sc301iot_1_out>;
 				data-lanes = <1 2>;
 			};
 		};
@@ -139,6 +153,28 @@
 		};
 	};
 
+	sc301iot: sc301iot@30 {
+		compatible = "smartsens,sc301iot";
+		status = "okay";
+		reg = <0x30>;
+		clocks = <&cru MCLK_REF_MIPI0>;
+		clock-names = "xvclk";
+		reset-gpios = <&gpio3 RK_PC5 GPIO_ACTIVE_HIGH>;
+		pwdn-gpios = <&gpio3 RK_PD2 GPIO_ACTIVE_HIGH>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&mipi_refclk_out0>;
+		rockchip,camera-module-index = <0>;
+		rockchip,camera-module-facing = "back";
+		rockchip,camera-module-name = "CMK-OT2349-PC1";
+		rockchip,camera-module-lens-name = "65IRC-F20";
+		port {
+			sc301iot_out: endpoint {
+				remote-endpoint = <&csi_dphy_input4>;
+				data-lanes = <1 2>;
+			};
+		};
+	};
+
 	sc4336: sc4336@30 {
 		compatible = "smartsens,sc4336";
 		status = "okay";
@@ -182,6 +218,28 @@
 			};
 		};
 	};
+
+	sc301iot_1: sc301iot_1@32 {
+		compatible = "smartsens,sc301iot";
+		status = "okay";
+		reg = <0x32>;
+		clocks = <&cru MCLK_REF_MIPI1>;
+		clock-names = "xvclk";
+		reset-gpios = <&gpio3 RK_PD1 GPIO_ACTIVE_HIGH>;
+		pwdn-gpios = <&gpio3 RK_PA4 GPIO_ACTIVE_HIGH>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&mipi_refclk_out1>;
+		rockchip,camera-module-index = <1>;
+		rockchip,camera-module-facing = "back";
+		rockchip,camera-module-name = "CMK-OT2349-PC1";
+		rockchip,camera-module-lens-name = "65IRC-F20";
+		port {
+			sc301iot_1_out: endpoint {
+				remote-endpoint = <&csi_dphy_input5>;
+				data-lanes = <1 2>;
+			};
+		};
+	};
 };
 
 &mipi0_csi2 {
diff --git a/kernel/arch/arm/boot/dts/rv1106-evb-ext-mcu-v10.dtsi b/kernel/arch/arm/boot/dts/rv1106-evb-ext-mcu-v10.dtsi
index cbc2e6a..2ab415b 100644
--- a/kernel/arch/arm/boot/dts/rv1106-evb-ext-mcu-v10.dtsi
+++ b/kernel/arch/arm/boot/dts/rv1106-evb-ext-mcu-v10.dtsi
@@ -81,6 +81,10 @@
 	status = "okay";
 	rockchip,data-sync-bypass;
 	pinctrl-names = "default";
+	/*
+	 * rgb3x8_pins for RGB3x8(8bit)
+	 * rgb565_pins for RGB565(16bit)
+	 */
 	pinctrl-0 = <&rgb565_pins>;
 
 	/*
@@ -88,19 +92,19 @@
 	 */
 	mcu_panel: mcu-panel {
 		/*
-		 * MEDIA_BUS_FMT_RGB888_3X8  for serial mcu
-		 * MEDIA_BUS_FMT_RGB565_1X16 for parallel mcu
+		 * MEDIA_BUS_FMT_RGB888_3X8  for RGB3x8(8bit)
+		 * MEDIA_BUS_FMT_RGB565_1X16 for RGB565(16bit)
 		 */
 		bus-format = <MEDIA_BUS_FMT_RGB565_1X16>;
 		backlight = <&backlight>;
 		enable-gpios = <&gpio1 RK_PA3 GPIO_ACTIVE_LOW>;
 		enable-delay-ms = <20>;
 		reset-gpios = <&gpio1 RK_PA4 GPIO_ACTIVE_LOW>;
-		reset-value = <0>;
 		reset-delay-ms = <10>;
 		prepare-delay-ms = <20>;
 		unprepare-delay-ms = <20>;
 		disable-delay-ms = <20>;
+		init-delay-ms = <10>;
 		width-mm = <217>;
 		height-mm = <136>;
 
@@ -155,20 +159,31 @@
 			  00   00  01  36
 			  01   00  01  48
 
-			  00   00  01  3a  //interface mode control
-			  01   00  01  55  //spi rgb:66(r1 r4 r5) mcu parallel: 55(r2 r3 r6)
-					   //                     mcu serial:   77(r1 r3 r6)
+			  00   00  01  3a
+			  01   00  01  55 /*
+					   * interface pixel format:
+					   * 66 for RGB3x8(8bit)
+					   * 55 for RGB565(16bit)
+					   */
 
-			  00   00  01  b0  //interface mode control
+			  00   00  01  b0
 			  01   00  01  00
 
-			  00   00  01  b1  //frame rate 70hz
-			  01   00  01  b0
+			  00   00  01  b1
+			  01   00  01  a0 /*
+					   * frame rate control:
+					   * 70 (45hz) for RGB3x8(8bit)
+					   * a0 (60hz) for RGB565(16bit)
+					   */
 			  01   00  01  11
 			  00   00  01  b4
 			  01   00  01  02
-			  00   00  01  B6  //RGB/MCU Interface Control
-			  01   00  01  02  //02 mcu, 32 rgb
+			  00   00  01  B6
+			  01   00  01  02 /*
+					   * display function control:
+					   * 32 for RGB
+					   * 02 for MCU
+					   */
 			  01   00  01  02
 
 			  00   00  01  b7
@@ -202,7 +217,11 @@
 			native-mode = <&kd050fwfba002_timing>;
 
 			kd050fwfba002_timing: timing0 {
-				clock-frequency = <20000000>;
+				/*
+				 * 7840125  for frame rate 45Hz
+				 * 10453500 for frame rate 60Hz
+				 */
+				clock-frequency = <10453500>;
 				hactive = <320>;
 				vactive = <480>;
 				hback-porch = <10>;
@@ -261,12 +280,25 @@
 &vop {
 	status = "okay";
 
+	/*
+	 * Default config is as follows:
+	 *
+	 * mcu-pix-total = <9>;
+	 * mcu-cs-pst = <1>;
+	 * mcu-cs-pend = <8>;
+	 * mcu-rw-pst = <2>;
+	 * mcu-rw-pend = <5>;
+	 * mcu-hold-mode = <0>; // default set to 0
+	 *
+	 * To increase the frame rate, reduce all parameters because
+	 * the max dclk rate of mcu is 150M in rv1103/rv1106.
+	 */
 	mcu-timing {
-		mcu-pix-total = <9>;
+		mcu-pix-total = <5>;
 		mcu-cs-pst = <1>;
-		mcu-cs-pend = <8>;
+		mcu-cs-pend = <4>;
 		mcu-rw-pst = <2>;
-		mcu-rw-pend = <5>;
+		mcu-rw-pend = <3>;
 
 		mcu-hold-mode = <0>; // default set to 0
 	};
diff --git a/kernel/arch/arm/boot/dts/rv1106-evb.dtsi b/kernel/arch/arm/boot/dts/rv1106-evb.dtsi
index 7b9f2d5..ca60386 100644
--- a/kernel/arch/arm/boot/dts/rv1106-evb.dtsi
+++ b/kernel/arch/arm/boot/dts/rv1106-evb.dtsi
@@ -72,6 +72,10 @@
 	status = "okay";
 };
 
+&rmii_phy {
+	bgs,increment = <0>;
+};
+
 &rng {
 	status = "okay";
 };
diff --git a/kernel/arch/arm/boot/dts/rv1106-tb-nofastae-emmc.dtsi b/kernel/arch/arm/boot/dts/rv1106-tb-nofastae-emmc.dtsi
new file mode 100644
index 0000000..1095b35
--- /dev/null
+++ b/kernel/arch/arm/boot/dts/rv1106-tb-nofastae-emmc.dtsi
@@ -0,0 +1,31 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Copyright (c) 2023 Rockchip Electronics Co., Ltd.
+ */
+
+#include "rv1106-tb-nofastae.dtsi"
+
+/ {
+	reserved-memory {
+		mmc_ecsd: mmc@3f000 {
+			reg = <0x3f000 0x00001000>;
+		};
+
+		mmc_idmac: mmc@100000 {
+			reg = <0x00100000 0x00100000>;
+		};
+	};
+
+	thunder_boot_mmc: thunder-boot-mmc {
+		compatible = "rockchip,thunder-boot-mmc";
+		reg = <0xffa90000 0x4000>;
+		memory-region-src = <&ramdisk_c>;
+		memory-region-dst = <&ramdisk_r>;
+		memory-region-idmac = <&mmc_idmac>;
+	};
+};
+
+&emmc {
+	memory-region-ecsd = <&mmc_ecsd>;
+	post-power-on-delay-ms = <0>;
+};
diff --git a/kernel/arch/arm/boot/dts/rv1106-tb-nofastae-spi-nor.dtsi b/kernel/arch/arm/boot/dts/rv1106-tb-nofastae-spi-nor.dtsi
new file mode 100644
index 0000000..e077009
--- /dev/null
+++ b/kernel/arch/arm/boot/dts/rv1106-tb-nofastae-spi-nor.dtsi
@@ -0,0 +1,19 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Copyright (c) 2023 Rockchip Electronics Co., Ltd.
+ */
+
+#include "rv1106-tb-nofastae.dtsi"
+
+/ {
+	thunder_boot_spi_nor: thunder-boot-spi-nor {
+		compatible = "rockchip,thunder-boot-sfc";
+		reg = <0xffac0000 0x4000>;
+		memory-region-src = <&ramdisk_c>;
+		memory-region-dst = <&ramdisk_r>;
+	};
+};
+
+&emmc {
+	status = "disabled";
+};
diff --git a/kernel/arch/arm/boot/dts/rv1106-tb-nofastae.dtsi b/kernel/arch/arm/boot/dts/rv1106-tb-nofastae.dtsi
new file mode 100644
index 0000000..de10bc2
--- /dev/null
+++ b/kernel/arch/arm/boot/dts/rv1106-tb-nofastae.dtsi
@@ -0,0 +1,35 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Copyright (c) 2023 Rockchip Electronics Co., Ltd.
+ */
+
+/ {
+	memory: memory {
+		device_type = "memory";
+		reg = <0x00000000 0x08000000>;
+	};
+
+	ramdisk: ramdisk {
+		compatible = "rockchip,ramdisk";
+		memory-region = <&ramdisk_r>;
+	};
+
+	reserved-memory {
+		#address-cells = <1>;
+		#size-cells = <1>;
+		ranges;
+
+		ramdisk_r: ramdisk_r {
+			reg = <0x800000 (10 * 0x00100000)>;
+		};
+
+		ramdisk_c: ramdisk_c {
+			reg = <0x1200000 (5 * 0x00100000)>;
+		};
+	};
+};
+
+&hw_decompress {
+	status = "okay";
+	memory-region = <&ramdisk_c>;
+};
diff --git a/kernel/arch/arm/boot/dts/rv1106-thunder-boot.dtsi b/kernel/arch/arm/boot/dts/rv1106-thunder-boot.dtsi
index ea2557c..5f357a6 100644
--- a/kernel/arch/arm/boot/dts/rv1106-thunder-boot.dtsi
+++ b/kernel/arch/arm/boot/dts/rv1106-thunder-boot.dtsi
@@ -98,3 +98,8 @@
 &rkisp_vir0 {
 	memory-region-thunderboot = <&rkisp_thunderboot>;
 };
+
+&rkvenc {
+	assigned-clocks = <&cru CLK_CORE_VEPU>;
+	assigned-clock-rates = <410000000>;
+};
diff --git a/kernel/arch/arm/boot/dts/rv1106.dtsi b/kernel/arch/arm/boot/dts/rv1106.dtsi
index f877096..ff475c1 100644
--- a/kernel/arch/arm/boot/dts/rv1106.dtsi
+++ b/kernel/arch/arm/boot/dts/rv1106.dtsi
@@ -239,6 +239,18 @@
 		};
 	};
 
+	mipi0_csi2: mipi0-csi2 {
+		compatible = "rockchip,rv1106-mipi-csi2";
+		rockchip,hw = <&mipi0_csi2_hw>, <&mipi1_csi2_hw>;
+		status = "disabled";
+	};
+
+	mipi1_csi2: mipi1-csi2 {
+		compatible = "rockchip,rv1106-mipi-csi2";
+		rockchip,hw = <&mipi0_csi2_hw>, <&mipi1_csi2_hw>;
+		status = "disabled";
+	};
+
 	mpp_srv: mpp-srv {
 		compatible = "rockchip,mpp-service";
 		rockchip,taskqueue-count = <2>;
@@ -1014,6 +1026,8 @@
 		#size-cells = <0>;
 		clocks = <&cru CLK_SPI1>, <&cru PCLK_SPI1>;
 		clock-names = "spiclk", "apb_pclk";
+		assigned-clocks = <&cru CLK_SPI1>;
+		assigned-clock-rates = <200000000>;
 		dmas = <&dmac 3>, <&dmac 2>;
 		dma-names = "tx", "rx";
 		pinctrl-names = "default";
@@ -1173,8 +1187,8 @@
 		status = "disabled";
 	};
 
-	mipi0_csi2: mipi-csi2@ffa20000 {
-		compatible = "rockchip,rk3588-mipi-csi2";
+	mipi0_csi2_hw: mipi-csi2-hw@ffa20000 {
+		compatible = "rockchip,rv1106-mipi-csi2-hw";
 		reg = <0xffa20000 0x10000>;
 		reg-names = "csihost_regs";
 		interrupts = <GIC_SPI 99 IRQ_TYPE_LEVEL_HIGH>,
@@ -1184,11 +1198,11 @@
 		clock-names = "pclk_csi2host", "clk_rxbyte_hs";
 		resets = <&cru SRST_P_CSIHOST0>;
 		reset-names = "srst_csihost_p";
-		status = "disabled";
+		status = "okay";
 	};
 
-	mipi1_csi2: mipi-csi2@ffa30000 {
-		compatible = "rockchip,rk3588-mipi-csi2";
+	mipi1_csi2_hw: mipi-csi2-hw@ffa30000 {
+		compatible = "rockchip,rv1106-mipi-csi2-hw";
 		reg = <0xffa30000 0x10000>;
 		reg-names = "csihost_regs";
 		interrupts = <GIC_SPI 101 IRQ_TYPE_LEVEL_HIGH>,
@@ -1198,7 +1212,7 @@
 		clock-names = "pclk_csi2host", "clk_rxbyte_hs";
 		resets = <&cru SRST_P_CSIHOST1>;
 		reset-names = "srst_csihost_p";
-		status = "disabled";
+		status = "okay";
 	};
 
 	rkvenc: rkvenc@ffa50000 {
@@ -1409,6 +1423,7 @@
 			snps,dis-tx-ipgap-linecheck-quirk;
 			snps,usb2-gadget-lpm-disable;
 			snps,usb2-lpm-disable;
+			snps,parkmode-disable-hs-quirk;
 			status = "disabled";
 		};
 	};
diff --git a/kernel/arch/arm/boot/dts/rv1106g-evb1-mcu-display-v20.dts b/kernel/arch/arm/boot/dts/rv1106g-evb1-mcu-display-v20.dts
new file mode 100644
index 0000000..21dbea7
--- /dev/null
+++ b/kernel/arch/arm/boot/dts/rv1106g-evb1-mcu-display-v20.dts
@@ -0,0 +1,314 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Copyright (c) 2023 Rockchip Electronics Co., Ltd.
+ */
+
+/dts-v1/;
+
+#include <dt-bindings/display/media-bus-format.h>
+#include "rv1106g-evb1-v11.dts"
+
+/ {
+	model = "Rockchip RV1106G EVB1 V11 Board + RK EVB MCU 8BIT Display V20 Ext Board";
+	compatible = "rockchip,rv1106g-evb1-mcu-display-v20", "rockchip,rv1106";
+
+	backlight: backlight {
+		status = "okay";
+		compatible = "pwm-backlight";
+		pwms = <&pwm3 0 25000 0>;
+		brightness-levels = <
+			  0   1   2   3   4   5   6   7
+			  8   9  10  11  12  13  14  15
+			 16  17  18  19  20  21  22  23
+			 24  25  26  27  28  29  30  31
+			 32  33  34  35  36  37  38  39
+			 40  41  42  43  44  45  46  47
+			 48  49  50  51  52  53  54  55
+			 56  57  58  59  60  61  62  63
+			 64  65  66  67  68  69  70  71
+			 72  73  74  75  76  77  78  79
+			 80  81  82  83  84  85  86  87
+			 88  89  90  91  92  93  94  95
+			 96  97  98  99 100 101 102 103
+			104 105 106 107 108 109 110 111
+			112 113 114 115 116 117 118 119
+			120 121 122 123 124 125 126 127
+			128 129 130 131 132 133 134 135
+			136 137 138 139 140 141 142 143
+			144 145 146 147 148 149 150 151
+			152 153 154 155 156 157 158 159
+			160 161 162 163 164 165 166 167
+			168 169 170 171 172 173 174 175
+			176 177 178 179 180 181 182 183
+			184 185 186 187 188 189 190 191
+			192 193 194 195 196 197 198 199
+			200 201 202 203 204 205 206 207
+			208 209 210 211 212 213 214 215
+			216 217 218 219 220 221 222 223
+			224 225 226 227 228 229 230 231
+			232 233 234 235 236 237 238 239
+			240 241 242 243 244 245 246 247
+			248 249 250 251 252 253 254 255>;
+		default-brightness-level = <200>;
+	};
+
+	reserved-memory {
+		#address-cells = <1>;
+		#size-cells = <1>;
+		ranges;
+
+		linux,cma {
+			compatible = "shared-dma-pool";
+			inactive;
+			reusable;
+			size = <0x1000000>;
+			linux,cma-default;
+		};
+
+		drm_logo: drm-logo@00000000 {
+			compatible = "rockchip,drm-logo";
+			reg = <0x0 0x0>;
+		};
+	};
+};
+
+&display_subsystem {
+	status = "okay";
+	logo-memory-region = <&drm_logo>;
+};
+
+&pwm3 {
+	status = "okay";
+	pinctrl-names = "active";
+	pinctrl-0 = <&pwm3m1_pins>;
+};
+
+&rgb {
+	status = "okay";
+	rockchip,data-sync-bypass;
+	pinctrl-names = "default";
+	/*
+	 * rgb3x8_pins for RGB3x8(8bit)
+	 * rgb565_pins for RGB565(16bit)
+	 */
+	pinctrl-0 = <&rgb3x8_pins>;
+
+	/*
+	 * 320x480 RGB/MCU screen K350C4516T
+	 */
+	mcu_panel: mcu-panel {
+		/*
+		 * MEDIA_BUS_FMT_RGB888_3X8  for RGB3x8(8bit)
+		 * MEDIA_BUS_FMT_RGB565_1X16 for RGB565(16bit)
+		 */
+		bus-format = <MEDIA_BUS_FMT_RGB888_3X8>;
+		backlight = <&backlight>;
+		enable-gpios = <&gpio0 RK_PA0 GPIO_ACTIVE_LOW>;
+		enable-delay-ms = <20>;
+		reset-gpios = <&gpio0 RK_PA2 GPIO_ACTIVE_LOW>;
+		reset-delay-ms = <10>;
+		prepare-delay-ms = <20>;
+		unprepare-delay-ms = <20>;
+		disable-delay-ms = <20>;
+		init-delay-ms = <10>;
+		width-mm = <217>;
+		height-mm = <136>;
+
+		// type:0 is cmd, 1 is data
+		panel-init-sequence = [
+			//type delay num val1 val2 val3
+			  00   00  01  e0
+			  01   00  01  00
+			  01   00  01  07
+			  01   00  01  0f
+			  01   00  01  0d
+			  01   00  01  1b
+			  01   00  01  0a
+			  01   00  01  3c
+
+			  01   00  01  78
+			  01   00  01  4a
+			  01   00  01  07
+			  01   00  01  0e
+			  01   00  01  09
+			  01   00  01  1b
+			  01   00  01  1e
+			  01   00  01  0f
+
+			  00   00  01  e1
+			  01   00  01  00
+			  01   00  01  22
+			  01   00  01  24
+			  01   00  01  06
+			  01   00  01  12
+			  01   00  01  07
+			  01   00  01  36
+
+			  01   00  01  47
+			  01   00  01  47
+			  01   00  01  06
+			  01   00  01  0a
+			  01   00  01  07
+			  01   00  01  30
+			  01   00  01  37
+			  01   00  01  0f
+
+			  00   00  01  c0
+			  01   00  01  10
+			  01   00  01  10
+
+			  00   00  01  c1
+			  01   00  01  41
+
+			  00   00  01  c5
+			  01   00  01  00
+			  01   00  01  22
+			  01   00  01  80
+
+			  00   00  01  36
+			  01   00  01  48
+
+			  00   00  01  3a
+			  01   00  01  66 /*
+					   * interface pixel format:
+					   * 66 for RGB3x8(8bit)
+					   * 55 for RGB565(16bit)
+					   */
+
+			  00   00  01  b0
+			  01   00  01  00
+
+			  00   00  01  b1
+			  01   00  01  70 /*
+					   * frame rate control:
+					   * 70 (45hz) for RGB3x8(8bit)
+					   * a0 (60hz) for RGB565(16bit)
+					   */
+			  01   00  01  11
+			  00   00  01  b4
+			  01   00  01  02
+			  00   00  01  B6
+			  01   00  01  02 /*
+					   * display function control:
+					   * 32 for RGB
+					   * 02 for MCU
+					   */
+			  01   00  01  02
+
+			  00   00  01  b7
+			  01   00  01  c6
+
+			  00   00  01  be
+			  01   00  01  00
+			  01   00  01  04
+
+			  00   00  01  e9
+			  01   00  01  00
+
+			  00   00  01  f7
+			  01   00  01  a9
+			  01   00  01  51
+			  01   00  01  2c
+			  01   00  01  82
+
+			  00   78  01  11
+			  00   32  01  29
+			  00   00  01  2c
+		];
+
+		panel-exit-sequence = [
+			//type delay num val1 val2 val3
+			00   0a  01  28
+			00   78  01  10
+		];
+
+		display-timings {
+			native-mode = <&kd050fwfba002_timing>;
+
+			kd050fwfba002_timing: timing0 {
+				/*
+				 * 7840125  for frame rate 45Hz
+				 * 10453500 for frame rate 60Hz
+				 */
+				clock-frequency = <7840125>;
+				hactive = <320>;
+				vactive = <480>;
+				hback-porch = <10>;
+				hfront-porch = <5>;
+				vback-porch = <10>;
+				vfront-porch = <5>;
+				hsync-len = <10>;
+				vsync-len = <10>;
+				hsync-active = <0>;
+				vsync-active = <0>;
+				de-active = <0>;
+				pixelclk-active = <1>;
+			};
+		};
+
+		port {
+			panel_in_rgb: endpoint {
+				remote-endpoint = <&rgb_out_panel>;
+			};
+		};
+	};
+
+	ports {
+		rgb_out: port@1 {
+			reg = <1>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			rgb_out_panel: endpoint@0 {
+				reg = <0>;
+				remote-endpoint = <&panel_in_rgb>;
+			};
+		};
+	};
+};
+
+&rgb_in_vop {
+	status = "okay";
+};
+
+&route_rgb {
+	status = "disabled";
+};
+
+/*
+ * The pins of sdmmc1 and lcd are multiplexed
+ */
+&sdio {
+	status = "disabled";
+};
+
+&sdio_pwrseq {
+	status = "disabled";
+};
+
+&vop {
+	status = "okay";
+
+	/*
+	 * Default config is as follows:
+	 *
+	 * mcu-pix-total = <9>;
+	 * mcu-cs-pst = <1>;
+	 * mcu-cs-pend = <8>;
+	 * mcu-rw-pst = <2>;
+	 * mcu-rw-pend = <5>;
+	 * mcu-hold-mode = <0>; // default set to 0
+	 *
+	 * To increase the frame rate, reduce all parameters because
+	 * the max dclk rate of mcu is 150M in rv1103/rv1106.
+	 */
+	mcu-timing {
+		mcu-pix-total = <5>;
+		mcu-cs-pst = <1>;
+		mcu-cs-pend = <4>;
+		mcu-rw-pst = <2>;
+		mcu-rw-pend = <3>;
+
+		mcu-hold-mode = <0>; // default set to 0
+	};
+};
diff --git a/kernel/arch/arm/boot/dts/rv1106g-evb1-v11-facial-gate.dts b/kernel/arch/arm/boot/dts/rv1106g-evb1-v11-facial-gate.dts
new file mode 100644
index 0000000..69dbb49
--- /dev/null
+++ b/kernel/arch/arm/boot/dts/rv1106g-evb1-v11-facial-gate.dts
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Copyright (c) 2023 Rockchip Electronics Co., Ltd.
+ */
+
+/dts-v1/;
+
+#include "rv1106g-evb1-rgb-display-v11.dts"
+
+/ {
+	model = "Rockchip RV1106G EVB1 V11 Board For Facial Gate";
+	compatible = "rockchip,rv1106g-evb1-v11-spi-nor-facial-gate", "rockchip,rv1106";
+};
+
+&emmc {
+	status = "disabled";
+};
+
+&sfc {
+	status = "okay";
+
+	flash@0 {
+		compatible = "jedec,spi-nor";
+		reg = <0>;
+		spi-max-frequency = <80000000>;
+		spi-rx-bus-width = <4>;
+		spi-tx-bus-width = <1>;
+	};
+};
diff --git a/kernel/arch/arm/boot/dts/rv1106g-evb1-v11-nofastae-spi-nand.dts b/kernel/arch/arm/boot/dts/rv1106g-evb1-v11-nofastae-spi-nand.dts
new file mode 100644
index 0000000..f653f8e
--- /dev/null
+++ b/kernel/arch/arm/boot/dts/rv1106g-evb1-v11-nofastae-spi-nand.dts
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Copyright (c) 2023 Rockchip Electronics Co., Ltd.
+ */
+
+/dts-v1/;
+
+#include "rv1106g-evb1-v11.dts"
+#include "rv1106-tb-nofastae.dtsi"
+
+/ {
+	model = "Rockchip RV1106G EVB1 V11 Board";
+	compatible = "rockchip,rv1106g-evb1-v11", "rockchip,rv1106";
+	chosen {
+		bootargs = "loglevel=0 rootfstype=erofs rootflags=dax console=ttyFIQ0 root=/dev/rd0 snd_soc_core.prealloc_buffer_size_kbytes=16 coherent_pool=0 driver_async_probe=dwmmc_rockchip";
+	};
+};
+
+&fiq_debugger {
+	rockchip,baudrate = <1500000>;
+};
+
+&ramdisk_r {
+	reg = <0x800000 (15 * 0x00100000)>;
+};
+
+&ramdisk_c {
+	reg = <0x1700000 (10 * 0x00100000)>;
+};
diff --git a/kernel/arch/arm/boot/dts/rv1106g-evb2-v10-dual-camera.dts b/kernel/arch/arm/boot/dts/rv1106g-evb2-v10-dual-camera.dts
index e748e20..b24766b 100644
--- a/kernel/arch/arm/boot/dts/rv1106g-evb2-v10-dual-camera.dts
+++ b/kernel/arch/arm/boot/dts/rv1106g-evb2-v10-dual-camera.dts
@@ -64,7 +64,7 @@
 
 			csi_dphy_input0: endpoint@0 {
 				reg = <0>;
-				remote-endpoint = <&sc3338_30_out>;
+				remote-endpoint = <&sc301iot_out>;
 				data-lanes = <1 2>;
 			};
 		};
@@ -96,7 +96,7 @@
 
 			csi_dphy_input1: endpoint@0 {
 				reg = <0>;
-				remote-endpoint = <&sc3338_32_out>;
+				remote-endpoint = <&sc230ai_out>;
 				data-lanes = <1 2>;
 			};
 		};
@@ -127,43 +127,44 @@
 &i2c4 {
 	rockchip,amp-shared;
 
-	sc3338_30: sc3338_30@30 {
-		compatible = "smartsens,sc3338";
+	sc230ai: sc230ai@30 {
+		compatible = "smartsens,sc230ai";
 		status = "okay";
 		reg = <0x30>;
-		clocks = <&cru MCLK_REF_MIPI0>;
+		clocks = <&cru MCLK_REF_MIPI1>;
 		clock-names = "xvclk";
-		pwdn-gpios = <&gpio3 RK_PC5 GPIO_ACTIVE_HIGH>;
+		reset-gpios = <&gpio3 RK_PD1 GPIO_ACTIVE_HIGH>;
 		pinctrl-names = "default";
-		pinctrl-0 = <&mipi_refclk_out0>;
-		rockchip,camera-module-index = <0>;
+		pinctrl-0 = <&mipi_refclk_out1>;
+		rockchip,camera-module-index = <1>;
 		rockchip,camera-module-facing = "back";
-		rockchip,camera-module-name = "FKO1";
-		rockchip,camera-module-lens-name = "30IRC-F16";
+		rockchip,camera-module-name = "CMK-OT2350-PC1";
+		rockchip,camera-module-lens-name = "65IRC-F16";
 		port {
-			sc3338_30_out: endpoint {
-				remote-endpoint = <&csi_dphy_input0>;
+			sc230ai_out: endpoint {
+				remote-endpoint = <&csi_dphy_input1>;
 				data-lanes = <1 2>;
 			};
 		};
 	};
 
-	sc3338_32: sc3338_32@32 {
-		compatible = "smartsens,sc3338";
+	sc301iot: sc301iot@32 {
+		compatible = "smartsens,sc301iot";
 		status = "okay";
 		reg = <0x32>;
-		clocks = <&cru MCLK_REF_MIPI1>;
+		clocks = <&cru MCLK_REF_MIPI0>;
 		clock-names = "xvclk";
-		pwdn-gpios = <&gpio3 RK_PC5 GPIO_ACTIVE_HIGH>;
+		reset-gpios = <&gpio3 RK_PC5 GPIO_ACTIVE_HIGH>;
+		pwdn-gpios = <&gpio3 RK_PD2 GPIO_ACTIVE_HIGH>;
 		pinctrl-names = "default";
-		pinctrl-0 = <&mipi_refclk_out1>;
-		rockchip,camera-module-index = <1>;
+		pinctrl-0 = <&mipi_refclk_out0>;
+		rockchip,camera-module-index = <0>;
 		rockchip,camera-module-facing = "back";
-		rockchip,camera-module-name = "FKO1";
-		rockchip,camera-module-lens-name = "30IRC-F16";
+		rockchip,camera-module-name = "CMK-OT2349-PC1";
+		rockchip,camera-module-lens-name = "65IRC-F20";
 		port {
-			sc3338_32_out: endpoint {
-				remote-endpoint = <&csi_dphy_input1>;
+			sc301iot_out: endpoint {
+				remote-endpoint = <&csi_dphy_input0>;
 				data-lanes = <1 2>;
 			};
 		};
@@ -327,29 +328,29 @@
 	/* reg's offset MUST match with RTOS */
 	/*
 	 * vicap, capture raw10, ceil(w*10/8/256)*256*h *4(buf num)
-	 * e.g. 2304x1296: 0xf30000
+	 * e.g. 2048x1536: 0xf00000
 	 * 0x008b0000 = (meta's reg offset) + (meta's reg size)
 	 *            = 0x00800000 + 0xb0000
 	 */
-	reg = <0x008b0000 0xf30000>;
+	reg = <0x008b0000 0xf00000>;
 };
 
 &ramdisk_r {
-	reg = <0x17e0000 (10 * 0x00100000)>;
+	reg = <0x17b0000 (10 * 0x00100000)>;
 };
 
 &ramdisk_c {
-	reg = <0x21e0000 (5 * 0x00100000)>;
+	reg = <0x21b0000 (5 * 0x00100000)>;
 };
 
 &rkisp1_thunderboot {
 	/*
 	 * vicap, capture raw10, ceil(w*10/8/256)*256*h *4(buf num)
-	 * e.g. 2304x1296: 0xf30000
-	 * 0x26e0000  = (ramdisk_c's reg offset) + (ramdisk_c's reg size)
-	 *            = 0x21e0000 + (5 * 0x00100000)
+	 * e.g. 1920x1080: 0xa8c0000
+	 * 0x26b0000  = (ramdisk_c's reg offset) + (ramdisk_c's reg size)
+	 *            = 0x21b0000 + (5 * 0x00100000)
 	 */
-	reg = <0x26e0000 0xf30000>;
+	reg = <0x26b0000 0xa8c000>;
 };
 
 &pinctrl {
diff --git a/kernel/arch/arm/boot/dts/rv1106g-evb2-v10.dts b/kernel/arch/arm/boot/dts/rv1106g-evb2-v10.dts
index 9027454..47b5646 100644
--- a/kernel/arch/arm/boot/dts/rv1106g-evb2-v10.dts
+++ b/kernel/arch/arm/boot/dts/rv1106g-evb2-v10.dts
@@ -107,7 +107,8 @@
 		reg = <0x30>;
 		clocks = <&cru MCLK_REF_MIPI0>;
 		clock-names = "xvclk";
-		pwdn-gpios = <&gpio3 RK_PC5 GPIO_ACTIVE_HIGH>;
+		reset-gpios = <&gpio3 RK_PC5 GPIO_ACTIVE_HIGH>;
+		pwdn-gpios = <&gpio3 RK_PD2 GPIO_ACTIVE_HIGH>;
 		pinctrl-names = "default";
 		pinctrl-0 = <&mipi_refclk_out0>;
 		rockchip,camera-module-index = <0>;
diff --git a/kernel/arch/arm/boot/dts/rv1106g-evb2-v11-trailcam-emmc.dts b/kernel/arch/arm/boot/dts/rv1106g-evb2-v11-trailcam-emmc.dts
new file mode 100644
index 0000000..0710c27
--- /dev/null
+++ b/kernel/arch/arm/boot/dts/rv1106g-evb2-v11-trailcam-emmc.dts
@@ -0,0 +1,622 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Copyright (c) 2023 Rockchip Electronics Co., Ltd.
+ */
+
+/dts-v1/;
+
+#include "rv1106.dtsi"
+#include "rv1106-evb-v10.dtsi"
+#include "dt-bindings/display/media-bus-format.h"
+#include "rv1106-thunder-boot-emmc.dtsi"
+
+/ {
+	model = "Rockchip RV1106G EVB2 V11 Trailcam Board";
+	compatible = "rockchip,rv1106g-evb2-v11-trailcam-emmc", "rockchip,rv1106";
+
+	chosen {
+		bootargs = "loglevel=0 rootfstype=erofs rootflags=dax console=ttyFIQ0 root=/dev/rd0 snd_soc_core.prealloc_buffer_size_kbytes=16 coherent_pool=0 driver_async_probe=dwmmc_rockchip";
+	};
+
+	/delete-node/ adc-keys;
+	adc-keys {
+		compatible = "adc-keys";
+		io-channels = <&saradc 0>;
+		io-channel-names = "buttons";
+		poll-interval = <100>;
+		keyup-threshold-microvolt = <901715>;
+
+		key_volumeup-key {
+			label = "key_volumeup";
+			linux,code = <KEY_VOLUMEUP>;
+			press-threshold-microvolt = <17578>;
+		};
+	};
+
+	backlight: backlight {
+		status = "okay";
+		compatible = "pwm-backlight";
+		pwms = <&pwm7 0 25000 0>;
+		brightness-levels = <
+			  0   1   2   3   4   5   6   7
+			  8   9  10  11  12  13  14  15
+			 16  17  18  19  20  21  22  23
+			 24  25  26  27  28  29  30  31
+			 32  33  34  35  36  37  38  39
+			 40  41  42  43  44  45  46  47
+			 48  49  50  51  52  53  54  55
+			 56  57  58  59  60  61  62  63
+			 64  65  66  67  68  69  70  71
+			 72  73  74  75  76  77  78  79
+			 80  81  82  83  84  85  86  87
+			 88  89  90  91  92  93  94  95
+			 96  97  98  99 100 101 102 103
+			104 105 106 107 108 109 110 111
+			112 113 114 115 116 117 118 119
+			120 121 122 123 124 125 126 127
+			128 129 130 131 132 133 134 135
+			136 137 138 139 140 141 142 143
+			144 145 146 147 148 149 150 151
+			152 153 154 155 156 157 158 159
+			160 161 162 163 164 165 166 167
+			168 169 170 171 172 173 174 175
+			176 177 178 179 180 181 182 183
+			184 185 186 187 188 189 190 191
+			192 193 194 195 196 197 198 199
+			200 201 202 203 204 205 206 207
+			208 209 210 211 212 213 214 215
+			216 217 218 219 220 221 222 223
+			224 225 226 227 228 229 230 231
+			232 233 234 235 236 237 238 239
+			240 241 242 243 244 245 246 247
+			248 249 250 251 252 253 254 255>;
+		default-brightness-level = <200>;
+	};
+
+	gpio-keys {
+		compatible = "gpio-keys";
+		autorepeat;
+
+		key {
+			gpios = <&gpio0 RK_PA1 GPIO_ACTIVE_HIGH>;
+			linux,code = <KEY_VOLUMEDOWN>;
+			label = "GPIO Key";
+			debounce-interval = <100>;
+		};
+	};
+
+	reserved-memory {
+		#address-cells = <1>;
+		#size-cells = <1>;
+		ranges;
+/*
+		linux,cma {
+			compatible = "shared-dma-pool";
+			inactive;
+			reusable;
+			size = <0x300000>;
+			linux,cma-default;
+		};
+*/
+		drm_logo: drm-logo@00000000 {
+			compatible = "rockchip,drm-logo";
+			reg = <0x0 0x0>;
+		};
+	};
+
+	vcc_1v8: vcc-1v8 {
+		compatible = "regulator-fixed";
+		regulator-name = "vcc_1v8";
+		regulator-always-on;
+		regulator-boot-on;
+		regulator-min-microvolt = <1800000>;
+		regulator-max-microvolt = <1800000>;
+	};
+
+	vcc_3v3: vcc-3v3 {
+		compatible = "regulator-fixed";
+		regulator-name = "vcc_3v3";
+		regulator-always-on;
+		regulator-boot-on;
+		regulator-min-microvolt = <3300000>;
+		regulator-max-microvolt = <3300000>;
+	};
+
+	vcc3v3_sd: vcc3v3-sd {
+		compatible = "regulator-fixed";
+		gpio = <&gpio2 RK_PA7 GPIO_ACTIVE_LOW>;
+		regulator-name = "vcc3v3_sd";
+		regulator-min-microvolt = <3300000>;
+		regulator-max-microvolt = <3300000>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&sdmmc_pwren>;
+	};
+
+	wireless_wlan: wireless-wlan {
+		compatible = "wlan-platdata";
+		WIFI,host_wake_irq = <&gpio1 RK_PB0 GPIO_ACTIVE_HIGH>;
+		status = "okay";
+	};
+};
+
+&acodec {
+	pa-ctl-gpios = <&gpio2 RK_PB0 GPIO_ACTIVE_HIGH>;
+};
+
+&csi2_dphy_hw {
+	status = "okay";
+};
+
+&csi2_dphy0 {
+	status = "okay";
+
+	ports {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		port@0 {
+			reg = <0>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			csi_dphy_input0: endpoint@0 {
+				reg = <0>;
+				remote-endpoint = <&sc3338_out>;
+				data-lanes = <1 2>;
+			};
+		};
+
+		port@1 {
+			reg = <1>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			csi_dphy_output: endpoint@0 {
+				reg = <0>;
+				remote-endpoint = <&mipi_csi2_input>;
+			};
+		};
+	};
+};
+
+&display_subsystem {
+	status = "okay";
+	logo-memory-region = <&drm_logo>;
+};
+
+&emmc {
+	status = "okay";
+};
+
+&fiq_debugger {
+	rockchip,baudrate = <1500000>;
+	pinctrl-names = "default";
+	pinctrl-0 = <&uart2m1_xfer>;
+};
+
+&i2c4 {
+	rockchip,amp-shared;
+
+	sc3338: sc3338@30 {
+		compatible = "smartsens,sc3338";
+		status = "okay";
+		reg = <0x30>;
+		clocks = <&cru MCLK_REF_MIPI0>;
+		clock-names = "xvclk";
+		pwdn-gpios = <&gpio3 RK_PC5 GPIO_ACTIVE_HIGH>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&mipi_refclk_out0>;
+		rockchip,camera-module-index = <0>;
+		rockchip,camera-module-facing = "back";
+		rockchip,camera-module-name = "FKO1";
+		rockchip,camera-module-lens-name = "30IRC-F16";
+		port {
+			sc3338_out: endpoint {
+				remote-endpoint = <&csi_dphy_input0>;
+				data-lanes = <1 2>;
+			};
+		};
+	};
+};
+
+&mipi0_csi2 {
+	status = "okay";
+
+	ports {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		port@0 {
+			reg = <0>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			mipi_csi2_input: endpoint@1 {
+				reg = <1>;
+				remote-endpoint = <&csi_dphy_output>;
+			};
+		};
+
+		port@1 {
+			reg = <1>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			mipi_csi2_output: endpoint@0 {
+				reg = <0>;
+				remote-endpoint = <&cif_mipi_in>;
+			};
+		};
+	};
+};
+
+&mailbox {
+	status = "okay";
+};
+
+&pwm7 {
+	status = "okay";
+	pinctrl-names = "active";
+	pinctrl-0 = <&pwm7m1_pins>;
+};
+
+&pinctrl {
+	sdmmc {
+		/omit-if-no-ref/
+		sdmmc_pwren: sdmmc-pwren {
+			rockchip,pins = <2 RK_PA7 RK_FUNC_GPIO &pcfg_pull_none>;
+		};
+	};
+};
+
+&pwm10 {
+	status = "okay";
+};
+
+&pwm11 {
+	status = "okay";
+};
+
+&rgb {
+	status = "okay";
+	rockchip,data-sync-bypass;
+	pinctrl-names = "default";
+	/*
+	 * rgb3x8_pins for RGB3x8(8bit)
+	 * rgb565_pins for RGB565(16bit)
+	 */
+	pinctrl-0 = <&rgb3x8_pins>;
+
+	/*
+	 * 320x480 RGB/MCU screen K350C4516T
+	 */
+	mcu_panel: mcu-panel {
+		/*
+		 * MEDIA_BUS_FMT_RGB888_3X8  for RGB3x8(8bit)
+		 * MEDIA_BUS_FMT_RGB565_1X16 for RGB565(16bit)
+		 */
+		bus-format = <MEDIA_BUS_FMT_RGB888_3X8>;
+		backlight = <&backlight>;
+		enable-gpios = <&gpio2 RK_PA6 GPIO_ACTIVE_LOW>;
+		enable-delay-ms = <20>;
+		reset-gpios = <&gpio1 RK_PA1 GPIO_ACTIVE_LOW>;
+		reset-delay-ms = <10>;
+		prepare-delay-ms = <20>;
+		unprepare-delay-ms = <20>;
+		disable-delay-ms = <20>;
+		init-delay-ms = <10>;
+		width-mm = <217>;
+		height-mm = <136>;
+
+		// type:0 is cmd, 1 is data
+		panel-init-sequence = [
+			//type delay num val1 val2 val3
+			  00   00  01  e0
+			  01   00  01  00
+			  01   00  01  07
+			  01   00  01  0f
+			  01   00  01  0d
+			  01   00  01  1b
+			  01   00  01  0a
+			  01   00  01  3c
+
+			  01   00  01  78
+			  01   00  01  4a
+			  01   00  01  07
+			  01   00  01  0e
+			  01   00  01  09
+			  01   00  01  1b
+			  01   00  01  1e
+			  01   00  01  0f
+
+			  00   00  01  e1
+			  01   00  01  00
+			  01   00  01  22
+			  01   00  01  24
+			  01   00  01  06
+			  01   00  01  12
+			  01   00  01  07
+			  01   00  01  36
+
+			  01   00  01  47
+			  01   00  01  47
+			  01   00  01  06
+			  01   00  01  0a
+			  01   00  01  07
+			  01   00  01  30
+			  01   00  01  37
+			  01   00  01  0f
+
+			  00   00  01  c0
+			  01   00  01  10
+			  01   00  01  10
+
+			  00   00  01  c1
+			  01   00  01  41
+
+			  00   00  01  c5
+			  01   00  01  00
+			  01   00  01  22
+			  01   00  01  80
+
+			  00   00  01  36
+			  01   00  01  48
+
+			  00   00  01  3a
+			  01   00  01  66 /*
+					   * interface pixel format:
+					   * 66 for RGB3x8(8bit)
+					   * 55 for RGB565(16bit)
+					   */
+
+			  00   00  01  b0
+			  01   00  01  00
+
+			  00   00  01  b1
+			  01   00  01  70 /*
+					   * frame rate control:
+					   * 70 (45hz) for RGB3x8(8bit)
+					   * a0 (60hz) for RGB565(16bit)
+					   */
+			  01   00  01  11
+			  00   00  01  b4
+			  01   00  01  02
+			  00   00  01  B6
+			  01   00  01  02 /*
+					   * display function control:
+					   * 32 for RGB
+					   * 02 for MCU
+					   */
+			  01   00  01  02
+
+			  00   00  01  b7
+			  01   00  01  c6
+
+			  00   00  01  be
+			  01   00  01  00
+			  01   00  01  04
+
+			  00   00  01  e9
+			  01   00  01  00
+
+			  00   00  01  f7
+			  01   00  01  a9
+			  01   00  01  51
+			  01   00  01  2c
+			  01   00  01  82
+
+			  00   78  01  11
+			  00   32  01  29
+			  00   00  01  2c
+		];
+
+		panel-exit-sequence = [
+			//type delay num val1 val2 val3
+			00   0a  01  28
+			00   78  01  10
+		];
+
+		display-timings {
+			native-mode = <&kd050fwfba002_timing>;
+
+			kd050fwfba002_timing: timing0 {
+				/*
+				 * 7840125  for frame rate 45Hz
+				 * 10453500 for frame rate 60Hz
+				 */
+				clock-frequency = <7840125>;
+				hactive = <320>;
+				vactive = <480>;
+				hback-porch = <10>;
+				hfront-porch = <5>;
+				vback-porch = <10>;
+				vfront-porch = <5>;
+				hsync-len = <10>;
+				vsync-len = <10>;
+				hsync-active = <0>;
+				vsync-active = <0>;
+				de-active = <0>;
+				pixelclk-active = <1>;
+			};
+		};
+
+		port {
+			panel_in_rgb: endpoint {
+				remote-endpoint = <&rgb_out_panel>;
+			};
+		};
+	};
+
+	ports {
+		rgb_out: port@1 {
+			reg = <1>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			rgb_out_panel: endpoint@0 {
+				reg = <0>;
+				remote-endpoint = <&panel_in_rgb>;
+			};
+		};
+	};
+};
+
+&rgb_in_vop {
+	status = "okay";
+};
+
+&route_rgb {
+	status = "disabled";
+};
+
+/*
+ * The pins of sdmmc1 and lcd are multiplexed
+ */
+&sdio {
+	status = "disabled";
+};
+
+&saradc {
+	status = "okay";
+	vref-supply = <&vcc_1v8>;
+};
+
+&thunder_boot_service {
+	status = "okay";
+};
+
+&rkisp_thunderboot {
+	/* reg's offset MUST match with RTOS */
+	/*
+	 * vicap, capture raw10, ceil(w*10/8/256)*256*h *4(buf num)
+	 * e.g. 2304x1296: 0xf30000
+	 */
+	reg = <0x00860000 0xf30000>;
+};
+
+&ramdisk_r {
+	reg = <0x1790000 (20 * 0x00100000)>;
+};
+
+&ramdisk_c {
+	reg = <0x2b90000 (10 * 0x00100000)>;
+};
+
+&rkcif {
+	status = "okay";
+};
+
+&rkcif_mipi_lvds {
+	status = "okay";
+	memory-region-thunderboot = <&rkisp_thunderboot>;
+
+	pinctrl-names = "default";
+	pinctrl-0 = <&mipi_pins>;
+	port {
+		/* MIPI CSI-2 endpoint */
+		cif_mipi_in: endpoint {
+			remote-endpoint = <&mipi_csi2_output>;
+		};
+	};
+};
+
+&rkcif_mipi_lvds_sditf {
+	status = "okay";
+
+	port {
+		/* MIPI CSI-2 endpoint */
+		mipi_lvds_sditf: endpoint {
+			remote-endpoint = <&isp_in>;
+		};
+	};
+};
+
+&rkisp {
+	status = "okay";
+};
+
+&rkisp_vir0 {
+	status = "okay";
+
+	port@0 {
+		isp_in: endpoint {
+			remote-endpoint = <&mipi_lvds_sditf>;
+		};
+	};
+};
+
+&sdio {
+	max-frequency = <50000000>;
+	bus-width = <1>;
+	cap-sd-highspeed;
+	cap-sdio-irq;
+	keep-power-in-suspend;
+	non-removable;
+	rockchip,default-sample-phase = <90>;
+	no-sd;
+	no-mmc;
+	supports-sdio;
+	pinctrl-names = "default";
+	pinctrl-0 = <&sdmmc1m0_cmd &sdmmc1m0_clk &sdmmc1m0_bus4>;
+	status = "okay";
+};
+
+&sdmmc {
+	max-frequency = <200000000>;
+	no-sdio;
+	no-mmc;
+	bus-width = <4>;
+	cap-mmc-highspeed;
+	cap-sd-highspeed;
+	disable-wp;
+	pinctrl-names = "normal",  "idle";
+	pinctrl-0 = <&sdmmc0_clk &sdmmc0_cmd &sdmmc0_det &sdmmc0_bus4>;
+	pinctrl-1 = <&sdmmc0_idle_pins &sdmmc0_det>;
+	vmmc-supply = <&vcc3v3_sd>;
+	status = "okay";
+};
+
+&sfc {
+	assigned-clocks = <&cru SCLK_SFC>;
+	assigned-clock-rates = <125000000>;
+	status = "disabled";
+
+	flash@0 {
+		compatible = "jedec,spi-nor";
+		reg = <0>;
+		spi-max-frequency = <125000000>;
+		spi-rx-bus-width = <4>;
+		spi-tx-bus-width = <1>;
+	};
+};
+
+&usbdrd_dwc3 {
+	dr_mode = "peripheral";
+};
+
+&vop {
+	status = "okay";
+
+	/*
+	 * Default config is as follows:
+	 *
+	 * mcu-pix-total = <9>;
+	 * mcu-cs-pst = <1>;
+	 * mcu-cs-pend = <8>;
+	 * mcu-rw-pst = <2>;
+	 * mcu-rw-pend = <5>;
+	 * mcu-hold-mode = <0>; // default set to 0
+	 *
+	 * To increase the frame rate, reduce all parameters because
+	 * the max dclk rate of mcu is 150M in rv1103/rv1106.
+	 */
+	mcu-timing {
+		mcu-pix-total = <5>;
+		mcu-cs-pst = <1>;
+		mcu-cs-pend = <4>;
+		mcu-rw-pst = <2>;
+		mcu-rw-pend = <3>;
+
+		mcu-hold-mode = <0>; // default set to 0
+	};
+};
diff --git a/kernel/arch/arm/boot/dts/rv1106g-evb2-v12-nofastae-emmc.dts b/kernel/arch/arm/boot/dts/rv1106g-evb2-v12-nofastae-emmc.dts
new file mode 100644
index 0000000..a49f45d
--- /dev/null
+++ b/kernel/arch/arm/boot/dts/rv1106g-evb2-v12-nofastae-emmc.dts
@@ -0,0 +1,134 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Copyright (c) 2023 Rockchip Electronics Co., Ltd.
+ */
+
+/dts-v1/;
+
+#include "rv1106.dtsi"
+#include "rv1106-evb-v10.dtsi"
+#include "rv1106-evb-cam.dtsi"
+#include "rv1106-tb-nofastae-emmc.dtsi"
+
+/ {
+	model = "Rockchip RV1106G EVB2 V12 Board";
+	compatible = "rockchip,rv1106g-evb2-v12", "rockchip,rv1106";
+
+	chosen {
+		bootargs = "loglevel=0 rootfstype=erofs rootflags=dax console=ttyFIQ0 root=/dev/rd0 snd_soc_core.prealloc_buffer_size_kbytes=16 coherent_pool=0 driver_async_probe=dwmmc_rockchip";
+	};
+
+	vcc_1v8: vcc-1v8 {
+		compatible = "regulator-fixed";
+		regulator-name = "vcc_1v8";
+		regulator-always-on;
+		regulator-boot-on;
+		regulator-min-microvolt = <1800000>;
+		regulator-max-microvolt = <1800000>;
+	};
+
+	vcc_3v3: vcc-3v3 {
+		compatible = "regulator-fixed";
+		regulator-name = "vcc_3v3";
+		regulator-always-on;
+		regulator-boot-on;
+		regulator-min-microvolt = <3300000>;
+		regulator-max-microvolt = <3300000>;
+	};
+
+	vcc3v3_sd: vcc3v3-sd {
+		compatible = "regulator-fixed";
+		gpio = <&gpio2 RK_PA7 GPIO_ACTIVE_LOW>;
+		regulator-name = "vcc3v3_sd";
+		regulator-min-microvolt = <3300000>;
+		regulator-max-microvolt = <3300000>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&sdmmc_pwren>;
+	};
+
+	wireless_wlan: wireless-wlan {
+		compatible = "wlan-platdata";
+		WIFI,host_wake_irq = <&gpio1 RK_PB0 GPIO_ACTIVE_HIGH>;
+		status = "okay";
+	};
+};
+
+&fiq_debugger {
+	rockchip,baudrate = <1500000>;
+	pinctrl-names = "default";
+	pinctrl-0 = <&uart2m1_xfer>;
+};
+
+&pinctrl {
+	sdmmc {
+		/omit-if-no-ref/
+		sdmmc_pwren: sdmmc-pwren {
+			rockchip,pins = <2 RK_PA7 RK_FUNC_GPIO &pcfg_pull_none>;
+		};
+	};
+};
+
+&pwm10 {
+	status = "okay";
+};
+
+&pwm11 {
+	status = "okay";
+};
+
+&ramdisk_r {
+	reg = <0x800000 (20 * 0x00100000)>;
+};
+
+&ramdisk_c {
+	reg = <0x1C00000 (10 * 0x00100000)>;
+};
+
+&sdio {
+	max-frequency = <50000000>;
+	bus-width = <1>;
+	cap-sd-highspeed;
+	cap-sdio-irq;
+	keep-power-in-suspend;
+	non-removable;
+	rockchip,default-sample-phase = <90>;
+	no-sd;
+	no-mmc;
+	supports-sdio;
+	pinctrl-names = "default";
+	pinctrl-0 = <&sdmmc1m0_cmd &sdmmc1m0_clk &sdmmc1m0_bus4>;
+	status = "okay";
+};
+
+&sdmmc {
+	max-frequency = <200000000>;
+	no-sdio;
+	no-mmc;
+	bus-width = <4>;
+	cap-mmc-highspeed;
+	cap-sd-highspeed;
+	disable-wp;
+	pinctrl-names = "normal", "idle";
+	pinctrl-0 = <&sdmmc0_clk &sdmmc0_cmd &sdmmc0_det &sdmmc0_bus4>;
+	pinctrl-1 = <&sdmmc0_idle_pins &sdmmc0_det>;
+	vmmc-supply = <&vcc3v3_sd>;
+	status = "okay";
+};
+
+&sfc {
+	assigned-clocks = <&cru SCLK_SFC>;
+	assigned-clock-rates = <125000000>;
+	status = "disabled";
+
+	flash@0 {
+		compatible = "jedec,spi-nor";
+		reg = <0>;
+		spi-max-frequency = <125000000>;
+		spi-rx-bus-width = <4>;
+		spi-tx-bus-width = <1>;
+	};
+};
+
+&usbdrd_dwc3 {
+	dr_mode = "peripheral";
+};
diff --git a/kernel/arch/arm/boot/dts/rv1106g-evb2-v12-nofastae-spi-nand.dts b/kernel/arch/arm/boot/dts/rv1106g-evb2-v12-nofastae-spi-nand.dts
new file mode 100644
index 0000000..a7b53a7
--- /dev/null
+++ b/kernel/arch/arm/boot/dts/rv1106g-evb2-v12-nofastae-spi-nand.dts
@@ -0,0 +1,129 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Copyright (c) 2023 Rockchip Electronics Co., Ltd.
+ */
+
+/dts-v1/;
+
+#include "rv1106.dtsi"
+#include "rv1106-evb-v10.dtsi"
+#include "rv1106-evb-cam.dtsi"
+#include "rv1106-tb-nofastae.dtsi"
+
+/ {
+	model = "Rockchip RV1106G EVB2 V12 Board SPI NAND";
+	compatible = "rockchip,rv1106g-evb2-v12", "rockchip,rv1106";
+	chosen {
+		bootargs = "loglevel=0 rootfstype=erofs rootflags=dax console=ttyFIQ0 root=/dev/rd0 snd_soc_core.prealloc_buffer_size_kbytes=16 coherent_pool=0 driver_async_probe=dwmmc_rockchip";
+	};
+
+	vcc_1v8: vcc-1v8 {
+		compatible = "regulator-fixed";
+		regulator-name = "vcc_1v8";
+		regulator-always-on;
+		regulator-boot-on;
+		regulator-min-microvolt = <1800000>;
+		regulator-max-microvolt = <1800000>;
+	};
+
+	vcc_3v3: vcc-3v3 {
+		compatible = "regulator-fixed";
+		regulator-name = "vcc_3v3";
+		regulator-always-on;
+		regulator-boot-on;
+		regulator-min-microvolt = <3300000>;
+		regulator-max-microvolt = <3300000>;
+	};
+
+	vcc3v3_sd: vcc3v3-sd {
+		compatible = "regulator-fixed";
+		gpio = <&gpio2 RK_PA7 GPIO_ACTIVE_LOW>;
+		regulator-name = "vcc3v3_sd";
+		regulator-min-microvolt = <3300000>;
+		regulator-max-microvolt = <3300000>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&sdmmc_pwren>;
+	};
+
+	wireless_wlan: wireless-wlan {
+		compatible = "wlan-platdata";
+		WIFI,host_wake_irq = <&gpio1 RK_PB0 GPIO_ACTIVE_HIGH>;
+		status = "okay";
+	};
+};
+
+&fiq_debugger {
+	rockchip,baudrate = <1500000>;
+};
+
+&pinctrl {
+	sdmmc {
+		/omit-if-no-ref/
+		sdmmc_pwren: sdmmc-pwren {
+			rockchip,pins = <2 RK_PA7 RK_FUNC_GPIO &pcfg_pull_none>;
+		};
+	};
+};
+
+&pwm10 {
+	status = "okay";
+};
+
+&pwm11 {
+	status = "okay";
+};
+
+&ramdisk_r {
+	reg = <0x800000 (15 * 0x00100000)>;
+};
+
+&ramdisk_c {
+	reg = <0x1700000 (10 * 0x00100000)>;
+};
+
+&sdio {
+	max-frequency = <50000000>;
+	bus-width = <1>;
+	cap-sd-highspeed;
+	cap-sdio-irq;
+	keep-power-in-suspend;
+	non-removable;
+	rockchip,default-sample-phase = <90>;
+	no-sd;
+	no-mmc;
+	supports-sdio;
+	pinctrl-names = "default";
+	pinctrl-0 = <&sdmmc1m0_cmd &sdmmc1m0_clk &sdmmc1m0_bus4>;
+	status = "okay";
+};
+
+&sdmmc {
+	max-frequency = <200000000>;
+	no-sdio;
+	no-mmc;
+	bus-width = <4>;
+	cap-mmc-highspeed;
+	cap-sd-highspeed;
+	disable-wp;
+	pinctrl-names = "normal", "idle";
+	pinctrl-0 = <&sdmmc0_clk &sdmmc0_cmd &sdmmc0_det &sdmmc0_bus4>;
+	pinctrl-1 = <&sdmmc0_idle_pins &sdmmc0_det>;
+	vmmc-supply = <&vcc3v3_sd>;
+	status = "okay";
+};
+
+&sfc {
+	status = "okay";
+
+	flash@0 {
+		compatible = "spi-nand";
+		reg = <0>;
+		spi-max-frequency = <75000000>;
+		spi-rx-bus-width = <4>;
+		spi-tx-bus-width = <1>;
+	};
+};
+
+&usbdrd_dwc3 {
+	dr_mode = "peripheral";
+};
diff --git a/kernel/arch/arm/boot/dts/rv1106g-evb2-v12-nofastae-spi-nor.dts b/kernel/arch/arm/boot/dts/rv1106g-evb2-v12-nofastae-spi-nor.dts
new file mode 100644
index 0000000..dcc4da3
--- /dev/null
+++ b/kernel/arch/arm/boot/dts/rv1106g-evb2-v12-nofastae-spi-nor.dts
@@ -0,0 +1,134 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Copyright (c) 2023 Rockchip Electronics Co., Ltd.
+ */
+
+/dts-v1/;
+
+#include "rv1106.dtsi"
+#include "rv1106-evb-v10.dtsi"
+#include "rv1106-evb-cam.dtsi"
+#include "rv1106-tb-nofastae-spi-nor.dtsi"
+
+/ {
+	model = "Rockchip RV1106G EVB2 V12 Board";
+	compatible = "rockchip,rv1106g-evb2-v12", "rockchip,rv1106";
+
+	chosen {
+		bootargs = "loglevel=0 rootfstype=erofs rootflags=dax console=ttyFIQ0 root=/dev/rd0 snd_soc_core.prealloc_buffer_size_kbytes=16 coherent_pool=0 driver_async_probe=dwmmc_rockchip";
+	};
+
+	vcc_1v8: vcc-1v8 {
+		compatible = "regulator-fixed";
+		regulator-name = "vcc_1v8";
+		regulator-always-on;
+		regulator-boot-on;
+		regulator-min-microvolt = <1800000>;
+		regulator-max-microvolt = <1800000>;
+	};
+
+	vcc_3v3: vcc-3v3 {
+		compatible = "regulator-fixed";
+		regulator-name = "vcc_3v3";
+		regulator-always-on;
+		regulator-boot-on;
+		regulator-min-microvolt = <3300000>;
+		regulator-max-microvolt = <3300000>;
+	};
+
+	vcc3v3_sd: vcc3v3-sd {
+		compatible = "regulator-fixed";
+		gpio = <&gpio2 RK_PA7 GPIO_ACTIVE_LOW>;
+		regulator-name = "vcc3v3_sd";
+		regulator-min-microvolt = <3300000>;
+		regulator-max-microvolt = <3300000>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&sdmmc_pwren>;
+	};
+
+	wireless_wlan: wireless-wlan {
+		compatible = "wlan-platdata";
+		WIFI,host_wake_irq = <&gpio1 RK_PB0 GPIO_ACTIVE_HIGH>;
+		status = "okay";
+	};
+};
+
+&fiq_debugger {
+	rockchip,baudrate = <1500000>;
+	pinctrl-names = "default";
+	pinctrl-0 = <&uart2m1_xfer>;
+};
+
+&pinctrl {
+	sdmmc {
+		/omit-if-no-ref/
+		sdmmc_pwren: sdmmc-pwren {
+			rockchip,pins = <2 RK_PA7 RK_FUNC_GPIO &pcfg_pull_none>;
+		};
+	};
+};
+
+&pwm10 {
+	status = "okay";
+};
+
+&pwm11 {
+	status = "okay";
+};
+
+&ramdisk_r {
+	reg = <0x800000 (10 * 0x00100000)>;
+};
+
+&ramdisk_c {
+	reg = <0x1200000 (5 * 0x00100000)>;
+};
+
+&sdio {
+	max-frequency = <50000000>;
+	bus-width = <1>;
+	cap-sd-highspeed;
+	cap-sdio-irq;
+	keep-power-in-suspend;
+	non-removable;
+	rockchip,default-sample-phase = <90>;
+	no-sd;
+	no-mmc;
+	supports-sdio;
+	pinctrl-names = "default";
+	pinctrl-0 = <&sdmmc1m0_cmd &sdmmc1m0_clk &sdmmc1m0_bus4>;
+	status = "okay";
+};
+
+&sdmmc {
+	max-frequency = <200000000>;
+	no-sdio;
+	no-mmc;
+	bus-width = <4>;
+	cap-mmc-highspeed;
+	cap-sd-highspeed;
+	disable-wp;
+	pinctrl-names = "normal", "idle";
+	pinctrl-0 = <&sdmmc0_clk &sdmmc0_cmd &sdmmc0_det &sdmmc0_bus4>;
+	pinctrl-1 = <&sdmmc0_idle_pins &sdmmc0_det>;
+	vmmc-supply = <&vcc3v3_sd>;
+	status = "okay";
+};
+
+&sfc {
+	assigned-clocks = <&cru SCLK_SFC>;
+	assigned-clock-rates = <125000000>;
+	status = "okay";
+
+	flash@0 {
+		compatible = "jedec,spi-nor";
+		reg = <0>;
+		spi-max-frequency = <125000000>;
+		spi-rx-bus-width = <4>;
+		spi-tx-bus-width = <1>;
+	};
+};
+
+&usbdrd_dwc3 {
+	dr_mode = "peripheral";
+};
diff --git a/kernel/arch/arm/boot/dts/rv1106g-evb2-v12-wakeup.dts b/kernel/arch/arm/boot/dts/rv1106g-evb2-v12-wakeup.dts
new file mode 100644
index 0000000..0ed1b84
--- /dev/null
+++ b/kernel/arch/arm/boot/dts/rv1106g-evb2-v12-wakeup.dts
@@ -0,0 +1,327 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Copyright (c) 2023 Rockchip Electronics Co., Ltd.
+ */
+
+/dts-v1/;
+
+#include "rv1106.dtsi"
+#include "rv1106-evb-v10.dtsi"
+
+/ {
+	model = "Rockchip RV1106G EVB2 V12 Board";
+	compatible = "rockchip,rv1106g-evb2-v12-wakeup", "rockchip,rv1106";
+
+	chosen {
+		bootargs = "clk_gate.always_on=1";
+	};
+
+	reserved-memory {
+		#address-cells = <1>;
+		#size-cells = <1>;
+		ranges;
+
+		rtos: rtos@40000 {
+			reg = <0x40000 0x3c000>;
+		};
+
+		meta: meta@800000 {
+			/* reg's offset MUST match with RTOS */
+			reg = <0x00800000 0x60000>;
+		};
+
+		rkisp_thunderboot: rkisp@860000 {
+			/* reg's offset MUST match with RTOS */
+			/*
+			 * vicap, capture raw10, ceil(w*10/8/256)*256*h *4(buf num)
+			 * e.g. 1920x1080: 0xa8c000
+			 */
+			reg = <0x00860000 0xa8c000>;
+		};
+	};
+
+	thunder_boot_service: thunder-boot-service {
+		compatible = "rockchip,thunder-boot-service";
+		mbox-names = "amp-rx";
+		mboxes = <&mailbox 1>;
+		resets = <&cru SRST_CORE_MCU>, <&cru SRST_CORE_MCU_PWRUP>,
+			 <&cru SRST_CORE_MCU_CPU>, <&cru SRST_T_CORE_MCU_CPU>;
+		reset-names = "core_mcu", "core_mcu_pwrup",
+			      "core_mcu_cpu", "t_core_mcu_cpu";
+		memory-region = <&rtos>;
+		status = "disabled";
+	};
+
+	vcc_1v8: vcc-1v8 {
+		compatible = "regulator-fixed";
+		regulator-name = "vcc_1v8";
+		regulator-always-on;
+		regulator-boot-on;
+		regulator-min-microvolt = <1800000>;
+		regulator-max-microvolt = <1800000>;
+	};
+
+	vcc_3v3: vcc-3v3 {
+		compatible = "regulator-fixed";
+		regulator-name = "vcc_3v3";
+		regulator-always-on;
+		regulator-boot-on;
+		regulator-min-microvolt = <3300000>;
+		regulator-max-microvolt = <3300000>;
+	};
+
+	vcc3v3_sd: vcc3v3-sd {
+		compatible = "regulator-fixed";
+		gpio = <&gpio2 RK_PA7 GPIO_ACTIVE_LOW>;
+		regulator-name = "vcc3v3_sd";
+		regulator-min-microvolt = <3300000>;
+		regulator-max-microvolt = <3300000>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&sdmmc_pwren>;
+	};
+
+	wireless_wlan: wireless-wlan {
+		compatible = "wlan-platdata";
+		WIFI,host_wake_irq = <&gpio1 RK_PB0 GPIO_ACTIVE_HIGH>;
+		status = "okay";
+	};
+
+	gpio-keys {
+		compatible = "gpio-keys";
+		autorepeat;
+		pinctrl-names = "default";
+		pinctrl-0 = <&pwr_key>;
+		power-key {
+			gpios = <&gpio0 RK_PA1 GPIO_ACTIVE_HIGH>;
+			linux,code = <KEY_POWER>;
+			label ="GPIO Key Power";
+			debounce-interval = <100>;
+			wakeup-source;
+			/* gpio-key,wakeup; */
+		};
+	};
+};
+
+&pinctrl {
+	buttons {
+		pwr_key: pwr-key {
+			rockchip,pins = <0 RK_PA1 RK_FUNC_GPIO &pcfg_pull_none>;
+		};
+	};
+};
+
+&csi2_dphy_hw {
+	status = "okay";
+};
+
+&csi2_dphy0 {
+	status = "okay";
+
+	ports {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		port@0 {
+			reg = <0>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			csi_dphy_input0: endpoint@0 {
+				reg = <0>;
+				remote-endpoint = <&sc3338_out>;
+				data-lanes = <1 2>;
+			};
+		};
+
+		port@1 {
+			reg = <1>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			csi_dphy_output: endpoint@0 {
+				reg = <0>;
+				remote-endpoint = <&mipi_csi2_input>;
+			};
+		};
+	};
+};
+
+&emmc {
+	status = "disabled";
+};
+
+&fiq_debugger {
+	rockchip,baudrate = <1500000>;
+	pinctrl-names = "default";
+	pinctrl-0 = <&uart2m1_xfer>;
+};
+
+&i2c4 {
+	rockchip,amp-shared;
+
+	sc3338: sc3338@30 {
+		compatible = "smartsens,sc3338";
+		status = "okay";
+		reg = <0x30>;
+		clocks = <&cru MCLK_REF_MIPI0>;
+		clock-names = "xvclk";
+		reset-gpios = <&gpio3 RK_PC5 GPIO_ACTIVE_HIGH>;
+		pwdn-gpios = <&gpio3 RK_PD2 GPIO_ACTIVE_HIGH>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&mipi_refclk_out0>;
+		rockchip,camera-module-index = <0>;
+		rockchip,camera-module-facing = "back";
+		rockchip,camera-module-name = "FKO1";
+		rockchip,camera-module-lens-name = "30IRC-F16";
+		port {
+			sc3338_out: endpoint {
+				remote-endpoint = <&csi_dphy_input0>;
+				data-lanes = <1 2>;
+			};
+		};
+	};
+};
+
+&mipi0_csi2 {
+	status = "okay";
+
+	ports {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		port@0 {
+			reg = <0>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			mipi_csi2_input: endpoint@1 {
+				reg = <1>;
+				remote-endpoint = <&csi_dphy_output>;
+			};
+		};
+
+		port@1 {
+			reg = <1>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			mipi_csi2_output: endpoint@0 {
+				reg = <0>;
+				remote-endpoint = <&cif_mipi_in>;
+			};
+		};
+	};
+};
+
+&mailbox {
+	status = "okay";
+};
+
+&rkcif {
+	status = "okay";
+};
+
+&rkcif_mipi_lvds {
+	status = "okay";
+	//memory-region-thunderboot = <&rkisp_thunderboot>;
+
+	pinctrl-names = "default";
+	pinctrl-0 = <&mipi_pins>;
+	port {
+		/* MIPI CSI-2 endpoint */
+		cif_mipi_in: endpoint {
+			remote-endpoint = <&mipi_csi2_output>;
+		};
+	};
+};
+
+&rkcif_mipi_lvds_sditf {
+	status = "okay";
+
+	port {
+		/* MIPI CSI-2 endpoint */
+		mipi_lvds_sditf: endpoint {
+			remote-endpoint = <&isp_in>;
+		};
+	};
+};
+
+&rkisp {
+	status = "okay";
+};
+
+&rkisp_vir0 {
+	status = "okay";
+
+	port@0 {
+		isp_in: endpoint {
+			remote-endpoint = <&mipi_lvds_sditf>;
+		};
+	};
+};
+
+&pinctrl {
+	sdmmc {
+		/omit-if-no-ref/
+		sdmmc_pwren: sdmmc-pwren {
+			rockchip,pins = <2 RK_PA7 RK_FUNC_GPIO &pcfg_pull_none>;
+		};
+	};
+};
+
+&pwm10 {
+	status = "okay";
+};
+
+&pwm11 {
+	status = "okay";
+};
+
+&sdio {
+	max-frequency = <50000000>;
+	bus-width = <1>;
+	cap-sd-highspeed;
+	cap-sdio-irq;
+	keep-power-in-suspend;
+	non-removable;
+	rockchip,default-sample-phase = <90>;
+	no-sd;
+	no-mmc;
+	supports-sdio;
+	pinctrl-names = "default";
+	pinctrl-0 = <&sdmmc1m0_cmd &sdmmc1m0_clk &sdmmc1m0_bus4>;
+	status = "okay";
+};
+
+&sdmmc {
+	max-frequency = <200000000>;
+	no-sdio;
+	no-mmc;
+	bus-width = <4>;
+	cap-mmc-highspeed;
+	cap-sd-highspeed;
+	disable-wp;
+	pinctrl-names = "normal", "idle";
+	pinctrl-0 = <&sdmmc0_clk &sdmmc0_cmd &sdmmc0_det &sdmmc0_bus4>;
+	pinctrl-1 = <&sdmmc0_idle_pins &sdmmc0_det>;
+	vmmc-supply = <&vcc3v3_sd>;
+	status = "okay";
+};
+
+&sfc {
+	assigned-clocks = <&cru SCLK_SFC>;
+	assigned-clock-rates = <125000000>;
+	status = "okay";
+
+	flash@0 {
+		compatible = "jedec,spi-nor";
+		reg = <0>;
+		spi-max-frequency = <125000000>;
+		spi-rx-bus-width = <4>;
+		spi-tx-bus-width = <1>;
+	};
+};
+
+&usbdrd_dwc3 {
+	dr_mode = "peripheral";
+};
diff --git a/kernel/arch/arm/boot/dts/rv1106g-smart-door-lock-rmsl-v10.dts b/kernel/arch/arm/boot/dts/rv1106g-smart-door-lock-rmsl-v10.dts
index 258cff7..255b4ee 100644
--- a/kernel/arch/arm/boot/dts/rv1106g-smart-door-lock-rmsl-v10.dts
+++ b/kernel/arch/arm/boot/dts/rv1106g-smart-door-lock-rmsl-v10.dts
@@ -117,6 +117,14 @@
 	status = "okay";
 };
 
+&ramdisk_r {
+	reg = <0x12ec000 (15 * 0x00100000)>;
+};
+
+&ramdisk_c {
+	reg = <0x21ec000 (7 * 0x00100000)>;
+};
+
 &sdmmc {
 	max-frequency = <50000000>;
 	bus-width = <1>;
diff --git a/kernel/arch/arm/boot/dts/rv1126.dtsi b/kernel/arch/arm/boot/dts/rv1126.dtsi
index 1c64f55..1a464c3 100644
--- a/kernel/arch/arm/boot/dts/rv1126.dtsi
+++ b/kernel/arch/arm/boot/dts/rv1126.dtsi
@@ -353,6 +353,12 @@
 		};
 	};
 
+	mipi_csi2: mipi-csi2 {
+		compatible = "rockchip,rv1126-mipi-csi2";
+		rockchip,hw = <&mipi_csi2_hw>;
+		status = "disabled";
+	};
+
 	mpp_srv: mpp-srv {
 		compatible = "rockchip,mpp-service";
 		rockchip,taskqueue-count = <4>;
@@ -1861,8 +1867,8 @@
 		status = "disabled";
 	};
 
-	mipi_csi2: mipi-csi2@ffb10000 {
-		compatible = "rockchip,rv1126-mipi-csi2";
+	mipi_csi2_hw: mipi-csi2-hw@ffb10000 {
+		compatible = "rockchip,rv1126-mipi-csi2-hw";
 		reg = <0xffb10000 0x10000>;
 		reg-names = "csihost_regs";
 		interrupts = <GIC_SPI 56 IRQ_TYPE_LEVEL_HIGH>,
@@ -2550,6 +2556,7 @@
 			snps,tx-fifo-resize;
 			snps,xhci-trb-ent-quirk;
 			snps,usb2-lpm-disable;
+			snps,parkmode-disable-hs-quirk;
 			status = "disabled";
 		};
 	};
diff --git a/kernel/arch/arm/configs/rk3308-aarch32-ia.config b/kernel/arch/arm/configs/rk3308-aarch32-ia.config
new file mode 100644
index 0000000..12c7791
--- /dev/null
+++ b/kernel/arch/arm/configs/rk3308-aarch32-ia.config
@@ -0,0 +1,325 @@
+CONFIG_BACKLIGHT_CLASS_DEVICE=y
+CONFIG_CMA=y
+CONFIG_DRM=y
+# CONFIG_ETHERNET is not set
+# CONFIG_ETHTOOL_NETLINK is not set
+CONFIG_FB=y
+# CONFIG_INPUT_KEYBOARD is not set
+# CONFIG_INPUT_MISC is not set
+CONFIG_INPUT_TOUCHSCREEN=y
+CONFIG_KCMP=y
+# CONFIG_MDIO_DEVICE is not set
+# CONFIG_MEDIA_CEC_SUPPORT is not set
+# CONFIG_MEDIA_SUPPORT is not set
+CONFIG_MFD_RK618=y
+# CONFIG_NEW_LEDS is not set
+# CONFIG_PHYLIB is not set
+# CONFIG_PWRSEQ_EMMC is not set
+# CONFIG_RK_NANDC_NAND is not set
+# CONFIG_SND_SOC_ROCKCHIP_SPDIF is not set
+CONFIG_SYNC_FILE=y
+# CONFIG_USB_SUPPORT is not set
+# CONFIG_VHOST_MENU is not set
+# CONFIG_VIRTIO_MENU is not set
+# CONFIG_BACKLIGHT_ADP8860 is not set
+# CONFIG_BACKLIGHT_ADP8870 is not set
+# CONFIG_BACKLIGHT_ARCXCNN is not set
+# CONFIG_BACKLIGHT_BD6107 is not set
+# CONFIG_BACKLIGHT_GPIO is not set
+# CONFIG_BACKLIGHT_KTD253 is not set
+# CONFIG_BACKLIGHT_LM3630A is not set
+# CONFIG_BACKLIGHT_LM3639 is not set
+# CONFIG_BACKLIGHT_LP855X is not set
+# CONFIG_BACKLIGHT_LV5207LP is not set
+CONFIG_BACKLIGHT_PWM=y
+# CONFIG_BACKLIGHT_QCOM_WLED is not set
+CONFIG_CLK_RK618=y
+CONFIG_CMA_ALIGNMENT=8
+CONFIG_CMA_AREAS=7
+# CONFIG_CMA_DEBUG is not set
+# CONFIG_CMA_DEBUGFS is not set
+# CONFIG_CMA_INACTIVE is not set
+CONFIG_CMA_SIZE_MBYTES=16
+# CONFIG_CMA_SIZE_SEL_MAX is not set
+CONFIG_CMA_SIZE_SEL_MBYTES=y
+# CONFIG_CMA_SIZE_SEL_MIN is not set
+# CONFIG_CMA_SIZE_SEL_PERCENTAGE is not set
+# CONFIG_CMA_SYSFS is not set
+CONFIG_COMMON_CLK_ROCKCHIP_REGMAP=y
+CONFIG_CONTIG_ALLOC=y
+# CONFIG_DMABUF_MOVE_NOTIFY is not set
+# CONFIG_DMABUF_SELFTESTS is not set
+# CONFIG_DMABUF_SYSFS_STATS is not set
+CONFIG_DMA_CMA=y
+# CONFIG_DMA_FENCE_TRACE is not set
+# CONFIG_DMA_PERNUMA_CMA is not set
+CONFIG_DMA_SHARED_BUFFER=y
+# CONFIG_DRM_ANALOGIX_ANX6345 is not set
+# CONFIG_DRM_ANALOGIX_ANX78XX is not set
+# CONFIG_DRM_ARCPGU is not set
+# CONFIG_DRM_ARMADA is not set
+CONFIG_DRM_BRIDGE=y
+# CONFIG_DRM_CDNS_DSI is not set
+# CONFIG_DRM_CDNS_MHDP8546 is not set
+# CONFIG_DRM_CHRONTEL_CH7033 is not set
+# CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS is not set
+# CONFIG_DRM_DEBUG_MM is not set
+# CONFIG_DRM_DEBUG_SELFTEST is not set
+# CONFIG_DRM_DISPLAY_CONNECTOR is not set
+# CONFIG_DRM_DP is not set
+# CONFIG_DRM_DP_AUX_CHARDEV is not set
+# CONFIG_DRM_DP_CEC is not set
+CONFIG_DRM_EDID=y
+# CONFIG_DRM_ETNAVIV is not set
+# CONFIG_DRM_EXYNOS is not set
+CONFIG_DRM_FBDEV_EMULATION=y
+# CONFIG_DRM_FBDEV_LEAK_PHYS_SMEM is not set
+CONFIG_DRM_FBDEV_OVERALLOC=100
+# CONFIG_DRM_FSL_DCU is not set
+CONFIG_DRM_GEM_CMA_HELPER=y
+# CONFIG_DRM_HDLCD is not set
+# CONFIG_DRM_I2C_ADV7511 is not set
+# CONFIG_DRM_I2C_CH7006 is not set
+# CONFIG_DRM_I2C_NXP_TDA9950 is not set
+# CONFIG_DRM_I2C_NXP_TDA998X is not set
+# CONFIG_DRM_I2C_SIL164 is not set
+CONFIG_DRM_IGNORE_IOTCL_PERMIT=y
+# CONFIG_DRM_ITE_IT6161 is not set
+CONFIG_DRM_KMS_FB_HELPER=y
+CONFIG_DRM_KMS_HELPER=y
+# CONFIG_DRM_KOMEDA is not set
+# CONFIG_DRM_LEGACY is not set
+# CONFIG_DRM_LIMA is not set
+# CONFIG_DRM_LOAD_EDID_FIRMWARE is not set
+# CONFIG_DRM_LONTIUM_LT9611 is not set
+# CONFIG_DRM_LVDS_CODEC is not set
+# CONFIG_DRM_MALI_DISPLAY is not set
+# CONFIG_DRM_MAXIM_MAX96745 is not set
+# CONFIG_DRM_MAXIM_MAX96755F is not set
+# CONFIG_DRM_MCDE is not set
+# CONFIG_DRM_MEGACHIPS_STDPXXXX_GE_B850V3_FW is not set
+CONFIG_DRM_MIPI_DSI=y
+# CONFIG_DRM_MXSFB is not set
+# CONFIG_DRM_NWL_MIPI_DSI is not set
+# CONFIG_DRM_NXP_PTN3460 is not set
+# CONFIG_DRM_OMAP is not set
+CONFIG_DRM_PANEL=y
+# CONFIG_DRM_PANEL_ARM_VERSATILE is not set
+# CONFIG_DRM_PANEL_ASUS_Z00T_TM5P5_NT35596 is not set
+# CONFIG_DRM_PANEL_BOE_HIMAX8279D is not set
+# CONFIG_DRM_PANEL_BOE_TV101WUM_NL6 is not set
+CONFIG_DRM_PANEL_BRIDGE=y
+# CONFIG_DRM_PANEL_ELIDA_KD35T133 is not set
+# CONFIG_DRM_PANEL_FEIXIN_K101_IM2BA02 is not set
+# CONFIG_DRM_PANEL_FEIYANG_FY07024DI26A30D is not set
+# CONFIG_DRM_PANEL_ILITEK_IL9322 is not set
+# CONFIG_DRM_PANEL_ILITEK_ILI9881C is not set
+# CONFIG_DRM_PANEL_INNOLUX_P079ZCA is not set
+# CONFIG_DRM_PANEL_JDI_LT070ME05000 is not set
+# CONFIG_DRM_PANEL_KINGDISPLAY_KD097D04 is not set
+# CONFIG_DRM_PANEL_LEADTEK_LTK050H3146W is not set
+# CONFIG_DRM_PANEL_LEADTEK_LTK500HD1829 is not set
+# CONFIG_DRM_PANEL_LG_LB035Q02 is not set
+# CONFIG_DRM_PANEL_LG_LG4573 is not set
+# CONFIG_DRM_PANEL_LVDS is not set
+# CONFIG_DRM_PANEL_MANTIX_MLAF057WE51 is not set
+# CONFIG_DRM_PANEL_MAXIM_MAX96752F is not set
+# CONFIG_DRM_PANEL_MAXIM_MAX96772 is not set
+# CONFIG_DRM_PANEL_NEC_NL8048HL11 is not set
+# CONFIG_DRM_PANEL_NOVATEK_NT35510 is not set
+# CONFIG_DRM_PANEL_NOVATEK_NT39016 is not set
+# CONFIG_DRM_PANEL_OLIMEX_LCD_OLINUXINO is not set
+CONFIG_DRM_PANEL_ORIENTATION_QUIRKS=y
+# CONFIG_DRM_PANEL_ORISETECH_OTM8009A is not set
+# CONFIG_DRM_PANEL_OSD_OSD101T2587_53TS is not set
+# CONFIG_DRM_PANEL_PANASONIC_VVX10F034N00 is not set
+# CONFIG_DRM_PANEL_RASPBERRYPI_TOUCHSCREEN is not set
+# CONFIG_DRM_PANEL_RAYDIUM_RM67191 is not set
+# CONFIG_DRM_PANEL_RAYDIUM_RM68200 is not set
+# CONFIG_DRM_PANEL_RONBO_RB070D30 is not set
+# CONFIG_DRM_PANEL_SAMSUNG_LD9040 is not set
+# CONFIG_DRM_PANEL_SAMSUNG_S6D16D0 is not set
+# CONFIG_DRM_PANEL_SAMSUNG_S6E3HA2 is not set
+# CONFIG_DRM_PANEL_SAMSUNG_S6E63J0X03 is not set
+# CONFIG_DRM_PANEL_SAMSUNG_S6E63M0 is not set
+# CONFIG_DRM_PANEL_SAMSUNG_S6E88A0_AMS452EF01 is not set
+# CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0 is not set
+# CONFIG_DRM_PANEL_SEIKO_43WVF1G is not set
+# CONFIG_DRM_PANEL_SHARP_LQ101R1SX01 is not set
+# CONFIG_DRM_PANEL_SHARP_LS037V7DW01 is not set
+# CONFIG_DRM_PANEL_SHARP_LS043T1LE01 is not set
+CONFIG_DRM_PANEL_SIMPLE=y
+# CONFIG_DRM_PANEL_SITRONIX_ST7701 is not set
+# CONFIG_DRM_PANEL_SITRONIX_ST7703 is not set
+# CONFIG_DRM_PANEL_SITRONIX_ST7789V is not set
+# CONFIG_DRM_PANEL_SONY_ACX424AKP is not set
+# CONFIG_DRM_PANEL_SONY_ACX565AKM is not set
+# CONFIG_DRM_PANEL_TPO_TD028TTEC1 is not set
+# CONFIG_DRM_PANEL_TPO_TD043MTEA1 is not set
+# CONFIG_DRM_PANEL_TPO_TPG110 is not set
+# CONFIG_DRM_PANEL_TRULY_NT35597_WQXGA is not set
+# CONFIG_DRM_PANEL_VISIONOX_RM69299 is not set
+# CONFIG_DRM_PANEL_XINPENG_XPP055C272 is not set
+# CONFIG_DRM_PANFROST is not set
+# CONFIG_DRM_PARADE_PS8622 is not set
+# CONFIG_DRM_PARADE_PS8640 is not set
+# CONFIG_DRM_PL111 is not set
+# CONFIG_DRM_RCAR_DW_HDMI is not set
+# CONFIG_DRM_RCAR_LVDS is not set
+# CONFIG_DRM_RK1000_TVE is not set
+CONFIG_DRM_ROCKCHIP=y
+CONFIG_DRM_ROCKCHIP_RK618=y
+# CONFIG_DRM_ROCKCHIP_VVOP is not set
+# CONFIG_DRM_ROHM_BU18XL82 is not set
+# CONFIG_DRM_SII902X is not set
+# CONFIG_DRM_SII9234 is not set
+# CONFIG_DRM_SIL_SII8620 is not set
+# CONFIG_DRM_SIMPLE_BRIDGE is not set
+# CONFIG_DRM_STI is not set
+# CONFIG_DRM_STM is not set
+# CONFIG_DRM_THINE_THC63LVD1024 is not set
+# CONFIG_DRM_TIDSS is not set
+# CONFIG_DRM_TILCDC is not set
+# CONFIG_DRM_TI_SN65DSI86 is not set
+# CONFIG_DRM_TI_TFP410 is not set
+# CONFIG_DRM_TI_TPD12S015 is not set
+# CONFIG_DRM_TOSHIBA_TC358762 is not set
+# CONFIG_DRM_TOSHIBA_TC358764 is not set
+# CONFIG_DRM_TOSHIBA_TC358767 is not set
+# CONFIG_DRM_TOSHIBA_TC358768 is not set
+# CONFIG_DRM_TOSHIBA_TC358775 is not set
+# CONFIG_DRM_TVE200 is not set
+# CONFIG_DRM_VGEM is not set
+# CONFIG_DRM_VKMS is not set
+# CONFIG_FB_ARMCLCD is not set
+CONFIG_FB_CFB_COPYAREA=y
+CONFIG_FB_CFB_FILLRECT=y
+CONFIG_FB_CFB_IMAGEBLIT=y
+CONFIG_FB_CMDLINE=y
+CONFIG_FB_DEFERRED_IO=y
+# CONFIG_FB_FOREIGN_ENDIAN is not set
+# CONFIG_FB_IBM_GXT4500 is not set
+# CONFIG_FB_METRONOME is not set
+# CONFIG_FB_MODE_HELPERS is not set
+CONFIG_FB_NOTIFY=y
+# CONFIG_FB_OPENCORES is not set
+# CONFIG_FB_S1D13XXX is not set
+# CONFIG_FB_SIMPLE is not set
+# CONFIG_FB_SSD1307 is not set
+CONFIG_FB_SYS_COPYAREA=y
+CONFIG_FB_SYS_FILLRECT=y
+CONFIG_FB_SYS_FOPS=y
+CONFIG_FB_SYS_IMAGEBLIT=y
+# CONFIG_FB_TFT is not set
+# CONFIG_FB_TILEBLITTING is not set
+# CONFIG_FB_VIRTUAL is not set
+# CONFIG_FIRMWARE_EDID is not set
+CONFIG_HDMI=y
+CONFIG_I2C_ALGOBIT=y
+# CONFIG_LOGO is not set
+# CONFIG_LT7911D_FB_NOTIFIER is not set
+CONFIG_MEMORY_ISOLATION=y
+CONFIG_MFD_CORE=y
+CONFIG_MIGRATION=y
+CONFIG_REGMAP_IRQ=y
+# CONFIG_REGULATOR_RASPBERRYPI_TOUCHSCREEN_ATTINY is not set
+# CONFIG_RK_CMA_PROCFS is not set
+# CONFIG_RK_DMABUF_PROCFS is not set
+# CONFIG_ROCKCHIP_ANALOGIX_DP is not set
+# CONFIG_ROCKCHIP_CDN_DP is not set
+# CONFIG_ROCKCHIP_DRM_DEBUG is not set
+# CONFIG_ROCKCHIP_DRM_DIRECT_SHOW is not set
+# CONFIG_ROCKCHIP_DRM_TVE is not set
+# CONFIG_ROCKCHIP_DW_DP is not set
+# CONFIG_ROCKCHIP_DW_HDCP2 is not set
+# CONFIG_ROCKCHIP_DW_HDMI is not set
+# CONFIG_ROCKCHIP_DW_MIPI_DSI is not set
+# CONFIG_ROCKCHIP_INNO_HDMI is not set
+# CONFIG_ROCKCHIP_LVDS is not set
+CONFIG_ROCKCHIP_RGB=y
+# CONFIG_ROCKCHIP_RK3066_HDMI is not set
+# CONFIG_ROCKCHIP_RKNPU is not set
+# CONFIG_ROCKCHIP_SERDES_DRM_PANEL is not set
+# CONFIG_ROCKCHIP_VCONN is not set
+CONFIG_ROCKCHIP_VOP=y
+# CONFIG_ROCKCHIP_VOP2 is not set
+# CONFIG_SW_SYNC is not set
+# CONFIG_TINYDRM_HX8357D is not set
+# CONFIG_TINYDRM_ILI9225 is not set
+# CONFIG_TINYDRM_ILI9341 is not set
+# CONFIG_TINYDRM_ILI9486 is not set
+# CONFIG_TINYDRM_MI0283QT is not set
+# CONFIG_TINYDRM_REPAPER is not set
+# CONFIG_TINYDRM_ST7586 is not set
+# CONFIG_TINYDRM_ST7735R is not set
+# CONFIG_TOUCHSCREEN_AD7877 is not set
+# CONFIG_TOUCHSCREEN_AD7879 is not set
+# CONFIG_TOUCHSCREEN_ADC is not set
+# CONFIG_TOUCHSCREEN_ADS7846 is not set
+# CONFIG_TOUCHSCREEN_AR1021_I2C is not set
+# CONFIG_TOUCHSCREEN_ATMEL_MXT is not set
+# CONFIG_TOUCHSCREEN_AUO_PIXCIR is not set
+# CONFIG_TOUCHSCREEN_BU21013 is not set
+# CONFIG_TOUCHSCREEN_BU21029 is not set
+# CONFIG_TOUCHSCREEN_CHIPONE_ICN8318 is not set
+# CONFIG_TOUCHSCREEN_CY8CTMA140 is not set
+# CONFIG_TOUCHSCREEN_CY8CTMG110 is not set
+# CONFIG_TOUCHSCREEN_CYTTSP4_CORE is not set
+# CONFIG_TOUCHSCREEN_CYTTSP_CORE is not set
+# CONFIG_TOUCHSCREEN_DYNAPRO is not set
+# CONFIG_TOUCHSCREEN_EDT_FT5X06 is not set
+# CONFIG_TOUCHSCREEN_EETI is not set
+# CONFIG_TOUCHSCREEN_EGALAX is not set
+# CONFIG_TOUCHSCREEN_EGALAX_SERIAL is not set
+# CONFIG_TOUCHSCREEN_EKTF2127 is not set
+# CONFIG_TOUCHSCREEN_ELAN is not set
+# CONFIG_TOUCHSCREEN_ELAN5515 is not set
+# CONFIG_TOUCHSCREEN_ELO is not set
+# CONFIG_TOUCHSCREEN_EXC3000 is not set
+# CONFIG_TOUCHSCREEN_FTS is not set
+# CONFIG_TOUCHSCREEN_FUJITSU is not set
+# CONFIG_TOUCHSCREEN_GOODIX is not set
+# CONFIG_TOUCHSCREEN_GSL3673 is not set
+# CONFIG_TOUCHSCREEN_GSL3673_800X1280 is not set
+# CONFIG_TOUCHSCREEN_GSLX680_PAD is not set
+CONFIG_TOUCHSCREEN_GT1X=y
+# CONFIG_TOUCHSCREEN_GUNZE is not set
+# CONFIG_TOUCHSCREEN_HAMPSHIRE is not set
+# CONFIG_TOUCHSCREEN_HIDEEP is not set
+# CONFIG_TOUCHSCREEN_ILI210X is not set
+# CONFIG_TOUCHSCREEN_IMX6UL_TSC is not set
+# CONFIG_TOUCHSCREEN_INEXIO is not set
+# CONFIG_TOUCHSCREEN_IQS5XX is not set
+# CONFIG_TOUCHSCREEN_MAX11801 is not set
+# CONFIG_TOUCHSCREEN_MCS5000 is not set
+# CONFIG_TOUCHSCREEN_MELFAS_MIP4 is not set
+# CONFIG_TOUCHSCREEN_MK712 is not set
+# CONFIG_TOUCHSCREEN_MMS114 is not set
+# CONFIG_TOUCHSCREEN_MTOUCH is not set
+# CONFIG_TOUCHSCREEN_PENMOUNT is not set
+# CONFIG_TOUCHSCREEN_PIXCIR is not set
+CONFIG_TOUCHSCREEN_PROPERTIES=y
+# CONFIG_TOUCHSCREEN_RM_TS is not set
+# CONFIG_TOUCHSCREEN_ROHM_BU21023 is not set
+# CONFIG_TOUCHSCREEN_S6SY761 is not set
+# CONFIG_TOUCHSCREEN_SILEAD is not set
+# CONFIG_TOUCHSCREEN_SIS_I2C is not set
+# CONFIG_TOUCHSCREEN_ST1232 is not set
+# CONFIG_TOUCHSCREEN_SURFACE3_SPI is not set
+# CONFIG_TOUCHSCREEN_SX8654 is not set
+# CONFIG_TOUCHSCREEN_TOUCHIT213 is not set
+# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set
+# CONFIG_TOUCHSCREEN_TOUCHWIN is not set
+# CONFIG_TOUCHSCREEN_TPS6507X is not set
+# CONFIG_TOUCHSCREEN_TSC2004 is not set
+# CONFIG_TOUCHSCREEN_TSC2005 is not set
+# CONFIG_TOUCHSCREEN_TSC2007 is not set
+# CONFIG_TOUCHSCREEN_TSC_SERIO is not set
+# CONFIG_TOUCHSCREEN_WACOM_I2C is not set
+# CONFIG_TOUCHSCREEN_WACOM_W8001 is not set
+# CONFIG_TOUCHSCREEN_WDT87XX_I2C is not set
+# CONFIG_TOUCHSCREEN_ZET6223 is not set
+# CONFIG_TOUCHSCREEN_ZFORCE is not set
+# CONFIG_TOUCHSCREEN_ZINITIX is not set
+# CONFIG_UDMABUF is not set
+CONFIG_VIDEOMODE_HELPERS=y
diff --git a/kernel/arch/arm/configs/rk3308bs_aarch32_mipi_display.config b/kernel/arch/arm/configs/rk3308bs_aarch32_mipi_display.config
new file mode 100644
index 0000000..02e4fba
--- /dev/null
+++ b/kernel/arch/arm/configs/rk3308bs_aarch32_mipi_display.config
@@ -0,0 +1,313 @@
+CONFIG_BACKLIGHT_CLASS_DEVICE=y
+CONFIG_CMA=y
+CONFIG_DRM=y
+CONFIG_FB=y
+CONFIG_INPUT_TOUCHSCREEN=y
+CONFIG_KCMP=y
+CONFIG_MFD_RK618=y
+CONFIG_SYNC_FILE=y
+# CONFIG_BACKLIGHT_ADP8860 is not set
+# CONFIG_BACKLIGHT_ADP8870 is not set
+# CONFIG_BACKLIGHT_ARCXCNN is not set
+# CONFIG_BACKLIGHT_BD6107 is not set
+# CONFIG_BACKLIGHT_GPIO is not set
+# CONFIG_BACKLIGHT_KTD253 is not set
+# CONFIG_BACKLIGHT_LED is not set
+# CONFIG_BACKLIGHT_LM3630A is not set
+# CONFIG_BACKLIGHT_LM3639 is not set
+# CONFIG_BACKLIGHT_LP855X is not set
+# CONFIG_BACKLIGHT_LV5207LP is not set
+CONFIG_BACKLIGHT_PWM=y
+# CONFIG_BACKLIGHT_QCOM_WLED is not set
+CONFIG_CLK_RK618=y
+CONFIG_CMA_ALIGNMENT=8
+CONFIG_CMA_AREAS=7
+# CONFIG_CMA_DEBUG is not set
+# CONFIG_CMA_DEBUGFS is not set
+# CONFIG_CMA_INACTIVE is not set
+CONFIG_CMA_SIZE_MBYTES=16
+# CONFIG_CMA_SIZE_SEL_MAX is not set
+CONFIG_CMA_SIZE_SEL_MBYTES=y
+# CONFIG_CMA_SIZE_SEL_MIN is not set
+# CONFIG_CMA_SIZE_SEL_PERCENTAGE is not set
+# CONFIG_CMA_SYSFS is not set
+CONFIG_COMMON_CLK_ROCKCHIP_REGMAP=y
+CONFIG_CONTIG_ALLOC=y
+# CONFIG_DMABUF_MOVE_NOTIFY is not set
+# CONFIG_DMABUF_SELFTESTS is not set
+# CONFIG_DMABUF_SYSFS_STATS is not set
+CONFIG_DMA_CMA=y
+# CONFIG_DMA_FENCE_TRACE is not set
+# CONFIG_DMA_PERNUMA_CMA is not set
+CONFIG_DMA_SHARED_BUFFER=y
+# CONFIG_DRM_ANALOGIX_ANX6345 is not set
+# CONFIG_DRM_ANALOGIX_ANX78XX is not set
+# CONFIG_DRM_ARCPGU is not set
+# CONFIG_DRM_ARMADA is not set
+CONFIG_DRM_BRIDGE=y
+# CONFIG_DRM_CDNS_DSI is not set
+# CONFIG_DRM_CDNS_MHDP8546 is not set
+# CONFIG_DRM_CHRONTEL_CH7033 is not set
+# CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS is not set
+# CONFIG_DRM_DEBUG_MM is not set
+# CONFIG_DRM_DEBUG_SELFTEST is not set
+# CONFIG_DRM_DISPLAY_CONNECTOR is not set
+CONFIG_DRM_DP=y
+# CONFIG_DRM_DP_AUX_CHARDEV is not set
+# CONFIG_DRM_DP_CEC is not set
+CONFIG_DRM_EDID=y
+# CONFIG_DRM_ETNAVIV is not set
+# CONFIG_DRM_EXYNOS is not set
+CONFIG_DRM_FBDEV_EMULATION=y
+# CONFIG_DRM_FBDEV_LEAK_PHYS_SMEM is not set
+CONFIG_DRM_FBDEV_OVERALLOC=100
+# CONFIG_DRM_FSL_DCU is not set
+CONFIG_DRM_GEM_CMA_HELPER=y
+# CONFIG_DRM_HDLCD is not set
+# CONFIG_DRM_I2C_ADV7511 is not set
+# CONFIG_DRM_I2C_CH7006 is not set
+# CONFIG_DRM_I2C_NXP_TDA9950 is not set
+# CONFIG_DRM_I2C_NXP_TDA998X is not set
+# CONFIG_DRM_I2C_SIL164 is not set
+CONFIG_DRM_IGNORE_IOTCL_PERMIT=y
+# CONFIG_DRM_ITE_IT6161 is not set
+CONFIG_DRM_KMS_FB_HELPER=y
+CONFIG_DRM_KMS_HELPER=y
+# CONFIG_DRM_KOMEDA is not set
+# CONFIG_DRM_LEGACY is not set
+# CONFIG_DRM_LIMA is not set
+# CONFIG_DRM_LOAD_EDID_FIRMWARE is not set
+# CONFIG_DRM_LONTIUM_LT9611 is not set
+# CONFIG_DRM_LVDS_CODEC is not set
+# CONFIG_DRM_MALI_DISPLAY is not set
+# CONFIG_DRM_MAXIM_MAX96745 is not set
+# CONFIG_DRM_MAXIM_MAX96755F is not set
+# CONFIG_DRM_MCDE is not set
+# CONFIG_DRM_MEGACHIPS_STDPXXXX_GE_B850V3_FW is not set
+CONFIG_DRM_MIPI_DSI=y
+# CONFIG_DRM_MXSFB is not set
+# CONFIG_DRM_NWL_MIPI_DSI is not set
+# CONFIG_DRM_NXP_PTN3460 is not set
+# CONFIG_DRM_OMAP is not set
+CONFIG_DRM_PANEL=y
+# CONFIG_DRM_PANEL_ARM_VERSATILE is not set
+# CONFIG_DRM_PANEL_ASUS_Z00T_TM5P5_NT35596 is not set
+# CONFIG_DRM_PANEL_BOE_HIMAX8279D is not set
+# CONFIG_DRM_PANEL_BOE_TV101WUM_NL6 is not set
+CONFIG_DRM_PANEL_BRIDGE=y
+# CONFIG_DRM_PANEL_ELIDA_KD35T133 is not set
+# CONFIG_DRM_PANEL_FEIXIN_K101_IM2BA02 is not set
+# CONFIG_DRM_PANEL_FEIYANG_FY07024DI26A30D is not set
+# CONFIG_DRM_PANEL_ILITEK_IL9322 is not set
+# CONFIG_DRM_PANEL_ILITEK_ILI9881C is not set
+# CONFIG_DRM_PANEL_INNOLUX_P079ZCA is not set
+# CONFIG_DRM_PANEL_JDI_LT070ME05000 is not set
+# CONFIG_DRM_PANEL_KINGDISPLAY_KD097D04 is not set
+# CONFIG_DRM_PANEL_LEADTEK_LTK050H3146W is not set
+# CONFIG_DRM_PANEL_LEADTEK_LTK500HD1829 is not set
+# CONFIG_DRM_PANEL_LG_LB035Q02 is not set
+# CONFIG_DRM_PANEL_LG_LG4573 is not set
+# CONFIG_DRM_PANEL_LVDS is not set
+# CONFIG_DRM_PANEL_MANTIX_MLAF057WE51 is not set
+# CONFIG_DRM_PANEL_MAXIM_MAX96752F is not set
+# CONFIG_DRM_PANEL_MAXIM_MAX96772 is not set
+# CONFIG_DRM_PANEL_NEC_NL8048HL11 is not set
+# CONFIG_DRM_PANEL_NOVATEK_NT35510 is not set
+# CONFIG_DRM_PANEL_NOVATEK_NT39016 is not set
+# CONFIG_DRM_PANEL_OLIMEX_LCD_OLINUXINO is not set
+CONFIG_DRM_PANEL_ORIENTATION_QUIRKS=y
+# CONFIG_DRM_PANEL_ORISETECH_OTM8009A is not set
+# CONFIG_DRM_PANEL_OSD_OSD101T2587_53TS is not set
+# CONFIG_DRM_PANEL_PANASONIC_VVX10F034N00 is not set
+# CONFIG_DRM_PANEL_RASPBERRYPI_TOUCHSCREEN is not set
+# CONFIG_DRM_PANEL_RAYDIUM_RM67191 is not set
+# CONFIG_DRM_PANEL_RAYDIUM_RM68200 is not set
+# CONFIG_DRM_PANEL_RONBO_RB070D30 is not set
+# CONFIG_DRM_PANEL_SAMSUNG_LD9040 is not set
+# CONFIG_DRM_PANEL_SAMSUNG_S6D16D0 is not set
+# CONFIG_DRM_PANEL_SAMSUNG_S6E3HA2 is not set
+# CONFIG_DRM_PANEL_SAMSUNG_S6E63J0X03 is not set
+# CONFIG_DRM_PANEL_SAMSUNG_S6E63M0 is not set
+# CONFIG_DRM_PANEL_SAMSUNG_S6E88A0_AMS452EF01 is not set
+# CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0 is not set
+# CONFIG_DRM_PANEL_SEIKO_43WVF1G is not set
+# CONFIG_DRM_PANEL_SHARP_LQ101R1SX01 is not set
+# CONFIG_DRM_PANEL_SHARP_LS037V7DW01 is not set
+# CONFIG_DRM_PANEL_SHARP_LS043T1LE01 is not set
+CONFIG_DRM_PANEL_SIMPLE=y
+# CONFIG_DRM_PANEL_SITRONIX_ST7701 is not set
+# CONFIG_DRM_PANEL_SITRONIX_ST7703 is not set
+# CONFIG_DRM_PANEL_SITRONIX_ST7789V is not set
+# CONFIG_DRM_PANEL_SONY_ACX424AKP is not set
+# CONFIG_DRM_PANEL_SONY_ACX565AKM is not set
+# CONFIG_DRM_PANEL_TPO_TD028TTEC1 is not set
+# CONFIG_DRM_PANEL_TPO_TD043MTEA1 is not set
+# CONFIG_DRM_PANEL_TPO_TPG110 is not set
+# CONFIG_DRM_PANEL_TRULY_NT35597_WQXGA is not set
+# CONFIG_DRM_PANEL_VISIONOX_RM69299 is not set
+# CONFIG_DRM_PANEL_XINPENG_XPP055C272 is not set
+# CONFIG_DRM_PANFROST is not set
+# CONFIG_DRM_PARADE_PS8622 is not set
+# CONFIG_DRM_PARADE_PS8640 is not set
+# CONFIG_DRM_PL111 is not set
+# CONFIG_DRM_RCAR_DW_HDMI is not set
+# CONFIG_DRM_RCAR_LVDS is not set
+# CONFIG_DRM_RK1000_TVE is not set
+CONFIG_DRM_ROCKCHIP=y
+CONFIG_DRM_ROCKCHIP_RK618=y
+# CONFIG_DRM_ROCKCHIP_VVOP is not set
+# CONFIG_DRM_ROHM_BU18XL82 is not set
+# CONFIG_DRM_SII902X is not set
+# CONFIG_DRM_SII9234 is not set
+# CONFIG_DRM_SIL_SII8620 is not set
+# CONFIG_DRM_SIMPLE_BRIDGE is not set
+# CONFIG_DRM_STI is not set
+# CONFIG_DRM_STM is not set
+# CONFIG_DRM_THINE_THC63LVD1024 is not set
+# CONFIG_DRM_TIDSS is not set
+# CONFIG_DRM_TILCDC is not set
+# CONFIG_DRM_TI_SN65DSI86 is not set
+# CONFIG_DRM_TI_TFP410 is not set
+# CONFIG_DRM_TI_TPD12S015 is not set
+# CONFIG_DRM_TOSHIBA_TC358762 is not set
+# CONFIG_DRM_TOSHIBA_TC358764 is not set
+# CONFIG_DRM_TOSHIBA_TC358767 is not set
+# CONFIG_DRM_TOSHIBA_TC358768 is not set
+# CONFIG_DRM_TOSHIBA_TC358775 is not set
+# CONFIG_DRM_TVE200 is not set
+# CONFIG_DRM_VGEM is not set
+# CONFIG_DRM_VKMS is not set
+# CONFIG_FB_ARMCLCD is not set
+CONFIG_FB_CFB_COPYAREA=y
+CONFIG_FB_CFB_FILLRECT=y
+CONFIG_FB_CFB_IMAGEBLIT=y
+CONFIG_FB_CMDLINE=y
+CONFIG_FB_DEFERRED_IO=y
+# CONFIG_FB_FOREIGN_ENDIAN is not set
+# CONFIG_FB_IBM_GXT4500 is not set
+# CONFIG_FB_METRONOME is not set
+# CONFIG_FB_MODE_HELPERS is not set
+CONFIG_FB_NOTIFY=y
+# CONFIG_FB_OPENCORES is not set
+# CONFIG_FB_S1D13XXX is not set
+# CONFIG_FB_SIMPLE is not set
+# CONFIG_FB_SSD1307 is not set
+CONFIG_FB_SYS_COPYAREA=y
+CONFIG_FB_SYS_FILLRECT=y
+CONFIG_FB_SYS_FOPS=y
+CONFIG_FB_SYS_IMAGEBLIT=y
+# CONFIG_FB_TFT is not set
+# CONFIG_FB_TILEBLITTING is not set
+# CONFIG_FB_VIRTUAL is not set
+# CONFIG_FIRMWARE_EDID is not set
+CONFIG_HDMI=y
+CONFIG_I2C_ALGOBIT=y
+# CONFIG_LOGO is not set
+# CONFIG_LT7911D_FB_NOTIFIER is not set
+CONFIG_MEMORY_ISOLATION=y
+CONFIG_MFD_CORE=y
+CONFIG_MIGRATION=y
+CONFIG_REGMAP_IRQ=y
+# CONFIG_REGULATOR_RASPBERRYPI_TOUCHSCREEN_ATTINY is not set
+# CONFIG_RK_CMA_PROCFS is not set
+# CONFIG_RK_DMABUF_PROCFS is not set
+# CONFIG_ROCKCHIP_ANALOGIX_DP is not set
+# CONFIG_ROCKCHIP_CDN_DP is not set
+# CONFIG_ROCKCHIP_DRM_DEBUG is not set
+# CONFIG_ROCKCHIP_DRM_DIRECT_SHOW is not set
+# CONFIG_ROCKCHIP_DRM_TVE is not set
+# CONFIG_ROCKCHIP_DW_DP is not set
+# CONFIG_ROCKCHIP_DW_HDCP2 is not set
+# CONFIG_ROCKCHIP_DW_HDMI is not set
+# CONFIG_ROCKCHIP_DW_MIPI_DSI is not set
+# CONFIG_ROCKCHIP_INNO_HDMI is not set
+# CONFIG_ROCKCHIP_LVDS is not set
+CONFIG_ROCKCHIP_RGB=y
+# CONFIG_ROCKCHIP_RK3066_HDMI is not set
+# CONFIG_ROCKCHIP_RKNPU is not set
+# CONFIG_ROCKCHIP_VCONN is not set
+CONFIG_ROCKCHIP_VOP=y
+# CONFIG_ROCKCHIP_VOP2 is not set
+# CONFIG_SW_SYNC is not set
+# CONFIG_TINYDRM_HX8357D is not set
+# CONFIG_TINYDRM_ILI9225 is not set
+# CONFIG_TINYDRM_ILI9341 is not set
+# CONFIG_TINYDRM_ILI9486 is not set
+# CONFIG_TINYDRM_MI0283QT is not set
+# CONFIG_TINYDRM_REPAPER is not set
+# CONFIG_TINYDRM_ST7586 is not set
+# CONFIG_TINYDRM_ST7735R is not set
+# CONFIG_TOUCHSCREEN_AD7877 is not set
+# CONFIG_TOUCHSCREEN_AD7879 is not set
+# CONFIG_TOUCHSCREEN_ADC is not set
+# CONFIG_TOUCHSCREEN_ADS7846 is not set
+# CONFIG_TOUCHSCREEN_AR1021_I2C is not set
+# CONFIG_TOUCHSCREEN_ATMEL_MXT is not set
+# CONFIG_TOUCHSCREEN_AUO_PIXCIR is not set
+# CONFIG_TOUCHSCREEN_BU21013 is not set
+# CONFIG_TOUCHSCREEN_BU21029 is not set
+# CONFIG_TOUCHSCREEN_CHIPONE_ICN8318 is not set
+# CONFIG_TOUCHSCREEN_CY8CTMA140 is not set
+# CONFIG_TOUCHSCREEN_CY8CTMG110 is not set
+# CONFIG_TOUCHSCREEN_CYTTSP4_CORE is not set
+# CONFIG_TOUCHSCREEN_CYTTSP_CORE is not set
+# CONFIG_TOUCHSCREEN_DYNAPRO is not set
+# CONFIG_TOUCHSCREEN_EDT_FT5X06 is not set
+# CONFIG_TOUCHSCREEN_EETI is not set
+# CONFIG_TOUCHSCREEN_EGALAX is not set
+# CONFIG_TOUCHSCREEN_EGALAX_SERIAL is not set
+# CONFIG_TOUCHSCREEN_EKTF2127 is not set
+# CONFIG_TOUCHSCREEN_ELAN is not set
+# CONFIG_TOUCHSCREEN_ELAN5515 is not set
+# CONFIG_TOUCHSCREEN_ELO is not set
+# CONFIG_TOUCHSCREEN_EXC3000 is not set
+# CONFIG_TOUCHSCREEN_FTS is not set
+# CONFIG_TOUCHSCREEN_FUJITSU is not set
+# CONFIG_TOUCHSCREEN_GOODIX is not set
+# CONFIG_TOUCHSCREEN_GSL3673 is not set
+# CONFIG_TOUCHSCREEN_GSL3673_800X1280 is not set
+# CONFIG_TOUCHSCREEN_GSLX680_PAD is not set
+CONFIG_TOUCHSCREEN_GT1X=y
+# CONFIG_TOUCHSCREEN_GUNZE is not set
+# CONFIG_TOUCHSCREEN_HAMPSHIRE is not set
+# CONFIG_TOUCHSCREEN_HIDEEP is not set
+# CONFIG_TOUCHSCREEN_ILI210X is not set
+# CONFIG_TOUCHSCREEN_IMX6UL_TSC is not set
+# CONFIG_TOUCHSCREEN_INEXIO is not set
+# CONFIG_TOUCHSCREEN_IQS5XX is not set
+# CONFIG_TOUCHSCREEN_MAX11801 is not set
+# CONFIG_TOUCHSCREEN_MCS5000 is not set
+# CONFIG_TOUCHSCREEN_MELFAS_MIP4 is not set
+# CONFIG_TOUCHSCREEN_MK712 is not set
+# CONFIG_TOUCHSCREEN_MMS114 is not set
+# CONFIG_TOUCHSCREEN_MTOUCH is not set
+# CONFIG_TOUCHSCREEN_PENMOUNT is not set
+# CONFIG_TOUCHSCREEN_PIXCIR is not set
+CONFIG_TOUCHSCREEN_PROPERTIES=y
+# CONFIG_TOUCHSCREEN_RM_TS is not set
+# CONFIG_TOUCHSCREEN_ROHM_BU21023 is not set
+# CONFIG_TOUCHSCREEN_S6SY761 is not set
+# CONFIG_TOUCHSCREEN_SILEAD is not set
+# CONFIG_TOUCHSCREEN_SIS_I2C is not set
+# CONFIG_TOUCHSCREEN_ST1232 is not set
+# CONFIG_TOUCHSCREEN_STMFTS is not set
+# CONFIG_TOUCHSCREEN_SURFACE3_SPI is not set
+# CONFIG_TOUCHSCREEN_SX8654 is not set
+# CONFIG_TOUCHSCREEN_TOUCHIT213 is not set
+# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set
+# CONFIG_TOUCHSCREEN_TOUCHWIN is not set
+# CONFIG_TOUCHSCREEN_TPS6507X is not set
+# CONFIG_TOUCHSCREEN_TSC2004 is not set
+# CONFIG_TOUCHSCREEN_TSC2005 is not set
+# CONFIG_TOUCHSCREEN_TSC2007 is not set
+# CONFIG_TOUCHSCREEN_TSC_SERIO is not set
+# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set
+# CONFIG_TOUCHSCREEN_WACOM_I2C is not set
+# CONFIG_TOUCHSCREEN_WACOM_W8001 is not set
+# CONFIG_TOUCHSCREEN_WDT87XX_I2C is not set
+# CONFIG_TOUCHSCREEN_ZET6223 is not set
+# CONFIG_TOUCHSCREEN_ZFORCE is not set
+# CONFIG_TOUCHSCREEN_ZINITIX is not set
+# CONFIG_UDMABUF is not set
+CONFIG_VIDEOMODE_HELPERS=y
+# CONFIG_VIRTIO_DMA_SHARED_BUFFER is not set
diff --git a/kernel/arch/arm/configs/rockchip_linux_defconfig b/kernel/arch/arm/configs/rockchip_linux_defconfig
index cbbbef5..23ab695 100644
--- a/kernel/arch/arm/configs/rockchip_linux_defconfig
+++ b/kernel/arch/arm/configs/rockchip_linux_defconfig
@@ -168,7 +168,6 @@
 CONFIG_BLK_DEV_DM=y
 CONFIG_DM_CRYPT=y
 CONFIG_DM_THIN_PROVISIONING=y
-CONFIG_DM_VERITY=y
 CONFIG_NETDEVICES=y
 CONFIG_TUN=y
 CONFIG_VETH=y
@@ -334,7 +333,9 @@
 CONFIG_SND_SOC_ROCKCHIP_SAI=y
 CONFIG_SND_SOC_ROCKCHIP_SPDIF=y
 CONFIG_SND_SOC_ROCKCHIP_MAX98090=y
+CONFIG_SND_SOC_ROCKCHIP_MULTICODECS=y
 CONFIG_SND_SOC_ROCKCHIP_RT5645=y
+CONFIG_SND_SOC_ROCKCHIP_HDMI=y
 CONFIG_SND_SOC_ES8311=y
 CONFIG_SND_SOC_ES8323=y
 CONFIG_SND_SOC_ES8396=y
@@ -414,11 +415,6 @@
 CONFIG_PL330_DMA=y
 CONFIG_STAGING=y
 CONFIG_ASHMEM=y
-CONFIG_FIQ_DEBUGGER=y
-CONFIG_FIQ_DEBUGGER_NO_SLEEP=y
-CONFIG_FIQ_DEBUGGER_CONSOLE=y
-CONFIG_FIQ_DEBUGGER_CONSOLE_DEFAULT_ENABLE=y
-CONFIG_FIQ_DEBUGGER_TRUST_ZONE=y
 CONFIG_COMMON_CLK_RK808=y
 CONFIG_ROCKCHIP_IOMMU=y
 CONFIG_CPU_RK312X=y
@@ -437,6 +433,11 @@
 CONFIG_ROCKCHIP_SUSPEND_MODE=y
 CONFIG_ROCKCHIP_SYSTEM_MONITOR=y
 CONFIG_ROCKCHIP_VENDOR_STORAGE_UPDATE_LOADER=y
+CONFIG_FIQ_DEBUGGER=y
+CONFIG_FIQ_DEBUGGER_NO_SLEEP=y
+CONFIG_FIQ_DEBUGGER_CONSOLE=y
+CONFIG_FIQ_DEBUGGER_CONSOLE_DEFAULT_ENABLE=y
+CONFIG_FIQ_DEBUGGER_TRUST_ZONE=y
 CONFIG_PM_DEVFREQ=y
 CONFIG_DEVFREQ_GOV_PERFORMANCE=y
 CONFIG_DEVFREQ_GOV_POWERSAVE=y
diff --git a/kernel/arch/arm/configs/rv1106-smart-door.config b/kernel/arch/arm/configs/rv1106-smart-door.config
index 7bc9197..f993824 100644
--- a/kernel/arch/arm/configs/rv1106-smart-door.config
+++ b/kernel/arch/arm/configs/rv1106-smart-door.config
@@ -29,6 +29,8 @@
 CONFIG_USB_SUPPORT=y
 CONFIG_VIDEO_GC2093=y
 CONFIG_VIDEO_SC035GS=y
+CONFIG_VIDEO_SC230AI=y
+CONFIG_VIDEO_SC301IOT=y
 CONFIG_VIDEO_SC3338=y
 CONFIG_WIRELESS=y
 CONFIG_WLAN=y
@@ -233,6 +235,7 @@
 # CONFIG_MTD_MCHP23K256 is not set
 # CONFIG_MTD_SPI_NAND is not set
 CONFIG_MTD_SPI_NOR=m
+# CONFIG_MTD_SPI_NOR_MISC is not set
 # CONFIG_MTD_SPI_NOR_USE_4K_SECTORS is not set
 # CONFIG_MTD_SST25L is not set
 # CONFIG_NL80211_TESTMODE is not set
@@ -257,8 +260,10 @@
 CONFIG_ROCKCHIP_MBOX=y
 # CONFIG_ROCKCHIP_MMC_VENDOR_STORAGE is not set
 CONFIG_ROCKCHIP_MTD_VENDOR_STORAGE=m
+# CONFIG_ROCKCHIP_RAM_VENDOR_STORAGE is not set
 CONFIG_ROCKCHIP_THUNDER_BOOT_SERVICE=y
 # CONFIG_RPMSG_QCOM_GLINK_RPM is not set
+# CONFIG_RPMSG_ROCKCHIP is not set
 # CONFIG_RTC_DRV_DS1302 is not set
 # CONFIG_RTC_DRV_DS1305 is not set
 # CONFIG_RTC_DRV_DS1343 is not set
diff --git a/kernel/arch/arm/configs/rv1106-tb-nofastae.config b/kernel/arch/arm/configs/rv1106-tb-nofastae.config
new file mode 100644
index 0000000..ec66807
--- /dev/null
+++ b/kernel/arch/arm/configs/rv1106-tb-nofastae.config
@@ -0,0 +1,421 @@
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_CRC16=m
+CONFIG_CRYPTO=y
+CONFIG_DAX=y
+CONFIG_EROFS_FS=y
+# CONFIG_ETHERNET is not set
+CONFIG_EXT4_FS=m
+CONFIG_FILE_LOCKING=y
+CONFIG_JFFS2_FS=y
+CONFIG_KERNEL_GZIP=y
+# CONFIG_KERNEL_XZ is not set
+CONFIG_LIBCRC32C=y
+CONFIG_MAILBOX=y
+# CONFIG_MDIO_DEVICE is not set
+CONFIG_MMC=y
+CONFIG_MSDOS_FS=m
+CONFIG_MSDOS_PARTITION=y
+CONFIG_MTD_BLOCK=m
+CONFIG_NLS_CODEPAGE_936=m
+# CONFIG_PHYLIB is not set
+CONFIG_PHY_ROCKCHIP_CSI2_DPHY=y
+CONFIG_PRINTK_TIME_FROM_ARM_ARCH_TIMER=y
+CONFIG_ROCKCHIP_DVBM=y
+CONFIG_ROCKCHIP_HW_DECOMPRESS=y
+CONFIG_ROCKCHIP_MULTI_RGA=y
+CONFIG_ROCKCHIP_RAMDISK=y
+CONFIG_ROCKCHIP_RGA_PROC_FS=y
+CONFIG_ROCKCHIP_THUNDER_BOOT=y
+CONFIG_ROCKCHIP_VENDOR_STORAGE=m
+# CONFIG_SLUB_SYSFS is not set
+CONFIG_SND_SIMPLE_CARD=m
+CONFIG_SND_SIMPLE_CARD_UTILS=m
+CONFIG_SND_SOC_ROCKCHIP=m
+CONFIG_SND_SOC_ROCKCHIP_I2S_TDM=m
+CONFIG_SND_SOC_RV1106=m
+CONFIG_SPI=y
+CONFIG_VFAT_FS=m
+CONFIG_VIDEO_ROCKCHIP_CIF=y
+CONFIG_VIDEO_ROCKCHIP_ISP=y
+CONFIG_VIDEO_SC230AI=y
+CONFIG_VIDEO_SC301IOT=y
+CONFIG_VIDEO_SC3338=y
+# CONFIG_AD2S1200 is not set
+# CONFIG_AD2S1210 is not set
+# CONFIG_AD2S90 is not set
+# CONFIG_AD5360 is not set
+# CONFIG_AD5421 is not set
+# CONFIG_AD5449 is not set
+# CONFIG_AD5504 is not set
+# CONFIG_AD5592R is not set
+# CONFIG_AD5624R_SPI is not set
+# CONFIG_AD5686_SPI is not set
+# CONFIG_AD5755 is not set
+# CONFIG_AD5758 is not set
+# CONFIG_AD5761 is not set
+# CONFIG_AD5764 is not set
+# CONFIG_AD5770R is not set
+# CONFIG_AD5791 is not set
+# CONFIG_AD7124 is not set
+# CONFIG_AD7192 is not set
+# CONFIG_AD7266 is not set
+# CONFIG_AD7280 is not set
+# CONFIG_AD7292 is not set
+# CONFIG_AD7298 is not set
+# CONFIG_AD7303 is not set
+# CONFIG_AD7476 is not set
+# CONFIG_AD7606_IFACE_SPI is not set
+# CONFIG_AD7766 is not set
+# CONFIG_AD7768_1 is not set
+# CONFIG_AD7780 is not set
+# CONFIG_AD7791 is not set
+# CONFIG_AD7793 is not set
+# CONFIG_AD7816 is not set
+# CONFIG_AD7887 is not set
+# CONFIG_AD7923 is not set
+# CONFIG_AD7949 is not set
+# CONFIG_AD8366 is not set
+# CONFIG_AD8801 is not set
+# CONFIG_AD9523 is not set
+# CONFIG_AD9832 is not set
+# CONFIG_AD9834 is not set
+# CONFIG_ADF4350 is not set
+# CONFIG_ADF4371 is not set
+# CONFIG_ADIS16080 is not set
+# CONFIG_ADIS16130 is not set
+# CONFIG_ADIS16136 is not set
+# CONFIG_ADIS16201 is not set
+# CONFIG_ADIS16203 is not set
+# CONFIG_ADIS16209 is not set
+# CONFIG_ADIS16240 is not set
+# CONFIG_ADIS16260 is not set
+# CONFIG_ADIS16400 is not set
+# CONFIG_ADIS16460 is not set
+# CONFIG_ADIS16475 is not set
+# CONFIG_ADIS16480 is not set
+# CONFIG_ADXL345_SPI is not set
+# CONFIG_ADXL372_SPI is not set
+# CONFIG_ADXRS290 is not set
+# CONFIG_ADXRS450 is not set
+# CONFIG_AFE4403 is not set
+# CONFIG_ALTERA_MBOX is not set
+# CONFIG_ARM_CRYPTO is not set
+# CONFIG_ARM_MHU is not set
+# CONFIG_ARM_SCMI_PROTOCOL is not set
+# CONFIG_ARM_SCPI_PROTOCOL is not set
+# CONFIG_AS3935 is not set
+# CONFIG_BMA220 is not set
+# CONFIG_BMC150_MAGN_SPI is not set
+# CONFIG_BMI160_SPI is not set
+# CONFIG_BSD_DISKLABEL is not set
+# CONFIG_CRYPTO_842 is not set
+# CONFIG_CRYPTO_ADIANTUM is not set
+# CONFIG_CRYPTO_AEGIS128 is not set
+# CONFIG_CRYPTO_AES is not set
+# CONFIG_CRYPTO_AES_TI is not set
+CONFIG_CRYPTO_ALGAPI=y
+CONFIG_CRYPTO_ALGAPI2=y
+# CONFIG_CRYPTO_ANSI_CPRNG is not set
+# CONFIG_CRYPTO_AUTHENC is not set
+# CONFIG_CRYPTO_BLAKE2B is not set
+# CONFIG_CRYPTO_BLAKE2S is not set
+# CONFIG_CRYPTO_BLOWFISH is not set
+# CONFIG_CRYPTO_CAMELLIA is not set
+# CONFIG_CRYPTO_CAST5 is not set
+# CONFIG_CRYPTO_CAST6 is not set
+# CONFIG_CRYPTO_CBC is not set
+# CONFIG_CRYPTO_CCM is not set
+# CONFIG_CRYPTO_CFB is not set
+# CONFIG_CRYPTO_CHACHA20 is not set
+# CONFIG_CRYPTO_CHACHA20POLY1305 is not set
+# CONFIG_CRYPTO_CMAC is not set
+# CONFIG_CRYPTO_CRC32 is not set
+CONFIG_CRYPTO_CRC32C=y
+# CONFIG_CRYPTO_CRCT10DIF is not set
+# CONFIG_CRYPTO_CRYPTD is not set
+# CONFIG_CRYPTO_CTR is not set
+# CONFIG_CRYPTO_CTS is not set
+# CONFIG_CRYPTO_CURVE25519 is not set
+# CONFIG_CRYPTO_DEFLATE is not set
+# CONFIG_CRYPTO_DES is not set
+# CONFIG_CRYPTO_DH is not set
+# CONFIG_CRYPTO_DRBG_MENU is not set
+# CONFIG_CRYPTO_ECB is not set
+# CONFIG_CRYPTO_ECDH is not set
+# CONFIG_CRYPTO_ECHAINIV is not set
+# CONFIG_CRYPTO_ECRDSA is not set
+# CONFIG_CRYPTO_ESSIV is not set
+# CONFIG_CRYPTO_FCRYPT is not set
+# CONFIG_CRYPTO_GCM is not set
+# CONFIG_CRYPTO_GHASH is not set
+CONFIG_CRYPTO_HASH=y
+CONFIG_CRYPTO_HASH2=y
+# CONFIG_CRYPTO_HMAC is not set
+# CONFIG_CRYPTO_HW is not set
+# CONFIG_CRYPTO_JITTERENTROPY is not set
+# CONFIG_CRYPTO_KEYWRAP is not set
+# CONFIG_CRYPTO_LIB_CHACHA is not set
+# CONFIG_CRYPTO_LIB_CHACHA20POLY1305 is not set
+# CONFIG_CRYPTO_LRW is not set
+# CONFIG_CRYPTO_LZ4 is not set
+# CONFIG_CRYPTO_LZ4HC is not set
+# CONFIG_CRYPTO_LZO is not set
+# CONFIG_CRYPTO_MANAGER is not set
+CONFIG_CRYPTO_MANAGER_DISABLE_TESTS=y
+# CONFIG_CRYPTO_MD4 is not set
+# CONFIG_CRYPTO_MD5 is not set
+# CONFIG_CRYPTO_MICHAEL_MIC is not set
+# CONFIG_CRYPTO_NULL is not set
+# CONFIG_CRYPTO_OFB is not set
+# CONFIG_CRYPTO_PCBC is not set
+# CONFIG_CRYPTO_POLY1305 is not set
+# CONFIG_CRYPTO_RMD128 is not set
+# CONFIG_CRYPTO_RMD160 is not set
+# CONFIG_CRYPTO_RMD256 is not set
+# CONFIG_CRYPTO_RMD320 is not set
+# CONFIG_CRYPTO_RSA is not set
+# CONFIG_CRYPTO_SALSA20 is not set
+# CONFIG_CRYPTO_SEQIV is not set
+# CONFIG_CRYPTO_SERPENT is not set
+# CONFIG_CRYPTO_SHA1 is not set
+# CONFIG_CRYPTO_SHA256 is not set
+# CONFIG_CRYPTO_SHA3 is not set
+# CONFIG_CRYPTO_SHA512 is not set
+# CONFIG_CRYPTO_SM2 is not set
+# CONFIG_CRYPTO_SM3 is not set
+# CONFIG_CRYPTO_SM4 is not set
+# CONFIG_CRYPTO_STREEBOG is not set
+# CONFIG_CRYPTO_TEST is not set
+# CONFIG_CRYPTO_TGR192 is not set
+# CONFIG_CRYPTO_TWOFISH is not set
+# CONFIG_CRYPTO_USER is not set
+# CONFIG_CRYPTO_USER_API_AEAD is not set
+# CONFIG_CRYPTO_USER_API_HASH is not set
+# CONFIG_CRYPTO_USER_API_RNG is not set
+# CONFIG_CRYPTO_USER_API_SKCIPHER is not set
+# CONFIG_CRYPTO_VMAC is not set
+# CONFIG_CRYPTO_WP512 is not set
+# CONFIG_CRYPTO_XCBC is not set
+# CONFIG_CRYPTO_XTS is not set
+# CONFIG_CRYPTO_XXHASH is not set
+# CONFIG_CRYPTO_ZSTD is not set
+# CONFIG_EEPROM_93XX46 is not set
+# CONFIG_EEPROM_AT25 is not set
+# CONFIG_EROFS_FS_DEBUG is not set
+# CONFIG_EROFS_FS_XATTR is not set
+# CONFIG_EROFS_FS_ZIP is not set
+# CONFIG_EXT4_DEBUG is not set
+# CONFIG_EXT4_FS_POSIX_ACL is not set
+# CONFIG_EXT4_FS_SECURITY is not set
+CONFIG_EXT4_USE_FOR_EXT2=y
+# CONFIG_EZX_PCAP is not set
+CONFIG_FAT_DEFAULT_CODEPAGE=936
+CONFIG_FAT_DEFAULT_IOCHARSET="cp936"
+CONFIG_FAT_DEFAULT_UTF8=y
+CONFIG_FAT_FS=m
+CONFIG_FS_DAX=y
+CONFIG_FS_IOMAP=y
+CONFIG_FS_MBCACHE=m
+# CONFIG_FXOS8700_SPI is not set
+# CONFIG_GPIO_74X164 is not set
+# CONFIG_GPIO_MAX3191X is not set
+# CONFIG_GPIO_MAX7301 is not set
+# CONFIG_GPIO_MC33880 is not set
+# CONFIG_GPIO_PISOSR is not set
+# CONFIG_GPIO_XRA1403 is not set
+# CONFIG_HI8435 is not set
+# CONFIG_IIO_SSP_SENSORHUB is not set
+# CONFIG_INITCALL_ASYNC is not set
+# CONFIG_INITRAMFS_FORCE is not set
+CONFIG_INITRAMFS_SOURCE=""
+CONFIG_INITRD_ASYNC=y
+# CONFIG_INV_ICM42600_SPI is not set
+# CONFIG_INV_MPU6050_SPI is not set
+CONFIG_JBD2=m
+# CONFIG_JBD2_DEBUG is not set
+# CONFIG_JFFS2_CMODE_FAVOURLZO is not set
+# CONFIG_JFFS2_CMODE_NONE is not set
+CONFIG_JFFS2_CMODE_PRIORITY=y
+# CONFIG_JFFS2_CMODE_SIZE is not set
+CONFIG_JFFS2_COMPRESSION_OPTIONS=y
+CONFIG_JFFS2_FS_DEBUG=0
+# CONFIG_JFFS2_FS_WBUF_VERIFY is not set
+CONFIG_JFFS2_FS_WRITEBUFFER=y
+# CONFIG_JFFS2_FS_XATTR is not set
+# CONFIG_JFFS2_LZO is not set
+# CONFIG_JFFS2_RTIME is not set
+# CONFIG_JFFS2_RUBIN is not set
+# CONFIG_JFFS2_SUMMARY is not set
+CONFIG_JFFS2_ZLIB=y
+# CONFIG_LATTICE_ECP3_CONFIG is not set
+CONFIG_LIB_MEMNEQ=y
+# CONFIG_LTC1660 is not set
+# CONFIG_LTC2496 is not set
+# CONFIG_LTC2632 is not set
+# CONFIG_LTC2983 is not set
+# CONFIG_MAILBOX_TEST is not set
+CONFIG_MANDATORY_FILE_LOCKING=y
+# CONFIG_MAX1027 is not set
+# CONFIG_MAX11100 is not set
+# CONFIG_MAX1118 is not set
+# CONFIG_MAX1241 is not set
+# CONFIG_MAX31856 is not set
+# CONFIG_MAX5481 is not set
+# CONFIG_MAX5487 is not set
+# CONFIG_MAXIM_THERMOCOUPLE is not set
+# CONFIG_MCP320X is not set
+# CONFIG_MCP3911 is not set
+# CONFIG_MCP41010 is not set
+# CONFIG_MCP4131 is not set
+# CONFIG_MCP4922 is not set
+# CONFIG_MFD_ARIZONA_SPI is not set
+# CONFIG_MFD_CPCAP is not set
+# CONFIG_MFD_DA9052_SPI is not set
+# CONFIG_MFD_INTEL_M10_BMC is not set
+# CONFIG_MFD_MC13XXX_SPI is not set
+# CONFIG_MFD_RK806_SPI is not set
+# CONFIG_MFD_TPS65912_SPI is not set
+# CONFIG_MFD_WM831X_SPI is not set
+# CONFIG_MICREL_KS8995MA is not set
+# CONFIG_MINIX_SUBPARTITION is not set
+# CONFIG_MMA7455_SPI is not set
+# CONFIG_MMC_ARMMMCI is not set
+CONFIG_MMC_BLOCK=m
+CONFIG_MMC_BLOCK_MINORS=32
+# CONFIG_MMC_CQHCI is not set
+# CONFIG_MMC_DEBUG is not set
+CONFIG_MMC_DW=m
+# CONFIG_MMC_DW_BLUEFIELD is not set
+# CONFIG_MMC_DW_EXYNOS is not set
+# CONFIG_MMC_DW_HI3798CV200 is not set
+# CONFIG_MMC_DW_K3 is not set
+CONFIG_MMC_DW_PLTFM=m
+CONFIG_MMC_DW_ROCKCHIP=m
+# CONFIG_MMC_HSQ is not set
+# CONFIG_MMC_MTK is not set
+CONFIG_MMC_QUEUE_DEPTH=1
+# CONFIG_MMC_SDHCI is not set
+# CONFIG_MMC_SPI is not set
+# CONFIG_MMC_TEST is not set
+# CONFIG_MMC_USDHI6ROL0 is not set
+# CONFIG_MOXTET is not set
+# CONFIG_MPL115_SPI is not set
+CONFIG_MTD_BLKDEVS=m
+# CONFIG_MTD_DATAFLASH is not set
+# CONFIG_MTD_MCHP23K256 is not set
+# CONFIG_MTD_SPI_NAND is not set
+CONFIG_MTD_SPI_NOR=m
+CONFIG_MTD_SPI_NOR_MISC=y
+# CONFIG_MTD_SPI_NOR_USE_4K_SECTORS is not set
+# CONFIG_MTD_SST25L is not set
+# CONFIG_PI433 is not set
+# CONFIG_PL320_MBOX is not set
+# CONFIG_PLATFORM_MHU is not set
+# CONFIG_PWRSEQ_EMMC is not set
+# CONFIG_PWRSEQ_SIMPLE is not set
+# CONFIG_RD_BZIP2 is not set
+# CONFIG_RD_GZIP is not set
+# CONFIG_RD_LZ4 is not set
+# CONFIG_RD_LZMA is not set
+# CONFIG_RD_LZO is not set
+# CONFIG_RD_XZ is not set
+# CONFIG_RD_ZSTD is not set
+CONFIG_REGMAP_SPI=y
+# CONFIG_REGULATOR_TPS6524X is not set
+# CONFIG_ROCKCHIP_MBOX is not set
+# CONFIG_ROCKCHIP_MMC_VENDOR_STORAGE is not set
+CONFIG_ROCKCHIP_MTD_VENDOR_STORAGE=m
+CONFIG_ROCKCHIP_RGA_DEBUGGER=y
+CONFIG_ROCKCHIP_THUNDER_BOOT_MMC=y
+# CONFIG_ROCKCHIP_THUNDER_BOOT_SERVICE is not set
+CONFIG_ROCKCHIP_THUNDER_BOOT_SFC=y
+# CONFIG_RPMSG_QCOM_GLINK_RPM is not set
+# CONFIG_RTC_DRV_DS1302 is not set
+# CONFIG_RTC_DRV_DS1305 is not set
+# CONFIG_RTC_DRV_DS1343 is not set
+# CONFIG_RTC_DRV_DS1347 is not set
+# CONFIG_RTC_DRV_DS1390 is not set
+# CONFIG_RTC_DRV_M41T93 is not set
+# CONFIG_RTC_DRV_M41T94 is not set
+# CONFIG_RTC_DRV_MAX6902 is not set
+# CONFIG_RTC_DRV_MAX6916 is not set
+# CONFIG_RTC_DRV_MCP795 is not set
+# CONFIG_RTC_DRV_PCF2123 is not set
+# CONFIG_RTC_DRV_R9701 is not set
+# CONFIG_RTC_DRV_RS5C348 is not set
+# CONFIG_RTC_DRV_RX4581 is not set
+# CONFIG_RTC_DRV_RX6110 is not set
+# CONFIG_SCA3000 is not set
+# CONFIG_SDIO_UART is not set
+# CONFIG_SENSORS_HMC5843_SPI is not set
+# CONFIG_SENSORS_RM3100_SPI is not set
+# CONFIG_SERIAL_IFX6X60 is not set
+# CONFIG_SERIAL_MAX3100 is not set
+# CONFIG_SERIAL_MAX310X is not set
+# CONFIG_SND_SOC_ADAU1761_SPI is not set
+# CONFIG_SND_SOC_AK4104 is not set
+# CONFIG_SND_SOC_CS4271_SPI is not set
+# CONFIG_SND_SOC_ES8328_SPI is not set
+# CONFIG_SND_SOC_PCM179X_SPI is not set
+# CONFIG_SND_SOC_PCM186X_SPI is not set
+# CONFIG_SND_SOC_PCM3060_SPI is not set
+# CONFIG_SND_SOC_PCM3168A_SPI is not set
+# CONFIG_SND_SOC_PCM512x_SPI is not set
+# CONFIG_SND_SOC_RK3399_GRU_SOUND is not set
+# CONFIG_SND_SOC_SSM2602_SPI is not set
+# CONFIG_SND_SOC_TLV320AIC23_SPI is not set
+# CONFIG_SND_SOC_TLV320AIC32X4_SPI is not set
+# CONFIG_SND_SOC_WM8770 is not set
+# CONFIG_SND_SOC_WM8804_SPI is not set
+# CONFIG_SND_SOC_ZL38060 is not set
+# CONFIG_SND_SPI is not set
+# CONFIG_SOLARIS_X86_PARTITION is not set
+# CONFIG_SPI_ALTERA is not set
+# CONFIG_SPI_AMD is not set
+# CONFIG_SPI_AXI_SPI_ENGINE is not set
+# CONFIG_SPI_BITBANG is not set
+# CONFIG_SPI_CADENCE is not set
+# CONFIG_SPI_CADENCE_QUADSPI is not set
+# CONFIG_SPI_DEBUG is not set
+# CONFIG_SPI_DESIGNWARE is not set
+# CONFIG_SPI_FSL_SPI is not set
+# CONFIG_SPI_GPIO is not set
+# CONFIG_SPI_LOOPBACK_TEST is not set
+CONFIG_SPI_MASTER=y
+CONFIG_SPI_MEM=y
+# CONFIG_SPI_MUX is not set
+# CONFIG_SPI_MXIC is not set
+# CONFIG_SPI_NXP_FLEXSPI is not set
+# CONFIG_SPI_OC_TINY is not set
+# CONFIG_SPI_PL022 is not set
+# CONFIG_SPI_ROCKCHIP is not set
+CONFIG_SPI_ROCKCHIP_SFC=y
+# CONFIG_SPI_SC18IS602 is not set
+# CONFIG_SPI_SIFIVE is not set
+# CONFIG_SPI_SLAVE is not set
+# CONFIG_SPI_SPIDEV is not set
+# CONFIG_SPI_TLE62X0 is not set
+# CONFIG_SPI_XCOMM is not set
+# CONFIG_SPI_XILINX is not set
+# CONFIG_SPI_ZYNQMP_GQSPI is not set
+# CONFIG_TI_ADC0832 is not set
+# CONFIG_TI_ADC084S021 is not set
+# CONFIG_TI_ADC108S102 is not set
+# CONFIG_TI_ADC12138 is not set
+# CONFIG_TI_ADC128S052 is not set
+# CONFIG_TI_ADC161S626 is not set
+# CONFIG_TI_ADS124S08 is not set
+# CONFIG_TI_ADS7950 is not set
+# CONFIG_TI_ADS8344 is not set
+# CONFIG_TI_ADS8688 is not set
+# CONFIG_TI_DAC082S085 is not set
+# CONFIG_TI_DAC7311 is not set
+# CONFIG_TI_DAC7612 is not set
+# CONFIG_TI_TLC4541 is not set
+# CONFIG_UNIXWARE_DISKLABEL is not set
+# CONFIG_VIDEO_GS1662 is not set
+# CONFIG_VIDEO_ROCKCHIP_PREISP is not set
+# CONFIG_VIDEO_ROCKCHIP_THUNDER_BOOT_ISP is not set
+# CONFIG_VIDEO_S5C73M3 is not set
+CONFIG_ZLIB_DEFLATE=y
+CONFIG_ZLIB_INFLATE=y
diff --git a/kernel/arch/arm/configs/rv1106-tee.config b/kernel/arch/arm/configs/rv1106-tee.config
index 430a40d..ae4c34e 100644
--- a/kernel/arch/arm/configs/rv1106-tee.config
+++ b/kernel/arch/arm/configs/rv1106-tee.config
@@ -1,13 +1,118 @@
 CONFIG_ARM_PSCI=y
+CONFIG_CRYPTO=y
 # CONFIG_FIQ_DEBUGGER_FIQ_GLUE is not set
 CONFIG_FIQ_DEBUGGER_TRUST_ZONE=y
+CONFIG_TEE=y
 CONFIG_ARM_CPU_SUSPEND=y
+# CONFIG_ARM_CRYPTO is not set
 # CONFIG_ARM_HIGHBANK_CPUIDLE is not set
 # CONFIG_ARM_PSCI_CPUIDLE is not set
 CONFIG_ARM_PSCI_FW=y
 # CONFIG_ARM_SCMI_PROTOCOL is not set
 CONFIG_ARM_SMCCC_SOC_ID=y
+# CONFIG_CRYPTO_842 is not set
+# CONFIG_CRYPTO_ADIANTUM is not set
+# CONFIG_CRYPTO_AEGIS128 is not set
+# CONFIG_CRYPTO_AES is not set
+# CONFIG_CRYPTO_AES_TI is not set
+CONFIG_CRYPTO_ALGAPI=y
+CONFIG_CRYPTO_ALGAPI2=y
+# CONFIG_CRYPTO_ANSI_CPRNG is not set
+# CONFIG_CRYPTO_AUTHENC is not set
+# CONFIG_CRYPTO_BLAKE2B is not set
+# CONFIG_CRYPTO_BLAKE2S is not set
+# CONFIG_CRYPTO_BLOWFISH is not set
+# CONFIG_CRYPTO_CAMELLIA is not set
+# CONFIG_CRYPTO_CAST5 is not set
+# CONFIG_CRYPTO_CAST6 is not set
+# CONFIG_CRYPTO_CBC is not set
+# CONFIG_CRYPTO_CCM is not set
+# CONFIG_CRYPTO_CFB is not set
+# CONFIG_CRYPTO_CHACHA20 is not set
+# CONFIG_CRYPTO_CHACHA20POLY1305 is not set
+# CONFIG_CRYPTO_CMAC is not set
+# CONFIG_CRYPTO_CRC32 is not set
+# CONFIG_CRYPTO_CRC32C is not set
+# CONFIG_CRYPTO_CRCT10DIF is not set
+# CONFIG_CRYPTO_CRYPTD is not set
+# CONFIG_CRYPTO_CTR is not set
+# CONFIG_CRYPTO_CTS is not set
+# CONFIG_CRYPTO_CURVE25519 is not set
+# CONFIG_CRYPTO_DEFLATE is not set
+# CONFIG_CRYPTO_DES is not set
+# CONFIG_CRYPTO_DEV_AMLOGIC_GXL is not set
+# CONFIG_CRYPTO_DEV_ATMEL_ECC is not set
+# CONFIG_CRYPTO_DEV_ATMEL_SHA204A is not set
+# CONFIG_CRYPTO_DEV_CCREE is not set
+# CONFIG_CRYPTO_DEV_ROCKCHIP is not set
+# CONFIG_CRYPTO_DEV_SAFEXCEL is not set
+# CONFIG_CRYPTO_DH is not set
+# CONFIG_CRYPTO_DRBG_MENU is not set
+# CONFIG_CRYPTO_ECB is not set
+# CONFIG_CRYPTO_ECDH is not set
+# CONFIG_CRYPTO_ECHAINIV is not set
+# CONFIG_CRYPTO_ECRDSA is not set
+# CONFIG_CRYPTO_ESSIV is not set
+# CONFIG_CRYPTO_FCRYPT is not set
+# CONFIG_CRYPTO_GCM is not set
+# CONFIG_CRYPTO_GHASH is not set
+CONFIG_CRYPTO_HASH=y
+CONFIG_CRYPTO_HASH2=y
+# CONFIG_CRYPTO_HMAC is not set
+CONFIG_CRYPTO_HW=y
+# CONFIG_CRYPTO_JITTERENTROPY is not set
+# CONFIG_CRYPTO_KEYWRAP is not set
+# CONFIG_CRYPTO_LIB_CHACHA is not set
+# CONFIG_CRYPTO_LIB_CHACHA20POLY1305 is not set
+# CONFIG_CRYPTO_LRW is not set
+# CONFIG_CRYPTO_LZ4 is not set
+# CONFIG_CRYPTO_LZ4HC is not set
+# CONFIG_CRYPTO_LZO is not set
+# CONFIG_CRYPTO_MANAGER is not set
+CONFIG_CRYPTO_MANAGER_DISABLE_TESTS=y
+# CONFIG_CRYPTO_MD4 is not set
+# CONFIG_CRYPTO_MD5 is not set
+# CONFIG_CRYPTO_MICHAEL_MIC is not set
+# CONFIG_CRYPTO_NULL is not set
+# CONFIG_CRYPTO_OFB is not set
+# CONFIG_CRYPTO_PCBC is not set
+# CONFIG_CRYPTO_POLY1305 is not set
+# CONFIG_CRYPTO_RMD128 is not set
+# CONFIG_CRYPTO_RMD160 is not set
+# CONFIG_CRYPTO_RMD256 is not set
+# CONFIG_CRYPTO_RMD320 is not set
+# CONFIG_CRYPTO_RSA is not set
+# CONFIG_CRYPTO_SALSA20 is not set
+# CONFIG_CRYPTO_SEQIV is not set
+# CONFIG_CRYPTO_SERPENT is not set
+CONFIG_CRYPTO_SHA1=y
+# CONFIG_CRYPTO_SHA256 is not set
+# CONFIG_CRYPTO_SHA3 is not set
+# CONFIG_CRYPTO_SHA512 is not set
+# CONFIG_CRYPTO_SM2 is not set
+# CONFIG_CRYPTO_SM3 is not set
+# CONFIG_CRYPTO_SM4 is not set
+# CONFIG_CRYPTO_STREEBOG is not set
+# CONFIG_CRYPTO_TEST is not set
+# CONFIG_CRYPTO_TGR192 is not set
+# CONFIG_CRYPTO_TWOFISH is not set
+# CONFIG_CRYPTO_USER is not set
+# CONFIG_CRYPTO_USER_API_AEAD is not set
+# CONFIG_CRYPTO_USER_API_HASH is not set
+# CONFIG_CRYPTO_USER_API_RNG is not set
+# CONFIG_CRYPTO_USER_API_SKCIPHER is not set
+# CONFIG_CRYPTO_VMAC is not set
+# CONFIG_CRYPTO_WP512 is not set
+# CONFIG_CRYPTO_XCBC is not set
+# CONFIG_CRYPTO_XTS is not set
+# CONFIG_CRYPTO_XXHASH is not set
+# CONFIG_CRYPTO_ZSTD is not set
 CONFIG_GLOB=y
 # CONFIG_GLOB_SELFTEST is not set
 CONFIG_HAVE_ARM_SMCCC_DISCOVERY=y
+CONFIG_HW_RANDOM_OPTEE=y
+CONFIG_LIB_MEMNEQ=y
+CONFIG_OPTEE=y
+CONFIG_OPTEE_SHM_NUM_PRIV_PAGES=1
+CONFIG_ROCKCHIP_SIP=y
 CONFIG_SOC_BUS=y
diff --git a/kernel/arch/arm/configs/rv1106-trailcam.config b/kernel/arch/arm/configs/rv1106-trailcam.config
new file mode 100644
index 0000000..91284d8
--- /dev/null
+++ b/kernel/arch/arm/configs/rv1106-trailcam.config
@@ -0,0 +1,506 @@
+CONFIG_BACKLIGHT_CLASS_DEVICE=y
+CONFIG_CONFIGFS_FS=y
+CONFIG_CRC16=y
+CONFIG_DRM=y
+CONFIG_EXT4_FS=y
+CONFIG_FB=y
+CONFIG_FILE_LOCKING=y
+CONFIG_I2C_GPIO=y
+CONFIG_I2C_MUX=y
+CONFIG_INPUT=y
+CONFIG_JFFS2_FS=y
+CONFIG_KCMP=y
+CONFIG_MAILBOX=y
+CONFIG_MMC_DW_ROCKCHIP=y
+CONFIG_MSDOS_PARTITION=y
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_ISO8859_1=y
+CONFIG_RK_CMA_PROCFS=y
+CONFIG_RK_DMABUF_PROCFS=y
+CONFIG_RK_MEMBLOCK_PROCFS=y
+CONFIG_ROCKCHIP_RVE=y
+CONFIG_SPI=y
+CONFIG_VFAT_FS=y
+CONFIG_VIDEO_SC230AI=y
+CONFIG_VIDEO_SC301IOT=y
+CONFIG_VIDEO_SC3338=y
+# CONFIG_AD2S1200 is not set
+# CONFIG_AD2S1210 is not set
+# CONFIG_AD2S90 is not set
+# CONFIG_AD5360 is not set
+# CONFIG_AD5421 is not set
+# CONFIG_AD5449 is not set
+# CONFIG_AD5504 is not set
+# CONFIG_AD5592R is not set
+# CONFIG_AD5624R_SPI is not set
+# CONFIG_AD5686_SPI is not set
+# CONFIG_AD5755 is not set
+# CONFIG_AD5758 is not set
+# CONFIG_AD5761 is not set
+# CONFIG_AD5764 is not set
+# CONFIG_AD5770R is not set
+# CONFIG_AD5791 is not set
+# CONFIG_AD7124 is not set
+# CONFIG_AD7192 is not set
+# CONFIG_AD7266 is not set
+# CONFIG_AD7280 is not set
+# CONFIG_AD7292 is not set
+# CONFIG_AD7298 is not set
+# CONFIG_AD7303 is not set
+# CONFIG_AD7476 is not set
+# CONFIG_AD7606_IFACE_SPI is not set
+# CONFIG_AD7766 is not set
+# CONFIG_AD7768_1 is not set
+# CONFIG_AD7780 is not set
+# CONFIG_AD7791 is not set
+# CONFIG_AD7793 is not set
+# CONFIG_AD7816 is not set
+# CONFIG_AD7887 is not set
+# CONFIG_AD7923 is not set
+# CONFIG_AD7949 is not set
+# CONFIG_AD8366 is not set
+# CONFIG_AD8801 is not set
+# CONFIG_AD9523 is not set
+# CONFIG_AD9832 is not set
+# CONFIG_AD9834 is not set
+# CONFIG_ADF4350 is not set
+# CONFIG_ADF4371 is not set
+# CONFIG_ADIS16080 is not set
+# CONFIG_ADIS16130 is not set
+# CONFIG_ADIS16136 is not set
+# CONFIG_ADIS16201 is not set
+# CONFIG_ADIS16203 is not set
+# CONFIG_ADIS16209 is not set
+# CONFIG_ADIS16240 is not set
+# CONFIG_ADIS16260 is not set
+# CONFIG_ADIS16400 is not set
+# CONFIG_ADIS16460 is not set
+# CONFIG_ADIS16475 is not set
+# CONFIG_ADIS16480 is not set
+# CONFIG_ADXL345_SPI is not set
+# CONFIG_ADXL372_SPI is not set
+# CONFIG_ADXRS290 is not set
+# CONFIG_ADXRS450 is not set
+# CONFIG_AFE4403 is not set
+# CONFIG_ALTERA_MBOX is not set
+# CONFIG_ARM_MHU is not set
+# CONFIG_ARM_SCMI_PROTOCOL is not set
+# CONFIG_ARM_SCPI_PROTOCOL is not set
+# CONFIG_AS3935 is not set
+# CONFIG_BACKLIGHT_ADP8860 is not set
+# CONFIG_BACKLIGHT_ADP8870 is not set
+# CONFIG_BACKLIGHT_ARCXCNN is not set
+# CONFIG_BACKLIGHT_BD6107 is not set
+# CONFIG_BACKLIGHT_GPIO is not set
+# CONFIG_BACKLIGHT_KTD253 is not set
+# CONFIG_BACKLIGHT_LM3630A is not set
+# CONFIG_BACKLIGHT_LM3639 is not set
+# CONFIG_BACKLIGHT_LP855X is not set
+# CONFIG_BACKLIGHT_LV5207LP is not set
+CONFIG_BACKLIGHT_PWM=y
+# CONFIG_BACKLIGHT_QCOM_WLED is not set
+# CONFIG_BMA220 is not set
+# CONFIG_BMC150_MAGN_SPI is not set
+# CONFIG_BMI160_SPI is not set
+# CONFIG_BSD_DISKLABEL is not set
+# CONFIG_DLM is not set
+# CONFIG_DRM_ANALOGIX_ANX6345 is not set
+# CONFIG_DRM_ANALOGIX_ANX78XX is not set
+# CONFIG_DRM_ARCPGU is not set
+# CONFIG_DRM_ARMADA is not set
+CONFIG_DRM_BRIDGE=y
+# CONFIG_DRM_CDNS_DSI is not set
+# CONFIG_DRM_CDNS_MHDP8546 is not set
+# CONFIG_DRM_CHRONTEL_CH7033 is not set
+# CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS is not set
+# CONFIG_DRM_DEBUG_MM is not set
+# CONFIG_DRM_DEBUG_SELFTEST is not set
+# CONFIG_DRM_DISPLAY_CONNECTOR is not set
+# CONFIG_DRM_DP is not set
+# CONFIG_DRM_DP_AUX_CHARDEV is not set
+# CONFIG_DRM_DP_CEC is not set
+# CONFIG_DRM_EDID is not set
+# CONFIG_DRM_ETNAVIV is not set
+# CONFIG_DRM_EXYNOS is not set
+CONFIG_DRM_FBDEV_EMULATION=y
+# CONFIG_DRM_FBDEV_LEAK_PHYS_SMEM is not set
+CONFIG_DRM_FBDEV_OVERALLOC=100
+# CONFIG_DRM_FSL_DCU is not set
+CONFIG_DRM_GEM_CMA_HELPER=y
+# CONFIG_DRM_HDLCD is not set
+# CONFIG_DRM_I2C_ADV7511 is not set
+# CONFIG_DRM_I2C_CH7006 is not set
+# CONFIG_DRM_I2C_NXP_TDA9950 is not set
+# CONFIG_DRM_I2C_NXP_TDA998X is not set
+# CONFIG_DRM_I2C_SIL164 is not set
+# CONFIG_DRM_IGNORE_IOTCL_PERMIT is not set
+# CONFIG_DRM_ITE_IT6161 is not set
+CONFIG_DRM_KMS_FB_HELPER=y
+CONFIG_DRM_KMS_HELPER=y
+# CONFIG_DRM_KOMEDA is not set
+# CONFIG_DRM_LEGACY is not set
+# CONFIG_DRM_LIMA is not set
+# CONFIG_DRM_LOAD_EDID_FIRMWARE is not set
+# CONFIG_DRM_LONTIUM_LT9611 is not set
+# CONFIG_DRM_LVDS_CODEC is not set
+# CONFIG_DRM_MALI_DISPLAY is not set
+# CONFIG_DRM_MAXIM_MAX96745 is not set
+# CONFIG_DRM_MAXIM_MAX96755F is not set
+# CONFIG_DRM_MCDE is not set
+# CONFIG_DRM_MEGACHIPS_STDPXXXX_GE_B850V3_FW is not set
+# CONFIG_DRM_MXSFB is not set
+# CONFIG_DRM_NWL_MIPI_DSI is not set
+# CONFIG_DRM_NXP_PTN3460 is not set
+# CONFIG_DRM_OMAP is not set
+CONFIG_DRM_PANEL=y
+# CONFIG_DRM_PANEL_ARM_VERSATILE is not set
+CONFIG_DRM_PANEL_BRIDGE=y
+# CONFIG_DRM_PANEL_ILITEK_IL9322 is not set
+# CONFIG_DRM_PANEL_LG_LB035Q02 is not set
+# CONFIG_DRM_PANEL_LG_LG4573 is not set
+# CONFIG_DRM_PANEL_LVDS is not set
+# CONFIG_DRM_PANEL_MAXIM_DESERIALIZER is not set
+# CONFIG_DRM_PANEL_NEC_NL8048HL11 is not set
+# CONFIG_DRM_PANEL_NOVATEK_NT39016 is not set
+# CONFIG_DRM_PANEL_OLIMEX_LCD_OLINUXINO is not set
+CONFIG_DRM_PANEL_ORIENTATION_QUIRKS=y
+# CONFIG_DRM_PANEL_SAMSUNG_LD9040 is not set
+# CONFIG_DRM_PANEL_SAMSUNG_S6E63M0 is not set
+# CONFIG_DRM_PANEL_SAMSUNG_S6E88A0_AMS452EF01 is not set
+# CONFIG_DRM_PANEL_SAMSUNG_S6E8AA0 is not set
+# CONFIG_DRM_PANEL_SEIKO_43WVF1G is not set
+# CONFIG_DRM_PANEL_SHARP_LS037V7DW01 is not set
+CONFIG_DRM_PANEL_SIMPLE=y
+# CONFIG_DRM_PANEL_SITRONIX_ST7789V is not set
+# CONFIG_DRM_PANEL_SONY_ACX565AKM is not set
+# CONFIG_DRM_PANEL_TPO_TD028TTEC1 is not set
+# CONFIG_DRM_PANEL_TPO_TD043MTEA1 is not set
+# CONFIG_DRM_PANEL_TPO_TPG110 is not set
+# CONFIG_DRM_PANFROST is not set
+# CONFIG_DRM_PARADE_PS8622 is not set
+# CONFIG_DRM_PARADE_PS8640 is not set
+# CONFIG_DRM_PL111 is not set
+# CONFIG_DRM_RCAR_DW_HDMI is not set
+# CONFIG_DRM_RCAR_LVDS is not set
+# CONFIG_DRM_RK1000_TVE is not set
+CONFIG_DRM_ROCKCHIP=y
+# CONFIG_DRM_ROCKCHIP_VVOP is not set
+# CONFIG_DRM_ROHM_BU18XL82 is not set
+# CONFIG_DRM_SII902X is not set
+# CONFIG_DRM_SII9234 is not set
+# CONFIG_DRM_SIL_SII8620 is not set
+# CONFIG_DRM_SIMPLE_BRIDGE is not set
+# CONFIG_DRM_STI is not set
+# CONFIG_DRM_STM is not set
+# CONFIG_DRM_THINE_THC63LVD1024 is not set
+# CONFIG_DRM_TIDSS is not set
+# CONFIG_DRM_TILCDC is not set
+# CONFIG_DRM_TI_SN65DSI86 is not set
+# CONFIG_DRM_TI_TFP410 is not set
+# CONFIG_DRM_TI_TPD12S015 is not set
+# CONFIG_DRM_TOSHIBA_TC358762 is not set
+# CONFIG_DRM_TOSHIBA_TC358764 is not set
+# CONFIG_DRM_TOSHIBA_TC358767 is not set
+# CONFIG_DRM_TOSHIBA_TC358768 is not set
+# CONFIG_DRM_TOSHIBA_TC358775 is not set
+# CONFIG_DRM_TVE200 is not set
+# CONFIG_DRM_VGEM is not set
+# CONFIG_DRM_VKMS is not set
+# CONFIG_EEPROM_93XX46 is not set
+# CONFIG_EEPROM_AT25 is not set
+# CONFIG_EXT4_DEBUG is not set
+# CONFIG_EXT4_FS_POSIX_ACL is not set
+# CONFIG_EXT4_FS_SECURITY is not set
+CONFIG_EXT4_USE_FOR_EXT2=y
+# CONFIG_EZX_PCAP is not set
+CONFIG_FAT_DEFAULT_CODEPAGE=437
+CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
+# CONFIG_FAT_DEFAULT_UTF8 is not set
+CONFIG_FAT_FS=y
+# CONFIG_FB_ARMCLCD is not set
+CONFIG_FB_CFB_COPYAREA=y
+CONFIG_FB_CFB_FILLRECT=y
+CONFIG_FB_CFB_IMAGEBLIT=y
+CONFIG_FB_CMDLINE=y
+CONFIG_FB_DEFERRED_IO=y
+# CONFIG_FB_FOREIGN_ENDIAN is not set
+# CONFIG_FB_IBM_GXT4500 is not set
+# CONFIG_FB_METRONOME is not set
+# CONFIG_FB_MODE_HELPERS is not set
+CONFIG_FB_NOTIFY=y
+# CONFIG_FB_OPENCORES is not set
+# CONFIG_FB_S1D13XXX is not set
+# CONFIG_FB_SIMPLE is not set
+# CONFIG_FB_SSD1307 is not set
+CONFIG_FB_SYS_COPYAREA=y
+CONFIG_FB_SYS_FILLRECT=y
+CONFIG_FB_SYS_FOPS=y
+CONFIG_FB_SYS_IMAGEBLIT=y
+# CONFIG_FB_TFT is not set
+# CONFIG_FB_TILEBLITTING is not set
+# CONFIG_FB_VIRTUAL is not set
+# CONFIG_FIRMWARE_EDID is not set
+CONFIG_FS_MBCACHE=y
+# CONFIG_FXOS8700_SPI is not set
+# CONFIG_GPIO_74X164 is not set
+# CONFIG_GPIO_MAX3191X is not set
+# CONFIG_GPIO_MAX7301 is not set
+# CONFIG_GPIO_MC33880 is not set
+# CONFIG_GPIO_PISOSR is not set
+# CONFIG_GPIO_XRA1403 is not set
+# CONFIG_HI8435 is not set
+# CONFIG_HID is not set
+CONFIG_I2C_ALGOBIT=y
+# CONFIG_I2C_ARB_GPIO_CHALLENGE is not set
+# CONFIG_I2C_DEMUX_PINCTRL is not set
+# CONFIG_I2C_GPIO_FAULT_INJECTOR is not set
+# CONFIG_I2C_HID is not set
+# CONFIG_I2C_MUX_GPIO is not set
+# CONFIG_I2C_MUX_GPMUX is not set
+# CONFIG_I2C_MUX_LTC4306 is not set
+# CONFIG_I2C_MUX_MLXCPLD is not set
+# CONFIG_I2C_MUX_PCA9541 is not set
+# CONFIG_I2C_MUX_PCA954x is not set
+# CONFIG_I2C_MUX_PINCTRL is not set
+# CONFIG_I2C_MUX_REG is not set
+# CONFIG_IIO_SSP_SENSORHUB is not set
+# CONFIG_INPUT_EVBUG is not set
+CONFIG_INPUT_EVDEV=y
+# CONFIG_INPUT_FF_MEMLESS is not set
+# CONFIG_INPUT_JOYDEV is not set
+# CONFIG_INPUT_JOYSTICK is not set
+CONFIG_INPUT_KEYBOARD=y
+# CONFIG_INPUT_MATRIXKMAP is not set
+# CONFIG_INPUT_MISC is not set
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_INPUT_MOUSEDEV is not set
+# CONFIG_INPUT_POLLDEV is not set
+# CONFIG_INPUT_SPARSEKMAP is not set
+# CONFIG_INPUT_TABLET is not set
+# CONFIG_INPUT_TOUCHSCREEN is not set
+# CONFIG_INV_ICM42600_SPI is not set
+# CONFIG_INV_MPU6050_SPI is not set
+CONFIG_JBD2=y
+# CONFIG_JBD2_DEBUG is not set
+# CONFIG_JFFS2_CMODE_FAVOURLZO is not set
+# CONFIG_JFFS2_CMODE_NONE is not set
+CONFIG_JFFS2_CMODE_PRIORITY=y
+# CONFIG_JFFS2_CMODE_SIZE is not set
+CONFIG_JFFS2_COMPRESSION_OPTIONS=y
+CONFIG_JFFS2_FS_DEBUG=0
+# CONFIG_JFFS2_FS_WBUF_VERIFY is not set
+CONFIG_JFFS2_FS_WRITEBUFFER=y
+# CONFIG_JFFS2_FS_XATTR is not set
+# CONFIG_JFFS2_LZO is not set
+# CONFIG_JFFS2_RTIME is not set
+# CONFIG_JFFS2_RUBIN is not set
+# CONFIG_JFFS2_SUMMARY is not set
+CONFIG_JFFS2_ZLIB=y
+CONFIG_KEYBOARD_ADC=y
+# CONFIG_KEYBOARD_ADP5588 is not set
+# CONFIG_KEYBOARD_ADP5589 is not set
+# CONFIG_KEYBOARD_ATKBD is not set
+# CONFIG_KEYBOARD_BCM is not set
+# CONFIG_KEYBOARD_CAP11XX is not set
+# CONFIG_KEYBOARD_DLINK_DIR685 is not set
+CONFIG_KEYBOARD_GPIO=y
+# CONFIG_KEYBOARD_GPIO_POLLED is not set
+# CONFIG_KEYBOARD_LKKBD is not set
+# CONFIG_KEYBOARD_LM8333 is not set
+# CONFIG_KEYBOARD_MATRIX is not set
+# CONFIG_KEYBOARD_MAX7359 is not set
+# CONFIG_KEYBOARD_MCS is not set
+# CONFIG_KEYBOARD_MPR121 is not set
+# CONFIG_KEYBOARD_NEWTON is not set
+# CONFIG_KEYBOARD_OMAP4 is not set
+# CONFIG_KEYBOARD_OPENCORES is not set
+# CONFIG_KEYBOARD_QT1050 is not set
+# CONFIG_KEYBOARD_QT1070 is not set
+# CONFIG_KEYBOARD_QT2160 is not set
+# CONFIG_KEYBOARD_SAMSUNG is not set
+# CONFIG_KEYBOARD_STOWAWAY is not set
+# CONFIG_KEYBOARD_SUNKBD is not set
+# CONFIG_KEYBOARD_TCA6416 is not set
+# CONFIG_KEYBOARD_TCA8418 is not set
+# CONFIG_KEYBOARD_XTKBD is not set
+# CONFIG_LATTICE_ECP3_CONFIG is not set
+# CONFIG_LOGO is not set
+# CONFIG_LTC1660 is not set
+# CONFIG_LTC2496 is not set
+# CONFIG_LTC2632 is not set
+# CONFIG_LTC2983 is not set
+# CONFIG_MAILBOX_TEST is not set
+# CONFIG_MANAGER_SBS is not set
+CONFIG_MANDATORY_FILE_LOCKING=y
+# CONFIG_MAX1027 is not set
+# CONFIG_MAX11100 is not set
+# CONFIG_MAX1118 is not set
+# CONFIG_MAX1241 is not set
+# CONFIG_MAX31856 is not set
+# CONFIG_MAX5481 is not set
+# CONFIG_MAX5487 is not set
+# CONFIG_MAXIM_THERMOCOUPLE is not set
+# CONFIG_MCP320X is not set
+# CONFIG_MCP3911 is not set
+# CONFIG_MCP41010 is not set
+# CONFIG_MCP4131 is not set
+# CONFIG_MCP4922 is not set
+# CONFIG_MFD_ARIZONA_SPI is not set
+# CONFIG_MFD_CPCAP is not set
+# CONFIG_MFD_DA9052_SPI is not set
+# CONFIG_MFD_INTEL_M10_BMC is not set
+# CONFIG_MFD_MC13XXX_SPI is not set
+# CONFIG_MFD_RK806_SPI is not set
+# CONFIG_MFD_TPS65912_SPI is not set
+# CONFIG_MFD_WM831X_SPI is not set
+# CONFIG_MICREL_KS8995MA is not set
+# CONFIG_MINIX_SUBPARTITION is not set
+# CONFIG_MMA7455_SPI is not set
+# CONFIG_MMC_SPI is not set
+# CONFIG_MOST is not set
+# CONFIG_MOXTET is not set
+# CONFIG_MPL115_SPI is not set
+# CONFIG_MTD_DATAFLASH is not set
+# CONFIG_MTD_MCHP23K256 is not set
+# CONFIG_MTD_SPI_NAND is not set
+# CONFIG_MTD_SPI_NOR is not set
+# CONFIG_MTD_SST25L is not set
+# CONFIG_NVME_TARGET is not set
+# CONFIG_OCFS2_FS is not set
+# CONFIG_PI433 is not set
+# CONFIG_PL320_MBOX is not set
+# CONFIG_PLATFORM_MHU is not set
+# CONFIG_RC_CORE is not set
+CONFIG_REGMAP_SPI=y
+# CONFIG_REGULATOR_RASPBERRYPI_TOUCHSCREEN_ATTINY is not set
+# CONFIG_REGULATOR_TPS6524X is not set
+# CONFIG_RMI4_CORE is not set
+# CONFIG_ROCKCHIP_ANALOGIX_DP is not set
+# CONFIG_ROCKCHIP_CDN_DP is not set
+# CONFIG_ROCKCHIP_DRM_CUBIC_LUT is not set
+# CONFIG_ROCKCHIP_DRM_DIRECT_SHOW is not set
+# CONFIG_ROCKCHIP_DRM_TVE is not set
+# CONFIG_ROCKCHIP_DW_DP is not set
+# CONFIG_ROCKCHIP_DW_HDCP2 is not set
+# CONFIG_ROCKCHIP_DW_HDMI is not set
+# CONFIG_ROCKCHIP_DW_MIPI_DSI is not set
+# CONFIG_ROCKCHIP_INNO_HDMI is not set
+# CONFIG_ROCKCHIP_LVDS is not set
+CONFIG_ROCKCHIP_MBOX=y
+# CONFIG_ROCKCHIP_MTD_VENDOR_STORAGE is not set
+# CONFIG_ROCKCHIP_REMOTECTL is not set
+CONFIG_ROCKCHIP_RGB=y
+# CONFIG_ROCKCHIP_RK3066_HDMI is not set
+# CONFIG_ROCKCHIP_RKNPU_DRM_GEM is not set
+CONFIG_ROCKCHIP_THUNDER_BOOT_SERVICE=y
+# CONFIG_ROCKCHIP_VCONN is not set
+CONFIG_ROCKCHIP_VOP=y
+# CONFIG_ROCKCHIP_VOP2 is not set
+# CONFIG_RPMSG_QCOM_GLINK_RPM is not set
+# CONFIG_RPMSG_ROCKCHIP is not set
+# CONFIG_RTC_DRV_DS1302 is not set
+# CONFIG_RTC_DRV_DS1305 is not set
+# CONFIG_RTC_DRV_DS1343 is not set
+# CONFIG_RTC_DRV_DS1347 is not set
+# CONFIG_RTC_DRV_DS1390 is not set
+# CONFIG_RTC_DRV_M41T93 is not set
+# CONFIG_RTC_DRV_M41T94 is not set
+# CONFIG_RTC_DRV_MAX6902 is not set
+# CONFIG_RTC_DRV_MAX6916 is not set
+# CONFIG_RTC_DRV_MCP795 is not set
+# CONFIG_RTC_DRV_PCF2123 is not set
+# CONFIG_RTC_DRV_R9701 is not set
+# CONFIG_RTC_DRV_RS5C348 is not set
+# CONFIG_RTC_DRV_RX4581 is not set
+# CONFIG_RTC_DRV_RX6110 is not set
+# CONFIG_SCA3000 is not set
+# CONFIG_SENSORS_HMC5843_SPI is not set
+# CONFIG_SENSORS_LIS3_I2C is not set
+# CONFIG_SENSORS_LIS3_SPI is not set
+# CONFIG_SENSORS_RM3100_SPI is not set
+# CONFIG_SENSOR_DEVICE is not set
+# CONFIG_SERIAL_IFX6X60 is not set
+# CONFIG_SERIAL_MAX3100 is not set
+# CONFIG_SERIAL_MAX310X is not set
+CONFIG_SND_JACK_INPUT_DEV=y
+# CONFIG_SND_SOC_ADAU1761_SPI is not set
+# CONFIG_SND_SOC_AK4104 is not set
+# CONFIG_SND_SOC_CS4271_SPI is not set
+# CONFIG_SND_SOC_CS42L52 is not set
+# CONFIG_SND_SOC_CS42L56 is not set
+# CONFIG_SND_SOC_ES8328_SPI is not set
+# CONFIG_SND_SOC_PCM179X_SPI is not set
+# CONFIG_SND_SOC_PCM186X_SPI is not set
+# CONFIG_SND_SOC_PCM3060_SPI is not set
+# CONFIG_SND_SOC_PCM3168A_SPI is not set
+# CONFIG_SND_SOC_PCM512x_SPI is not set
+# CONFIG_SND_SOC_RK3399_GRU_SOUND is not set
+# CONFIG_SND_SOC_SSM2602_SPI is not set
+# CONFIG_SND_SOC_TLV320AIC23_SPI is not set
+# CONFIG_SND_SOC_TLV320AIC32X4_SPI is not set
+# CONFIG_SND_SOC_WM8770 is not set
+# CONFIG_SND_SOC_WM8804_SPI is not set
+# CONFIG_SND_SOC_WM8962 is not set
+# CONFIG_SND_SOC_ZL38060 is not set
+# CONFIG_SND_SPI is not set
+# CONFIG_SOLARIS_X86_PARTITION is not set
+# CONFIG_SPI_ALTERA is not set
+# CONFIG_SPI_AMD is not set
+# CONFIG_SPI_AXI_SPI_ENGINE is not set
+# CONFIG_SPI_BITBANG is not set
+# CONFIG_SPI_CADENCE is not set
+# CONFIG_SPI_CADENCE_QUADSPI is not set
+# CONFIG_SPI_DEBUG is not set
+# CONFIG_SPI_DESIGNWARE is not set
+# CONFIG_SPI_FSL_SPI is not set
+# CONFIG_SPI_GPIO is not set
+# CONFIG_SPI_LOOPBACK_TEST is not set
+CONFIG_SPI_MASTER=y
+CONFIG_SPI_MEM=y
+# CONFIG_SPI_MUX is not set
+# CONFIG_SPI_MXIC is not set
+# CONFIG_SPI_NXP_FLEXSPI is not set
+# CONFIG_SPI_OC_TINY is not set
+# CONFIG_SPI_PL022 is not set
+CONFIG_SPI_ROCKCHIP=y
+# CONFIG_SPI_ROCKCHIP_MISCDEV is not set
+CONFIG_SPI_ROCKCHIP_SFC=y
+# CONFIG_SPI_SC18IS602 is not set
+# CONFIG_SPI_SIFIVE is not set
+# CONFIG_SPI_SLAVE is not set
+CONFIG_SPI_SPIDEV=y
+# CONFIG_SPI_TLE62X0 is not set
+# CONFIG_SPI_XCOMM is not set
+# CONFIG_SPI_XILINX is not set
+# CONFIG_SPI_ZYNQMP_GQSPI is not set
+# CONFIG_TINYDRM_HX8357D is not set
+# CONFIG_TINYDRM_ILI9225 is not set
+# CONFIG_TINYDRM_ILI9341 is not set
+# CONFIG_TINYDRM_ILI9486 is not set
+# CONFIG_TINYDRM_MI0283QT is not set
+# CONFIG_TINYDRM_REPAPER is not set
+# CONFIG_TINYDRM_ST7586 is not set
+# CONFIG_TINYDRM_ST7735R is not set
+# CONFIG_TI_ADC0832 is not set
+# CONFIG_TI_ADC084S021 is not set
+# CONFIG_TI_ADC108S102 is not set
+# CONFIG_TI_ADC12138 is not set
+# CONFIG_TI_ADC128S052 is not set
+# CONFIG_TI_ADC161S626 is not set
+# CONFIG_TI_ADS124S08 is not set
+# CONFIG_TI_ADS7950 is not set
+# CONFIG_TI_ADS8344 is not set
+# CONFIG_TI_ADS8688 is not set
+# CONFIG_TI_DAC082S085 is not set
+# CONFIG_TI_DAC7311 is not set
+# CONFIG_TI_DAC7612 is not set
+# CONFIG_TI_TLC4541 is not set
+# CONFIG_UNIXWARE_DISKLABEL is not set
+CONFIG_VIDEOMODE_HELPERS=y
+# CONFIG_VIDEO_GS1662 is not set
+# CONFIG_VIDEO_MAX9286 is not set
+# CONFIG_VIDEO_ROCKCHIP_PREISP is not set
+# CONFIG_VIDEO_S5C73M3 is not set
+CONFIG_ZLIB_DEFLATE=y
+CONFIG_ZLIB_INFLATE=y
+
diff --git a/kernel/arch/arm/configs/rv1106-wakeup.config b/kernel/arch/arm/configs/rv1106-wakeup.config
new file mode 100644
index 0000000..65f9b60
--- /dev/null
+++ b/kernel/arch/arm/configs/rv1106-wakeup.config
@@ -0,0 +1,127 @@
+CONFIG_INPUT=y
+CONFIG_RV1106_HPMCU_FAST_WAKEUP=y
+CONFIG_VIDEO_SC301IOT=y
+# CONFIG_VIDEO_SC3336 is not set
+CONFIG_VIDEO_SC3338=y
+# CONFIG_VIDEO_SC4336 is not set
+# CONFIG_VIDEO_SC530AI is not set
+CONFIG_HID=y
+# CONFIG_HIDRAW is not set
+# CONFIG_HID_A4TECH is not set
+# CONFIG_HID_ACRUX is not set
+# CONFIG_HID_ALPS is not set
+# CONFIG_HID_APPLE is not set
+# CONFIG_HID_AUREAL is not set
+# CONFIG_HID_BATTERY_STRENGTH is not set
+# CONFIG_HID_BELKIN is not set
+# CONFIG_HID_CHERRY is not set
+# CONFIG_HID_CMEDIA is not set
+# CONFIG_HID_COUGAR is not set
+# CONFIG_HID_CYPRESS is not set
+# CONFIG_HID_DRAGONRISE is not set
+# CONFIG_HID_ELECOM is not set
+# CONFIG_HID_EMS_FF is not set
+# CONFIG_HID_EZKEY is not set
+# CONFIG_HID_GEMBIRD is not set
+CONFIG_HID_GENERIC=y
+# CONFIG_HID_GFRM is not set
+# CONFIG_HID_GLORIOUS is not set
+# CONFIG_HID_GREENASIA is not set
+# CONFIG_HID_GYRATION is not set
+# CONFIG_HID_ICADE is not set
+# CONFIG_HID_ITE is not set
+# CONFIG_HID_JABRA is not set
+# CONFIG_HID_KENSINGTON is not set
+# CONFIG_HID_KEYTOUCH is not set
+# CONFIG_HID_KYE is not set
+# CONFIG_HID_LCPOWER is not set
+# CONFIG_HID_LENOVO is not set
+# CONFIG_HID_MACALLY is not set
+# CONFIG_HID_MAGICMOUSE is not set
+# CONFIG_HID_MALTRON is not set
+# CONFIG_HID_MAYFLASH is not set
+# CONFIG_HID_MICROSOFT is not set
+# CONFIG_HID_MONTEREY is not set
+# CONFIG_HID_MULTITOUCH is not set
+# CONFIG_HID_NINTENDO is not set
+# CONFIG_HID_NTI is not set
+# CONFIG_HID_ORTEK is not set
+# CONFIG_HID_PANTHERLORD is not set
+# CONFIG_HID_PETALYNX is not set
+# CONFIG_HID_PICOLCD is not set
+# CONFIG_HID_PLANTRONICS is not set
+# CONFIG_HID_PLAYSTATION is not set
+# CONFIG_HID_PRIMAX is not set
+# CONFIG_HID_REDRAGON is not set
+# CONFIG_HID_RMI is not set
+# CONFIG_HID_SAITEK is not set
+# CONFIG_HID_SENSOR_HUB is not set
+# CONFIG_HID_SMARTJOYPLUS is not set
+# CONFIG_HID_SPEEDLINK is not set
+# CONFIG_HID_STEAM is not set
+# CONFIG_HID_STEELSERIES is not set
+# CONFIG_HID_SUNPLUS is not set
+# CONFIG_HID_THRUSTMASTER is not set
+# CONFIG_HID_TIVO is not set
+# CONFIG_HID_TOPSEED is not set
+# CONFIG_HID_TWINHAN is not set
+# CONFIG_HID_UDRAW_PS3 is not set
+# CONFIG_HID_VIEWSONIC is not set
+# CONFIG_HID_VIVALDI is not set
+# CONFIG_HID_WALTOP is not set
+# CONFIG_HID_XINMO is not set
+# CONFIG_HID_ZEROPLUS is not set
+# CONFIG_HID_ZYDACRON is not set
+# CONFIG_I2C_HID is not set
+# CONFIG_INPUT_EVBUG is not set
+# CONFIG_INPUT_EVDEV is not set
+# CONFIG_INPUT_FF_MEMLESS is not set
+# CONFIG_INPUT_JOYDEV is not set
+# CONFIG_INPUT_JOYSTICK is not set
+CONFIG_INPUT_KEYBOARD=y
+# CONFIG_INPUT_MATRIXKMAP is not set
+# CONFIG_INPUT_MISC is not set
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_INPUT_MOUSEDEV is not set
+# CONFIG_INPUT_POLLDEV is not set
+# CONFIG_INPUT_SPARSEKMAP is not set
+# CONFIG_INPUT_TABLET is not set
+# CONFIG_INPUT_TOUCHSCREEN is not set
+# CONFIG_KEYBOARD_ADC is not set
+# CONFIG_KEYBOARD_ADP5588 is not set
+# CONFIG_KEYBOARD_ADP5589 is not set
+# CONFIG_KEYBOARD_ATKBD is not set
+# CONFIG_KEYBOARD_BCM is not set
+# CONFIG_KEYBOARD_CAP11XX is not set
+# CONFIG_KEYBOARD_DLINK_DIR685 is not set
+CONFIG_KEYBOARD_GPIO=y
+# CONFIG_KEYBOARD_GPIO_POLLED is not set
+# CONFIG_KEYBOARD_LKKBD is not set
+# CONFIG_KEYBOARD_LM8333 is not set
+# CONFIG_KEYBOARD_MATRIX is not set
+# CONFIG_KEYBOARD_MAX7359 is not set
+# CONFIG_KEYBOARD_MCS is not set
+# CONFIG_KEYBOARD_MPR121 is not set
+# CONFIG_KEYBOARD_NEWTON is not set
+# CONFIG_KEYBOARD_OMAP4 is not set
+# CONFIG_KEYBOARD_OPENCORES is not set
+# CONFIG_KEYBOARD_QT1050 is not set
+# CONFIG_KEYBOARD_QT1070 is not set
+# CONFIG_KEYBOARD_QT2160 is not set
+# CONFIG_KEYBOARD_SAMSUNG is not set
+# CONFIG_KEYBOARD_STOWAWAY is not set
+# CONFIG_KEYBOARD_SUNKBD is not set
+# CONFIG_KEYBOARD_TCA6416 is not set
+# CONFIG_KEYBOARD_TCA8418 is not set
+# CONFIG_KEYBOARD_XTKBD is not set
+# CONFIG_RC_CORE is not set
+# CONFIG_RMI4_CORE is not set
+# CONFIG_ROCKCHIP_REMOTECTL is not set
+# CONFIG_SENSORS_LIS3_I2C is not set
+# CONFIG_SENSORS_LIS3_SPI is not set
+# CONFIG_SENSOR_DEVICE is not set
+CONFIG_SND_JACK_INPUT_DEV=y
+# CONFIG_SND_SOC_CS42L52 is not set
+# CONFIG_SND_SOC_CS42L56 is not set
+# CONFIG_SND_SOC_WM8962 is not set
+# CONFIG_UHID is not set
diff --git a/kernel/arch/arm/include/asm/fixmap.h b/kernel/arch/arm/include/asm/fixmap.h
index 707068f..9575b40 100644
--- a/kernel/arch/arm/include/asm/fixmap.h
+++ b/kernel/arch/arm/include/asm/fixmap.h
@@ -7,14 +7,14 @@
 #define FIXADDR_TOP		(FIXADDR_END - PAGE_SIZE)
 
 #include <linux/pgtable.h>
-#include <asm/kmap_size.h>
+#include <asm/kmap_types.h>
 
 enum fixed_addresses {
 	FIX_EARLYCON_MEM_BASE,
 	__end_of_permanent_fixed_addresses,
 
 	FIX_KMAP_BEGIN = __end_of_permanent_fixed_addresses,
-	FIX_KMAP_END = FIX_KMAP_BEGIN + (KM_MAX_IDX * NR_CPUS) - 1,
+	FIX_KMAP_END = FIX_KMAP_BEGIN + (KM_TYPE_NR * NR_CPUS) - 1,
 
 	/* Support writing RO kernel text via kprobes, jump labels, etc. */
 	FIX_TEXT_POKE0,
diff --git a/kernel/arch/arm/include/asm/hardirq.h b/kernel/arch/arm/include/asm/hardirq.h
index 706efaf..b95848e 100644
--- a/kernel/arch/arm/include/asm/hardirq.h
+++ b/kernel/arch/arm/include/asm/hardirq.h
@@ -2,11 +2,16 @@
 #ifndef __ASM_HARDIRQ_H
 #define __ASM_HARDIRQ_H
 
+#include <linux/cache.h>
+#include <linux/threads.h>
 #include <asm/irq.h>
 
-#define __ARCH_IRQ_EXIT_IRQS_DISABLED	1
-#define ack_bad_irq ack_bad_irq
+typedef struct {
+	unsigned int __softirq_pending;
+} ____cacheline_aligned irq_cpustat_t;
 
-#include <asm-generic/hardirq.h>
+#include <linux/irq_cpustat.h>	/* Standard mappings for irq_cpustat_t above */
+
+#define __ARCH_IRQ_EXIT_IRQS_DISABLED	1
 
 #endif /* __ASM_HARDIRQ_H */
diff --git a/kernel/arch/arm/include/asm/highmem.h b/kernel/arch/arm/include/asm/highmem.h
index b22dffa..31811be 100644
--- a/kernel/arch/arm/include/asm/highmem.h
+++ b/kernel/arch/arm/include/asm/highmem.h
@@ -2,8 +2,7 @@
 #ifndef _ASM_HIGHMEM_H
 #define _ASM_HIGHMEM_H
 
-#include <asm/kmap_size.h>
-#include <asm/fixmap.h>
+#include <asm/kmap_types.h>
 
 #define PKMAP_BASE		(PAGE_OFFSET - PMD_SIZE)
 #define LAST_PKMAP		PTRS_PER_PTE
@@ -47,32 +46,19 @@
 
 #ifdef ARCH_NEEDS_KMAP_HIGH_GET
 extern void *kmap_high_get(struct page *page);
-
-static inline void *arch_kmap_local_high_get(struct page *page)
-{
-	if (IS_ENABLED(CONFIG_DEBUG_HIGHMEM) && !cache_is_vivt())
-		return NULL;
-	return kmap_high_get(page);
-}
-#define arch_kmap_local_high_get arch_kmap_local_high_get
-
-#else /* ARCH_NEEDS_KMAP_HIGH_GET */
+#else
 static inline void *kmap_high_get(struct page *page)
 {
 	return NULL;
 }
-#endif /* !ARCH_NEEDS_KMAP_HIGH_GET */
+#endif
 
-#define arch_kmap_local_post_map(vaddr, pteval)				\
-	local_flush_tlb_kernel_page(vaddr)
-
-#define arch_kmap_local_pre_unmap(vaddr)				\
-do {									\
-	if (cache_is_vivt())						\
-		__cpuc_flush_dcache_area((void *)vaddr, PAGE_SIZE);	\
-} while (0)
-
-#define arch_kmap_local_post_unmap(vaddr)				\
-	local_flush_tlb_kernel_page(vaddr)
+/*
+ * The following functions are already defined by <linux/highmem.h>
+ * when CONFIG_HIGHMEM is not set.
+ */
+#ifdef CONFIG_HIGHMEM
+extern void *kmap_atomic_pfn(unsigned long pfn);
+#endif
 
 #endif
diff --git a/kernel/arch/arm/include/asm/irq.h b/kernel/arch/arm/include/asm/irq.h
index 1cbcc46..46d4114 100644
--- a/kernel/arch/arm/include/asm/irq.h
+++ b/kernel/arch/arm/include/asm/irq.h
@@ -31,8 +31,6 @@
 void init_IRQ(void);
 
 #ifdef CONFIG_SMP
-#include <linux/cpumask.h>
-
 extern void arch_trigger_cpumask_backtrace(const cpumask_t *mask,
 					   bool exclude_self);
 #define arch_trigger_cpumask_backtrace arch_trigger_cpumask_backtrace
diff --git a/kernel/arch/arm/include/asm/kmap_types.h b/kernel/arch/arm/include/asm/kmap_types.h
new file mode 100644
index 0000000..5590940
--- /dev/null
+++ b/kernel/arch/arm/include/asm/kmap_types.h
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __ARM_KMAP_TYPES_H
+#define __ARM_KMAP_TYPES_H
+
+/*
+ * This is the "bare minimum".  AIO seems to require this.
+ */
+#define KM_TYPE_NR 16
+
+#endif
diff --git a/kernel/arch/arm/include/asm/spinlock_types.h b/kernel/arch/arm/include/asm/spinlock_types.h
index a37c080..5976958 100644
--- a/kernel/arch/arm/include/asm/spinlock_types.h
+++ b/kernel/arch/arm/include/asm/spinlock_types.h
@@ -2,6 +2,10 @@
 #ifndef __ASM_SPINLOCK_TYPES_H
 #define __ASM_SPINLOCK_TYPES_H
 
+#ifndef __LINUX_SPINLOCK_TYPES_H
+# error "please don't include this file directly"
+#endif
+
 #define TICKET_SHIFT	16
 
 typedef struct {
diff --git a/kernel/arch/arm/include/asm/thread_info.h b/kernel/arch/arm/include/asm/thread_info.h
index dc2cd31..eb7ce27 100644
--- a/kernel/arch/arm/include/asm/thread_info.h
+++ b/kernel/arch/arm/include/asm/thread_info.h
@@ -46,7 +46,6 @@
 struct thread_info {
 	unsigned long		flags;		/* low level flags */
 	int			preempt_count;	/* 0 => preemptable, <0 => bug */
-	int			preempt_lazy_count; /* 0 => preemptable, <0 => bug */
 	mm_segment_t		addr_limit;	/* address limit */
 	struct task_struct	*task;		/* main task structure */
 	__u32			cpu;		/* cpu */
@@ -139,7 +138,6 @@
 #define TIF_SYSCALL_TRACEPOINT	6	/* syscall tracepoint instrumentation */
 #define TIF_SECCOMP		7	/* seccomp syscall filtering active */
 #define TIF_NOTIFY_SIGNAL	8	/* signal notifications exist */
-#define TIF_NEED_RESCHED_LAZY	9
 
 #define TIF_USING_IWMMXT	17
 #define TIF_MEMDIE		18	/* is terminating due to OOM killer */
@@ -154,7 +152,6 @@
 #define _TIF_SYSCALL_TRACEPOINT	(1 << TIF_SYSCALL_TRACEPOINT)
 #define _TIF_SECCOMP		(1 << TIF_SECCOMP)
 #define _TIF_NOTIFY_SIGNAL	(1 << TIF_NOTIFY_SIGNAL)
-#define _TIF_NEED_RESCHED_LAZY	(1 << TIF_NEED_RESCHED_LAZY)
 #define _TIF_USING_IWMMXT	(1 << TIF_USING_IWMMXT)
 
 /* Checks for any syscall work in entry-common.S */
@@ -166,7 +163,6 @@
  */
 #define _TIF_WORK_MASK		(_TIF_NEED_RESCHED | _TIF_SIGPENDING | \
 				 _TIF_NOTIFY_RESUME | _TIF_UPROBE | \
-				 _TIF_NEED_RESCHED_LAZY | \
 				 _TIF_NOTIFY_SIGNAL)
 
 #endif /* __KERNEL__ */
diff --git a/kernel/arch/arm/kernel/asm-offsets.c b/kernel/arch/arm/kernel/asm-offsets.c
index 024c65c..70993af 100644
--- a/kernel/arch/arm/kernel/asm-offsets.c
+++ b/kernel/arch/arm/kernel/asm-offsets.c
@@ -43,7 +43,6 @@
   BLANK();
   DEFINE(TI_FLAGS,		offsetof(struct thread_info, flags));
   DEFINE(TI_PREEMPT,		offsetof(struct thread_info, preempt_count));
-  DEFINE(TI_PREEMPT_LAZY,	offsetof(struct thread_info, preempt_lazy_count));
   DEFINE(TI_ADDR_LIMIT,		offsetof(struct thread_info, addr_limit));
   DEFINE(TI_TASK,		offsetof(struct thread_info, task));
   DEFINE(TI_CPU,		offsetof(struct thread_info, cpu));
diff --git a/kernel/arch/arm/kernel/entry-armv.S b/kernel/arch/arm/kernel/entry-armv.S
index d608655..030351d 100644
--- a/kernel/arch/arm/kernel/entry-armv.S
+++ b/kernel/arch/arm/kernel/entry-armv.S
@@ -206,18 +206,11 @@
 
 #ifdef CONFIG_PREEMPTION
 	ldr	r8, [tsk, #TI_PREEMPT]		@ get preempt count
-	teq	r8, #0				@ if preempt count != 0
-	bne	1f				@ return from exeption
 	ldr	r0, [tsk, #TI_FLAGS]		@ get flags
-	tst	r0, #_TIF_NEED_RESCHED		@ if NEED_RESCHED is set
-	blne	svc_preempt			@ preempt!
-
-	ldr	r8, [tsk, #TI_PREEMPT_LAZY]	@ get preempt lazy count
-	teq	r8, #0				@ if preempt lazy count != 0
+	teq	r8, #0				@ if preempt count != 0
 	movne	r0, #0				@ force flags to 0
-	tst	r0, #_TIF_NEED_RESCHED_LAZY
+	tst	r0, #_TIF_NEED_RESCHED
 	blne	svc_preempt
-1:
 #endif
 
 	svc_exit r5, irq = 1			@ return from exception
@@ -232,14 +225,8 @@
 1:	bl	preempt_schedule_irq		@ irq en/disable is done inside
 	ldr	r0, [tsk, #TI_FLAGS]		@ get new tasks TI_FLAGS
 	tst	r0, #_TIF_NEED_RESCHED
-	bne	1b
-	tst	r0, #_TIF_NEED_RESCHED_LAZY
 	reteq	r8				@ go again
-	ldr	r0, [tsk, #TI_PREEMPT_LAZY]	@ get preempt lazy count
-	teq	r0, #0				@ if preempt lazy count != 0
-	beq	1b
-	ret	r8				@ go again
-
+	b	1b
 #endif
 
 __und_fault:
diff --git a/kernel/arch/arm/kernel/entry-common.S b/kernel/arch/arm/kernel/entry-common.S
index a30b1a1..9b3c737 100644
--- a/kernel/arch/arm/kernel/entry-common.S
+++ b/kernel/arch/arm/kernel/entry-common.S
@@ -92,7 +92,6 @@
 	ldr	r1, [tsk, #TI_FLAGS]		@ re-check for syscall tracing
 	movs	r1, r1, lsl #16
 	beq	no_work_pending
-do_slower_path:
  UNWIND(.fnend		)
 ENDPROC(ret_fast_syscall)
 
diff --git a/kernel/arch/arm/kernel/signal.c b/kernel/arch/arm/kernel/signal.c
index f04ccf1..a3a38d0 100644
--- a/kernel/arch/arm/kernel/signal.c
+++ b/kernel/arch/arm/kernel/signal.c
@@ -649,8 +649,7 @@
 	 */
 	trace_hardirqs_off();
 	do {
-		if (likely(thread_flags & (_TIF_NEED_RESCHED |
-					   _TIF_NEED_RESCHED_LAZY))) {
+		if (likely(thread_flags & _TIF_NEED_RESCHED)) {
 			schedule();
 		} else {
 			if (unlikely(!user_mode(regs)))
diff --git a/kernel/arch/arm/kernel/smp.c b/kernel/arch/arm/kernel/smp.c
index d29e892..123432b 100644
--- a/kernel/arch/arm/kernel/smp.c
+++ b/kernel/arch/arm/kernel/smp.c
@@ -675,7 +675,9 @@
 		break;
 
 	case IPI_CPU_BACKTRACE:
+		printk_nmi_enter();
 		nmi_cpu_backtrace(get_irq_regs());
+		printk_nmi_exit();
 		break;
 
 	default:
diff --git a/kernel/arch/arm/mach-rockchip/Kconfig b/kernel/arch/arm/mach-rockchip/Kconfig
index 95b6da9..922b2e7 100644
--- a/kernel/arch/arm/mach-rockchip/Kconfig
+++ b/kernel/arch/arm/mach-rockchip/Kconfig
@@ -22,3 +22,9 @@
 	help
 	  Support for Rockchip's Cortex-A9 Single-to-Quad-Core-SoCs
 	  containing the RK2928, RK30xx and RK31xx series.
+
+config RV1106_HPMCU_FAST_WAKEUP
+	bool "Rockchip RV1106 HPMCU fast wakeup configuration support"
+	depends on PM_SLEEP && CPU_RV1106
+	help
+	  This config aims to support HPMCU fast wakeup.
diff --git a/kernel/arch/arm/mach-rockchip/rv1106_pm.c b/kernel/arch/arm/mach-rockchip/rv1106_pm.c
index a4d1c0f..7767b44 100644
--- a/kernel/arch/arm/mach-rockchip/rv1106_pm.c
+++ b/kernel/arch/arm/mach-rockchip/rv1106_pm.c
@@ -15,6 +15,7 @@
 #include <asm/fiq_glue.h>
 #include <asm/tlbflush.h>
 #include <asm/suspend.h>
+#include <linux/irqchip/arm-gic.h>
 
 #include "rkpm_gicv2.h"
 #include "rkpm_helpers.h"
@@ -91,6 +92,7 @@
 static void __iomem *pmu_base;
 static void __iomem *nstimer_base;
 static void __iomem *stimer_base;
+static void __iomem *mbox_base;
 static void __iomem *ddrc_base;
 static void __iomem *ioc_base[5];
 static void __iomem *gpio_base[5];
@@ -309,7 +311,10 @@
 
 static void gic400_restore(void)
 {
-	rkpm_gicv2_dist_restore(gicd_base, &gicd_ctx_save);
+	if (IS_ENABLED(CONFIG_RV1106_HPMCU_FAST_WAKEUP))
+		writel_relaxed(0x3, gicd_base + GIC_DIST_CTRL);
+	else
+		rkpm_gicv2_dist_restore(gicd_base, &gicd_ctx_save);
 	rkpm_gicv2_cpu_restore(gicd_base, gicc_base, &gicc_ctx_save);
 }
 
@@ -408,6 +413,87 @@
 
 	rkpm_bootdata_l2ctlr_f = 1;
 	rkpm_bootdata_l2ctlr = rv1106_l2_config();
+}
+
+static void writel_clrset_bits(u32 clr, u32 set, void __iomem *addr)
+{
+	u32 val = readl_relaxed(addr);
+
+	val &= ~clr;
+	val |= set;
+	writel_relaxed(val, addr);
+}
+
+static void gic_irq_en(int irq)
+{
+	writel_clrset_bits(0xff << irq % 4 * 8, 0x1 << irq % 4 * 8,
+			   gicd_base + GIC_DIST_TARGET + (irq >> 2 << 2));
+	writel_clrset_bits(0xff << irq % 4 * 8, 0xa0 << irq % 4 * 8,
+			   gicd_base + GIC_DIST_PRI + (irq >> 2 << 2));
+	writel_clrset_bits(0x3 << irq % 16 * 2, 0x1 << irq % 16 * 2,
+			   gicd_base + GIC_DIST_CONFIG + (irq >> 4 << 2));
+	writel_clrset_bits(BIT(irq % 32), BIT(irq % 32),
+			   gicd_base + GIC_DIST_IGROUP + (irq >> 5 << 2));
+
+	dsb(sy);
+	writel_relaxed(0x1 << irq % 32, gicd_base + GIC_DIST_ENABLE_SET + (irq >> 5 << 2));
+	dsb(sy);
+}
+
+static int is_hpmcu_mbox_int(void)
+{
+	return !!(readl(mbox_base + RV1106_MBOX_B2A_STATUS) & BIT(0));
+}
+
+static void hpmcu_start(void)
+{
+	/* enable hpmcu mailbox AP irq */
+	gic_irq_en(RV1106_HPMCU_MBOX_IRQ_AP);
+
+	/* tell hpmcu that we are currently in system wake up. */
+	writel(RV1106_SYS_IS_WKUP, pmu_base + RV1106_PMU_SYS_REG(0));
+
+	/* set the mcu uncache area, usually set the devices address */
+	writel(0xff000, coregrf_base + RV1106_COREGRF_CACHE_PERI_ADDR_START);
+	writel(0xffc00, coregrf_base + RV1106_COREGRF_CACHE_PERI_ADDR_END);
+	/* Reset the hp mcu */
+	writel(0x1e001e, corecru_base + RV1106_COERCRU_SFTRST_CON(1));
+	/* set the mcu addr */
+	writel(RV1106_HPMCU_BOOT_ADDR,
+	       coresgrf_base + RV1106_CORESGRF_HPMCU_BOOTADDR);
+	dsb(sy);
+
+	/* release the mcu */
+	writel(0x1e0000, corecru_base + RV1106_COERCRU_SFTRST_CON(1));
+	dsb(sy);
+}
+
+static int hpmcu_fast_wkup(void)
+{
+	u32 cmd;
+
+	hpmcu_start();
+
+	while (1) {
+		rkpm_printstr("-s-\n");
+		dsb(sy);
+		wfi();
+		rkpm_printstr("-w-\n");
+
+		if (is_hpmcu_mbox_int()) {
+			rkpm_printstr("-h-mbox-\n");
+			/* clear system wake up state */
+			writel(0, pmu_base + RV1106_PMU_SYS_REG(0));
+			writel(BIT(0), mbox_base + RV1106_MBOX_B2A_STATUS);
+			break;
+		}
+	}
+
+	cmd = readl(mbox_base + RV1106_MBOX_B2A_CMD_0);
+	if (cmd == RV1106_MBOX_CMD_AP_SUSPEND)
+		return 1;
+	else
+		return 0;
 }
 
 static void clock_suspend(void)
@@ -616,10 +702,11 @@
 	ddr_data.ioc1_1a_iomux_l = readl_relaxed(ioc_base[1] + 0);
 
 	pmu_wkup_con =
-		/* BIT(RV1106_PMU_WAKEUP_TIMEROUT_EN) | */
 		/* BIT(RV1106_PMU_WAKEUP_CPU_INT_EN) | */
 		BIT(RV1106_PMU_WAKEUP_GPIO_INT_EN) |
 		0;
+	if (IS_ENABLED(CONFIG_RV1106_HPMCU_FAST_WAKEUP))
+		pmu_wkup_con |= BIT(RV1106_PMU_WAKEUP_TIMEROUT_EN);
 
 	pmu_pwr_con =
 		BIT(RV1106_PMU_PWRMODE_EN) |
@@ -949,7 +1036,18 @@
 
 	cpu_do_idle();
 
-	pr_err("%s: Failed to suspend\n", __func__);
+#if RV1106_WAKEUP_TO_SYSTEM_RESET
+	/* If reaches here, it means wakeup source cames before cpu enter wfi.
+	 * So we should do system reset if RV1106_WAKEUP_TO_SYSTEM_RESET.
+	 */
+	writel_relaxed(0x000c000c, cru_base + RV1106_CRU_GLB_RST_CON);
+	writel_relaxed(0xffff0000, pmugrf_base + RV1106_PMUGRF_SOC_CON(4));
+	writel_relaxed(0xffff0000, pmugrf_base + RV1106_PMUGRF_SOC_CON(5));
+	dsb(sy);
+	writel_relaxed(0xfdb9, cru_base + RV1106_CRU_GLB_SRST_FST);
+#endif
+
+	rkpm_printstr("Failed to suspend\n");
 
 	return 1;
 }
@@ -964,6 +1062,7 @@
 
 	rkpm_printch('-');
 
+RE_ENTER_SLEEP:
 	clock_suspend();
 	rkpm_printch('0');
 
@@ -1000,6 +1099,17 @@
 
 	clock_resume();
 	rkpm_printch('-');
+
+	/* Check whether it's time_out wakeup */
+	if (IS_ENABLED(CONFIG_RV1106_HPMCU_FAST_WAKEUP) && ddr_data.pmu_wkup_int_st == 0) {
+		if (hpmcu_fast_wkup()) {
+			rkpm_gicv2_dist_restore(gicd_base, &gicd_ctx_save);
+			goto RE_ENTER_SLEEP;
+		} else {
+			rkpm_gicv2_dist_restore(gicd_base, &gicd_ctx_save);
+			rkpm_gicv2_cpu_restore(gicd_base, gicc_base, &gicc_ctx_save);
+		}
+	}
 
 	fiq_glue_resume();
 
@@ -1062,6 +1172,7 @@
 	corecru_base = dev_reg_base + RV1106_CORECRU_OFFSET;
 	venccru_base = dev_reg_base + RV1106_VENCCRU_OFFSET;
 	vocru_base = dev_reg_base + RV1106_VOCRU_OFFSET;
+	mbox_base = dev_reg_base + RV1106_MBOX_OFFSET;
 
 	ioc_base[0] = dev_reg_base + RV1106_GPIO0IOC_OFFSET;
 	ioc_base[1] = dev_reg_base + RV1106_GPIO1IOC_OFFSET;
diff --git a/kernel/arch/arm/mach-rockchip/rv1106_pm.h b/kernel/arch/arm/mach-rockchip/rv1106_pm.h
index 0afd51e..da857a5 100644
--- a/kernel/arch/arm/mach-rockchip/rv1106_pm.h
+++ b/kernel/arch/arm/mach-rockchip/rv1106_pm.h
@@ -7,6 +7,7 @@
 #define __MACH_ROCKCHIP_RV1106_PM_H
 
 #define RV1106_WAKEUP_TO_SYSTEM_RESET	0
+#define RV1106_HPMCU_FAST_WKUP_TIMEOUT	2000 /* ms */
 
 #define RV1106_PERIGRF_OFFSET		0x0
 #define RV1106_VENCGRF_OFFSET		0x10000
@@ -54,6 +55,7 @@
 
 #define RV1106_NSTIMER_OFFSET		0x580000
 #define RV1106_STIMER_OFFSET		0x590000
+#define RV1106_MBOX_OFFSET		0x5c0000
 #define RV1106_PMUSRAM_OFFSET		0x670000
 #define RV1106_DDRC_OFFSET		0x800000
 #define RV1106_FW_DDR_OFFSET		0x900000
@@ -70,6 +72,8 @@
 #define RV1106_CRU_MODE_CON00		0x280
 #define RV1106_CRU_GATE_CON(i)		(0x800 + (i) * 4)
 #define RV1106_CRU_GATE_CON_NUM		4
+#define RV1106_CRU_GLB_SRST_FST		0xc08
+#define RV1106_CRU_GLB_RST_CON		0xc10
 
 #define CRU_PLLCON1_PWRDOWN		BIT(13)
 #define CRU_PLLCON1_LOCK_STATUS		BIT(10)
@@ -101,6 +105,7 @@
 #define RV1106_CORECRU_GATE_CON(i)	(0x800 + (i) * 4)
 #define RV1106_COERCRU_CLKSEL_CON(i)	(0x300 + (i) * 4)
 #define RV1106_CORECRU_GATE_CON_NUM	2
+#define RV1106_COERCRU_SFTRST_CON(i)	(0xa00 + (i) * 4)
 
 /* grf */
 #define RV1106_PMUGRF_SOC_CON(i)	((i) * 4)
@@ -109,6 +114,11 @@
 #define RV1106_PMUSGRF_SOC_CON(i)	((i) * 4)
 
 #define RV1106_DDRGRF_CON(i)		((i) * 0x4)
+
+#define RV1106_CORESGRF_HPMCU_BOOTADDR		0x44
+
+#define RV1106_COREGRF_CACHE_PERI_ADDR_START	0x24
+#define RV1106_COREGRF_CACHE_PERI_ADDR_END	0x28
 
 /* pvmt */
 #define RV1106_PVTM_CON(i)		(0x4 + (i) * 4)
@@ -177,6 +187,17 @@
 #define PMU_SUSPEND_MAGIC		0x02468ace
 #define PMU_RESUME_MAGIC		0x13579bdf
 
+/* mcu */
+#define RV1106_MBOX_B2A_STATUS		0x2c
+#define RV1106_MBOX_B2A_CMD_0		0x30
+
+#define RV1106_HPMCU_MBOX_IRQ_AP	33
+
+#define RV1106_HPMCU_BOOT_ADDR		0x40000
+#define RV1106_MBOX_CMD_AP_SUSPEND	0x12345600
+#define RV1106_MBOX_CMD_AP_RESUME	0x12345601
+#define RV1106_SYS_IS_WKUP		0x87654300
+
 #ifndef __ASSEMBLER__
 extern unsigned long rkpm_bootdata_cpusp;
 extern unsigned long rkpm_bootdata_cpu_code;
diff --git a/kernel/arch/arm/mach-rockchip/rv1106_sleep.S b/kernel/arch/arm/mach-rockchip/rv1106_sleep.S
index a8a8c1d..bb50dff 100644
--- a/kernel/arch/arm/mach-rockchip/rv1106_sleep.S
+++ b/kernel/arch/arm/mach-rockchip/rv1106_sleep.S
@@ -13,6 +13,9 @@
 #define RV1106_PMUGRF_SOC_CON4		0xff020010
 #define RV1106_CRU_GLB_SRST_FST		0xff3b0c08
 
+#define RV1106_CRU_GLB_RST_CON_ADDR	0xff3b0c10
+#define CRU_FST_RST_PMU_VAL		0x000c000c
+
 #if RV1106_SLEEP_DEBUG
 /********************* console used for sleep.S ******************************/
 #define UART_REG_DLL	(0x00)
@@ -98,6 +101,11 @@
 	ldr	r1, [r1]
 	str	r1, [r0]
 
+	/* enable first reset trigger pmu reset */
+	ldr	r0, =RV1106_CRU_GLB_RST_CON_ADDR
+	ldr	r1, =CRU_FST_RST_PMU_VAL
+	str	r1, [r0]
+
 	/* clear pmu reset hold */
 	ldr	r0, =RV1106_PMUGRF_SOC_CON4
 	ldr	r1, =0xffff0000
diff --git a/kernel/arch/arm/mm/Makefile b/kernel/arch/arm/mm/Makefile
index c4ce477..7cb1699 100644
--- a/kernel/arch/arm/mm/Makefile
+++ b/kernel/arch/arm/mm/Makefile
@@ -19,6 +19,7 @@
 obj-$(CONFIG_DEBUG_VIRTUAL)	+= physaddr.o
 
 obj-$(CONFIG_ALIGNMENT_TRAP)	+= alignment.o
+obj-$(CONFIG_HIGHMEM)		+= highmem.o
 obj-$(CONFIG_HUGETLB_PAGE)	+= hugetlbpage.o
 obj-$(CONFIG_ARM_PV_FIXUP)	+= pv-fixup-asm.o
 
diff --git a/kernel/arch/arm/mm/cache-feroceon-l2.c b/kernel/arch/arm/mm/cache-feroceon-l2.c
index 8732876..5c1b7a7 100644
--- a/kernel/arch/arm/mm/cache-feroceon-l2.c
+++ b/kernel/arch/arm/mm/cache-feroceon-l2.c
@@ -49,9 +49,9 @@
 	 * we simply install a virtual mapping for it only for the
 	 * TLB lookup to occur, hence no need to flush the untouched
 	 * memory mapping afterwards (note: a cache flush may happen
-	 * in some circumstances depending on the path taken in kunmap_local).
+	 * in some circumstances depending on the path taken in kunmap_atomic).
 	 */
-	void *vaddr = kmap_local_pfn(paddr >> PAGE_SHIFT);
+	void *vaddr = kmap_atomic_pfn(paddr >> PAGE_SHIFT);
 	return (unsigned long)vaddr + (paddr & ~PAGE_MASK);
 #else
 	return __phys_to_virt(paddr);
@@ -61,7 +61,7 @@
 static inline void l2_put_va(unsigned long vaddr)
 {
 #ifdef CONFIG_HIGHMEM
-	kunmap_local((void *)vaddr);
+	kunmap_atomic((void *)vaddr);
 #endif
 }
 
diff --git a/kernel/arch/arm/mm/cache-xsc3l2.c b/kernel/arch/arm/mm/cache-xsc3l2.c
index 0e0a3ab..d20d7af 100644
--- a/kernel/arch/arm/mm/cache-xsc3l2.c
+++ b/kernel/arch/arm/mm/cache-xsc3l2.c
@@ -59,7 +59,7 @@
 {
 #ifdef CONFIG_HIGHMEM
 	if (va != -1)
-		kunmap_local((void *)va);
+		kunmap_atomic((void *)va);
 #endif
 }
 
@@ -75,7 +75,7 @@
 		 * in place for it.
 		 */
 		l2_unmap_va(prev_va);
-		va = (unsigned long)kmap_local_pfn(pa >> PAGE_SHIFT);
+		va = (unsigned long)kmap_atomic_pfn(pa >> PAGE_SHIFT);
 	}
 	return va + (pa_offset >> (32 - PAGE_SHIFT));
 #else
diff --git a/kernel/arch/arm/mm/fault.c b/kernel/arch/arm/mm/fault.c
index 59487ee..efa4020 100644
--- a/kernel/arch/arm/mm/fault.c
+++ b/kernel/arch/arm/mm/fault.c
@@ -400,9 +400,6 @@
 	if (addr < TASK_SIZE)
 		return do_page_fault(addr, fsr, regs);
 
-	if (interrupts_enabled(regs))
-		local_irq_enable();
-
 	if (user_mode(regs))
 		goto bad_area;
 
@@ -473,9 +470,6 @@
 static int
 do_sect_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
 {
-	if (interrupts_enabled(regs))
-		local_irq_enable();
-
 	do_bad_area(addr, fsr, regs);
 	return 0;
 }
diff --git a/kernel/arch/arm/mm/highmem.c b/kernel/arch/arm/mm/highmem.c
new file mode 100644
index 0000000..187fab2
--- /dev/null
+++ b/kernel/arch/arm/mm/highmem.c
@@ -0,0 +1,121 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * arch/arm/mm/highmem.c -- ARM highmem support
+ *
+ * Author:	Nicolas Pitre
+ * Created:	september 8, 2008
+ * Copyright:	Marvell Semiconductors Inc.
+ */
+
+#include <linux/module.h>
+#include <linux/highmem.h>
+#include <linux/interrupt.h>
+#include <asm/fixmap.h>
+#include <asm/cacheflush.h>
+#include <asm/tlbflush.h>
+#include "mm.h"
+
+static inline void set_fixmap_pte(int idx, pte_t pte)
+{
+	unsigned long vaddr = __fix_to_virt(idx);
+	pte_t *ptep = virt_to_kpte(vaddr);
+
+	set_pte_ext(ptep, pte, 0);
+	local_flush_tlb_kernel_page(vaddr);
+}
+
+static inline pte_t get_fixmap_pte(unsigned long vaddr)
+{
+	pte_t *ptep = virt_to_kpte(vaddr);
+
+	return *ptep;
+}
+
+void *kmap_atomic_high_prot(struct page *page, pgprot_t prot)
+{
+	unsigned int idx;
+	unsigned long vaddr;
+	void *kmap;
+	int type;
+
+#ifdef CONFIG_DEBUG_HIGHMEM
+	/*
+	 * There is no cache coherency issue when non VIVT, so force the
+	 * dedicated kmap usage for better debugging purposes in that case.
+	 */
+	if (!cache_is_vivt())
+		kmap = NULL;
+	else
+#endif
+		kmap = kmap_high_get(page);
+	if (kmap)
+		return kmap;
+
+	type = kmap_atomic_idx_push();
+
+	idx = FIX_KMAP_BEGIN + type + KM_TYPE_NR * smp_processor_id();
+	vaddr = __fix_to_virt(idx);
+#ifdef CONFIG_DEBUG_HIGHMEM
+	/*
+	 * With debugging enabled, kunmap_atomic forces that entry to 0.
+	 * Make sure it was indeed properly unmapped.
+	 */
+	BUG_ON(!pte_none(get_fixmap_pte(vaddr)));
+#endif
+	/*
+	 * When debugging is off, kunmap_atomic leaves the previous mapping
+	 * in place, so the contained TLB flush ensures the TLB is updated
+	 * with the new mapping.
+	 */
+	set_fixmap_pte(idx, mk_pte(page, prot));
+
+	return (void *)vaddr;
+}
+EXPORT_SYMBOL(kmap_atomic_high_prot);
+
+void kunmap_atomic_high(void *kvaddr)
+{
+	unsigned long vaddr = (unsigned long) kvaddr & PAGE_MASK;
+	int idx, type;
+
+	if (kvaddr >= (void *)FIXADDR_START) {
+		type = kmap_atomic_idx();
+		idx = FIX_KMAP_BEGIN + type + KM_TYPE_NR * smp_processor_id();
+
+		if (cache_is_vivt())
+			__cpuc_flush_dcache_area((void *)vaddr, PAGE_SIZE);
+#ifdef CONFIG_DEBUG_HIGHMEM
+		BUG_ON(vaddr != __fix_to_virt(idx));
+		set_fixmap_pte(idx, __pte(0));
+#else
+		(void) idx;  /* to kill a warning */
+#endif
+		kmap_atomic_idx_pop();
+	} else if (vaddr >= PKMAP_ADDR(0) && vaddr < PKMAP_ADDR(LAST_PKMAP)) {
+		/* this address was obtained through kmap_high_get() */
+		kunmap_high(pte_page(pkmap_page_table[PKMAP_NR(vaddr)]));
+	}
+}
+EXPORT_SYMBOL(kunmap_atomic_high);
+
+void *kmap_atomic_pfn(unsigned long pfn)
+{
+	unsigned long vaddr;
+	int idx, type;
+	struct page *page = pfn_to_page(pfn);
+
+	preempt_disable();
+	pagefault_disable();
+	if (!PageHighMem(page))
+		return page_address(page);
+
+	type = kmap_atomic_idx_push();
+	idx = FIX_KMAP_BEGIN + type + KM_TYPE_NR * smp_processor_id();
+	vaddr = __fix_to_virt(idx);
+#ifdef CONFIG_DEBUG_HIGHMEM
+	BUG_ON(!pte_none(get_fixmap_pte(vaddr)));
+#endif
+	set_fixmap_pte(idx, pfn_pte(pfn, kmap_prot));
+
+	return (void *)vaddr;
+}
diff --git a/kernel/arch/arm64/Kconfig b/kernel/arch/arm64/Kconfig
index d4f0b27..88ac9f0 100644
--- a/kernel/arch/arm64/Kconfig
+++ b/kernel/arch/arm64/Kconfig
@@ -78,7 +78,6 @@
 	select ARCH_SUPPORTS_ATOMIC_RMW
 	select ARCH_SUPPORTS_INT128 if CC_HAS_INT128 && (GCC_VERSION >= 50000 || CC_IS_CLANG)
 	select ARCH_SUPPORTS_NUMA_BALANCING
-	select ARCH_SUPPORTS_RT if HAVE_POSIX_CPU_TIMERS_TASK_WORK
 	select ARCH_WANT_COMPAT_IPC_PARSE_VERSION if COMPAT
 	select ARCH_WANT_DEFAULT_BPF_JIT
 	select ARCH_WANT_DEFAULT_TOPDOWN_MMAP_LAYOUT
@@ -183,7 +182,6 @@
 	select HAVE_PERF_EVENTS
 	select HAVE_PERF_REGS
 	select HAVE_PERF_USER_STACK_DUMP
-	select HAVE_PREEMPT_LAZY
 	select HAVE_REGS_AND_STACK_ACCESS_API
 	select HAVE_FUNCTION_ARG_ACCESS_API
 	select HAVE_FUTEX_CMPXCHG if FUTEX
@@ -206,7 +204,6 @@
 	select PCI_DOMAINS_GENERIC if PCI
 	select PCI_ECAM if (ACPI && PCI)
 	select PCI_SYSCALL if PCI
-	select HAVE_POSIX_CPU_TIMERS_TASK_WORK if !KVM
 	select POWER_RESET
 	select POWER_SUPPLY
 	select SET_FS
diff --git a/kernel/arch/arm64/boot/dts/rockchip/rk3568-android.dtsi b/kernel/arch/arm64/boot/dts/rockchip/rk3568-android.dtsi
old mode 100755
new mode 100644
diff --git a/kernel/arch/arm64/boot/dts/rockchip/rk3568-evb1-ddr4-v10.dtsi b/kernel/arch/arm64/boot/dts/rockchip/rk3568-evb1-ddr4-v10.dtsi
index fb8880f..7ef9000 100644
--- a/kernel/arch/arm64/boot/dts/rockchip/rk3568-evb1-ddr4-v10.dtsi
+++ b/kernel/arch/arm64/boot/dts/rockchip/rk3568-evb1-ddr4-v10.dtsi
@@ -8,21 +8,12 @@
 
 #include <dt-bindings/gpio/gpio.h>
 #include <dt-bindings/pinctrl/rockchip.h>
-#include <dt-bindings/display/media-bus-format.h>
 #include "rk3568.dtsi"
 #include "rk3568-evb.dtsi"
 
 / {
 	model = "Rockchip RK3568 EVB1 DDR4 V10 Board";
 	compatible = "rockchip,rk3568-evb1-ddr4-v10", "rockchip,rk3568";
-
-	rk_headset: rk-headset {
-		compatible = "rockchip_headset";
-		headset_gpio = <&gpio0 RK_PD5 GPIO_ACTIVE_LOW>;
-		spk_ctl_gpio = <&gpio4 RK_PC2 GPIO_ACTIVE_LOW>;//AMP_SD_GPIO4_C2_3V3
-		pinctrl-names = "default";
-		pinctrl-0 = <&hp_det>;
-	};
 
 	vcc2v5_sys: vcc2v5-ddr {
 		compatible = "regulator-fixed";
@@ -84,7 +75,7 @@
 		regulator-max-microvolt = <3300000>;
 		vin-supply = <&vcc5v0_sys>;
 	};
-#if 0
+
 	vcc_camera: vcc-camera-regulator {
 		compatible = "regulator-fixed";
 		gpio = <&gpio0 RK_PC1 GPIO_ACTIVE_HIGH>;
@@ -94,174 +85,7 @@
 		enable-active-high;
 		regulator-always-on;
 		regulator-boot-on;
-    	};
-#endif
-	ndj_io_init {
-         	compatible = "nk_io_control";	
-			pinctrl-names = "default";
-        	pinctrl-0 = <&nk_io_gpio>;
-			
-			//gpio_op0 = <&gpio1 RK_PD0 GPIO_ACTIVE_HIGH>;
-			
-			vcc_5v {
-				gpio_num = <&gpio1 RK_PA4 GPIO_ACTIVE_HIGH>; //VCC5_IO_EN_GPIO1_A4_3V3
-				gpio_function = <0>;	
-			};
-			
-			vcc_12v {
-				gpio_num = <&gpio0 RK_PC7 GPIO_ACTIVE_HIGH>;  //VCC12_IO_EN_GPIO0_C7_3V3
-				gpio_function = <0>;
-			};
-			
-			hub_host2_rst {
-				gpio_num = <&gpio4 RK_PD2 GPIO_ACTIVE_HIGH>;  //HUB_RST_GPIO4_D2_3V3
-				gpio_function = <3>;
-			};
-			
-			hub_host3 {
-				gpio_num = <&gpio4 RK_PB2 GPIO_ACTIVE_HIGH>; //HOST3_EN_GPIO4_B2_1V8
-				gpio_function = <0>;
-			};
-			
-			wake_4g {
-				gpio_num = <&gpio1 RK_PB1 GPIO_ACTIVE_LOW>; //4G_WAKEUP_GPIO01_B1_3V3
-				gpio_function = <0>;
-			};
-			
-			air_mode_4g {
-				gpio_num = <&gpio1 RK_PB0 GPIO_ACTIVE_LOW>; //4G_AIR_MODE_GPIO01_B0_3V3
-				gpio_function = <0>;
-			};
-			
-			reset_4g {
-				gpio_num = <&gpio1 RK_PB2 GPIO_ACTIVE_LOW>; //4G_RST_GPIO01_B2_3V3
-				gpio_function = <3>;
-			};
-			
-			en_4g {
-				gpio_num = <&gpio0 RK_PC6 GPIO_ACTIVE_HIGH>; //4G_PWREN_H_GPIO0_C6
-				gpio_function = <0>;
-			};
-			
-			hp_en {
-				gpio_num = <&gpio3 RK_PA6 GPIO_ACTIVE_HIGH>;//HP_EN_GPIO3_A6_3V3
-				gpio_function = <0>;
-			};
-			
-			usb_ogt {
-				gpio_num = <&gpio0 RK_PC2 GPIO_ACTIVE_HIGH>; //OTG_EN_OC_GPIO0_C2
-				gpio_function = <0>;
-			};
-			
-			m2_wifi_pwr {
-				gpio_num = <&gpio3 RK_PC6 GPIO_ACTIVE_HIGH>;//WIFI_PWREN_GPIO3_C6_1V8
-				gpio_function = <0>;
-			};
-			
-			 
-	#if 0		
-			do1 {
-				gpio_num = <&gpio1 RK_PD0 GPIO_ACTIVE_LOW>; 
-				gpio_function = <0>;
-			};
-			
-			do2 {
-				gpio_num = <&gpio1 RK_PD2 GPIO_ACTIVE_HIGH>;
-				gpio_function = <0>;
-			};
-			
-			do3 {
-				gpio_num = <&gpio1 RK_PD1 GPIO_ACTIVE_HIGH>;
-				gpio_function = <0>;
-			};
-			
-			do4 {
-				gpio_num = <&gpio1 RK_PD3 GPIO_ACTIVE_HIGH>;
-				gpio_function = <0>;
-			};
-			
-			do5 {
-				gpio_num = <&gpio2 RK_PD6 GPIO_ACTIVE_LOW>; 
-				gpio_function = <0>;
-			};
-			
-			do6 {
-				gpio_num = <&gpio2 RK_PD7 GPIO_ACTIVE_LOW>; 
-				gpio_function = <0>;
-			};
-			
-			do7 {
-				gpio_num = <&gpio3 RK_PA0 GPIO_ACTIVE_LOW>; 
-				gpio_function = <0>;
-			};
-			
-			di1 {
-				gpio_num = <&gpio2 RK_PD5 GPIO_ACTIVE_HIGH>;
-				gpio_function = <1>;
-			};
-	#endif
-		};
-#if 0			
-	    nk_io_init {
-                compatible = "nk_io_control";
-//				vcc3_io_en_gpio = <&gpio0 RK_PC4 GPIO_ACTIVE_HIGH>; //VCC3_IO_EN_GPIO0_C4_3V3
-                hub_host2_5V_rest_gpio = <&gpio4 RK_PD2 GPIO_ACTIVE_HIGH>; //HUB_RST_GPIO4_D2_3V3
-                hub_host3_5v_gpio = <&gpio4 RK_PB2 GPIO_ACTIVE_HIGH>; //HOST3_EN_GPIO4_B2_1V8
-                vcc_5v_io = <&gpio1 RK_PA4 GPIO_ACTIVE_HIGH>;   //VCC5_IO_EN_GPIO1_A4_3V3
-                vcc_12v_io = <&gpio0 RK_PC7 GPIO_ACTIVE_HIGH>; //VCC12_IO_EN_GPIO0_C7_3V3
-                en_4g_gpio = <&gpio0 RK_PC6 GPIO_ACTIVE_HIGH>; //4G_PWREN_H_GPIO0_C6
-                reset_4g_gpio = <&gpio1 RK_PB2 GPIO_ACTIVE_HIGH>; //4G_RST_GPIO01_B2_3V3
-                air_mode_4g_gpio = <&gpio1 RK_PB0 GPIO_ACTIVE_HIGH>; //4G_AIR_MODE_GPIO01_B0_3V3
-                wake_4g_gpio = <&gpio1 RK_PB1 GPIO_ACTIVE_HIGH>; //4G_WAKEUP_GPIO01_B1_3V3
-				hp_en_gpio = <&gpio3 RK_PA6 GPIO_ACTIVE_HIGH>;//HP_EN_GPIO3_A6_3V3
-//              spk_out_gpio = <&gpio4 RK_PC2 GPIO_ACTIVE_HIGH>;//AMP_SD_GPIO4_C2_3V3			
-				wifi_power_en_gpio = <&gpio3 RK_PC6 GPIO_ACTIVE_HIGH>; //WIFI_PWREN_GPIO3_C6_1V8
-//				pcie_power_en_gpio = <&gpio0 RK_PD4 GPIO_ACTIVE_HIGH>;//PCIE_PWREN_H_GPIO0_D4				
-				pinctrl-names = "default";
-				pinctrl-0 = <&nk_io_gpio>;				
-        };
-#endif	
-		panel: panel {
-				compatible = "simple-panel";
-				backlight = <&backlight>;
-				power-supply = <&vcc3v3_lcd0_n>;
-				enable-gpios = <&gpio2 RK_PD4 GPIO_ACTIVE_HIGH>; //LCD0_VDD_H_GPIO2_D4				
-				edp-bl-gpios = <&gpio0 RK_PB7 GPIO_ACTIVE_HIGH>;  //LCD0_PWBLK_H_GPIO0_B7
-				edp-bl-en = <&gpio0 RK_PB0 GPIO_ACTIVE_HIGH>; //LCD0_BKLT_EN_3V3
-				bus-format = <MEDIA_BUS_FMT_RGB888_1X24>;
-				bpc = <8>;
-				prepare-delay-ms = <200>;
-				enable-delay-ms = <20>;
-				lvds-gpio0 = <&gpio3 RK_PD2 GPIO_ACTIVE_HIGH>; //7511_GPIO0-GPIO3_D2 
-				lvds-gpio1 = <&gpio3 RK_PD3 GPIO_ACTIVE_HIGH>; //7511_GPIO1-GPIO3_D3 
-				lvds-gpio2 = <&gpio3 RK_PD4 GPIO_ACTIVE_HIGH>; //7511_GPIO2-GPIO3_D4 
-				lvds-gpio3 = <&gpio3 RK_PD5 GPIO_ACTIVE_HIGH>; //7511_GPIO3-GPIO3_D5
-				nodka-lvds = <15>;
-
-				display-timings {
-                native-mode = <&timing>;
-                timing: timing {
-					clock-frequency = <72500000>;
-					hactive = <1280>;
-					vactive = <800>;
-					hfront-porch = <70>;
-					hsync-len = <2>;
-					hback-porch = <88>;
-					vfront-porch = <7>;
-					vsync-len = <4>;
-					vback-porch = <17>;
-					hsync-active = <21>;
-					vsync-active = <0>;
-					de-active = <0>;
-					pixelclk-active = <0>;			
-					};
-				};
-				port {
-					panel_in_lvds: endpoint {
-					remote-endpoint = <&lvds_out>;
-						};
-					};   
-				};
+	};
 };
 
 &combphy0_us {
@@ -277,11 +101,11 @@
 };
 
 &csi2_dphy_hw {
-	status = "disabled";
+	status = "okay";
 };
 
 &csi2_dphy0 {
-	status = "disabled";
+	status = "okay";
 
 	ports {
 		#address-cells = <1>;
@@ -324,12 +148,8 @@
  * video_phy0 needs to be enabled
  * when dsi0 is enabled
  */
-&video_phy0 {
-	status = "disabled";
-};
-
 &dsi0 {
-	status = "disabled";
+	status = "okay";
 };
 
 &dsi0_in_vp0 {
@@ -337,7 +157,7 @@
 };
 
 &dsi0_in_vp1 {
-	status = "disabled";
+	status = "okay";
 };
 
 &dsi0_panel {
@@ -365,30 +185,7 @@
 };
 
 &edp {
-	//hpd-gpios = <&gpio0 RK_PC2 GPIO_ACTIVE_HIGH>;	
-    force-hpd;
-    status = "disabled";
-};
-
-&lvds {
-    status = "disabled";
-    ports {
-        port@1 {
-            reg = <1>;
-            lvds_out: endpoint {
-                remote-endpoint = <&panel_in_lvds>;
-            };
-        };
-   
-	};
-};
-
-&route_lvds{
-	 status = "disabled";
-	 connect = <&vp2_out_lvds>;
-};
-
-&lvds_in_vp2 {
+	hpd-gpios = <&gpio0 RK_PC2 GPIO_ACTIVE_HIGH>;
 	status = "okay";
 };
 
@@ -397,65 +194,18 @@
 };
 
 &edp_in_vp0 {
-    status = "disabled";
+	status = "okay";
 };
 
 &edp_in_vp1 {
-    status = "okay";
-
-};
-
-&route_edp {
-    status = "okay";
-    connect = <&vp1_out_edp>;
-};
-
-
-/*
-*  edp_end
-*/
-
-/*
-*  Hdmi_start
-*/
-
-&hdmi {
-	status = "okay";
-	rockchip,phy-table =
-		<92812500  0x8009 0x0000 0x0270>,
-		<165000000 0x800b 0x0000 0x026d>,
-		<185625000 0x800b 0x0000 0x01ed>,
-		<297000000 0x800b 0x0000 0x01ad>,
-		<594000000 0x8029 0x0000 0x0088>,
-		<000000000 0x0000 0x0000 0x0000>;
-};
-
-&route_hdmi {
-	status = "okay";
-	connect = <&vp0_out_hdmi>;
-};
-
-&hdmi_in_vp0 {
-	status = "okay";
-};
-
-&hdmi_in_vp1 {
 	status = "disabled";
 };
-
-&hdmi_sound {
-	status = "okay";
-};
-
-/*
- *  Hdmi_END
-*/
 
 &gmac0 {
 	phy-mode = "rgmii";
 	clock_in_out = "output";
 
-	snps,reset-gpio = <&gpio2 RK_PC6 GPIO_ACTIVE_LOW>;
+	snps,reset-gpio = <&gpio2 RK_PD3 GPIO_ACTIVE_LOW>;
 	snps,reset-active-low;
 	/* Reset time is 20ms, 100ms for rtl8211f */
 	snps,reset-delays-us = <0 20000 100000>;
@@ -475,16 +225,14 @@
 	rx_delay = <0x2f>;
 
 	phy-handle = <&rgmii_phy0>;
-	
-	status = "disabled";
-	
+	status = "okay";
 };
 
 &gmac1 {
 	phy-mode = "rgmii";
 	clock_in_out = "output";
 
-	snps,reset-gpio = <&gpio3 RK_PB0 GPIO_ACTIVE_LOW>;
+	snps,reset-gpio = <&gpio2 RK_PD1 GPIO_ACTIVE_LOW>;
 	snps,reset-active-low;
 	/* Reset time is 20ms, 100ms for rtl8211f */
 	snps,reset-delays-us = <0 20000 100000>;
@@ -511,26 +259,12 @@
  * power-supply should switche to vcc3v3_lcd1_n
  * when mipi panel is connected to dsi1.
  */
-
-
-&i2c3 {
-	status = "okay";
-       //mac eeprom
-        eeprom@51 {
-                //compatible = "atmel,24c02";
-                compatible = "atmel,24c256";
-                reg = <0x51>;
-        };
-	
-	  //nk-mcu
-        nkmcu@15 {
-                compatible = "nk_mcu";
-                reg = <0x15>;
-        };
+&gt1x {
+	power-supply = <&vcc3v3_lcd0_n>;
 };
 
 &i2c4 {
-	status = "disabled";
+	status = "okay";
 	gc8034: gc8034@37 {
 		compatible = "galaxycore,gc8034";
 		status = "okay";
@@ -575,7 +309,7 @@
 		};
 	};
 	ov5695: ov5695@36 {
-		status = "disabled";
+		status = "okay";
 		compatible = "ovti,ov5695";
 		reg = <0x36>;
 		clocks = <&cru CLK_CIF_OUT>;
@@ -595,19 +329,6 @@
 				data-lanes = <1 2>;
 			};
 		};
-	};
-};
-
-&i2c5 {
-	status = "okay";
-
-	hym8563: hym8563@51 {
-		compatible = "haoyu,hym8563";
-		reg = <0x51>;
-		#clock-cells = <0>;
-		clock-frequency = <32768>;
-		clock-output-names = "xin32k";
-		/* rtc_int is not connected */
 	};
 };
 
@@ -644,75 +365,46 @@
 };
 
 &pinctrl {
-//	cam {
-//		camera_pwr: camera-pwr {
-//			rockchip,pins =
-//				/* camera power en */
-//				<0 RK_PC1 RK_FUNC_GPIO &pcfg_pull_none>;
-//		};
-//	};
+	cam {
+		camera_pwr: camera-pwr {
+			rockchip,pins =
+				/* camera power en */
+				<0 RK_PC1 RK_FUNC_GPIO &pcfg_pull_none>;
+		};
+	};
 	headphone {
 		hp_det: hp-det {
-			rockchip,pins = <0 RK_PD5 RK_FUNC_GPIO &pcfg_pull_down>,
-							<4 RK_PC2 RK_FUNC_GPIO &pcfg_pull_down>;
+			rockchip,pins = <3 RK_PC2 RK_FUNC_GPIO &pcfg_pull_down>;
 		};
 	};
 
 	wireless-wlan {
 		wifi_host_wake_irq: wifi-host-wake-irq {
-			rockchip,pins = <2 RK_PB2 RK_FUNC_GPIO &pcfg_pull_down>;
+			rockchip,pins = <3 RK_PD4 RK_FUNC_GPIO &pcfg_pull_down>;
 		};
 	};
 
 	wireless-bluetooth {
-		uart1_gpios: uart1-gpios {
-			rockchip,pins = <2 RK_PB5 RK_FUNC_GPIO &pcfg_pull_none>;
+		uart8_gpios: uart8-gpios {
+			rockchip,pins = <2 RK_PB1 RK_FUNC_GPIO &pcfg_pull_none>;
 		};
 	};
-	
-	lcd1 {
-        lcd1_rst_gpio: lcd1-rst-gpio {
-            rockchip,pins = <3 RK_PC7 RK_FUNC_GPIO &pcfg_pull_none>;
-        };
-    };
-		
-	nk_io_init{
-		nk_io_gpio: nk-io-gpio{
-			rockchip,pins = <0 RK_PB0 RK_FUNC_GPIO &pcfg_pull_none>,
-							<0 RK_PB7 RK_FUNC_GPIO &pcfg_pull_none>,
-							<0 RK_PC4 RK_FUNC_GPIO &pcfg_pull_none>,
-							<4 RK_PD2 RK_FUNC_GPIO &pcfg_pull_none>,
-							<4 RK_PB2 RK_FUNC_GPIO &pcfg_pull_none>,
-							<1 RK_PA4 RK_FUNC_GPIO &pcfg_pull_none>,
-							<0 RK_PC7 RK_FUNC_GPIO &pcfg_pull_none>,
-							<1 RK_PB2 RK_FUNC_GPIO &pcfg_pull_none>,
-							<1 RK_PB0 RK_FUNC_GPIO &pcfg_pull_none>,
-							<1 RK_PB1 RK_FUNC_GPIO &pcfg_pull_none>,
-							<3 RK_PA6 RK_FUNC_GPIO &pcfg_pull_none>,
-							<3 RK_PD2 RK_FUNC_GPIO &pcfg_pull_none>,
-							<3 RK_PD3 RK_FUNC_GPIO &pcfg_pull_none>,
-							<3 RK_PD4 RK_FUNC_GPIO &pcfg_pull_none>,
-							<3 RK_PD5 RK_FUNC_GPIO &pcfg_pull_none>,
-							<3 RK_PD1 RK_FUNC_GPIO &pcfg_pull_none>,
-							<2 RK_PD5 RK_FUNC_GPIO &pcfg_pull_none>,//93 SPI2_CS0_M1_3V3
-							<2 RK_PD6 RK_FUNC_GPIO &pcfg_pull_none>,//94 SPI2_MOSI_M1_3V3
-							<2 RK_PD7 RK_FUNC_GPIO &pcfg_pull_none>,//95 SPI2_MISO_M1_3V3
-							<3 RK_PA0 RK_FUNC_GPIO &pcfg_pull_none>,//96 SPI2_CLK_M1_3V3
-							<0 RK_PC6 RK_FUNC_GPIO &pcfg_pull_none>;
-		};
-	};
+};
+
+&rk809_sound {
+	hp-det-gpio = <&gpio3 RK_PC2 GPIO_ACTIVE_LOW>;
 };
 
 &rkisp {
-	status = "disabled";
+	status = "okay";
 };
 
 &rkisp_mmu {
-	status = "disabled";
+	status = "okay";
 };
 
 &rkisp_vir0 {
-	status = "disabled";
+	status = "okay";
 
 	port {
 		#address-cells = <1>;
@@ -725,30 +417,35 @@
 	};
 };
 
+&route_dsi0 {
+	status = "okay";
+	connect = <&vp1_out_dsi0>;
+};
 
+&route_edp {
+	status = "okay";
+	connect = <&vp0_out_edp>;
+};
 
 &sata2 {
 	status = "okay";
 };
 
 &sdmmc2 {
-        status = "disabled";
-};
-
-&sdmmc1 {
-        max-frequency = <150000000>;
-        supports-sdio;
-        bus-width = <4>;
-        disable-wp;
-        cap-sd-highspeed;
-        cap-sdio-irq;
-        keep-power-in-suspend;
-        mmc-pwrseq = <&sdio_pwrseq>;
-        non-removable;
-        pinctrl-names = "default";
-        pinctrl-0 = <&sdmmc1_bus4 &sdmmc1_cmd &sdmmc1_clk>;
-        sd-uhs-sdr104;
-        status = "okay";
+	max-frequency = <150000000>;
+	no-sd;
+	no-mmc;
+	bus-width = <4>;
+	disable-wp;
+	cap-sd-highspeed;
+	cap-sdio-irq;
+	keep-power-in-suspend;
+	mmc-pwrseq = <&sdio_pwrseq>;
+	non-removable;
+	pinctrl-names = "default";
+	pinctrl-0 = <&sdmmc2m0_bus4 &sdmmc2m0_cmd &sdmmc2m0_clk>;
+	sd-uhs-sdr104;
+	status = "okay";
 };
 
 &spdif_8ch {
@@ -758,25 +455,25 @@
 };
 
 &uart8 {
-	status = "disabled";
+	status = "okay";
 	pinctrl-names = "default";
 	pinctrl-0 = <&uart8m0_xfer &uart8m0_ctsn>;
 };
 
 &vcc3v3_lcd0_n {
-	gpio = <&gpio0 RK_PC4 GPIO_ACTIVE_HIGH>; 
+	gpio = <&gpio0 RK_PC7 GPIO_ACTIVE_HIGH>;
 	enable-active-high;
 };
 
 &vcc3v3_lcd1_n {
-	gpio = <&gpio3 RK_PA3 GPIO_ACTIVE_HIGH>; //MIPI_3V3EN_GPIO3_A3_d_3V3
+	gpio = <&gpio0 RK_PC5 GPIO_ACTIVE_HIGH>;
 	enable-active-high;
 };
 
 &wireless_wlan {
 	pinctrl-names = "default";
 	pinctrl-0 = <&wifi_host_wake_irq>;
-	WIFI,host_wake_irq = <&gpio2 RK_PB2 GPIO_ACTIVE_HIGH>;
+	WIFI,host_wake_irq = <&gpio3 RK_PD4 GPIO_ACTIVE_HIGH>;
 };
 
 &wireless_bluetooth {
@@ -784,47 +481,12 @@
 	clocks = <&rk809 1>;
 	clock-names = "ext_clock";
 	//wifi-bt-power-toggle;
-	uart_rts_gpios = <&gpio2 RK_PB5 GPIO_ACTIVE_LOW>;
+	uart_rts_gpios = <&gpio2 RK_PB1 GPIO_ACTIVE_LOW>;
 	pinctrl-names = "default", "rts_gpio";
-	pinctrl-0 = <&uart1m0_rtsn>;
-	pinctrl-1 = <&uart1_gpios>;
-	BT,reset_gpio    = <&gpio2 RK_PB7 GPIO_ACTIVE_HIGH>;
-    BT,wake_gpio     = <&gpio2 RK_PC0 GPIO_ACTIVE_HIGH>;
-    BT,wake_host_irq = <&gpio2 RK_PC1 GPIO_ACTIVE_HIGH>;
+	pinctrl-0 = <&uart8m0_rtsn>;
+	pinctrl-1 = <&uart8_gpios>;
+	BT,reset_gpio    = <&gpio3 RK_PA0 GPIO_ACTIVE_HIGH>;
+	BT,wake_gpio     = <&gpio3 RK_PA2 GPIO_ACTIVE_HIGH>;
+	BT,wake_host_irq = <&gpio3 RK_PA1 GPIO_ACTIVE_HIGH>;
 	status = "okay";
-};
-
-&uart0 {
-	status = "disabled";
-};
-
-&uart1 {	
-	pinctrl-names = "default";	
-	pinctrl-0 = <&uart1m0_xfer &uart1m0_ctsn>;	
-	status = "disabled";		
-};
-
-&uart3 {
-	status = "okay";
-	pinctrl-0 = <&uart3m1_xfer>;
-};
-
-&uart4 {
-	status = "okay";
-	pinctrl-0 = <&uart4m1_xfer>;
-};
-
-&uart5 {
-	status = "okay";
-	pinctrl-0 = <&uart5m1_xfer>;
-};
-
-&uart7 {
-	status = "disabled";
-	pinctrl-0 = <&uart7m1_xfer>;
-};
-
-&uart9 {
-	status = "disabled";
-	pinctrl-0 = <&uart9m1_xfer>;
 };
diff --git a/kernel/arch/arm64/boot/dts/rockchip/rk3568.dtsi b/kernel/arch/arm64/boot/dts/rockchip/rk3568.dtsi
old mode 100755
new mode 100644
diff --git a/kernel/arch/arm64/configs/rk3308bs_mipi_display.config b/kernel/arch/arm64/configs/rk3308bs_mipi_display.config
new file mode 100644
index 0000000..24498f0
--- /dev/null
+++ b/kernel/arch/arm64/configs/rk3308bs_mipi_display.config
@@ -0,0 +1,128 @@
+CONFIG_CMA=y
+CONFIG_INPUT_TOUCHSCREEN=y
+CONFIG_MFD_RK618=y
+CONFIG_CLK_RK618=y
+CONFIG_CMA_ALIGNMENT=8
+CONFIG_CMA_AREAS=7
+# CONFIG_CMA_DEBUG is not set
+# CONFIG_CMA_DEBUGFS is not set
+# CONFIG_CMA_INACTIVE is not set
+CONFIG_CMA_SIZE_MBYTES=16
+# CONFIG_CMA_SIZE_SEL_MAX is not set
+CONFIG_CMA_SIZE_SEL_MBYTES=y
+# CONFIG_CMA_SIZE_SEL_MIN is not set
+# CONFIG_CMA_SIZE_SEL_PERCENTAGE is not set
+# CONFIG_CMA_SYSFS is not set
+CONFIG_COMMON_CLK_ROCKCHIP_REGMAP=y
+CONFIG_CONTIG_ALLOC=y
+CONFIG_DMA_CMA=y
+# CONFIG_DMA_PERNUMA_CMA is not set
+CONFIG_DRM_MIPI_DSI=y
+# CONFIG_DRM_PANEL_ASUS_Z00T_TM5P5_NT35596 is not set
+# CONFIG_DRM_PANEL_BOE_HIMAX8279D is not set
+# CONFIG_DRM_PANEL_BOE_TV101WUM_NL6 is not set
+# CONFIG_DRM_PANEL_ELIDA_KD35T133 is not set
+# CONFIG_DRM_PANEL_FEIXIN_K101_IM2BA02 is not set
+# CONFIG_DRM_PANEL_FEIYANG_FY07024DI26A30D is not set
+# CONFIG_DRM_PANEL_ILITEK_ILI9881C is not set
+# CONFIG_DRM_PANEL_INNOLUX_P079ZCA is not set
+# CONFIG_DRM_PANEL_JDI_LT070ME05000 is not set
+# CONFIG_DRM_PANEL_KINGDISPLAY_KD097D04 is not set
+# CONFIG_DRM_PANEL_LEADTEK_LTK050H3146W is not set
+# CONFIG_DRM_PANEL_LEADTEK_LTK500HD1829 is not set
+# CONFIG_DRM_PANEL_MANTIX_MLAF057WE51 is not set
+# CONFIG_DRM_PANEL_NOVATEK_NT35510 is not set
+# CONFIG_DRM_PANEL_ORISETECH_OTM8009A is not set
+# CONFIG_DRM_PANEL_OSD_OSD101T2587_53TS is not set
+# CONFIG_DRM_PANEL_PANASONIC_VVX10F034N00 is not set
+# CONFIG_DRM_PANEL_RASPBERRYPI_TOUCHSCREEN is not set
+# CONFIG_DRM_PANEL_RAYDIUM_RM67191 is not set
+# CONFIG_DRM_PANEL_RAYDIUM_RM68200 is not set
+# CONFIG_DRM_PANEL_RONBO_RB070D30 is not set
+# CONFIG_DRM_PANEL_SAMSUNG_S6D16D0 is not set
+# CONFIG_DRM_PANEL_SAMSUNG_S6E3HA2 is not set
+# CONFIG_DRM_PANEL_SAMSUNG_S6E63J0X03 is not set
+# CONFIG_DRM_PANEL_SHARP_LQ101R1SX01 is not set
+# CONFIG_DRM_PANEL_SHARP_LS043T1LE01 is not set
+# CONFIG_DRM_PANEL_SITRONIX_ST7701 is not set
+# CONFIG_DRM_PANEL_SITRONIX_ST7703 is not set
+# CONFIG_DRM_PANEL_SONY_ACX424AKP is not set
+# CONFIG_DRM_PANEL_TRULY_NT35597_WQXGA is not set
+# CONFIG_DRM_PANEL_VISIONOX_RM69299 is not set
+# CONFIG_DRM_PANEL_XINPENG_XPP055C272 is not set
+CONFIG_DRM_ROCKCHIP_RK618=y
+CONFIG_MEMORY_ISOLATION=y
+# CONFIG_RK628_MISC is not set
+# CONFIG_RK_CMA_PROCFS is not set
+# CONFIG_SND_SOC_ROCKCHIP_I2S_TDM_MULTI_LANES is not set
+# CONFIG_TOUCHSCREEN_AD7877 is not set
+# CONFIG_TOUCHSCREEN_AD7879 is not set
+# CONFIG_TOUCHSCREEN_ADC is not set
+# CONFIG_TOUCHSCREEN_ADS7846 is not set
+# CONFIG_TOUCHSCREEN_AR1021_I2C is not set
+# CONFIG_TOUCHSCREEN_ATMEL_MXT is not set
+# CONFIG_TOUCHSCREEN_AUO_PIXCIR is not set
+# CONFIG_TOUCHSCREEN_BU21013 is not set
+# CONFIG_TOUCHSCREEN_BU21029 is not set
+# CONFIG_TOUCHSCREEN_CHIPONE_ICN8318 is not set
+# CONFIG_TOUCHSCREEN_CY8CTMA140 is not set
+# CONFIG_TOUCHSCREEN_CY8CTMG110 is not set
+# CONFIG_TOUCHSCREEN_CYTTSP4_CORE is not set
+# CONFIG_TOUCHSCREEN_CYTTSP_CORE is not set
+# CONFIG_TOUCHSCREEN_DYNAPRO is not set
+# CONFIG_TOUCHSCREEN_EDT_FT5X06 is not set
+# CONFIG_TOUCHSCREEN_EETI is not set
+# CONFIG_TOUCHSCREEN_EGALAX is not set
+# CONFIG_TOUCHSCREEN_EGALAX_SERIAL is not set
+# CONFIG_TOUCHSCREEN_EKTF2127 is not set
+# CONFIG_TOUCHSCREEN_ELAN is not set
+# CONFIG_TOUCHSCREEN_ELAN5515 is not set
+# CONFIG_TOUCHSCREEN_ELO is not set
+# CONFIG_TOUCHSCREEN_EXC3000 is not set
+# CONFIG_TOUCHSCREEN_FTS is not set
+# CONFIG_TOUCHSCREEN_FUJITSU is not set
+# CONFIG_TOUCHSCREEN_GOODIX is not set
+# CONFIG_TOUCHSCREEN_GSL3673 is not set
+# CONFIG_TOUCHSCREEN_GSL3673_800X1280 is not set
+# CONFIG_TOUCHSCREEN_GSLX680_PAD is not set
+CONFIG_TOUCHSCREEN_GT1X=y
+# CONFIG_TOUCHSCREEN_GUNZE is not set
+# CONFIG_TOUCHSCREEN_HAMPSHIRE is not set
+# CONFIG_TOUCHSCREEN_HIDEEP is not set
+# CONFIG_TOUCHSCREEN_ILI210X is not set
+# CONFIG_TOUCHSCREEN_IMX6UL_TSC is not set
+# CONFIG_TOUCHSCREEN_INEXIO is not set
+# CONFIG_TOUCHSCREEN_IQS5XX is not set
+# CONFIG_TOUCHSCREEN_MAX11801 is not set
+# CONFIG_TOUCHSCREEN_MCS5000 is not set
+# CONFIG_TOUCHSCREEN_MELFAS_MIP4 is not set
+# CONFIG_TOUCHSCREEN_MK712 is not set
+# CONFIG_TOUCHSCREEN_MMS114 is not set
+# CONFIG_TOUCHSCREEN_MTOUCH is not set
+# CONFIG_TOUCHSCREEN_PENMOUNT is not set
+# CONFIG_TOUCHSCREEN_PIXCIR is not set
+CONFIG_TOUCHSCREEN_PROPERTIES=y
+# CONFIG_TOUCHSCREEN_RM_TS is not set
+# CONFIG_TOUCHSCREEN_ROHM_BU21023 is not set
+# CONFIG_TOUCHSCREEN_S6SY761 is not set
+# CONFIG_TOUCHSCREEN_SILEAD is not set
+# CONFIG_TOUCHSCREEN_SIS_I2C is not set
+# CONFIG_TOUCHSCREEN_ST1232 is not set
+# CONFIG_TOUCHSCREEN_STMFTS is not set
+# CONFIG_TOUCHSCREEN_SURFACE3_SPI is not set
+# CONFIG_TOUCHSCREEN_SX8654 is not set
+# CONFIG_TOUCHSCREEN_TOUCHIT213 is not set
+# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set
+# CONFIG_TOUCHSCREEN_TOUCHWIN is not set
+# CONFIG_TOUCHSCREEN_TPS6507X is not set
+# CONFIG_TOUCHSCREEN_TSC2004 is not set
+# CONFIG_TOUCHSCREEN_TSC2005 is not set
+# CONFIG_TOUCHSCREEN_TSC2007 is not set
+# CONFIG_TOUCHSCREEN_TSC_SERIO is not set
+# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set
+# CONFIG_TOUCHSCREEN_WACOM_I2C is not set
+# CONFIG_TOUCHSCREEN_WACOM_W8001 is not set
+# CONFIG_TOUCHSCREEN_WDT87XX_I2C is not set
+# CONFIG_TOUCHSCREEN_ZET6223 is not set
+# CONFIG_TOUCHSCREEN_ZFORCE is not set
+# CONFIG_TOUCHSCREEN_ZINITIX is not set
diff --git a/kernel/arch/arm64/configs/rockchip_defconfig b/kernel/arch/arm64/configs/rockchip_defconfig
index 835b0b6..2e6450c 100644
--- a/kernel/arch/arm64/configs/rockchip_defconfig
+++ b/kernel/arch/arm64/configs/rockchip_defconfig
@@ -554,6 +554,8 @@
 CONFIG_MFD_RK630_I2C=y
 CONFIG_MFD_RK806_SPI=y
 CONFIG_MFD_RK808=y
+CONFIG_MFD_RKX110_X120=y
+CONFIG_ROCKCHIP_SERDES_DRM_PANEL=y
 CONFIG_REGULATOR=y
 CONFIG_REGULATOR_FIXED_VOLTAGE=y
 CONFIG_REGULATOR_ACT8865=y
@@ -619,7 +621,6 @@
 CONFIG_DRM_DP_AUX_CHARDEV=y
 CONFIG_DRM_LOAD_EDID_FIRMWARE=y
 CONFIG_DRM_ROCKCHIP=y
-CONFIG_ROCKCHIP_DRM_CUBIC_LUT=y
 CONFIG_ROCKCHIP_ANALOGIX_DP=y
 CONFIG_ROCKCHIP_CDN_DP=y
 CONFIG_ROCKCHIP_DRM_TVE=y
@@ -634,6 +635,7 @@
 CONFIG_DRM_ROCKCHIP_RK628=y
 CONFIG_DRM_PANEL_SIMPLE=y
 CONFIG_DRM_PANEL_MAXIM_MAX96752F=y
+CONFIG_DRM_PANEL_MAXIM_MAX96772=y
 CONFIG_DRM_DISPLAY_CONNECTOR=y
 CONFIG_DRM_MAXIM_MAX96745=y
 CONFIG_DRM_MAXIM_MAX96755F=y
@@ -690,6 +692,8 @@
 CONFIG_SND_SOC_ROCKCHIP=y
 CONFIG_SND_SOC_ROCKCHIP_I2S=y
 CONFIG_SND_SOC_ROCKCHIP_I2S_TDM=y
+CONFIG_SND_SOC_ROCKCHIP_I2S_TDM_MULTI_LANES=y
+CONFIG_SND_SOC_ROCKCHIP_MULTI_DAIS=y
 CONFIG_SND_SOC_ROCKCHIP_PDM=y
 CONFIG_SND_SOC_ROCKCHIP_SAI=y
 CONFIG_SND_SOC_ROCKCHIP_SPDIF=y
@@ -713,6 +717,7 @@
 CONFIG_SND_SOC_RK_CODEC_DIGITAL=y
 CONFIG_SND_SOC_RK_DSM=y
 CONFIG_SND_SOC_RT5640=y
+CONFIG_SND_SOC_RT5651=y
 CONFIG_SND_SOC_SPDIF=y
 CONFIG_SND_SOC_AW883XX=y
 CONFIG_SND_SIMPLE_CARD=y
@@ -853,6 +858,8 @@
 CONFIG_LEDS_TRIGGER_HEARTBEAT=y
 CONFIG_LEDS_TRIGGER_BACKLIGHT=y
 CONFIG_LEDS_TRIGGER_DEFAULT_ON=y
+CONFIG_EDAC=y
+CONFIG_EDAC_ROCKCHIP=y
 CONFIG_RTC_CLASS=y
 CONFIG_RTC_DRV_HYM8563=y
 CONFIG_RTC_DRV_RK808=y
@@ -905,6 +912,7 @@
 CONFIG_RK_CONSOLE_THREAD=y
 CONFIG_ROCKCHIP_DEBUG=y
 CONFIG_ROCKCHIP_MINIDUMP=y
+CONFIG_ROCKCHIP_MINIDUMP_MAX_ENTRIES=512
 CONFIG_ROCKCHIP_MINIDUMP_PANIC_DUMP=y
 CONFIG_ROCKCHIP_DYN_MINIDUMP_STACK=y
 CONFIG_PM_DEVFREQ=y
@@ -939,6 +947,7 @@
 CONFIG_PHY_ROCKCHIP_TYPEC=y
 CONFIG_PHY_ROCKCHIP_USB=y
 CONFIG_PHY_ROCKCHIP_USBDP=y
+CONFIG_RAS=y
 CONFIG_ANDROID=y
 CONFIG_ANDROID_BINDER_IPC=y
 CONFIG_ANDROID_BINDERFS=y
@@ -947,7 +956,6 @@
 CONFIG_ROCKCHIP_OTP=y
 CONFIG_TEE=y
 CONFIG_OPTEE=y
-CONFIG_RK_NAND=y
 CONFIG_RK_HEADSET=y
 CONFIG_ROCKCHIP_RKNPU=y
 CONFIG_EXT4_FS=y
diff --git a/kernel/arch/arm64/configs/rockchip_linux_defconfig b/kernel/arch/arm64/configs/rockchip_linux_defconfig
index fe0a79b..1f047f8 100644
--- a/kernel/arch/arm64/configs/rockchip_linux_defconfig
+++ b/kernel/arch/arm64/configs/rockchip_linux_defconfig
@@ -66,8 +66,6 @@
 CONFIG_GENERIC_CLOCKEVENTS=y
 CONFIG_ARCH_HAS_TICK_BROADCAST=y
 CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y
-CONFIG_HAVE_POSIX_CPU_TIMERS_TASK_WORK=y
-CONFIG_POSIX_CPU_TIMERS_TASK_WORK=y
 
 #
 # Timers subsystem
@@ -83,14 +81,9 @@
 CONFIG_HIGH_RES_TIMERS=y
 # end of Timers subsystem
 
-CONFIG_HAVE_PREEMPT_LAZY=y
-CONFIG_PREEMPT_LAZY=y
-# CONFIG_PREEMPT_NONE is not set
+CONFIG_PREEMPT_NONE=y
 # CONFIG_PREEMPT_VOLUNTARY is not set
 # CONFIG_PREEMPT is not set
-CONFIG_PREEMPT_RT=y
-CONFIG_PREEMPT_COUNT=y
-CONFIG_PREEMPTION=y
 
 #
 # CPU/Task time and stats accounting
@@ -109,17 +102,13 @@
 # RCU Subsystem
 #
 CONFIG_TREE_RCU=y
-CONFIG_PREEMPT_RCU=y
 # CONFIG_RCU_EXPERT is not set
 CONFIG_SRCU=y
 CONFIG_TREE_SRCU=y
 CONFIG_TASKS_RCU_GENERIC=y
-CONFIG_TASKS_RCU=y
 CONFIG_TASKS_TRACE_RCU=y
 CONFIG_RCU_STALL_COMMON=y
 CONFIG_RCU_NEED_SEGCBLIST=y
-CONFIG_RCU_BOOST=y
-CONFIG_RCU_BOOST_DELAY=500
 CONFIG_RCU_NOCB_CPU=y
 # end of RCU Subsystem
 
@@ -240,11 +229,14 @@
 CONFIG_SLUB_DEBUG=y
 CONFIG_SLUB_MEMCG_SYSFS_ON=y
 # CONFIG_COMPAT_BRK is not set
+# CONFIG_SLAB is not set
 CONFIG_SLUB=y
+# CONFIG_SLOB is not set
 CONFIG_SLAB_MERGE_DEFAULT=y
 # CONFIG_SLAB_FREELIST_RANDOM is not set
 # CONFIG_SLAB_FREELIST_HARDENED is not set
 # CONFIG_SHUFFLE_PAGE_ALLOCATOR is not set
+CONFIG_SLUB_CPU_PARTIAL=y
 CONFIG_SYSTEM_DATA_VERIFICATION=y
 # CONFIG_PROFILING is not set
 CONFIG_TRACEPOINTS=y
@@ -509,7 +501,17 @@
 #
 # CPU Idle
 #
-# CONFIG_CPU_IDLE is not set
+CONFIG_CPU_IDLE=y
+# CONFIG_CPU_IDLE_GOV_LADDER is not set
+CONFIG_CPU_IDLE_GOV_MENU=y
+# CONFIG_CPU_IDLE_GOV_TEO is not set
+
+#
+# ARM CPU Idle Drivers
+#
+# CONFIG_ARM_CPUIDLE is not set
+# CONFIG_ARM_PSCI_CPUIDLE is not set
+# end of ARM CPU Idle Drivers
 # end of CPU Idle
 
 #
@@ -520,10 +522,10 @@
 CONFIG_CPU_FREQ_GOV_COMMON=y
 CONFIG_CPU_FREQ_STAT=y
 # CONFIG_CPU_FREQ_TIMES is not set
-CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y
+# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set
 # CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set
 # CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set
-# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set
+CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y
 # CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set
 # CONFIG_CPU_FREQ_DEFAULT_GOV_SCHEDUTIL is not set
 # CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE is not set
@@ -580,6 +582,7 @@
 
 CONFIG_EFI_EARLYCON=y
 CONFIG_ARM_PSCI_FW=y
+# CONFIG_ARM_PSCI_CHECKER is not set
 CONFIG_HAVE_ARM_SMCCC=y
 CONFIG_HAVE_ARM_SMCCC_DISCOVERY=y
 CONFIG_ARM_SMCCC_SOC_ID=y
@@ -683,7 +686,6 @@
 CONFIG_OLD_SIGSUSPEND3=y
 CONFIG_COMPAT_OLD_SIGACTION=y
 CONFIG_COMPAT_32BIT_TIME=y
-CONFIG_ARCH_SUPPORTS_RT=y
 CONFIG_HAVE_ARCH_VMAP_STACK=y
 CONFIG_VMAP_STACK=y
 CONFIG_ARCH_HAS_STRICT_KERNEL_RWX=y
@@ -773,6 +775,57 @@
 # end of IO Schedulers
 
 CONFIG_ASN1=y
+CONFIG_ARCH_INLINE_SPIN_TRYLOCK=y
+CONFIG_ARCH_INLINE_SPIN_TRYLOCK_BH=y
+CONFIG_ARCH_INLINE_SPIN_LOCK=y
+CONFIG_ARCH_INLINE_SPIN_LOCK_BH=y
+CONFIG_ARCH_INLINE_SPIN_LOCK_IRQ=y
+CONFIG_ARCH_INLINE_SPIN_LOCK_IRQSAVE=y
+CONFIG_ARCH_INLINE_SPIN_UNLOCK=y
+CONFIG_ARCH_INLINE_SPIN_UNLOCK_BH=y
+CONFIG_ARCH_INLINE_SPIN_UNLOCK_IRQ=y
+CONFIG_ARCH_INLINE_SPIN_UNLOCK_IRQRESTORE=y
+CONFIG_ARCH_INLINE_READ_LOCK=y
+CONFIG_ARCH_INLINE_READ_LOCK_BH=y
+CONFIG_ARCH_INLINE_READ_LOCK_IRQ=y
+CONFIG_ARCH_INLINE_READ_LOCK_IRQSAVE=y
+CONFIG_ARCH_INLINE_READ_UNLOCK=y
+CONFIG_ARCH_INLINE_READ_UNLOCK_BH=y
+CONFIG_ARCH_INLINE_READ_UNLOCK_IRQ=y
+CONFIG_ARCH_INLINE_READ_UNLOCK_IRQRESTORE=y
+CONFIG_ARCH_INLINE_WRITE_LOCK=y
+CONFIG_ARCH_INLINE_WRITE_LOCK_BH=y
+CONFIG_ARCH_INLINE_WRITE_LOCK_IRQ=y
+CONFIG_ARCH_INLINE_WRITE_LOCK_IRQSAVE=y
+CONFIG_ARCH_INLINE_WRITE_UNLOCK=y
+CONFIG_ARCH_INLINE_WRITE_UNLOCK_BH=y
+CONFIG_ARCH_INLINE_WRITE_UNLOCK_IRQ=y
+CONFIG_ARCH_INLINE_WRITE_UNLOCK_IRQRESTORE=y
+CONFIG_INLINE_SPIN_TRYLOCK=y
+CONFIG_INLINE_SPIN_TRYLOCK_BH=y
+CONFIG_INLINE_SPIN_LOCK=y
+CONFIG_INLINE_SPIN_LOCK_BH=y
+CONFIG_INLINE_SPIN_LOCK_IRQ=y
+CONFIG_INLINE_SPIN_LOCK_IRQSAVE=y
+CONFIG_INLINE_SPIN_UNLOCK_BH=y
+CONFIG_INLINE_SPIN_UNLOCK_IRQ=y
+CONFIG_INLINE_SPIN_UNLOCK_IRQRESTORE=y
+CONFIG_INLINE_READ_LOCK=y
+CONFIG_INLINE_READ_LOCK_BH=y
+CONFIG_INLINE_READ_LOCK_IRQ=y
+CONFIG_INLINE_READ_LOCK_IRQSAVE=y
+CONFIG_INLINE_READ_UNLOCK=y
+CONFIG_INLINE_READ_UNLOCK_BH=y
+CONFIG_INLINE_READ_UNLOCK_IRQ=y
+CONFIG_INLINE_READ_UNLOCK_IRQRESTORE=y
+CONFIG_INLINE_WRITE_LOCK=y
+CONFIG_INLINE_WRITE_LOCK_BH=y
+CONFIG_INLINE_WRITE_LOCK_IRQ=y
+CONFIG_INLINE_WRITE_LOCK_IRQSAVE=y
+CONFIG_INLINE_WRITE_UNLOCK=y
+CONFIG_INLINE_WRITE_UNLOCK_BH=y
+CONFIG_INLINE_WRITE_UNLOCK_IRQ=y
+CONFIG_INLINE_WRITE_UNLOCK_IRQRESTORE=y
 CONFIG_ARCH_SUPPORTS_ATOMIC_RMW=y
 CONFIG_MUTEX_SPIN_ON_OWNER=y
 CONFIG_RWSEM_SPIN_ON_OWNER=y
@@ -780,6 +833,7 @@
 CONFIG_ARCH_USE_QUEUED_SPINLOCKS=y
 CONFIG_QUEUED_SPINLOCKS=y
 CONFIG_ARCH_USE_QUEUED_RWLOCKS=y
+CONFIG_QUEUED_RWLOCKS=y
 CONFIG_ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE=y
 CONFIG_ARCH_HAS_SYSCALL_WRAPPER=y
 # CONFIG_GKI_HIDDEN_DRM_CONFIGS is not set
@@ -847,6 +901,7 @@
 CONFIG_DEFAULT_MMAP_MIN_ADDR=32768
 CONFIG_ARCH_SUPPORTS_MEMORY_FAILURE=y
 # CONFIG_MEMORY_FAILURE is not set
+# CONFIG_TRANSPARENT_HUGEPAGE is not set
 # CONFIG_CLEANCACHE is not set
 CONFIG_CMA=y
 # CONFIG_CMA_INACTIVE is not set
@@ -1205,6 +1260,7 @@
 CONFIG_XPS=y
 # CONFIG_CGROUP_NET_PRIO is not set
 # CONFIG_CGROUP_NET_CLASSID is not set
+CONFIG_NET_RX_BUSY_POLL=y
 CONFIG_BQL=y
 # CONFIG_BPF_JIT is not set
 # CONFIG_BPF_STREAM_PARSER is not set
@@ -1604,6 +1660,13 @@
 #
 # Misc devices
 #
+
+#
+# RK628 misc driver
+#
+# CONFIG_RK628_MISC is not set
+# end of RK628 misc driver
+
 # CONFIG_RK803 is not set
 # CONFIG_PCIE_FUNC_RKEP is not set
 # CONFIG_LT7911D_FB_NOTIFIER is not set
@@ -2689,6 +2752,7 @@
 # CONFIG_GPIO_GW_PLD is not set
 # CONFIG_GPIO_MAX7300 is not set
 # CONFIG_GPIO_MAX732X is not set
+# CONFIG_GPIO_NCA9539 is not set
 # CONFIG_GPIO_PCA953X is not set
 # CONFIG_GPIO_PCA9570 is not set
 # CONFIG_GPIO_PCF857X is not set
@@ -3075,6 +3139,13 @@
 CONFIG_MFD_RK806_SPI=y
 CONFIG_MFD_RK808=y
 # CONFIG_MFD_RK1000 is not set
+
+#
+# driver for different display serdes
+#
+# CONFIG_MFD_SERDES_DISPLAY is not set
+# CONFIG_MFD_RKX110_X120 is not set
+# CONFIG_ROCKCHIP_SERDES_DRM_PANEL is not set
 # CONFIG_MFD_RN5T618 is not set
 # CONFIG_MFD_SEC_CORE is not set
 # CONFIG_MFD_SI476X_CORE is not set
@@ -3193,7 +3264,6 @@
 CONFIG_CEC_NOTIFIER=y
 CONFIG_MEDIA_CEC_SUPPORT=y
 # CONFIG_CEC_CH7322 is not set
-# CONFIG_CEC_GPIO is not set
 # CONFIG_USB_PULSE8_CEC is not set
 # CONFIG_USB_RAINSHADOW_CEC is not set
 CONFIG_MEDIA_SUPPORT=y
@@ -3347,6 +3417,7 @@
 # CONFIG_VIDEO_ROCKCHIP_ISPP_FEC is not set
 # CONFIG_VIDEO_ROCKCHIP_ISPP_VERSION_V10 is not set
 CONFIG_VIDEO_ROCKCHIP_ISPP_VERSION_V20=y
+CONFIG_VIDEO_ROCKCHIP_HDMIRX_CLASS=y
 CONFIG_VIDEO_ROCKCHIP_HDMIRX=y
 # CONFIG_VIDEO_XILINX is not set
 CONFIG_V4L_MEM2MEM_DRIVERS=y
@@ -3440,6 +3511,7 @@
 # CONFIG_VIDEO_MAX96712 is not set
 # CONFIG_VIDEO_MAX96714 is not set
 # CONFIG_VIDEO_MAX96722 is not set
+# CONFIG_VIDEO_DES_MAXIM4C is not set
 
 #
 # Video and audio decoders
@@ -3497,6 +3569,7 @@
 # Camera sensor devices
 #
 # CONFIG_VIDEO_AR0230 is not set
+# CONFIG_VIDEO_AR0822 is not set
 # CONFIG_VIDEO_GC02M2 is not set
 # CONFIG_VIDEO_GC08A3 is not set
 # CONFIG_VIDEO_GC1084 is not set
@@ -3535,6 +3608,7 @@
 # CONFIG_VIDEO_IMX586 is not set
 # CONFIG_VIDEO_JX_K17 is not set
 # CONFIG_VIDEO_OS02G10 is not set
+# CONFIG_VIDEO_OS02K10 is not set
 # CONFIG_VIDEO_OS03B10 is not set
 CONFIG_VIDEO_OS04A10=y
 # CONFIG_VIDEO_OS05A20 is not set
@@ -3586,10 +3660,12 @@
 # CONFIG_VIDEO_SC031GS is not set
 # CONFIG_VIDEO_SC035GS is not set
 # CONFIG_VIDEO_SC132GS is not set
+# CONFIG_VIDEO_SC1346 is not set
 # CONFIG_VIDEO_SC200AI is not set
 # CONFIG_VIDEO_SC210IOT is not set
 # CONFIG_VIDEO_SC2232 is not set
 # CONFIG_VIDEO_SC2239 is not set
+# CONFIG_VIDEO_SC223A is not set
 # CONFIG_VIDEO_SC230AI is not set
 # CONFIG_VIDEO_SC2310 is not set
 # CONFIG_VIDEO_SC2336 is not set
@@ -3604,6 +3680,7 @@
 # CONFIG_VIDEO_SC500AI is not set
 # CONFIG_VIDEO_SC501AI is not set
 # CONFIG_VIDEO_SC530AI is not set
+# CONFIG_VIDEO_SC5336 is not set
 # CONFIG_VIDEO_SC850SL is not set
 # CONFIG_VIDEO_SENSOR_ADAPTER is not set
 # CONFIG_VIDEO_SR030PC30 is not set
@@ -3919,7 +3996,6 @@
 # CONFIG_DRM_VGEM is not set
 # CONFIG_DRM_VKMS is not set
 CONFIG_DRM_ROCKCHIP=y
-# CONFIG_ROCKCHIP_DRM_CUBIC_LUT is not set
 # CONFIG_ROCKCHIP_DRM_DEBUG is not set
 # CONFIG_ROCKCHIP_DRM_DIRECT_SHOW is not set
 CONFIG_ROCKCHIP_VOP=y
@@ -3974,6 +4050,7 @@
 # CONFIG_DRM_PANEL_NOVATEK_NT39016 is not set
 # CONFIG_DRM_PANEL_MANTIX_MLAF057WE51 is not set
 # CONFIG_DRM_PANEL_MAXIM_MAX96752F is not set
+# CONFIG_DRM_PANEL_MAXIM_MAX96772 is not set
 # CONFIG_DRM_PANEL_OLIMEX_LCD_OLINUXINO is not set
 # CONFIG_DRM_PANEL_ORISETECH_OTM8009A is not set
 # CONFIG_DRM_PANEL_OSD_OSD101T2587_53TS is not set
@@ -4381,9 +4458,11 @@
 # CONFIG_SND_SOC_ROCKCHIP_DLP is not set
 CONFIG_SND_SOC_ROCKCHIP_I2S=y
 CONFIG_SND_SOC_ROCKCHIP_I2S_TDM=y
+# CONFIG_SND_SOC_ROCKCHIP_I2S_TDM_MULTI_LANES is not set
 # CONFIG_SND_SOC_ROCKCHIP_MULTI_DAIS is not set
 CONFIG_SND_SOC_ROCKCHIP_PDM=y
 CONFIG_SND_SOC_ROCKCHIP_SAI=y
+# CONFIG_SND_SOC_ROCKCHIP_SAI_VERBOSE is not set
 CONFIG_SND_SOC_ROCKCHIP_SPDIF=y
 CONFIG_SND_SOC_ROCKCHIP_SPDIFRX=y
 # CONFIG_SND_SOC_ROCKCHIP_VAD is not set
@@ -4527,6 +4606,7 @@
 # CONFIG_SND_SOC_TAS5720 is not set
 # CONFIG_SND_SOC_TAS6424 is not set
 # CONFIG_SND_SOC_TDA7419 is not set
+# CONFIG_SND_SOC_TDA7803 is not set
 # CONFIG_SND_SOC_TFA9879 is not set
 # CONFIG_SND_SOC_TLV320AIC23_I2C is not set
 # CONFIG_SND_SOC_TLV320AIC23_SPI is not set
@@ -4803,6 +4883,7 @@
 #
 CONFIG_USB_DWC3_HAPS=y
 CONFIG_USB_DWC3_OF_SIMPLE=y
+CONFIG_USB_DWC3_ROCKCHIP_INNO=y
 CONFIG_USB_DWC2=y
 # CONFIG_USB_DWC2_HOST is not set
 
@@ -5130,6 +5211,7 @@
 # CONFIG_LEDS_TRIGGER_MTD is not set
 CONFIG_LEDS_TRIGGER_HEARTBEAT=y
 # CONFIG_LEDS_TRIGGER_BACKLIGHT is not set
+# CONFIG_LEDS_TRIGGER_CPU is not set
 # CONFIG_LEDS_TRIGGER_ACTIVITY is not set
 # CONFIG_LEDS_TRIGGER_GPIO is not set
 # CONFIG_LEDS_TRIGGER_DEFAULT_ON is not set
@@ -5175,7 +5257,7 @@
 # CONFIG_RTC_DRV_DS1672 is not set
 CONFIG_RTC_DRV_HYM8563=y
 # CONFIG_RTC_DRV_MAX6900 is not set
-CONFIG_RTC_DRV_RK808=y
+# CONFIG_RTC_DRV_RK808 is not set
 # CONFIG_RTC_DRV_ROCKCHIP is not set
 # CONFIG_RTC_DRV_RS5C372 is not set
 # CONFIG_RTC_DRV_ISL1208 is not set
@@ -5517,6 +5599,7 @@
 # CONFIG_PLATFORM_MHU is not set
 # CONFIG_PL320_MBOX is not set
 CONFIG_ROCKCHIP_MBOX=y
+# CONFIG_ROCKCHIP_MBOX_DEMO is not set
 # CONFIG_ALTERA_MBOX is not set
 # CONFIG_MAILBOX_TEST is not set
 CONFIG_IOMMU_IOVA=y
@@ -7061,7 +7144,6 @@
 # end of Scheduler Debugging
 
 # CONFIG_DEBUG_TIMEKEEPING is not set
-CONFIG_DEBUG_PREEMPT=y
 
 #
 # Lock Debugging (spinlocks, mutexes, etc...)
@@ -7076,6 +7158,7 @@
 # CONFIG_DEBUG_RWSEMS is not set
 # CONFIG_DEBUG_LOCK_ALLOC is not set
 # CONFIG_DEBUG_ATOMIC_SLEEP is not set
+# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set
 # CONFIG_LOCK_TORTURE_TEST is not set
 # CONFIG_WW_MUTEX_SELFTEST is not set
 # CONFIG_SCF_TORTURE_TEST is not set
@@ -7136,7 +7219,6 @@
 # CONFIG_FUNCTION_TRACER is not set
 # CONFIG_STACK_TRACER is not set
 # CONFIG_IRQSOFF_TRACER is not set
-# CONFIG_PREEMPT_TRACER is not set
 # CONFIG_SCHED_TRACER is not set
 # CONFIG_HWLAT_TRACER is not set
 # CONFIG_ENABLE_DEFAULT_TRACERS is not set
diff --git a/kernel/arch/arm64/configs/rockchip_rt.config b/kernel/arch/arm64/configs/rockchip_rt.config
index 2c6e12e..e57555e 100644
--- a/kernel/arch/arm64/configs/rockchip_rt.config
+++ b/kernel/arch/arm64/configs/rockchip_rt.config
@@ -1,24 +1,4 @@
-# CONFIG_ARM_PSCI_CPUIDLE is not set
 # CONFIG_ARM_ROCKCHIP_DMC_DEVFREQ is not set
-# CONFIG_CGROUP_CPUACCT is not set
-# CONFIG_CGROUP_SCHED is not set
+# CONFIG_ARM_PSCI_CPUIDLE is not set
 # CONFIG_CPU_FREQ_TIMES is not set
-# CONFIG_CPU_FREQ_THERMAL is not set
-# CONFIG_DEBUG_SPINLOCK is not set
-# CONFIG_FTRACE is not set
-# CONFIG_IRQ_TIME_ACCOUNTING is not set
-# CONFIG_MALI_BIFROST_ENABLE_TRACE is not set
-# CONFIG_MALI_BIFROST_SYSTEM_TRACE is not set
-# CONFIG_PSI is not set
-# CONFIG_PERF_EVENTS is not set
-# CONFIG_PROFILING is not set
-# CONFIG_SCHED_DEBUG is not set
-# CONFIG_SCHED_INFO is not set
-# CONFIG_SWAP is not set
-# CONFIG_TASKSTATS is not set
-# CONFIG_ZRAM is not set
-CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y
-CONFIG_JUMP_LABEL=y
-CONFIG_HZ_PERIODIC=y
-CONFIG_HZ_1000=y
 CONFIG_PREEMPT_RT=y
diff --git a/kernel/arch/arm64/include/asm/hardirq.h b/kernel/arch/arm64/include/asm/hardirq.h
index cbfa7b6..5ffa4ba 100644
--- a/kernel/arch/arm64/include/asm/hardirq.h
+++ b/kernel/arch/arm64/include/asm/hardirq.h
@@ -13,8 +13,11 @@
 #include <asm/kvm_arm.h>
 #include <asm/sysreg.h>
 
-#define ack_bad_irq ack_bad_irq
-#include <asm-generic/hardirq.h>
+typedef struct {
+	unsigned int __softirq_pending;
+} ____cacheline_aligned irq_cpustat_t;
+
+#include <linux/irq_cpustat.h>	/* Standard mappings for irq_cpustat_t above */
 
 #define __ARCH_IRQ_EXIT_IRQS_DISABLED	1
 
diff --git a/kernel/arch/arm64/include/asm/pgtable.h b/kernel/arch/arm64/include/asm/pgtable.h
index 7fb56c6..35fe1ca 100644
--- a/kernel/arch/arm64/include/asm/pgtable.h
+++ b/kernel/arch/arm64/include/asm/pgtable.h
@@ -996,8 +996,7 @@
  */
 static inline bool arch_faults_on_old_pte(void)
 {
-	/* The register read below requires a stable CPU to make any sense */
-	cant_migrate();
+	WARN_ON(preemptible());
 
 	return !cpu_has_hw_af();
 }
diff --git a/kernel/arch/arm64/include/asm/preempt.h b/kernel/arch/arm64/include/asm/preempt.h
index 7a5770d..e83f098 100644
--- a/kernel/arch/arm64/include/asm/preempt.h
+++ b/kernel/arch/arm64/include/asm/preempt.h
@@ -70,43 +70,17 @@
 	 * interrupt occurring between the non-atomic READ_ONCE/WRITE_ONCE
 	 * pair.
 	 */
-	if (!pc || !READ_ONCE(ti->preempt_count))
-		return true;
-#ifdef CONFIG_PREEMPT_LAZY
-	if ((pc & ~PREEMPT_NEED_RESCHED))
-		return false;
-	if (current_thread_info()->preempt_lazy_count)
-		return false;
-	return test_thread_flag(TIF_NEED_RESCHED_LAZY);
-#else
-	return false;
-#endif
+	return !pc || !READ_ONCE(ti->preempt_count);
 }
 
 static inline bool should_resched(int preempt_offset)
 {
-#ifdef CONFIG_PREEMPT_LAZY
-	u64 pc = READ_ONCE(current_thread_info()->preempt_count);
-	if (pc == preempt_offset)
-		return true;
-
-	if ((pc & ~PREEMPT_NEED_RESCHED) != preempt_offset)
-		return false;
-
-	if (current_thread_info()->preempt_lazy_count)
-		return false;
-	return test_thread_flag(TIF_NEED_RESCHED_LAZY);
-#else
 	u64 pc = READ_ONCE(current_thread_info()->preempt_count);
 	return pc == preempt_offset;
-#endif
 }
 
 #ifdef CONFIG_PREEMPTION
 void preempt_schedule(void);
-#ifdef CONFIG_PREEMPT_RT
-void preempt_schedule_lock(void);
-#endif
 #define __preempt_schedule() preempt_schedule()
 void preempt_schedule_notrace(void);
 #define __preempt_schedule_notrace() preempt_schedule_notrace()
diff --git a/kernel/arch/arm64/include/asm/spinlock_types.h b/kernel/arch/arm64/include/asm/spinlock_types.h
index 6672b05..18782f0 100644
--- a/kernel/arch/arm64/include/asm/spinlock_types.h
+++ b/kernel/arch/arm64/include/asm/spinlock_types.h
@@ -5,6 +5,10 @@
 #ifndef __ASM_SPINLOCK_TYPES_H
 #define __ASM_SPINLOCK_TYPES_H
 
+#if !defined(__LINUX_SPINLOCK_TYPES_H) && !defined(__ASM_SPINLOCK_H)
+# error "please don't include this file directly"
+#endif
+
 #include <asm-generic/qspinlock_types.h>
 #include <asm-generic/qrwlock_types.h>
 
diff --git a/kernel/arch/arm64/include/asm/thread_info.h b/kernel/arch/arm64/include/asm/thread_info.h
index 2afd9ce..cdcf307 100644
--- a/kernel/arch/arm64/include/asm/thread_info.h
+++ b/kernel/arch/arm64/include/asm/thread_info.h
@@ -29,7 +29,6 @@
 #ifdef CONFIG_ARM64_SW_TTBR0_PAN
 	u64			ttbr0;		/* saved TTBR0_EL1 */
 #endif
-	int			preempt_lazy_count;	/* 0 => preemptable, <0 => bug */
 	union {
 		u64		preempt_count;	/* 0 => preemptible, <0 => bug */
 		struct {
@@ -70,12 +69,11 @@
 #define TIF_FSCHECK		5	/* Check FS is USER_DS on return */
 #define TIF_MTE_ASYNC_FAULT	6	/* MTE Asynchronous Tag Check Fault */
 #define TIF_NOTIFY_SIGNAL	7	/* signal notifications exist */
-#define TIF_NEED_RESCHED_LAZY	8
-#define TIF_SYSCALL_TRACE	9	/* syscall trace active */
-#define TIF_SYSCALL_AUDIT	10	/* syscall auditing */
-#define TIF_SYSCALL_TRACEPOINT	11	/* syscall tracepoint for ftrace */
-#define TIF_SECCOMP		12	/* syscall secure computing */
-#define TIF_SYSCALL_EMU		13	/* syscall emulation active */
+#define TIF_SYSCALL_TRACE	8	/* syscall trace active */
+#define TIF_SYSCALL_AUDIT	9	/* syscall auditing */
+#define TIF_SYSCALL_TRACEPOINT	10	/* syscall tracepoint for ftrace */
+#define TIF_SECCOMP		11	/* syscall secure computing */
+#define TIF_SYSCALL_EMU		12	/* syscall emulation active */
 #define TIF_MEMDIE		18	/* is terminating due to OOM killer */
 #define TIF_FREEZE		19
 #define TIF_RESTORE_SIGMASK	20
@@ -101,15 +99,13 @@
 #define _TIF_32BIT		(1 << TIF_32BIT)
 #define _TIF_SVE		(1 << TIF_SVE)
 #define _TIF_MTE_ASYNC_FAULT	(1 << TIF_MTE_ASYNC_FAULT)
-#define _TIF_NEED_RESCHED_LAZY	(1 << TIF_NEED_RESCHED_LAZY)
 #define _TIF_NOTIFY_SIGNAL	(1 << TIF_NOTIFY_SIGNAL)
 
 #define _TIF_WORK_MASK		(_TIF_NEED_RESCHED | _TIF_SIGPENDING | \
 				 _TIF_NOTIFY_RESUME | _TIF_FOREIGN_FPSTATE | \
 				 _TIF_UPROBE | _TIF_FSCHECK | _TIF_MTE_ASYNC_FAULT | \
-				 _TIF_NEED_RESCHED_LAZY | _TIF_NOTIFY_SIGNAL)
+				 _TIF_NOTIFY_SIGNAL)
 
-#define _TIF_NEED_RESCHED_MASK	(_TIF_NEED_RESCHED | _TIF_NEED_RESCHED_LAZY)
 #define _TIF_SYSCALL_WORK	(_TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT | \
 				 _TIF_SYSCALL_TRACEPOINT | _TIF_SECCOMP | \
 				 _TIF_SYSCALL_EMU)
diff --git a/kernel/arch/arm64/kernel/asm-offsets.c b/kernel/arch/arm64/kernel/asm-offsets.c
index b904ad9..93da876 100644
--- a/kernel/arch/arm64/kernel/asm-offsets.c
+++ b/kernel/arch/arm64/kernel/asm-offsets.c
@@ -30,7 +30,6 @@
   BLANK();
   DEFINE(TSK_TI_FLAGS,		offsetof(struct task_struct, thread_info.flags));
   DEFINE(TSK_TI_PREEMPT,	offsetof(struct task_struct, thread_info.preempt_count));
-  DEFINE(TSK_TI_PREEMPT_LAZY,	offsetof(struct task_struct, thread_info.preempt_lazy_count));
   DEFINE(TSK_TI_ADDR_LIMIT,	offsetof(struct task_struct, thread_info.addr_limit));
 #ifdef CONFIG_ARM64_SW_TTBR0_PAN
   DEFINE(TSK_TI_TTBR0,		offsetof(struct task_struct, thread_info.ttbr0));
diff --git a/kernel/arch/arm64/kernel/entry.S b/kernel/arch/arm64/kernel/entry.S
index 03d6d7b..9f19e6b 100644
--- a/kernel/arch/arm64/kernel/entry.S
+++ b/kernel/arch/arm64/kernel/entry.S
@@ -626,18 +626,9 @@
 	mrs	x0, daif
 	orr	x24, x24, x0
 alternative_else_nop_endif
-
-	cbz     x24, 1f                                 // (need_resched + count) == 0
-	cbnz    w24, 2f                                 // count != 0
-
-	ldr     w24, [tsk, #TSK_TI_PREEMPT_LAZY]        // get preempt lazy count
-	cbnz    w24, 2f                                 // preempt lazy count != 0
-
-	ldr     x0, [tsk, #TSK_TI_FLAGS]                // get flags
-	tbz     x0, #TIF_NEED_RESCHED_LAZY, 2f          // needs rescheduling?
+	cbnz	x24, 1f				// preempt count != 0 || NMI return path
+	bl	arm64_preempt_schedule_irq	// irq en/disable is done inside
 1:
-	bl      arm64_preempt_schedule_irq              // irq en/disable is done inside
-2:
 #endif
 
 	mov	x0, sp
diff --git a/kernel/arch/arm64/kernel/fpsimd.c b/kernel/arch/arm64/kernel/fpsimd.c
index c347a88..5335a6b 100644
--- a/kernel/arch/arm64/kernel/fpsimd.c
+++ b/kernel/arch/arm64/kernel/fpsimd.c
@@ -180,10 +180,7 @@
  */
 static void get_cpu_fpsimd_context(void)
 {
-	if (!IS_ENABLED(CONFIG_PREEMPT_RT))
-		local_bh_disable();
-	else
-		preempt_disable();
+	local_bh_disable();
 	__get_cpu_fpsimd_context();
 }
 
@@ -204,10 +201,7 @@
 static void put_cpu_fpsimd_context(void)
 {
 	__put_cpu_fpsimd_context();
-	if (!IS_ENABLED(CONFIG_PREEMPT_RT))
-		local_bh_enable();
-	else
-		preempt_enable();
+	local_bh_enable();
 }
 
 static bool have_cpu_fpsimd_context(void)
@@ -215,14 +209,21 @@
 	return !preemptible() && __this_cpu_read(fpsimd_context_busy);
 }
 
-static void *sve_free_atomic(struct task_struct *task)
+/*
+ * Call __sve_free() directly only if you know task can't be scheduled
+ * or preempted.
+ */
+static void __sve_free(struct task_struct *task)
 {
-	void *sve_state = task->thread.sve_state;
+	kfree(task->thread.sve_state);
+	task->thread.sve_state = NULL;
+}
 
+static void sve_free(struct task_struct *task)
+{
 	WARN_ON(test_tsk_thread_flag(task, TIF_SVE));
 
-	task->thread.sve_state = NULL;
-	return sve_state;
+	__sve_free(task);
 }
 
 /*
@@ -583,7 +584,6 @@
 int sve_set_vector_length(struct task_struct *task,
 			  unsigned long vl, unsigned long flags)
 {
-	void *mem = NULL;
 	if (flags & ~(unsigned long)(PR_SVE_VL_INHERIT |
 				     PR_SVE_SET_VL_ONEXEC))
 		return -EINVAL;
@@ -637,10 +637,9 @@
 	 * Force reallocation of task SVE state to the correct size
 	 * on next use:
 	 */
-	mem = sve_free_atomic(task);
+	sve_free(task);
 
 	task->thread.sve_vl = vl;
-	kfree(mem);
 
 out:
 	update_tsk_thread_flag(task, TIF_SVE_VL_INHERIT,
@@ -918,9 +917,7 @@
  */
 void fpsimd_release_task(struct task_struct *dead_task)
 {
-	void *mem = NULL;
-	mem = sve_free_atomic(dead_task);
-	kfree(mem);
+	__sve_free(dead_task);
 }
 
 #endif /* CONFIG_ARM64_SVE */
@@ -1025,7 +1022,6 @@
 void fpsimd_flush_thread(void)
 {
 	int vl, supported_vl;
-	void *mem = NULL;
 
 	if (!system_supports_fpsimd())
 		return;
@@ -1038,7 +1034,7 @@
 
 	if (system_supports_sve()) {
 		clear_thread_flag(TIF_SVE);
-		mem = sve_free_atomic(current);
+		sve_free(current);
 
 		/*
 		 * Reset the task vector length as required.
@@ -1072,7 +1068,6 @@
 	}
 
 	put_cpu_fpsimd_context();
-	kfree(mem);
 }
 
 /*
diff --git a/kernel/arch/arm64/kernel/signal.c b/kernel/arch/arm64/kernel/signal.c
index 94eed0d..b6fbbd5 100644
--- a/kernel/arch/arm64/kernel/signal.c
+++ b/kernel/arch/arm64/kernel/signal.c
@@ -921,7 +921,7 @@
 		/* Check valid user FS if needed */
 		addr_limit_user_check();
 
-		if (thread_flags & _TIF_NEED_RESCHED_MASK) {
+		if (thread_flags & _TIF_NEED_RESCHED) {
 			/* Unmask Debug and SError for the next task */
 			local_daif_restore(DAIF_PROCCTX_NOIRQ);
 
diff --git a/kernel/arch/arm64/kernel/traps.c b/kernel/arch/arm64/kernel/traps.c
index a6ba436..49b4b7b 100644
--- a/kernel/arch/arm64/kernel/traps.c
+++ b/kernel/arch/arm64/kernel/traps.c
@@ -48,6 +48,10 @@
 
 #include <trace/hooks/traps.h>
 
+#if IS_ENABLED(CONFIG_ROCKCHIP_MINIDUMP)
+#include <soc/rockchip/rk_minidump.h>
+#endif
+
 static const char *handler[]= {
 	"Synchronous Abort",
 	"IRQ",
@@ -123,6 +127,9 @@
 	int ret;
 	unsigned long flags;
 
+#if IS_ENABLED(CONFIG_ROCKCHIP_MINIDUMP)
+	rk_minidump_update_cpu_regs(regs);
+#endif
 	raw_spin_lock_irqsave(&die_lock, flags);
 
 	oops_enter();
diff --git a/kernel/arch/arm64/kvm/arm.c b/kernel/arch/arm64/kvm/arm.c
index 0ef3bfe..78550c8 100644
--- a/kernel/arch/arm64/kvm/arm.c
+++ b/kernel/arch/arm64/kvm/arm.c
@@ -750,7 +750,7 @@
 		 * involves poking the GIC, which must be done in a
 		 * non-preemptible context.
 		 */
-		migrate_disable();
+		preempt_disable();
 
 		kvm_pmu_flush_hwstate(vcpu);
 
@@ -799,7 +799,7 @@
 				kvm_timer_sync_user(vcpu);
 			kvm_vgic_sync_hwstate(vcpu);
 			local_irq_enable();
-			migrate_enable();
+			preempt_enable();
 			continue;
 		}
 
@@ -871,7 +871,7 @@
 		/* Exit types that need handling before we can be preempted */
 		handle_exit_early(vcpu, ret);
 
-		migrate_enable();
+		preempt_enable();
 
 		/*
 		 * The ARMv8 architecture doesn't give the hypervisor
diff --git a/kernel/arch/csky/Kconfig b/kernel/arch/csky/Kconfig
index c9f2533..7bf0a61 100644
--- a/kernel/arch/csky/Kconfig
+++ b/kernel/arch/csky/Kconfig
@@ -286,7 +286,6 @@
 config HIGHMEM
 	bool "High Memory Support"
 	depends on !CPU_CK610
-	select KMAP_LOCAL
 	default y
 
 config FORCE_MAX_ZONEORDER
diff --git a/kernel/arch/csky/include/asm/fixmap.h b/kernel/arch/csky/include/asm/fixmap.h
index 4b589cc..81f9477 100644
--- a/kernel/arch/csky/include/asm/fixmap.h
+++ b/kernel/arch/csky/include/asm/fixmap.h
@@ -8,7 +8,7 @@
 #include <asm/memory.h>
 #ifdef CONFIG_HIGHMEM
 #include <linux/threads.h>
-#include <asm/kmap_size.h>
+#include <asm/kmap_types.h>
 #endif
 
 enum fixed_addresses {
@@ -17,7 +17,7 @@
 #endif
 #ifdef CONFIG_HIGHMEM
 	FIX_KMAP_BEGIN,
-	FIX_KMAP_END = FIX_KMAP_BEGIN + (KM_MAX_IDX * NR_CPUS) - 1,
+	FIX_KMAP_END = FIX_KMAP_BEGIN + (KM_TYPE_NR * NR_CPUS) - 1,
 #endif
 	__end_of_fixed_addresses
 };
diff --git a/kernel/arch/csky/include/asm/highmem.h b/kernel/arch/csky/include/asm/highmem.h
index 1f4ed3f..14645e3 100644
--- a/kernel/arch/csky/include/asm/highmem.h
+++ b/kernel/arch/csky/include/asm/highmem.h
@@ -9,7 +9,7 @@
 #include <linux/init.h>
 #include <linux/interrupt.h>
 #include <linux/uaccess.h>
-#include <asm/kmap_size.h>
+#include <asm/kmap_types.h>
 #include <asm/cache.h>
 
 /* undef for production */
@@ -32,11 +32,9 @@
 
 #define ARCH_HAS_KMAP_FLUSH_TLB
 extern void kmap_flush_tlb(unsigned long addr);
+extern void *kmap_atomic_pfn(unsigned long pfn);
 
 #define flush_cache_kmaps() do {} while (0)
-
-#define arch_kmap_local_post_map(vaddr, pteval)	kmap_flush_tlb(vaddr)
-#define arch_kmap_local_post_unmap(vaddr)	kmap_flush_tlb(vaddr)
 
 extern void kmap_init(void);
 
diff --git a/kernel/arch/csky/mm/highmem.c b/kernel/arch/csky/mm/highmem.c
index 4161df3..89c1080 100644
--- a/kernel/arch/csky/mm/highmem.c
+++ b/kernel/arch/csky/mm/highmem.c
@@ -9,6 +9,8 @@
 #include <asm/tlbflush.h>
 #include <asm/cacheflush.h>
 
+static pte_t *kmap_pte;
+
 unsigned long highstart_pfn, highend_pfn;
 
 void kmap_flush_tlb(unsigned long addr)
@@ -17,7 +19,67 @@
 }
 EXPORT_SYMBOL(kmap_flush_tlb);
 
-void __init kmap_init(void)
+void *kmap_atomic_high_prot(struct page *page, pgprot_t prot)
+{
+	unsigned long vaddr;
+	int idx, type;
+
+	type = kmap_atomic_idx_push();
+	idx = type + KM_TYPE_NR*smp_processor_id();
+	vaddr = __fix_to_virt(FIX_KMAP_BEGIN + idx);
+#ifdef CONFIG_DEBUG_HIGHMEM
+	BUG_ON(!pte_none(*(kmap_pte - idx)));
+#endif
+	set_pte(kmap_pte-idx, mk_pte(page, prot));
+	flush_tlb_one((unsigned long)vaddr);
+
+	return (void *)vaddr;
+}
+EXPORT_SYMBOL(kmap_atomic_high_prot);
+
+void kunmap_atomic_high(void *kvaddr)
+{
+	unsigned long vaddr = (unsigned long) kvaddr & PAGE_MASK;
+	int idx;
+
+	if (vaddr < FIXADDR_START)
+		return;
+
+#ifdef CONFIG_DEBUG_HIGHMEM
+	idx = KM_TYPE_NR*smp_processor_id() + kmap_atomic_idx();
+
+	BUG_ON(vaddr != __fix_to_virt(FIX_KMAP_BEGIN + idx));
+
+	pte_clear(&init_mm, vaddr, kmap_pte - idx);
+	flush_tlb_one(vaddr);
+#else
+	(void) idx; /* to kill a warning */
+#endif
+	kmap_atomic_idx_pop();
+}
+EXPORT_SYMBOL(kunmap_atomic_high);
+
+/*
+ * This is the same as kmap_atomic() but can map memory that doesn't
+ * have a struct page associated with it.
+ */
+void *kmap_atomic_pfn(unsigned long pfn)
+{
+	unsigned long vaddr;
+	int idx, type;
+
+	pagefault_disable();
+
+	type = kmap_atomic_idx_push();
+	idx = type + KM_TYPE_NR*smp_processor_id();
+	vaddr = __fix_to_virt(FIX_KMAP_BEGIN + idx);
+	set_pte(kmap_pte-idx, pfn_pte(pfn, PAGE_KERNEL));
+	flush_tlb_one(vaddr);
+
+	return (void *) vaddr;
+}
+
+static void __init kmap_pages_init(void)
 {
 	unsigned long vaddr;
 	pgd_t *pgd;
@@ -34,3 +96,14 @@
 	pte = pte_offset_kernel(pmd, vaddr);
 	pkmap_page_table = pte;
 }
+
+void __init kmap_init(void)
+{
+	unsigned long vaddr;
+
+	kmap_pages_init();
+
+	vaddr = __fix_to_virt(FIX_KMAP_BEGIN);
+
+	kmap_pte = pte_offset_kernel((pmd_t *)pgd_offset_k(vaddr), vaddr);
+}
diff --git a/kernel/arch/hexagon/include/asm/spinlock_types.h b/kernel/arch/hexagon/include/asm/spinlock_types.h
index de72fb2..19d2334 100644
--- a/kernel/arch/hexagon/include/asm/spinlock_types.h
+++ b/kernel/arch/hexagon/include/asm/spinlock_types.h
@@ -8,6 +8,10 @@
 #ifndef _ASM_SPINLOCK_TYPES_H
 #define _ASM_SPINLOCK_TYPES_H
 
+#ifndef __LINUX_SPINLOCK_TYPES_H
+# error "please don't include this file directly"
+#endif
+
 typedef struct {
 	volatile unsigned int lock;
 } arch_spinlock_t;
diff --git a/kernel/arch/ia64/include/asm/kmap_types.h b/kernel/arch/ia64/include/asm/kmap_types.h
new file mode 100644
index 0000000..5c268cf
--- /dev/null
+++ b/kernel/arch/ia64/include/asm/kmap_types.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _ASM_IA64_KMAP_TYPES_H
+#define _ASM_IA64_KMAP_TYPES_H
+
+#ifdef CONFIG_DEBUG_HIGHMEM
+#define  __WITH_KM_FENCE
+#endif
+
+#include <asm-generic/kmap_types.h>
+
+#undef __WITH_KM_FENCE
+
+#endif /* _ASM_IA64_KMAP_TYPES_H */
diff --git a/kernel/arch/ia64/include/asm/spinlock_types.h b/kernel/arch/ia64/include/asm/spinlock_types.h
index 681408d..6e345fe 100644
--- a/kernel/arch/ia64/include/asm/spinlock_types.h
+++ b/kernel/arch/ia64/include/asm/spinlock_types.h
@@ -2,6 +2,10 @@
 #ifndef _ASM_IA64_SPINLOCK_TYPES_H
 #define _ASM_IA64_SPINLOCK_TYPES_H
 
+#ifndef __LINUX_SPINLOCK_TYPES_H
+# error "please don't include this file directly"
+#endif
+
 typedef struct {
 	volatile unsigned int lock;
 } arch_spinlock_t;
diff --git a/kernel/arch/ia64/kernel/time.c b/kernel/arch/ia64/kernel/time.c
index 733e0e3..7abc5f3 100644
--- a/kernel/arch/ia64/kernel/time.c
+++ b/kernel/arch/ia64/kernel/time.c
@@ -138,8 +138,12 @@
 	struct thread_info *ti = task_thread_info(tsk);
 	__u64 stime = vtime_delta(tsk);
 
-	if (tsk->flags & PF_VCPU)
+	if ((tsk->flags & PF_VCPU) && !irq_count())
 		ti->gtime += stime;
+	else if (hardirq_count())
+		ti->hardirq_time += stime;
+	else if (in_serving_softirq())
+		ti->softirq_time += stime;
 	else
 		ti->stime += stime;
 }
@@ -150,20 +154,6 @@
 	struct thread_info *ti = task_thread_info(tsk);
 
 	ti->idle_time += vtime_delta(tsk);
-}
-
-void vtime_account_softirq(struct task_struct *tsk)
-{
-	struct thread_info *ti = task_thread_info(tsk);
-
-	ti->softirq_time += vtime_delta(tsk);
-}
-
-void vtime_account_hardirq(struct task_struct *tsk)
-{
-	struct thread_info *ti = task_thread_info(tsk);
-
-	ti->hardirq_time += vtime_delta(tsk);
 }
 
 #endif /* CONFIG_VIRT_CPU_ACCOUNTING_NATIVE */
diff --git a/kernel/arch/microblaze/Kconfig b/kernel/arch/microblaze/Kconfig
index 7f6ca0a..33925ff 100644
--- a/kernel/arch/microblaze/Kconfig
+++ b/kernel/arch/microblaze/Kconfig
@@ -155,7 +155,6 @@
 config HIGHMEM
 	bool "High memory support"
 	depends on MMU
-	select KMAP_LOCAL
 	help
 	  The address space of Microblaze processors is only 4 Gigabytes large
 	  and it has to accommodate user address space, kernel address
diff --git a/kernel/arch/microblaze/include/asm/fixmap.h b/kernel/arch/microblaze/include/asm/fixmap.h
index e6e9288..0379ce5 100644
--- a/kernel/arch/microblaze/include/asm/fixmap.h
+++ b/kernel/arch/microblaze/include/asm/fixmap.h
@@ -20,7 +20,7 @@
 #include <asm/page.h>
 #ifdef CONFIG_HIGHMEM
 #include <linux/threads.h>
-#include <asm/kmap_size.h>
+#include <asm/kmap_types.h>
 #endif
 
 #define FIXADDR_TOP	((unsigned long)(-PAGE_SIZE))
@@ -47,7 +47,7 @@
 	FIX_HOLE,
 #ifdef CONFIG_HIGHMEM
 	FIX_KMAP_BEGIN,	/* reserved pte's for temporary kernel mappings */
-	FIX_KMAP_END = FIX_KMAP_BEGIN + (KM_MAX_IDX * num_possible_cpus()) - 1,
+	FIX_KMAP_END = FIX_KMAP_BEGIN + (KM_TYPE_NR * num_possible_cpus()) - 1,
 #endif
 	__end_of_fixed_addresses
 };
diff --git a/kernel/arch/microblaze/include/asm/highmem.h b/kernel/arch/microblaze/include/asm/highmem.h
index 4418633..284ca8f 100644
--- a/kernel/arch/microblaze/include/asm/highmem.h
+++ b/kernel/arch/microblaze/include/asm/highmem.h
@@ -25,6 +25,7 @@
 #include <linux/uaccess.h>
 #include <asm/fixmap.h>
 
+extern pte_t *kmap_pte;
 extern pte_t *pkmap_page_table;
 
 /*
@@ -50,11 +51,6 @@
 #define PKMAP_ADDR(nr)  (PKMAP_BASE + ((nr) << PAGE_SHIFT))
 
 #define flush_cache_kmaps()	{ flush_icache(); flush_dcache(); }
-
-#define arch_kmap_local_post_map(vaddr, pteval)	\
-	local_flush_tlb_page(NULL, vaddr);
-#define arch_kmap_local_post_unmap(vaddr)	\
-	local_flush_tlb_page(NULL, vaddr);
 
 #endif /* __KERNEL__ */
 
diff --git a/kernel/arch/microblaze/mm/Makefile b/kernel/arch/microblaze/mm/Makefile
index 8ced711..1b16875 100644
--- a/kernel/arch/microblaze/mm/Makefile
+++ b/kernel/arch/microblaze/mm/Makefile
@@ -6,3 +6,4 @@
 obj-y := consistent.o init.o
 
 obj-$(CONFIG_MMU) += pgtable.o mmu_context.o fault.o
+obj-$(CONFIG_HIGHMEM) += highmem.o
diff --git a/kernel/arch/microblaze/mm/highmem.c b/kernel/arch/microblaze/mm/highmem.c
new file mode 100644
index 0000000..92e0890
--- /dev/null
+++ b/kernel/arch/microblaze/mm/highmem.c
@@ -0,0 +1,78 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * highmem.c: virtual kernel memory mappings for high memory
+ *
+ * PowerPC version, stolen from the i386 version.
+ *
+ * Used in CONFIG_HIGHMEM systems for memory pages which
+ * are not addressable by direct kernel virtual addresses.
+ *
+ * Copyright (C) 1999 Gerhard Wichert, Siemens AG
+ *		      Gerhard.Wichert@pdb.siemens.de
+ *
+ *
+ * Redesigned the x86 32-bit VM architecture to deal with
+ * up to 16 Terrabyte physical memory. With current x86 CPUs
+ * we now support up to 64 Gigabytes physical RAM.
+ *
+ * Copyright (C) 1999 Ingo Molnar <mingo@redhat.com>
+ *
+ * Reworked for PowerPC by various contributors. Moved from
+ * highmem.h by Benjamin Herrenschmidt (c) 2009 IBM Corp.
+ */
+
+#include <linux/export.h>
+#include <linux/highmem.h>
+
+/*
+ * The use of kmap_atomic/kunmap_atomic is discouraged - kmap/kunmap
+ * gives a more generic (and caching) interface. But kmap_atomic can
+ * be used in IRQ contexts, so in some (very limited) cases we need
+ * it.
+ */
+#include <asm/tlbflush.h>
+
+void *kmap_atomic_high_prot(struct page *page, pgprot_t prot)
+{
+
+	unsigned long vaddr;
+	int idx, type;
+
+	type = kmap_atomic_idx_push();
+	idx = type + KM_TYPE_NR*smp_processor_id();
+	vaddr = __fix_to_virt(FIX_KMAP_BEGIN + idx);
+#ifdef CONFIG_DEBUG_HIGHMEM
+	BUG_ON(!pte_none(*(kmap_pte-idx)));
+#endif
+	set_pte_at(&init_mm, vaddr, kmap_pte-idx, mk_pte(page, prot));
+	local_flush_tlb_page(NULL, vaddr);
+
+	return (void *) vaddr;
+}
+EXPORT_SYMBOL(kmap_atomic_high_prot);
+
+void kunmap_atomic_high(void *kvaddr)
+{
+	unsigned long vaddr = (unsigned long) kvaddr & PAGE_MASK;
+	int type;
+	unsigned int idx;
+
+	if (vaddr < __fix_to_virt(FIX_KMAP_END))
+		return;
+
+	type = kmap_atomic_idx();
+
+	idx = type + KM_TYPE_NR * smp_processor_id();
+#ifdef CONFIG_DEBUG_HIGHMEM
+	BUG_ON(vaddr != __fix_to_virt(FIX_KMAP_BEGIN + idx));
+#endif
+	/*
+	 * force other mappings to Oops if they'll try to access
+	 * this pte without first remap it
+	 */
+	pte_clear(&init_mm, vaddr, kmap_pte-idx);
+	local_flush_tlb_page(NULL, vaddr);
+
+	kmap_atomic_idx_pop();
+}
+EXPORT_SYMBOL(kunmap_atomic_high);
diff --git a/kernel/arch/microblaze/mm/init.c b/kernel/arch/microblaze/mm/init.c
index 1f4b5b3..45da639 100644
--- a/kernel/arch/microblaze/mm/init.c
+++ b/kernel/arch/microblaze/mm/init.c
@@ -49,11 +49,17 @@
 EXPORT_SYMBOL(min_low_pfn);
 EXPORT_SYMBOL(max_low_pfn);
 
+#ifdef CONFIG_HIGHMEM
+pte_t *kmap_pte;
+EXPORT_SYMBOL(kmap_pte);
+
 static void __init highmem_init(void)
 {
 	pr_debug("%x\n", (u32)PKMAP_BASE);
 	map_page(PKMAP_BASE, 0, 0);	/* XXX gross */
 	pkmap_page_table = virt_to_kpte(PKMAP_BASE);
+
+	kmap_pte = virt_to_kpte(__fix_to_virt(FIX_KMAP_BEGIN));
 }
 
 static void highmem_setup(void)
diff --git a/kernel/arch/mips/Kconfig b/kernel/arch/mips/Kconfig
index 30622f2..3442bdd 100644
--- a/kernel/arch/mips/Kconfig
+++ b/kernel/arch/mips/Kconfig
@@ -2730,7 +2730,6 @@
 config HIGHMEM
 	bool "High Memory Support"
 	depends on 32BIT && CPU_SUPPORTS_HIGHMEM && SYS_SUPPORTS_HIGHMEM && !CPU_MIPS32_3_5_EVA
-	select KMAP_LOCAL
 
 config CPU_SUPPORTS_HIGHMEM
 	bool
diff --git a/kernel/arch/mips/include/asm/fixmap.h b/kernel/arch/mips/include/asm/fixmap.h
index beea147..743535b 100644
--- a/kernel/arch/mips/include/asm/fixmap.h
+++ b/kernel/arch/mips/include/asm/fixmap.h
@@ -17,7 +17,7 @@
 #include <spaces.h>
 #ifdef CONFIG_HIGHMEM
 #include <linux/threads.h>
-#include <asm/kmap_size.h>
+#include <asm/kmap_types.h>
 #endif
 
 /*
@@ -52,7 +52,7 @@
 #ifdef CONFIG_HIGHMEM
 	/* reserved pte's for temporary kernel mappings */
 	FIX_KMAP_BEGIN = FIX_CMAP_END + 1,
-	FIX_KMAP_END = FIX_KMAP_BEGIN + (KM_MAX_IDX * NR_CPUS) - 1,
+	FIX_KMAP_END = FIX_KMAP_BEGIN+(KM_TYPE_NR*NR_CPUS)-1,
 #endif
 	__end_of_fixed_addresses
 };
diff --git a/kernel/arch/mips/include/asm/highmem.h b/kernel/arch/mips/include/asm/highmem.h
index 1716181..9f021cf 100644
--- a/kernel/arch/mips/include/asm/highmem.h
+++ b/kernel/arch/mips/include/asm/highmem.h
@@ -24,7 +24,7 @@
 #include <linux/interrupt.h>
 #include <linux/uaccess.h>
 #include <asm/cpu-features.h>
-#include <asm/kmap_size.h>
+#include <asm/kmap_types.h>
 
 /* declarations for highmem.c */
 extern unsigned long highstart_pfn, highend_pfn;
@@ -48,11 +48,11 @@
 
 #define ARCH_HAS_KMAP_FLUSH_TLB
 extern void kmap_flush_tlb(unsigned long addr);
+extern void *kmap_atomic_pfn(unsigned long pfn);
 
 #define flush_cache_kmaps()	BUG_ON(cpu_has_dc_aliases)
 
-#define arch_kmap_local_post_map(vaddr, pteval)	local_flush_tlb_one(vaddr)
-#define arch_kmap_local_post_unmap(vaddr)	local_flush_tlb_one(vaddr)
+extern void kmap_init(void);
 
 #endif /* __KERNEL__ */
 
diff --git a/kernel/arch/mips/include/asm/kmap_types.h b/kernel/arch/mips/include/asm/kmap_types.h
new file mode 100644
index 0000000..16665dc
--- /dev/null
+++ b/kernel/arch/mips/include/asm/kmap_types.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _ASM_KMAP_TYPES_H
+#define _ASM_KMAP_TYPES_H
+
+#ifdef CONFIG_DEBUG_HIGHMEM
+#define	 __WITH_KM_FENCE
+#endif
+
+#include <asm-generic/kmap_types.h>
+
+#undef __WITH_KM_FENCE
+
+#endif
diff --git a/kernel/arch/mips/kernel/crash_dump.c b/kernel/arch/mips/kernel/crash_dump.c
index 9aba83e..01b2bd9 100644
--- a/kernel/arch/mips/kernel/crash_dump.c
+++ b/kernel/arch/mips/kernel/crash_dump.c
@@ -5,6 +5,8 @@
 #include <linux/uaccess.h>
 #include <linux/slab.h>
 
+static void *kdump_buf_page;
+
 /**
  * copy_oldmem_page - copy one page from "oldmem"
  * @pfn: page frame number to be copied
@@ -15,25 +17,51 @@
  * @userbuf: if set, @buf is in user address space, use copy_to_user(),
  *	otherwise @buf is in kernel address space, use memcpy().
  *
- * Copy a page from "oldmem". For this page, there might be no pte mapped
+ * Copy a page from "oldmem". For this page, there is no pte mapped
  * in the current kernel.
+ *
+ * Calling copy_to_user() in atomic context is not desirable. Hence first
+ * copying the data to a pre-allocated kernel page and then copying to user
+ * space in non-atomic context.
  */
-ssize_t copy_oldmem_page(unsigned long pfn, char *buf, size_t csize,
-			 unsigned long offset, int userbuf)
+ssize_t copy_oldmem_page(unsigned long pfn, char *buf,
+			 size_t csize, unsigned long offset, int userbuf)
 {
 	void  *vaddr;
 
 	if (!csize)
 		return 0;
 
-	vaddr = kmap_local_pfn(pfn);
+	vaddr = kmap_atomic_pfn(pfn);
 
 	if (!userbuf) {
-		memcpy(buf, vaddr + offset, csize);
+		memcpy(buf, (vaddr + offset), csize);
+		kunmap_atomic(vaddr);
 	} else {
-		if (copy_to_user(buf, vaddr + offset, csize))
-			csize = -EFAULT;
+		if (!kdump_buf_page) {
+			pr_warn("Kdump: Kdump buffer page not allocated\n");
+
+			return -EFAULT;
+		}
+		copy_page(kdump_buf_page, vaddr);
+		kunmap_atomic(vaddr);
+		if (copy_to_user(buf, (kdump_buf_page + offset), csize))
+			return -EFAULT;
 	}
 
 	return csize;
 }
+
+static int __init kdump_buf_page_init(void)
+{
+	int ret = 0;
+
+	kdump_buf_page = kmalloc(PAGE_SIZE, GFP_KERNEL);
+	if (!kdump_buf_page) {
+		pr_warn("Kdump: Failed to allocate kdump buffer page\n");
+		ret = -ENOMEM;
+	}
+
+	return ret;
+}
+arch_initcall(kdump_buf_page_init);
diff --git a/kernel/arch/mips/mm/highmem.c b/kernel/arch/mips/mm/highmem.c
index 57e2f08..5fec7f4 100644
--- a/kernel/arch/mips/mm/highmem.c
+++ b/kernel/arch/mips/mm/highmem.c
@@ -8,6 +8,8 @@
 #include <asm/fixmap.h>
 #include <asm/tlbflush.h>
 
+static pte_t *kmap_pte;
+
 unsigned long highstart_pfn, highend_pfn;
 
 void kmap_flush_tlb(unsigned long addr)
@@ -15,3 +17,78 @@
 	flush_tlb_one(addr);
 }
 EXPORT_SYMBOL(kmap_flush_tlb);
+
+void *kmap_atomic_high_prot(struct page *page, pgprot_t prot)
+{
+	unsigned long vaddr;
+	int idx, type;
+
+	type = kmap_atomic_idx_push();
+	idx = type + KM_TYPE_NR*smp_processor_id();
+	vaddr = __fix_to_virt(FIX_KMAP_BEGIN + idx);
+#ifdef CONFIG_DEBUG_HIGHMEM
+	BUG_ON(!pte_none(*(kmap_pte - idx)));
+#endif
+	set_pte(kmap_pte-idx, mk_pte(page, prot));
+	local_flush_tlb_one((unsigned long)vaddr);
+
+	return (void*) vaddr;
+}
+EXPORT_SYMBOL(kmap_atomic_high_prot);
+
+void kunmap_atomic_high(void *kvaddr)
+{
+	unsigned long vaddr = (unsigned long) kvaddr & PAGE_MASK;
+	int type __maybe_unused;
+
+	if (vaddr < FIXADDR_START)
+		return;
+
+	type = kmap_atomic_idx();
+#ifdef CONFIG_DEBUG_HIGHMEM
+	{
+		int idx = type + KM_TYPE_NR * smp_processor_id();
+
+		BUG_ON(vaddr != __fix_to_virt(FIX_KMAP_BEGIN + idx));
+
+		/*
+		 * force other mappings to Oops if they'll try to access
+		 * this pte without first remap it
+		 */
+		pte_clear(&init_mm, vaddr, kmap_pte-idx);
+		local_flush_tlb_one(vaddr);
+	}
+#endif
+	kmap_atomic_idx_pop();
+}
+EXPORT_SYMBOL(kunmap_atomic_high);
+
+/*
+ * This is the same as kmap_atomic() but can map memory that doesn't
+ * have a struct page associated with it.
+ */
+void *kmap_atomic_pfn(unsigned long pfn)
+{
+	unsigned long vaddr;
+	int idx, type;
+
+	preempt_disable();
+	pagefault_disable();
+
+	type = kmap_atomic_idx_push();
+	idx = type + KM_TYPE_NR*smp_processor_id();
+	vaddr = __fix_to_virt(FIX_KMAP_BEGIN + idx);
+	set_pte(kmap_pte-idx, pfn_pte(pfn, PAGE_KERNEL));
+	flush_tlb_one(vaddr);
+
+	return (void*) vaddr;
+}
+
+void __init kmap_init(void)
+{
+	unsigned long kmap_vstart;
+
+	/* cache the first kmap pte */
+	kmap_vstart = __fix_to_virt(FIX_KMAP_BEGIN);
+	kmap_pte = virt_to_kpte(kmap_vstart);
+}
diff --git a/kernel/arch/mips/mm/init.c b/kernel/arch/mips/mm/init.c
index bc80893..07e84a7 100644
--- a/kernel/arch/mips/mm/init.c
+++ b/kernel/arch/mips/mm/init.c
@@ -36,6 +36,7 @@
 #include <asm/cachectl.h>
 #include <asm/cpu.h>
 #include <asm/dma.h>
+#include <asm/kmap_types.h>
 #include <asm/maar.h>
 #include <asm/mmu_context.h>
 #include <asm/sections.h>
@@ -401,6 +402,9 @@
 
 	pagetable_init();
 
+#ifdef CONFIG_HIGHMEM
+	kmap_init();
+#endif
 #ifdef CONFIG_ZONE_DMA
 	max_zone_pfns[ZONE_DMA] = MAX_DMA_PFN;
 #endif
diff --git a/kernel/arch/nds32/Kconfig.cpu b/kernel/arch/nds32/Kconfig.cpu
index c107599..f88a12f 100644
--- a/kernel/arch/nds32/Kconfig.cpu
+++ b/kernel/arch/nds32/Kconfig.cpu
@@ -157,7 +157,6 @@
 config HIGHMEM
 	bool "High Memory Support"
 	depends on MMU && !CPU_CACHE_ALIASING
-	select KMAP_LOCAL
 	help
 	  The address space of Andes processors is only 4 Gigabytes large
 	  and it has to accommodate user address space, kernel address
diff --git a/kernel/arch/nds32/include/asm/fixmap.h b/kernel/arch/nds32/include/asm/fixmap.h
index 2fa09a2..5a4bf11 100644
--- a/kernel/arch/nds32/include/asm/fixmap.h
+++ b/kernel/arch/nds32/include/asm/fixmap.h
@@ -6,7 +6,7 @@
 
 #ifdef CONFIG_HIGHMEM
 #include <linux/threads.h>
-#include <asm/kmap_size.h>
+#include <asm/kmap_types.h>
 #endif
 
 enum fixed_addresses {
@@ -14,7 +14,7 @@
 	FIX_KMAP_RESERVED,
 	FIX_KMAP_BEGIN,
 #ifdef CONFIG_HIGHMEM
-	FIX_KMAP_END = FIX_KMAP_BEGIN + (KM_MAX_IDX * NR_CPUS) - 1,
+	FIX_KMAP_END = FIX_KMAP_BEGIN + (KM_TYPE_NR * NR_CPUS),
 #endif
 	FIX_EARLYCON_MEM_BASE,
 	__end_of_fixed_addresses
diff --git a/kernel/arch/nds32/include/asm/highmem.h b/kernel/arch/nds32/include/asm/highmem.h
index 16159a8..fe986d0 100644
--- a/kernel/arch/nds32/include/asm/highmem.h
+++ b/kernel/arch/nds32/include/asm/highmem.h
@@ -5,6 +5,7 @@
 #define _ASM_HIGHMEM_H
 
 #include <asm/proc-fns.h>
+#include <asm/kmap_types.h>
 #include <asm/fixmap.h>
 
 /*
@@ -44,22 +45,11 @@
 extern void kmap_init(void);
 
 /*
- * FIXME: The below looks broken vs. a kmap_atomic() in task context which
- * is interupted and another kmap_atomic() happens in interrupt context.
- * But what do I know about nds32. -- tglx
+ * The following functions are already defined by <linux/highmem.h>
+ * when CONFIG_HIGHMEM is not set.
  */
-#define arch_kmap_local_post_map(vaddr, pteval)			\
-	do {							\
-		__nds32__tlbop_inv(vaddr);			\
-		__nds32__mtsr_dsb(vaddr, NDS32_SR_TLB_VPN);	\
-		__nds32__tlbop_rwr(pteval);			\
-		__nds32__isb();					\
-	} while (0)
-
-#define arch_kmap_local_pre_unmap(vaddr)			\
-	do {							\
-		__nds32__tlbop_inv(vaddr);			\
-		__nds32__isb();					\
-	} while (0)
+#ifdef CONFIG_HIGHMEM
+extern void *kmap_atomic_pfn(unsigned long pfn);
+#endif
 
 #endif
diff --git a/kernel/arch/nds32/mm/Makefile b/kernel/arch/nds32/mm/Makefile
index 14fb2e8..897ecaf 100644
--- a/kernel/arch/nds32/mm/Makefile
+++ b/kernel/arch/nds32/mm/Makefile
@@ -3,6 +3,7 @@
                                    mm-nds32.o cacheflush.o proc.o
 
 obj-$(CONFIG_ALIGNMENT_TRAP)	+= alignment.o
+obj-$(CONFIG_HIGHMEM)           += highmem.o
 
 ifdef CONFIG_FUNCTION_TRACER
 CFLAGS_REMOVE_proc.o     = $(CC_FLAGS_FTRACE)
diff --git a/kernel/arch/nds32/mm/highmem.c b/kernel/arch/nds32/mm/highmem.c
new file mode 100644
index 0000000..4284cd5
--- /dev/null
+++ b/kernel/arch/nds32/mm/highmem.c
@@ -0,0 +1,48 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2005-2017 Andes Technology Corporation
+
+#include <linux/export.h>
+#include <linux/highmem.h>
+#include <linux/sched.h>
+#include <linux/smp.h>
+#include <linux/interrupt.h>
+#include <linux/memblock.h>
+#include <asm/fixmap.h>
+#include <asm/tlbflush.h>
+
+void *kmap_atomic_high_prot(struct page *page, pgprot_t prot)
+{
+	unsigned int idx;
+	unsigned long vaddr, pte;
+	int type;
+	pte_t *ptep;
+
+	type = kmap_atomic_idx_push();
+
+	idx = type + KM_TYPE_NR * smp_processor_id();
+	vaddr = __fix_to_virt(FIX_KMAP_BEGIN + idx);
+	pte = (page_to_pfn(page) << PAGE_SHIFT) | prot;
+	ptep = pte_offset_kernel(pmd_off_k(vaddr), vaddr);
+	set_pte(ptep, pte);
+
+	__nds32__tlbop_inv(vaddr);
+	__nds32__mtsr_dsb(vaddr, NDS32_SR_TLB_VPN);
+	__nds32__tlbop_rwr(pte);
+	__nds32__isb();
+	return (void *)vaddr;
+}
+EXPORT_SYMBOL(kmap_atomic_high_prot);
+
+void kunmap_atomic_high(void *kvaddr)
+{
+	if (kvaddr >= (void *)FIXADDR_START) {
+		unsigned long vaddr = (unsigned long)kvaddr;
+		pte_t *ptep;
+		kmap_atomic_idx_pop();
+		__nds32__tlbop_inv(vaddr);
+		__nds32__isb();
+		ptep = pte_offset_kernel(pmd_off_k(vaddr), vaddr);
+		set_pte(ptep, 0);
+	}
+}
+EXPORT_SYMBOL(kunmap_atomic_high);
diff --git a/kernel/arch/openrisc/mm/init.c b/kernel/arch/openrisc/mm/init.c
index f3fa02b..5e88c35 100644
--- a/kernel/arch/openrisc/mm/init.c
+++ b/kernel/arch/openrisc/mm/init.c
@@ -33,6 +33,7 @@
 #include <asm/io.h>
 #include <asm/tlb.h>
 #include <asm/mmu_context.h>
+#include <asm/kmap_types.h>
 #include <asm/fixmap.h>
 #include <asm/tlbflush.h>
 #include <asm/sections.h>
diff --git a/kernel/arch/openrisc/mm/ioremap.c b/kernel/arch/openrisc/mm/ioremap.c
index 5aed97a..a978590 100644
--- a/kernel/arch/openrisc/mm/ioremap.c
+++ b/kernel/arch/openrisc/mm/ioremap.c
@@ -15,6 +15,7 @@
 #include <linux/io.h>
 #include <linux/pgtable.h>
 #include <asm/pgalloc.h>
+#include <asm/kmap_types.h>
 #include <asm/fixmap.h>
 #include <asm/bug.h>
 #include <linux/sched.h>
diff --git a/kernel/arch/parisc/include/asm/hardirq.h b/kernel/arch/parisc/include/asm/hardirq.h
index fad29aa..7f70395 100644
--- a/kernel/arch/parisc/include/asm/hardirq.h
+++ b/kernel/arch/parisc/include/asm/hardirq.h
@@ -32,6 +32,7 @@
 DECLARE_PER_CPU_SHARED_ALIGNED(irq_cpustat_t, irq_stat);
 
 #define __ARCH_IRQ_STAT
+#define __IRQ_STAT(cpu, member) (irq_stat[cpu].member)
 #define inc_irq_stat(member)	this_cpu_inc(irq_stat.member)
 #define __inc_irq_stat(member)	__this_cpu_inc(irq_stat.member)
 #define ack_bad_irq(irq) WARN(1, "unexpected IRQ trap at vector %02x\n", irq)
diff --git a/kernel/arch/parisc/include/asm/kmap_types.h b/kernel/arch/parisc/include/asm/kmap_types.h
new file mode 100644
index 0000000..3e70b5c
--- /dev/null
+++ b/kernel/arch/parisc/include/asm/kmap_types.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _ASM_KMAP_TYPES_H
+#define _ASM_KMAP_TYPES_H
+
+#ifdef CONFIG_DEBUG_HIGHMEM
+#define  __WITH_KM_FENCE
+#endif
+
+#include <asm-generic/kmap_types.h>
+
+#undef __WITH_KM_FENCE
+
+#endif
diff --git a/kernel/arch/powerpc/Kconfig b/kernel/arch/powerpc/Kconfig
index 065ed52..78dd6be 100644
--- a/kernel/arch/powerpc/Kconfig
+++ b/kernel/arch/powerpc/Kconfig
@@ -146,7 +146,6 @@
 	select ARCH_MIGHT_HAVE_PC_SERIO
 	select ARCH_OPTIONAL_KERNEL_RWX		if ARCH_HAS_STRICT_KERNEL_RWX
 	select ARCH_SUPPORTS_ATOMIC_RMW
-	select ARCH_SUPPORTS_RT if HAVE_POSIX_CPU_TIMERS_TASK_WORK
 	select ARCH_USE_BUILTIN_BSWAP
 	select ARCH_USE_CMPXCHG_LOCKREF		if PPC64
 	select ARCH_USE_QUEUED_RWLOCKS		if PPC_QUEUED_SPINLOCKS
@@ -231,7 +230,6 @@
 	select HAVE_HARDLOCKUP_DETECTOR_PERF	if PERF_EVENTS && HAVE_PERF_EVENTS_NMI && !HAVE_HARDLOCKUP_DETECTOR_ARCH
 	select HAVE_PERF_REGS
 	select HAVE_PERF_USER_STACK_DUMP
-	select HAVE_PREEMPT_LAZY
 	select MMU_GATHER_RCU_TABLE_FREE
 	select MMU_GATHER_PAGE_SIZE
 	select HAVE_REGS_AND_STACK_ACCESS_API
@@ -239,7 +237,6 @@
 	select HAVE_SYSCALL_TRACEPOINTS
 	select HAVE_VIRT_CPU_ACCOUNTING
 	select HAVE_IRQ_TIME_ACCOUNTING
-	select HAVE_POSIX_CPU_TIMERS_TASK_WORK  if !KVM
 	select HAVE_RSEQ
 	select IOMMU_HELPER			if PPC64
 	select IRQ_DOMAIN
@@ -413,7 +410,6 @@
 config HIGHMEM
 	bool "High memory support"
 	depends on PPC32
-	select KMAP_LOCAL
 
 source "kernel/Kconfig.hz"
 
diff --git a/kernel/arch/powerpc/include/asm/cmpxchg.h b/kernel/arch/powerpc/include/asm/cmpxchg.h
index 7371f7e..cf091c4 100644
--- a/kernel/arch/powerpc/include/asm/cmpxchg.h
+++ b/kernel/arch/powerpc/include/asm/cmpxchg.h
@@ -5,7 +5,7 @@
 #ifdef __KERNEL__
 #include <linux/compiler.h>
 #include <asm/synch.h>
-#include <linux/bits.h>
+#include <linux/bug.h>
 
 #ifdef __BIG_ENDIAN
 #define BITOFF_CAL(size, off)	((sizeof(u32) - size - off) * BITS_PER_BYTE)
diff --git a/kernel/arch/powerpc/include/asm/fixmap.h b/kernel/arch/powerpc/include/asm/fixmap.h
index a832aea..897cc68 100644
--- a/kernel/arch/powerpc/include/asm/fixmap.h
+++ b/kernel/arch/powerpc/include/asm/fixmap.h
@@ -20,7 +20,7 @@
 #include <asm/page.h>
 #ifdef CONFIG_HIGHMEM
 #include <linux/threads.h>
-#include <asm/kmap_size.h>
+#include <asm/kmap_types.h>
 #endif
 
 #ifdef CONFIG_PPC64
@@ -61,7 +61,7 @@
 	FIX_EARLY_DEBUG_BASE = FIX_EARLY_DEBUG_TOP+(ALIGN(SZ_128K, PAGE_SIZE)/PAGE_SIZE)-1,
 #ifdef CONFIG_HIGHMEM
 	FIX_KMAP_BEGIN,	/* reserved pte's for temporary kernel mappings */
-	FIX_KMAP_END = FIX_KMAP_BEGIN + (KM_MAX_IDX * NR_CPUS) - 1,
+	FIX_KMAP_END = FIX_KMAP_BEGIN+(KM_TYPE_NR*NR_CPUS)-1,
 #endif
 #ifdef CONFIG_PPC_8xx
 	/* For IMMR we need an aligned 512K area */
diff --git a/kernel/arch/powerpc/include/asm/highmem.h b/kernel/arch/powerpc/include/asm/highmem.h
index 80a5ae7..104026f 100644
--- a/kernel/arch/powerpc/include/asm/highmem.h
+++ b/kernel/arch/powerpc/include/asm/highmem.h
@@ -24,10 +24,12 @@
 #ifdef __KERNEL__
 
 #include <linux/interrupt.h>
+#include <asm/kmap_types.h>
 #include <asm/cacheflush.h>
 #include <asm/page.h>
 #include <asm/fixmap.h>
 
+extern pte_t *kmap_pte;
 extern pte_t *pkmap_page_table;
 
 /*
@@ -57,11 +59,6 @@
 #define PKMAP_ADDR(nr)  (PKMAP_BASE + ((nr) << PAGE_SHIFT))
 
 #define flush_cache_kmaps()	flush_cache_all()
-
-#define arch_kmap_local_post_map(vaddr, pteval)	\
-	local_flush_tlb_page(NULL, vaddr)
-#define arch_kmap_local_post_unmap(vaddr)	\
-	local_flush_tlb_page(NULL, vaddr)
 
 #endif /* __KERNEL__ */
 
diff --git a/kernel/arch/powerpc/include/asm/kmap_types.h b/kernel/arch/powerpc/include/asm/kmap_types.h
new file mode 100644
index 0000000..c8fa182
--- /dev/null
+++ b/kernel/arch/powerpc/include/asm/kmap_types.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#ifndef _ASM_POWERPC_KMAP_TYPES_H
+#define _ASM_POWERPC_KMAP_TYPES_H
+
+#ifdef __KERNEL__
+
+/*
+ */
+
+#define KM_TYPE_NR 16
+
+#endif	/* __KERNEL__ */
+#endif	/* _ASM_POWERPC_KMAP_TYPES_H */
diff --git a/kernel/arch/powerpc/include/asm/simple_spinlock_types.h b/kernel/arch/powerpc/include/asm/simple_spinlock_types.h
index d45561e..0f3cdd8 100644
--- a/kernel/arch/powerpc/include/asm/simple_spinlock_types.h
+++ b/kernel/arch/powerpc/include/asm/simple_spinlock_types.h
@@ -2,7 +2,7 @@
 #ifndef _ASM_POWERPC_SIMPLE_SPINLOCK_TYPES_H
 #define _ASM_POWERPC_SIMPLE_SPINLOCK_TYPES_H
 
-#if !defined(__LINUX_SPINLOCK_TYPES_H) && !defined(__LINUX_RT_MUTEX_H)
+#ifndef __LINUX_SPINLOCK_TYPES_H
 # error "please don't include this file directly"
 #endif
 
diff --git a/kernel/arch/powerpc/include/asm/spinlock_types.h b/kernel/arch/powerpc/include/asm/spinlock_types.h
index cc6922a..c5d742f 100644
--- a/kernel/arch/powerpc/include/asm/spinlock_types.h
+++ b/kernel/arch/powerpc/include/asm/spinlock_types.h
@@ -2,6 +2,10 @@
 #ifndef _ASM_POWERPC_SPINLOCK_TYPES_H
 #define _ASM_POWERPC_SPINLOCK_TYPES_H
 
+#ifndef __LINUX_SPINLOCK_TYPES_H
+# error "please don't include this file directly"
+#endif
+
 #ifdef CONFIG_PPC_QUEUED_SPINLOCKS
 #include <asm-generic/qspinlock_types.h>
 #include <asm-generic/qrwlock_types.h>
diff --git a/kernel/arch/powerpc/include/asm/stackprotector.h b/kernel/arch/powerpc/include/asm/stackprotector.h
index b1653c1..1c8460e 100644
--- a/kernel/arch/powerpc/include/asm/stackprotector.h
+++ b/kernel/arch/powerpc/include/asm/stackprotector.h
@@ -24,11 +24,7 @@
 	unsigned long canary;
 
 	/* Try to get a semi random initial value. */
-#ifdef CONFIG_PREEMPT_RT
-	canary = (unsigned long)&canary;
-#else
 	canary = get_random_canary();
-#endif
 	canary ^= mftb();
 	canary ^= LINUX_VERSION_CODE;
 	canary &= CANARY_MASK;
diff --git a/kernel/arch/powerpc/include/asm/thread_info.h b/kernel/arch/powerpc/include/asm/thread_info.h
index 23bfe23..ff31d2f 100644
--- a/kernel/arch/powerpc/include/asm/thread_info.h
+++ b/kernel/arch/powerpc/include/asm/thread_info.h
@@ -54,8 +54,6 @@
 struct thread_info {
 	int		preempt_count;		/* 0 => preemptable,
 						   <0 => BUG */
-	int             preempt_lazy_count;	/* 0 => preemptable,
-						   <0 => BUG */
 	unsigned long	local_flags;		/* private flags for thread */
 #ifdef CONFIG_LIVEPATCH
 	unsigned long *livepatch_sp;
@@ -106,12 +104,11 @@
 #define TIF_SINGLESTEP		8	/* singlestepping active */
 #define TIF_NOHZ		9	/* in adaptive nohz mode */
 #define TIF_SECCOMP		10	/* secure computing */
-
-#define TIF_NEED_RESCHED_LAZY	11	/* lazy rescheduling necessary */
-#define TIF_SYSCALL_TRACEPOINT	12	/* syscall tracepoint instrumentation */
-
+#define TIF_RESTOREALL		11	/* Restore all regs (implies NOERROR) */
+#define TIF_NOERROR		12	/* Force successful syscall return */
 #define TIF_NOTIFY_RESUME	13	/* callback before returning to user */
 #define TIF_UPROBE		14	/* breakpointed or single-stepping */
+#define TIF_SYSCALL_TRACEPOINT	15	/* syscall tracepoint instrumentation */
 #define TIF_EMULATE_STACK_STORE	16	/* Is an instruction emulation
 						for stack store? */
 #define TIF_MEMDIE		17	/* is terminating due to OOM killer */
@@ -120,9 +117,6 @@
 #endif
 #define TIF_POLLING_NRFLAG	19	/* true if poll_idle() is polling TIF_NEED_RESCHED */
 #define TIF_32BIT		20	/* 32 bit binary */
-#define TIF_RESTOREALL		21	/* Restore all regs (implies NOERROR) */
-#define TIF_NOERROR		22	/* Force successful syscall return */
-
 
 /* as above, but as bit values */
 #define _TIF_SYSCALL_TRACE	(1<<TIF_SYSCALL_TRACE)
@@ -143,7 +137,6 @@
 #define _TIF_SYSCALL_TRACEPOINT	(1<<TIF_SYSCALL_TRACEPOINT)
 #define _TIF_EMULATE_STACK_STORE	(1<<TIF_EMULATE_STACK_STORE)
 #define _TIF_NOHZ		(1<<TIF_NOHZ)
-#define _TIF_NEED_RESCHED_LAZY	(1<<TIF_NEED_RESCHED_LAZY)
 #define _TIF_SYSCALL_EMU	(1<<TIF_SYSCALL_EMU)
 #define _TIF_SYSCALL_DOTRACE	(_TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT | \
 				 _TIF_SECCOMP | _TIF_SYSCALL_TRACEPOINT | \
@@ -152,9 +145,8 @@
 #define _TIF_USER_WORK_MASK	(_TIF_SIGPENDING | _TIF_NEED_RESCHED | \
 				 _TIF_NOTIFY_RESUME | _TIF_UPROBE | \
 				 _TIF_RESTORE_TM | _TIF_PATCH_PENDING | \
-				 _TIF_NEED_RESCHED_LAZY | _TIF_NOTIFY_SIGNAL)
+				 _TIF_NOTIFY_SIGNAL)
 #define _TIF_PERSYSCALL_MASK	(_TIF_RESTOREALL|_TIF_NOERROR)
-#define _TIF_NEED_RESCHED_MASK	(_TIF_NEED_RESCHED | _TIF_NEED_RESCHED_LAZY)
 
 /* Bits in local_flags */
 /* Don't move TLF_NAPPING without adjusting the code in entry_32.S */
diff --git a/kernel/arch/powerpc/kernel/asm-offsets.c b/kernel/arch/powerpc/kernel/asm-offsets.c
index 5973791..5c12525 100644
--- a/kernel/arch/powerpc/kernel/asm-offsets.c
+++ b/kernel/arch/powerpc/kernel/asm-offsets.c
@@ -189,7 +189,6 @@
 	OFFSET(TI_FLAGS, thread_info, flags);
 	OFFSET(TI_LOCAL_FLAGS, thread_info, local_flags);
 	OFFSET(TI_PREEMPT, thread_info, preempt_count);
-	OFFSET(TI_PREEMPT_LAZY, thread_info, preempt_lazy_count);
 
 #ifdef CONFIG_PPC64
 	OFFSET(DCACHEL1BLOCKSIZE, ppc64_caches, l1d.block_size);
diff --git a/kernel/arch/powerpc/kernel/entry_32.S b/kernel/arch/powerpc/kernel/entry_32.S
index fc9517a..459f5d0 100644
--- a/kernel/arch/powerpc/kernel/entry_32.S
+++ b/kernel/arch/powerpc/kernel/entry_32.S
@@ -414,9 +414,7 @@
 	mtmsr	r10
 	lwz	r9,TI_FLAGS(r2)
 	li	r8,-MAX_ERRNO
-	lis	r0,(_TIF_SYSCALL_DOTRACE|_TIF_SINGLESTEP|_TIF_USER_WORK_MASK|_TIF_PERSYSCALL_MASK)@h
-	ori	r0,r0, (_TIF_SYSCALL_DOTRACE|_TIF_SINGLESTEP|_TIF_USER_WORK_MASK|_TIF_PERSYSCALL_MASK)@l
-	and.	r0,r9,r0
+	andi.	r0,r9,(_TIF_SYSCALL_DOTRACE|_TIF_SINGLESTEP|_TIF_USER_WORK_MASK|_TIF_PERSYSCALL_MASK)
 	bne-	syscall_exit_work
 	cmplw	0,r3,r8
 	blt+	syscall_exit_cont
@@ -532,13 +530,13 @@
 	b	syscall_dotrace_cont
 
 syscall_exit_work:
-	andis.	r0,r9,_TIF_RESTOREALL@h
+	andi.	r0,r9,_TIF_RESTOREALL
 	beq+	0f
 	REST_NVGPRS(r1)
 	b	2f
 0:	cmplw	0,r3,r8
 	blt+	1f
-	andis.	r0,r9,_TIF_NOERROR@h
+	andi.	r0,r9,_TIF_NOERROR
 	bne-	1f
 	lwz	r11,_CCR(r1)			/* Load CR */
 	neg	r3,r3
@@ -547,12 +545,12 @@
 
 1:	stw	r6,RESULT(r1)	/* Save result */
 	stw	r3,GPR3(r1)	/* Update return value */
-2:	andis.	r0,r9,(_TIF_PERSYSCALL_MASK)@h
+2:	andi.	r0,r9,(_TIF_PERSYSCALL_MASK)
 	beq	4f
 
 	/* Clear per-syscall TIF flags if any are set.  */
 
-	lis	r11,(_TIF_PERSYSCALL_MASK)@h
+	li	r11,_TIF_PERSYSCALL_MASK
 	addi	r12,r2,TI_FLAGS
 3:	lwarx	r8,0,r12
 	andc	r8,r8,r11
@@ -929,14 +927,7 @@
 	cmpwi	0,r0,0		/* if non-zero, just restore regs and return */
 	bne	restore_kuap
 	andi.	r8,r8,_TIF_NEED_RESCHED
-	bne+	1f
-	lwz	r0,TI_PREEMPT_LAZY(r2)
-	cmpwi	0,r0,0          /* if non-zero, just restore regs and return */
-	bne	restore_kuap
-	lwz	r0,TI_FLAGS(r2)
-	andi.	r0,r0,_TIF_NEED_RESCHED_LAZY
 	beq+	restore_kuap
-1:
 	lwz	r3,_MSR(r1)
 	andi.	r0,r3,MSR_EE	/* interrupts off? */
 	beq	restore_kuap	/* don't schedule if so */
@@ -1257,7 +1248,7 @@
 #endif /* !(CONFIG_4xx || CONFIG_BOOKE) */
 
 do_work:			/* r10 contains MSR_KERNEL here */
-	andi.	r0,r9,_TIF_NEED_RESCHED_MASK
+	andi.	r0,r9,_TIF_NEED_RESCHED
 	beq	do_user_signal
 
 do_resched:			/* r10 contains MSR_KERNEL here */
@@ -1276,7 +1267,7 @@
 	LOAD_REG_IMMEDIATE(r10,MSR_KERNEL)
 	mtmsr	r10		/* disable interrupts */
 	lwz	r9,TI_FLAGS(r2)
-	andi.	r0,r9,_TIF_NEED_RESCHED_MASK
+	andi.	r0,r9,_TIF_NEED_RESCHED
 	bne-	do_resched
 	andi.	r0,r9,_TIF_USER_WORK_MASK
 	beq	restore_user
diff --git a/kernel/arch/powerpc/kernel/exceptions-64e.S b/kernel/arch/powerpc/kernel/exceptions-64e.S
index 715ff29..f579ce4 100644
--- a/kernel/arch/powerpc/kernel/exceptions-64e.S
+++ b/kernel/arch/powerpc/kernel/exceptions-64e.S
@@ -1080,7 +1080,7 @@
 	li	r10, -1
 	mtspr	SPRN_DBSR,r10
 	b	restore
-1:	andi.	r0,r4,_TIF_NEED_RESCHED_MASK
+1:	andi.	r0,r4,_TIF_NEED_RESCHED
 	beq	2f
 	bl	restore_interrupts
 	SCHEDULE_USER
@@ -1132,20 +1132,12 @@
 	bne-	0b
 1:
 
-#ifdef CONFIG_PREEMPTION
+#ifdef CONFIG_PREEMPT
 	/* Check if we need to preempt */
-	lwz	r8,TI_PREEMPT(r9)
-	cmpwi	0,r8,0		/* if non-zero, just restore regs and return */
-	bne	restore
 	andi.	r0,r4,_TIF_NEED_RESCHED
-	bne+	check_count
-
-	andi.	r0,r4,_TIF_NEED_RESCHED_LAZY
 	beq+	restore
-	lwz	r8,TI_PREEMPT_LAZY(r9)
-
 	/* Check that preempt_count() == 0 and interrupts are enabled */
-check_count:
+	lwz	r8,TI_PREEMPT(r9)
 	cmpwi	cr0,r8,0
 	bne	restore
 	ld	r0,SOFTE(r1)
@@ -1166,7 +1158,7 @@
 	 * interrupted after loading SRR0/1.
 	 */
 	wrteei	0
-#endif /* CONFIG_PREEMPTION */
+#endif /* CONFIG_PREEMPT */
 
 restore:
 	/*
diff --git a/kernel/arch/powerpc/kernel/irq.c b/kernel/arch/powerpc/kernel/irq.c
index 5ad4f27..e8a5484 100644
--- a/kernel/arch/powerpc/kernel/irq.c
+++ b/kernel/arch/powerpc/kernel/irq.c
@@ -753,12 +753,10 @@
 void *softirq_ctx[NR_CPUS] __read_mostly;
 void *hardirq_ctx[NR_CPUS] __read_mostly;
 
-#ifndef CONFIG_PREEMPT_RT
 void do_softirq_own_stack(void)
 {
 	call_do_softirq(softirq_ctx[smp_processor_id()]);
 }
-#endif
 
 irq_hw_number_t virq_to_hw(unsigned int virq)
 {
diff --git a/kernel/arch/powerpc/kernel/misc_32.S b/kernel/arch/powerpc/kernel/misc_32.S
index 08ee95a..717e658 100644
--- a/kernel/arch/powerpc/kernel/misc_32.S
+++ b/kernel/arch/powerpc/kernel/misc_32.S
@@ -31,7 +31,6 @@
  * We store the saved ksp_limit in the unused part
  * of the STACK_FRAME_OVERHEAD
  */
-#ifndef CONFIG_PREEMPT_RT
 _GLOBAL(call_do_softirq)
 	mflr	r0
 	stw	r0,4(r1)
@@ -47,7 +46,6 @@
 	stw	r10,THREAD+KSP_LIMIT(r2)
 	mtlr	r0
 	blr
-#endif
 
 /*
  * void call_do_irq(struct pt_regs *regs, void *sp);
diff --git a/kernel/arch/powerpc/kernel/misc_64.S b/kernel/arch/powerpc/kernel/misc_64.S
index a6b33f7..0704658 100644
--- a/kernel/arch/powerpc/kernel/misc_64.S
+++ b/kernel/arch/powerpc/kernel/misc_64.S
@@ -27,7 +27,6 @@
 
 	.text
 
-#ifndef CONFIG_PREEMPT_RT
 _GLOBAL(call_do_softirq)
 	mflr	r0
 	std	r0,16(r1)
@@ -38,7 +37,6 @@
 	ld	r0,16(r1)
 	mtlr	r0
 	blr
-#endif
 
 _GLOBAL(call_do_irq)
 	mflr	r0
diff --git a/kernel/arch/powerpc/kernel/nvram_64.c b/kernel/arch/powerpc/kernel/nvram_64.c
index 1ef55f4..532f226 100644
--- a/kernel/arch/powerpc/kernel/nvram_64.c
+++ b/kernel/arch/powerpc/kernel/nvram_64.c
@@ -73,8 +73,7 @@
 };
 
 static void oops_to_nvram(struct kmsg_dumper *dumper,
-			  enum kmsg_dump_reason reason,
-			  struct kmsg_dumper_iter *iter);
+			  enum kmsg_dump_reason reason);
 
 static struct kmsg_dumper nvram_kmsg_dumper = {
 	.dump = oops_to_nvram
@@ -644,8 +643,7 @@
  * partition.  If that's too much, go back and capture uncompressed text.
  */
 static void oops_to_nvram(struct kmsg_dumper *dumper,
-			  enum kmsg_dump_reason reason,
-			  struct kmsg_dumper_iter *iter)
+			  enum kmsg_dump_reason reason)
 {
 	struct oops_log_info *oops_hdr = (struct oops_log_info *)oops_buf;
 	static unsigned int oops_count = 0;
@@ -683,13 +681,13 @@
 		return;
 
 	if (big_oops_buf) {
-		kmsg_dump_get_buffer(iter, false,
+		kmsg_dump_get_buffer(dumper, false,
 				     big_oops_buf, big_oops_buf_sz, &text_len);
 		rc = zip_oops(text_len);
 	}
 	if (rc != 0) {
-		kmsg_dump_rewind(iter);
-		kmsg_dump_get_buffer(iter, false,
+		kmsg_dump_rewind(dumper);
+		kmsg_dump_get_buffer(dumper, false,
 				     oops_data, oops_data_sz, &text_len);
 		err_type = ERR_TYPE_KERNEL_PANIC;
 		oops_hdr->version = cpu_to_be16(OOPS_HDR_VERSION);
diff --git a/kernel/arch/powerpc/kernel/syscall_64.c b/kernel/arch/powerpc/kernel/syscall_64.c
index ae3212d..310bcd7 100644
--- a/kernel/arch/powerpc/kernel/syscall_64.c
+++ b/kernel/arch/powerpc/kernel/syscall_64.c
@@ -193,7 +193,7 @@
 	ti_flags = READ_ONCE(*ti_flagsp);
 	while (unlikely(ti_flags & (_TIF_USER_WORK_MASK & ~_TIF_RESTORE_TM))) {
 		local_irq_enable();
-		if (ti_flags & _TIF_NEED_RESCHED_MASK) {
+		if (ti_flags & _TIF_NEED_RESCHED) {
 			schedule();
 		} else {
 			/*
@@ -277,7 +277,7 @@
 	ti_flags = READ_ONCE(*ti_flagsp);
 	while (unlikely(ti_flags & (_TIF_USER_WORK_MASK & ~_TIF_RESTORE_TM))) {
 		local_irq_enable(); /* returning to user: may enable */
-		if (ti_flags & _TIF_NEED_RESCHED_MASK) {
+		if (ti_flags & _TIF_NEED_RESCHED) {
 			schedule();
 		} else {
 			if (ti_flags & _TIF_SIGPENDING)
@@ -361,14 +361,10 @@
 		/* Returning to a kernel context with local irqs enabled. */
 		WARN_ON_ONCE(!(regs->msr & MSR_EE));
 again:
-		if (IS_ENABLED(CONFIG_PREEMPTION)) {
+		if (IS_ENABLED(CONFIG_PREEMPT)) {
 			/* Return to preemptible kernel context */
 			if (unlikely(*ti_flagsp & _TIF_NEED_RESCHED)) {
 				if (preempt_count() == 0)
-					preempt_schedule_irq();
-			} else if (unlikely(*ti_flagsp & _TIF_NEED_RESCHED_LAZY)) {
-				if ((preempt_count() == 0) &&
-				    (current_thread_info()->preempt_lazy_count == 0))
 					preempt_schedule_irq();
 			}
 		}
diff --git a/kernel/arch/powerpc/kernel/time.c b/kernel/arch/powerpc/kernel/time.c
index 7e0a497..1d20f0f 100644
--- a/kernel/arch/powerpc/kernel/time.c
+++ b/kernel/arch/powerpc/kernel/time.c
@@ -312,11 +312,12 @@
 	return stime_scaled;
 }
 
-static unsigned long vtime_delta(struct cpu_accounting_data *acct,
+static unsigned long vtime_delta(struct task_struct *tsk,
 				 unsigned long *stime_scaled,
 				 unsigned long *steal_time)
 {
 	unsigned long now, stime;
+	struct cpu_accounting_data *acct = get_accounting(tsk);
 
 	WARN_ON_ONCE(!irqs_disabled());
 
@@ -331,30 +332,29 @@
 	return stime;
 }
 
-static void vtime_delta_kernel(struct cpu_accounting_data *acct,
-			       unsigned long *stime, unsigned long *stime_scaled)
-{
-	unsigned long steal_time;
-
-	*stime = vtime_delta(acct, stime_scaled, &steal_time);
-	*stime -= min(*stime, steal_time);
-	acct->steal_time += steal_time;
-}
-
 void vtime_account_kernel(struct task_struct *tsk)
 {
+	unsigned long stime, stime_scaled, steal_time;
 	struct cpu_accounting_data *acct = get_accounting(tsk);
-	unsigned long stime, stime_scaled;
 
-	vtime_delta_kernel(acct, &stime, &stime_scaled);
+	stime = vtime_delta(tsk, &stime_scaled, &steal_time);
 
-	if (tsk->flags & PF_VCPU) {
+	stime -= min(stime, steal_time);
+	acct->steal_time += steal_time;
+
+	if ((tsk->flags & PF_VCPU) && !irq_count()) {
 		acct->gtime += stime;
 #ifdef CONFIG_ARCH_HAS_SCALED_CPUTIME
 		acct->utime_scaled += stime_scaled;
 #endif
 	} else {
-		acct->stime += stime;
+		if (hardirq_count())
+			acct->hardirq_time += stime;
+		else if (in_serving_softirq())
+			acct->softirq_time += stime;
+		else
+			acct->stime += stime;
+
 #ifdef CONFIG_ARCH_HAS_SCALED_CPUTIME
 		acct->stime_scaled += stime_scaled;
 #endif
@@ -367,32 +367,8 @@
 	unsigned long stime, stime_scaled, steal_time;
 	struct cpu_accounting_data *acct = get_accounting(tsk);
 
-	stime = vtime_delta(acct, &stime_scaled, &steal_time);
+	stime = vtime_delta(tsk, &stime_scaled, &steal_time);
 	acct->idle_time += stime + steal_time;
-}
-
-static void vtime_account_irq_field(struct cpu_accounting_data *acct,
-				    unsigned long *field)
-{
-	unsigned long stime, stime_scaled;
-
-	vtime_delta_kernel(acct, &stime, &stime_scaled);
-	*field += stime;
-#ifdef CONFIG_ARCH_HAS_SCALED_CPUTIME
-	acct->stime_scaled += stime_scaled;
-#endif
-}
-
-void vtime_account_softirq(struct task_struct *tsk)
-{
-	struct cpu_accounting_data *acct = get_accounting(tsk);
-	vtime_account_irq_field(acct, &acct->softirq_time);
-}
-
-void vtime_account_hardirq(struct task_struct *tsk)
-{
-	struct cpu_accounting_data *acct = get_accounting(tsk);
-	vtime_account_irq_field(acct, &acct->hardirq_time);
 }
 
 static void vtime_flush_scaled(struct task_struct *tsk,
diff --git a/kernel/arch/powerpc/kernel/traps.c b/kernel/arch/powerpc/kernel/traps.c
index 8eaa8c2..069d451 100644
--- a/kernel/arch/powerpc/kernel/traps.c
+++ b/kernel/arch/powerpc/kernel/traps.c
@@ -170,6 +170,7 @@
 
 extern void panic_flush_kmsg_end(void)
 {
+	printk_safe_flush_on_panic();
 	kmsg_dump(KMSG_DUMP_PANIC);
 	bust_spinlocks(0);
 	debug_locks_off();
@@ -259,17 +260,12 @@
 
 static int __die(const char *str, struct pt_regs *regs, long err)
 {
-	const char *pr = "";
-
 	printk("Oops: %s, sig: %ld [#%d]\n", str, err, ++die_counter);
-
-	if (IS_ENABLED(CONFIG_PREEMPTION))
-		pr = IS_ENABLED(CONFIG_PREEMPT_RT) ? " PREEMPT_RT" : " PREEMPT";
 
 	printk("%s PAGE_SIZE=%luK%s%s%s%s%s%s %s\n",
 	       IS_ENABLED(CONFIG_CPU_LITTLE_ENDIAN) ? "LE" : "BE",
 	       PAGE_SIZE / 1024, get_mmu_str(),
-	       pr,
+	       IS_ENABLED(CONFIG_PREEMPT) ? " PREEMPT" : "",
 	       IS_ENABLED(CONFIG_SMP) ? " SMP" : "",
 	       IS_ENABLED(CONFIG_SMP) ? (" NR_CPUS=" __stringify(NR_CPUS)) : "",
 	       debug_pagealloc_enabled() ? " DEBUG_PAGEALLOC" : "",
diff --git a/kernel/arch/powerpc/kernel/watchdog.c b/kernel/arch/powerpc/kernel/watchdog.c
index db40e20..75b2a6c 100644
--- a/kernel/arch/powerpc/kernel/watchdog.c
+++ b/kernel/arch/powerpc/kernel/watchdog.c
@@ -185,6 +185,11 @@
 
 	wd_smp_unlock(&flags);
 
+	printk_safe_flush();
+	/*
+	 * printk_safe_flush() seems to require another print
+	 * before anything actually goes out to console.
+	 */
 	if (sysctl_hardlockup_all_cpu_backtrace)
 		trigger_allbutself_cpu_backtrace();
 
diff --git a/kernel/arch/powerpc/kexec/crash.c b/kernel/arch/powerpc/kexec/crash.c
index d488311..c9a8898 100644
--- a/kernel/arch/powerpc/kexec/crash.c
+++ b/kernel/arch/powerpc/kexec/crash.c
@@ -311,6 +311,9 @@
 	unsigned int i;
 	int (*old_handler)(struct pt_regs *regs);
 
+	/* Avoid hardlocking with irresponsive CPU holding logbuf_lock */
+	printk_nmi_enter();
+
 	/*
 	 * This function is only called after the system
 	 * has panicked or is otherwise in a critical state.
diff --git a/kernel/arch/powerpc/kvm/Kconfig b/kernel/arch/powerpc/kvm/Kconfig
index efb5bfe..549591d 100644
--- a/kernel/arch/powerpc/kvm/Kconfig
+++ b/kernel/arch/powerpc/kvm/Kconfig
@@ -178,7 +178,6 @@
 config KVM_MPIC
 	bool "KVM in-kernel MPIC emulation"
 	depends on KVM && E500
-	depends on !PREEMPT_RT
 	select HAVE_KVM_IRQCHIP
 	select HAVE_KVM_IRQFD
 	select HAVE_KVM_IRQ_ROUTING
diff --git a/kernel/arch/powerpc/mm/Makefile b/kernel/arch/powerpc/mm/Makefile
index 3b4e9e4..55b4a8b 100644
--- a/kernel/arch/powerpc/mm/Makefile
+++ b/kernel/arch/powerpc/mm/Makefile
@@ -16,6 +16,7 @@
 obj-$(CONFIG_PPC_MM_SLICES)	+= slice.o
 obj-$(CONFIG_HUGETLB_PAGE)	+= hugetlbpage.o
 obj-$(CONFIG_NOT_COHERENT_CACHE) += dma-noncoherent.o
+obj-$(CONFIG_HIGHMEM)		+= highmem.o
 obj-$(CONFIG_PPC_COPRO_BASE)	+= copro_fault.o
 obj-$(CONFIG_PPC_PTDUMP)	+= ptdump/
 obj-$(CONFIG_KASAN)		+= kasan/
diff --git a/kernel/arch/powerpc/mm/highmem.c b/kernel/arch/powerpc/mm/highmem.c
new file mode 100644
index 0000000..624b443
--- /dev/null
+++ b/kernel/arch/powerpc/mm/highmem.c
@@ -0,0 +1,67 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * highmem.c: virtual kernel memory mappings for high memory
+ *
+ * PowerPC version, stolen from the i386 version.
+ *
+ * Used in CONFIG_HIGHMEM systems for memory pages which
+ * are not addressable by direct kernel virtual addresses.
+ *
+ * Copyright (C) 1999 Gerhard Wichert, Siemens AG
+ *		      Gerhard.Wichert@pdb.siemens.de
+ *
+ *
+ * Redesigned the x86 32-bit VM architecture to deal with
+ * up to 16 Terrabyte physical memory. With current x86 CPUs
+ * we now support up to 64 Gigabytes physical RAM.
+ *
+ * Copyright (C) 1999 Ingo Molnar <mingo@redhat.com>
+ *
+ * Reworked for PowerPC by various contributors. Moved from
+ * highmem.h by Benjamin Herrenschmidt (c) 2009 IBM Corp.
+ */
+
+#include <linux/highmem.h>
+#include <linux/module.h>
+
+void *kmap_atomic_high_prot(struct page *page, pgprot_t prot)
+{
+	unsigned long vaddr;
+	int idx, type;
+
+	type = kmap_atomic_idx_push();
+	idx = type + KM_TYPE_NR*smp_processor_id();
+	vaddr = __fix_to_virt(FIX_KMAP_BEGIN + idx);
+	WARN_ON(IS_ENABLED(CONFIG_DEBUG_HIGHMEM) && !pte_none(*(kmap_pte - idx)));
+	__set_pte_at(&init_mm, vaddr, kmap_pte-idx, mk_pte(page, prot), 1);
+	local_flush_tlb_page(NULL, vaddr);
+
+	return (void*) vaddr;
+}
+EXPORT_SYMBOL(kmap_atomic_high_prot);
+
+void kunmap_atomic_high(void *kvaddr)
+{
+	unsigned long vaddr = (unsigned long) kvaddr & PAGE_MASK;
+
+	if (vaddr < __fix_to_virt(FIX_KMAP_END))
+		return;
+
+	if (IS_ENABLED(CONFIG_DEBUG_HIGHMEM)) {
+		int type = kmap_atomic_idx();
+		unsigned int idx;
+
+		idx = type + KM_TYPE_NR * smp_processor_id();
+		WARN_ON(vaddr != __fix_to_virt(FIX_KMAP_BEGIN + idx));
+
+		/*
+		 * force other mappings to Oops if they'll try to access
+		 * this pte without first remap it
+		 */
+		pte_clear(&init_mm, vaddr, kmap_pte-idx);
+		local_flush_tlb_page(NULL, vaddr);
+	}
+
+	kmap_atomic_idx_pop();
+}
+EXPORT_SYMBOL(kunmap_atomic_high);
diff --git a/kernel/arch/powerpc/mm/mem.c b/kernel/arch/powerpc/mm/mem.c
index ae7c136..1ed276d 100644
--- a/kernel/arch/powerpc/mm/mem.c
+++ b/kernel/arch/powerpc/mm/mem.c
@@ -63,6 +63,11 @@
 unsigned long long memory_limit;
 bool init_mem_is_free;
 
+#ifdef CONFIG_HIGHMEM
+pte_t *kmap_pte;
+EXPORT_SYMBOL(kmap_pte);
+#endif
+
 pgprot_t phys_mem_access_prot(struct file *file, unsigned long pfn,
 			      unsigned long size, pgprot_t vma_prot)
 {
@@ -232,6 +237,8 @@
 
 	map_kernel_page(PKMAP_BASE, 0, __pgprot(0));	/* XXX gross */
 	pkmap_page_table = virt_to_kpte(PKMAP_BASE);
+
+	kmap_pte = virt_to_kpte(__fix_to_virt(FIX_KMAP_BEGIN));
 #endif /* CONFIG_HIGHMEM */
 
 	printk(KERN_DEBUG "Top of RAM: 0x%llx, Total RAM: 0x%llx\n",
diff --git a/kernel/arch/powerpc/platforms/powernv/opal-kmsg.c b/kernel/arch/powerpc/platforms/powernv/opal-kmsg.c
index ec86284..6c3bc4b 100644
--- a/kernel/arch/powerpc/platforms/powernv/opal-kmsg.c
+++ b/kernel/arch/powerpc/platforms/powernv/opal-kmsg.c
@@ -20,8 +20,7 @@
  * message, it just ensures that OPAL completely flushes the console buffer.
  */
 static void kmsg_dump_opal_console_flush(struct kmsg_dumper *dumper,
-					 enum kmsg_dump_reason reason,
-					 struct kmsg_dumper_iter *iter)
+				     enum kmsg_dump_reason reason)
 {
 	/*
 	 * Outside of a panic context the pollers will continue to run,
diff --git a/kernel/arch/powerpc/platforms/pseries/iommu.c b/kernel/arch/powerpc/platforms/pseries/iommu.c
index f05555d..245f1f8 100644
--- a/kernel/arch/powerpc/platforms/pseries/iommu.c
+++ b/kernel/arch/powerpc/platforms/pseries/iommu.c
@@ -24,7 +24,6 @@
 #include <linux/of.h>
 #include <linux/iommu.h>
 #include <linux/rculist.h>
-#include <linux/local_lock.h>
 #include <asm/io.h>
 #include <asm/prom.h>
 #include <asm/rtas.h>
@@ -191,13 +190,7 @@
 	return ret;
 }
 
-struct tce_page {
-	__be64 * page;
-	local_lock_t lock;
-};
-static DEFINE_PER_CPU(struct tce_page, tce_page) = {
-	.lock = INIT_LOCAL_LOCK(lock),
-};
+static DEFINE_PER_CPU(__be64 *, tce_page);
 
 static int tce_buildmulti_pSeriesLP(struct iommu_table *tbl, long tcenum,
 				     long npages, unsigned long uaddr,
@@ -219,10 +212,9 @@
 		                           direction, attrs);
 	}
 
-	/* to protect tcep and the page behind it */
-	local_lock_irqsave(&tce_page.lock, flags);
+	local_irq_save(flags);	/* to protect tcep and the page behind it */
 
-	tcep = __this_cpu_read(tce_page.page);
+	tcep = __this_cpu_read(tce_page);
 
 	/* This is safe to do since interrupts are off when we're called
 	 * from iommu_alloc{,_sg}()
@@ -231,12 +223,12 @@
 		tcep = (__be64 *)__get_free_page(GFP_ATOMIC);
 		/* If allocation fails, fall back to the loop implementation */
 		if (!tcep) {
-			local_unlock_irqrestore(&tce_page.lock, flags);
+			local_irq_restore(flags);
 			return tce_build_pSeriesLP(tbl->it_index, tcenum,
 					tbl->it_page_shift,
 					npages, uaddr, direction, attrs);
 		}
-		__this_cpu_write(tce_page.page, tcep);
+		__this_cpu_write(tce_page, tcep);
 	}
 
 	rpn = __pa(uaddr) >> TCE_SHIFT;
@@ -266,7 +258,7 @@
 		tcenum += limit;
 	} while (npages > 0 && !rc);
 
-	local_unlock_irqrestore(&tce_page.lock, flags);
+	local_irq_restore(flags);
 
 	if (unlikely(rc == H_NOT_ENOUGH_RESOURCES)) {
 		ret = (int)rc;
@@ -437,17 +429,16 @@
 				DMA_BIDIRECTIONAL, 0);
 	}
 
-	/* to protect tcep and the page behind it */
-	local_lock_irq(&tce_page.lock);
-	tcep = __this_cpu_read(tce_page.page);
+	local_irq_disable();	/* to protect tcep and the page behind it */
+	tcep = __this_cpu_read(tce_page);
 
 	if (!tcep) {
 		tcep = (__be64 *)__get_free_page(GFP_ATOMIC);
 		if (!tcep) {
-			local_unlock_irq(&tce_page.lock);
+			local_irq_enable();
 			return -ENOMEM;
 		}
-		__this_cpu_write(tce_page.page, tcep);
+		__this_cpu_write(tce_page, tcep);
 	}
 
 	proto_tce = TCE_PCI_READ | TCE_PCI_WRITE;
@@ -490,7 +481,7 @@
 
 	/* error cleanup: caller will clear whole range */
 
-	local_unlock_irq(&tce_page.lock);
+	local_irq_enable();
 	return rc;
 }
 
diff --git a/kernel/arch/powerpc/xmon/xmon.c b/kernel/arch/powerpc/xmon/xmon.c
index d62b8e0..5559edf 100644
--- a/kernel/arch/powerpc/xmon/xmon.c
+++ b/kernel/arch/powerpc/xmon/xmon.c
@@ -3005,7 +3005,7 @@
 static void
 dump_log_buf(void)
 {
-	struct kmsg_dumper_iter iter = { .active = 1 };
+	struct kmsg_dumper dumper = { .active = 1 };
 	unsigned char buf[128];
 	size_t len;
 
@@ -3017,9 +3017,9 @@
 	catch_memory_errors = 1;
 	sync();
 
-	kmsg_dump_rewind(&iter);
+	kmsg_dump_rewind_nolock(&dumper);
 	xmon_start_pagination();
-	while (kmsg_dump_get_line(&iter, false, buf, sizeof(buf), &len)) {
+	while (kmsg_dump_get_line_nolock(&dumper, false, buf, sizeof(buf), &len)) {
 		buf[len] = '\0';
 		printf("%s", buf);
 	}
diff --git a/kernel/arch/s390/Kconfig b/kernel/arch/s390/Kconfig
index 2e78071..8789939 100644
--- a/kernel/arch/s390/Kconfig
+++ b/kernel/arch/s390/Kconfig
@@ -183,7 +183,6 @@
 	select HAVE_RSEQ
 	select HAVE_SYSCALL_TRACEPOINTS
 	select HAVE_VIRT_CPU_ACCOUNTING
-	select HAVE_VIRT_CPU_ACCOUNTING_IDLE
 	select IOMMU_HELPER		if PCI
 	select IOMMU_SUPPORT		if PCI
 	select MODULES_USE_ELF_RELA
diff --git a/kernel/arch/s390/include/asm/spinlock_types.h b/kernel/arch/s390/include/asm/spinlock_types.h
index 8e28e81..cfed272 100644
--- a/kernel/arch/s390/include/asm/spinlock_types.h
+++ b/kernel/arch/s390/include/asm/spinlock_types.h
@@ -2,6 +2,10 @@
 #ifndef __ASM_SPINLOCK_TYPES_H
 #define __ASM_SPINLOCK_TYPES_H
 
+#ifndef __LINUX_SPINLOCK_TYPES_H
+# error "please don't include this file directly"
+#endif
+
 typedef struct {
 	int lock;
 } __attribute__ ((aligned (4))) arch_spinlock_t;
diff --git a/kernel/arch/s390/include/asm/vtime.h b/kernel/arch/s390/include/asm/vtime.h
index fac6a67..3622d4e 100644
--- a/kernel/arch/s390/include/asm/vtime.h
+++ b/kernel/arch/s390/include/asm/vtime.h
@@ -2,6 +2,7 @@
 #ifndef _S390_VTIME_H
 #define _S390_VTIME_H
 
+#define __ARCH_HAS_VTIME_ACCOUNT
 #define __ARCH_HAS_VTIME_TASK_SWITCH
 
 #endif /* _S390_VTIME_H */
diff --git a/kernel/arch/s390/kernel/vtime.c b/kernel/arch/s390/kernel/vtime.c
index 9b3c597..579ec3a 100644
--- a/kernel/arch/s390/kernel/vtime.c
+++ b/kernel/arch/s390/kernel/vtime.c
@@ -223,49 +223,34 @@
 	S390_lowcore.avg_steal_timer = avg_steal;
 }
 
-static u64 vtime_delta(void)
-{
-	u64 timer = S390_lowcore.last_update_timer;
-
-	S390_lowcore.last_update_timer = get_vtimer();
-
-	return timer - S390_lowcore.last_update_timer;
-}
-
 /*
  * Update process times based on virtual cpu times stored by entry.S
  * to the lowcore fields user_timer, system_timer & steal_clock.
  */
-void vtime_account_kernel(struct task_struct *tsk)
+void vtime_account_irq_enter(struct task_struct *tsk)
 {
-	u64 delta = vtime_delta();
+	u64 timer;
 
-	if (tsk->flags & PF_VCPU)
-		S390_lowcore.guest_timer += delta;
+	timer = S390_lowcore.last_update_timer;
+	S390_lowcore.last_update_timer = get_vtimer();
+	timer -= S390_lowcore.last_update_timer;
+
+	if ((tsk->flags & PF_VCPU) && (irq_count() == 0))
+		S390_lowcore.guest_timer += timer;
+	else if (hardirq_count())
+		S390_lowcore.hardirq_timer += timer;
+	else if (in_serving_softirq())
+		S390_lowcore.softirq_timer += timer;
 	else
-		S390_lowcore.system_timer += delta;
+		S390_lowcore.system_timer += timer;
 
-	virt_timer_forward(delta);
+	virt_timer_forward(timer);
 }
+EXPORT_SYMBOL_GPL(vtime_account_irq_enter);
+
+void vtime_account_kernel(struct task_struct *tsk)
+__attribute__((alias("vtime_account_irq_enter")));
 EXPORT_SYMBOL_GPL(vtime_account_kernel);
-
-void vtime_account_softirq(struct task_struct *tsk)
-{
-	u64 delta = vtime_delta();
-
-	S390_lowcore.softirq_timer += delta;
-
-	virt_timer_forward(delta);
-}
-
-void vtime_account_hardirq(struct task_struct *tsk)
-{
-	u64 delta = vtime_delta();
-
-	S390_lowcore.hardirq_timer += delta;
-
-	virt_timer_forward(delta);
-}
 
 /*
  * Sorted add to a list. List is linear searched until first bigger
diff --git a/kernel/arch/sh/include/asm/fixmap.h b/kernel/arch/sh/include/asm/fixmap.h
index b07fbc7..f38adc1 100644
--- a/kernel/arch/sh/include/asm/fixmap.h
+++ b/kernel/arch/sh/include/asm/fixmap.h
@@ -13,6 +13,9 @@
 #include <linux/kernel.h>
 #include <linux/threads.h>
 #include <asm/page.h>
+#ifdef CONFIG_HIGHMEM
+#include <asm/kmap_types.h>
+#endif
 
 /*
  * Here we define all the compile-time 'special' virtual
@@ -50,6 +53,11 @@
 	FIX_CMAP_BEGIN,
 	FIX_CMAP_END = FIX_CMAP_BEGIN + (FIX_N_COLOURS * NR_CPUS) - 1,
 
+#ifdef CONFIG_HIGHMEM
+	FIX_KMAP_BEGIN,	/* reserved pte's for temporary kernel mappings */
+	FIX_KMAP_END = FIX_KMAP_BEGIN + (KM_TYPE_NR * NR_CPUS) - 1,
+#endif
+
 #ifdef CONFIG_IOREMAP_FIXED
 	/*
 	 * FIX_IOREMAP entries are useful for mapping physical address
diff --git a/kernel/arch/sh/include/asm/hardirq.h b/kernel/arch/sh/include/asm/hardirq.h
index 9fe4495..edaea35 100644
--- a/kernel/arch/sh/include/asm/hardirq.h
+++ b/kernel/arch/sh/include/asm/hardirq.h
@@ -2,10 +2,16 @@
 #ifndef __ASM_SH_HARDIRQ_H
 #define __ASM_SH_HARDIRQ_H
 
-extern void ack_bad_irq(unsigned int irq);
-#define ack_bad_irq ack_bad_irq
-#define ARCH_WANTS_NMI_IRQSTAT
+#include <linux/threads.h>
+#include <linux/irq.h>
 
-#include <asm-generic/hardirq.h>
+typedef struct {
+	unsigned int __softirq_pending;
+	unsigned int __nmi_count;		/* arch dependent */
+} ____cacheline_aligned irq_cpustat_t;
+
+#include <linux/irq_cpustat.h>	/* Standard mappings for irq_cpustat_t above */
+
+extern void ack_bad_irq(unsigned int irq);
 
 #endif /* __ASM_SH_HARDIRQ_H */
diff --git a/kernel/arch/sh/include/asm/kmap_types.h b/kernel/arch/sh/include/asm/kmap_types.h
new file mode 100644
index 0000000..b78107f
--- /dev/null
+++ b/kernel/arch/sh/include/asm/kmap_types.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __SH_KMAP_TYPES_H
+#define __SH_KMAP_TYPES_H
+
+/* Dummy header just to define km_type. */
+
+#ifdef CONFIG_DEBUG_HIGHMEM
+#define  __WITH_KM_FENCE
+#endif
+
+#include <asm-generic/kmap_types.h>
+
+#undef __WITH_KM_FENCE
+
+#endif
diff --git a/kernel/arch/sh/include/asm/spinlock_types.h b/kernel/arch/sh/include/asm/spinlock_types.h
index 22ca9a9..e82369f 100644
--- a/kernel/arch/sh/include/asm/spinlock_types.h
+++ b/kernel/arch/sh/include/asm/spinlock_types.h
@@ -2,6 +2,10 @@
 #ifndef __ASM_SH_SPINLOCK_TYPES_H
 #define __ASM_SH_SPINLOCK_TYPES_H
 
+#ifndef __LINUX_SPINLOCK_TYPES_H
+# error "please don't include this file directly"
+#endif
+
 typedef struct {
 	volatile unsigned int lock;
 } arch_spinlock_t;
diff --git a/kernel/arch/sh/kernel/irq.c b/kernel/arch/sh/kernel/irq.c
index 5db7af5..5717c7c 100644
--- a/kernel/arch/sh/kernel/irq.c
+++ b/kernel/arch/sh/kernel/irq.c
@@ -44,7 +44,7 @@
 
 	seq_printf(p, "%*s: ", prec, "NMI");
 	for_each_online_cpu(j)
-		seq_printf(p, "%10u ", per_cpu(irq_stat.__nmi_count, j));
+		seq_printf(p, "%10u ", nmi_count(j));
 	seq_printf(p, "  Non-maskable interrupts\n");
 
 	seq_printf(p, "%*s: %10u\n", prec, "ERR", atomic_read(&irq_err_count));
@@ -148,7 +148,6 @@
 	hardirq_ctx[cpu] = NULL;
 }
 
-#ifndef CONFIG_PREEMPT_RT
 void do_softirq_own_stack(void)
 {
 	struct thread_info *curctx;
@@ -176,7 +175,6 @@
 		  "r5", "r6", "r7", "r8", "r9", "r15", "t", "pr"
 	);
 }
-#endif
 #else
 static inline void handle_one_irq(unsigned int irq)
 {
diff --git a/kernel/arch/sh/kernel/traps.c b/kernel/arch/sh/kernel/traps.c
index f5beecd..9c3d32b 100644
--- a/kernel/arch/sh/kernel/traps.c
+++ b/kernel/arch/sh/kernel/traps.c
@@ -186,7 +186,7 @@
 	arch_ftrace_nmi_enter();
 
 	nmi_enter();
-	this_cpu_inc(irq_stat.__nmi_count);
+	nmi_count(cpu)++;
 
 	switch (notify_die(DIE_NMI, "NMI", regs, 0, vec & 0xff, SIGINT)) {
 	case NOTIFY_OK:
diff --git a/kernel/arch/sh/mm/init.c b/kernel/arch/sh/mm/init.c
index 0db6919..3348e0c 100644
--- a/kernel/arch/sh/mm/init.c
+++ b/kernel/arch/sh/mm/init.c
@@ -362,6 +362,9 @@
 	mem_init_print_info(NULL);
 	pr_info("virtual kernel memory layout:\n"
 		"    fixmap  : 0x%08lx - 0x%08lx   (%4ld kB)\n"
+#ifdef CONFIG_HIGHMEM
+		"    pkmap   : 0x%08lx - 0x%08lx   (%4ld kB)\n"
+#endif
 		"    vmalloc : 0x%08lx - 0x%08lx   (%4ld MB)\n"
 		"    lowmem  : 0x%08lx - 0x%08lx   (%4ld MB) (cached)\n"
 #ifdef CONFIG_UNCACHED_MAPPING
@@ -373,6 +376,11 @@
 		FIXADDR_START, FIXADDR_TOP,
 		(FIXADDR_TOP - FIXADDR_START) >> 10,
 
+#ifdef CONFIG_HIGHMEM
+		PKMAP_BASE, PKMAP_BASE+LAST_PKMAP*PAGE_SIZE,
+		(LAST_PKMAP*PAGE_SIZE) >> 10,
+#endif
+
 		(unsigned long)VMALLOC_START, VMALLOC_END,
 		(VMALLOC_END - VMALLOC_START) >> 20,
 
diff --git a/kernel/arch/sparc/Kconfig b/kernel/arch/sparc/Kconfig
index a38d00d..530b7ec 100644
--- a/kernel/arch/sparc/Kconfig
+++ b/kernel/arch/sparc/Kconfig
@@ -139,7 +139,6 @@
 config HIGHMEM
 	bool
 	default y if SPARC32
-	select KMAP_LOCAL
 
 config ZONE_DMA
 	bool
diff --git a/kernel/arch/sparc/include/asm/highmem.h b/kernel/arch/sparc/include/asm/highmem.h
index 8751162..6c35f0d 100644
--- a/kernel/arch/sparc/include/asm/highmem.h
+++ b/kernel/arch/sparc/include/asm/highmem.h
@@ -24,6 +24,7 @@
 #include <linux/interrupt.h>
 #include <linux/pgtable.h>
 #include <asm/vaddrs.h>
+#include <asm/kmap_types.h>
 #include <asm/pgtsrmmu.h>
 
 /* declarations for highmem.c */
@@ -31,6 +32,8 @@
 
 #define kmap_prot __pgprot(SRMMU_ET_PTE | SRMMU_PRIV | SRMMU_CACHE)
 extern pte_t *pkmap_page_table;
+
+void kmap_init(void) __init;
 
 /*
  * Right now we initialize only a single pte table. It can be extended
@@ -49,11 +52,6 @@
 #define PKMAP_END (PKMAP_ADDR(LAST_PKMAP))
 
 #define flush_cache_kmaps()	flush_cache_all()
-
-/* FIXME: Use __flush_tlb_one(vaddr) instead of flush_cache_all() -- Anton */
-#define arch_kmap_local_post_map(vaddr, pteval)	flush_cache_all()
-#define arch_kmap_local_post_unmap(vaddr)	flush_cache_all()
-
 
 #endif /* __KERNEL__ */
 
diff --git a/kernel/arch/sparc/include/asm/kmap_types.h b/kernel/arch/sparc/include/asm/kmap_types.h
new file mode 100644
index 0000000..55a99b6
--- /dev/null
+++ b/kernel/arch/sparc/include/asm/kmap_types.h
@@ -0,0 +1,11 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _ASM_KMAP_TYPES_H
+#define _ASM_KMAP_TYPES_H
+
+/* Dummy header just to define km_type.  None of this
+ * is actually used on sparc.  -DaveM
+ */
+
+#include <asm-generic/kmap_types.h>
+
+#endif
diff --git a/kernel/arch/sparc/include/asm/vaddrs.h b/kernel/arch/sparc/include/asm/vaddrs.h
index 4fec034..84d054b 100644
--- a/kernel/arch/sparc/include/asm/vaddrs.h
+++ b/kernel/arch/sparc/include/asm/vaddrs.h
@@ -32,13 +32,13 @@
 #define SRMMU_NOCACHE_ALCRATIO	64	/* 256 pages per 64MB of system RAM */
 
 #ifndef __ASSEMBLY__
-#include <asm/kmap_size.h>
+#include <asm/kmap_types.h>
 
 enum fixed_addresses {
 	FIX_HOLE,
 #ifdef CONFIG_HIGHMEM
 	FIX_KMAP_BEGIN,
-	FIX_KMAP_END = (KM_MAX_IDX * NR_CPUS),
+	FIX_KMAP_END = (KM_TYPE_NR * NR_CPUS),
 #endif
 	__end_of_fixed_addresses
 };
diff --git a/kernel/arch/sparc/kernel/irq_64.c b/kernel/arch/sparc/kernel/irq_64.c
index eb21682..3ec9f14 100644
--- a/kernel/arch/sparc/kernel/irq_64.c
+++ b/kernel/arch/sparc/kernel/irq_64.c
@@ -854,7 +854,6 @@
 	set_irq_regs(old_regs);
 }
 
-#ifndef CONFIG_PREEMPT_RT
 void do_softirq_own_stack(void)
 {
 	void *orig_sp, *sp = softirq_stack[smp_processor_id()];
@@ -869,7 +868,6 @@
 	__asm__ __volatile__("mov %0, %%sp"
 			     : : "r" (orig_sp));
 }
-#endif
 
 #ifdef CONFIG_HOTPLUG_CPU
 void fixup_irqs(void)
diff --git a/kernel/arch/sparc/mm/Makefile b/kernel/arch/sparc/mm/Makefile
index 68db1f8..b078205 100644
--- a/kernel/arch/sparc/mm/Makefile
+++ b/kernel/arch/sparc/mm/Makefile
@@ -15,3 +15,6 @@
 
 # Only used by sparc64
 obj-$(CONFIG_HUGETLB_PAGE) += hugetlbpage.o
+
+# Only used by sparc32
+obj-$(CONFIG_HIGHMEM)   += highmem.o
diff --git a/kernel/arch/sparc/mm/highmem.c b/kernel/arch/sparc/mm/highmem.c
new file mode 100644
index 0000000..8f2a2af
--- /dev/null
+++ b/kernel/arch/sparc/mm/highmem.c
@@ -0,0 +1,115 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ *  highmem.c: virtual kernel memory mappings for high memory
+ *
+ *  Provides kernel-static versions of atomic kmap functions originally
+ *  found as inlines in include/asm-sparc/highmem.h.  These became
+ *  needed as kmap_atomic() and kunmap_atomic() started getting
+ *  called from within modules.
+ *  -- Tomas Szepe <szepe@pinerecords.com>, September 2002
+ *
+ *  But kmap_atomic() and kunmap_atomic() cannot be inlined in
+ *  modules because they are loaded with btfixup-ped functions.
+ */
+
+/*
+ * The use of kmap_atomic/kunmap_atomic is discouraged - kmap/kunmap
+ * gives a more generic (and caching) interface. But kmap_atomic can
+ * be used in IRQ contexts, so in some (very limited) cases we need it.
+ *
+ * XXX This is an old text. Actually, it's good to use atomic kmaps,
+ * provided you remember that they are atomic and not try to sleep
+ * with a kmap taken, much like a spinlock. Non-atomic kmaps are
+ * shared by CPUs, and so precious, and establishing them requires IPI.
+ * Atomic kmaps are lightweight and we may have NCPUS more of them.
+ */
+#include <linux/highmem.h>
+#include <linux/export.h>
+#include <linux/mm.h>
+
+#include <asm/cacheflush.h>
+#include <asm/tlbflush.h>
+#include <asm/vaddrs.h>
+
+static pte_t *kmap_pte;
+
+void __init kmap_init(void)
+{
+	unsigned long address = __fix_to_virt(FIX_KMAP_BEGIN);
+
+        /* cache the first kmap pte */
+        kmap_pte = virt_to_kpte(address);
+}
+
+void *kmap_atomic_high_prot(struct page *page, pgprot_t prot)
+{
+	unsigned long vaddr;
+	long idx, type;
+
+	type = kmap_atomic_idx_push();
+	idx = type + KM_TYPE_NR*smp_processor_id();
+	vaddr = __fix_to_virt(FIX_KMAP_BEGIN + idx);
+
+/* XXX Fix - Anton */
+#if 0
+	__flush_cache_one(vaddr);
+#else
+	flush_cache_all();
+#endif
+
+#ifdef CONFIG_DEBUG_HIGHMEM
+	BUG_ON(!pte_none(*(kmap_pte-idx)));
+#endif
+	set_pte(kmap_pte-idx, mk_pte(page, prot));
+/* XXX Fix - Anton */
+#if 0
+	__flush_tlb_one(vaddr);
+#else
+	flush_tlb_all();
+#endif
+
+	return (void*) vaddr;
+}
+EXPORT_SYMBOL(kmap_atomic_high_prot);
+
+void kunmap_atomic_high(void *kvaddr)
+{
+	unsigned long vaddr = (unsigned long) kvaddr & PAGE_MASK;
+	int type;
+
+	if (vaddr < FIXADDR_START)
+		return;
+
+	type = kmap_atomic_idx();
+
+#ifdef CONFIG_DEBUG_HIGHMEM
+	{
+		unsigned long idx;
+
+		idx = type + KM_TYPE_NR * smp_processor_id();
+		BUG_ON(vaddr != __fix_to_virt(FIX_KMAP_BEGIN+idx));
+
+		/* XXX Fix - Anton */
+#if 0
+		__flush_cache_one(vaddr);
+#else
+		flush_cache_all();
+#endif
+
+		/*
+		 * force other mappings to Oops if they'll try to access
+		 * this pte without first remap it
+		 */
+		pte_clear(&init_mm, vaddr, kmap_pte-idx);
+		/* XXX Fix - Anton */
+#if 0
+		__flush_tlb_one(vaddr);
+#else
+		flush_tlb_all();
+#endif
+	}
+#endif
+
+	kmap_atomic_idx_pop();
+}
+EXPORT_SYMBOL(kunmap_atomic_high);
diff --git a/kernel/arch/sparc/mm/srmmu.c b/kernel/arch/sparc/mm/srmmu.c
index a03caa5..0070f8b 100644
--- a/kernel/arch/sparc/mm/srmmu.c
+++ b/kernel/arch/sparc/mm/srmmu.c
@@ -971,6 +971,8 @@
 
 	sparc_context_init(num_contexts);
 
+	kmap_init();
+
 	{
 		unsigned long max_zone_pfn[MAX_NR_ZONES] = { 0 };
 
diff --git a/kernel/arch/um/include/asm/fixmap.h b/kernel/arch/um/include/asm/fixmap.h
index 2efac58..2c697a1 100644
--- a/kernel/arch/um/include/asm/fixmap.h
+++ b/kernel/arch/um/include/asm/fixmap.h
@@ -3,6 +3,7 @@
 #define __UM_FIXMAP_H
 
 #include <asm/processor.h>
+#include <asm/kmap_types.h>
 #include <asm/archparam.h>
 #include <asm/page.h>
 #include <linux/threads.h>
diff --git a/kernel/arch/um/include/asm/hardirq.h b/kernel/arch/um/include/asm/hardirq.h
index 52e2c36..b426796 100644
--- a/kernel/arch/um/include/asm/hardirq.h
+++ b/kernel/arch/um/include/asm/hardirq.h
@@ -2,7 +2,22 @@
 #ifndef __ASM_UM_HARDIRQ_H
 #define __ASM_UM_HARDIRQ_H
 
-#include <asm-generic/hardirq.h>
+#include <linux/cache.h>
+#include <linux/threads.h>
+
+typedef struct {
+	unsigned int __softirq_pending;
+} ____cacheline_aligned irq_cpustat_t;
+
+#include <linux/irq_cpustat.h>	/* Standard mappings for irq_cpustat_t above */
+#include <linux/irq.h>
+
+#ifndef ack_bad_irq
+static inline void ack_bad_irq(unsigned int irq)
+{
+	printk(KERN_CRIT "unexpected IRQ trap at vector %02x\n", irq);
+}
+#endif
 
 #define __ARCH_IRQ_EXIT_IRQS_DISABLED 1
 
diff --git a/kernel/arch/um/include/asm/kmap_types.h b/kernel/arch/um/include/asm/kmap_types.h
new file mode 100644
index 0000000..b0bd12d
--- /dev/null
+++ b/kernel/arch/um/include/asm/kmap_types.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* 
+ * Copyright (C) 2002 Jeff Dike (jdike@karaya.com)
+ */
+
+#ifndef __UM_KMAP_TYPES_H
+#define __UM_KMAP_TYPES_H
+
+/* No more #include "asm/arch/kmap_types.h" ! */
+
+#define KM_TYPE_NR 14
+
+#endif
diff --git a/kernel/arch/um/kernel/kmsg_dump.c b/kernel/arch/um/kernel/kmsg_dump.c
index 1739994..e4abac6 100644
--- a/kernel/arch/um/kernel/kmsg_dump.c
+++ b/kernel/arch/um/kernel/kmsg_dump.c
@@ -1,19 +1,15 @@
 // SPDX-License-Identifier: GPL-2.0
 #include <linux/kmsg_dump.h>
-#include <linux/spinlock.h>
 #include <linux/console.h>
 #include <shared/init.h>
 #include <shared/kern.h>
 #include <os.h>
 
 static void kmsg_dumper_stdout(struct kmsg_dumper *dumper,
-				enum kmsg_dump_reason reason,
-				struct kmsg_dumper_iter *iter)
+				enum kmsg_dump_reason reason)
 {
-	static DEFINE_SPINLOCK(lock);
 	static char line[1024];
 	struct console *con;
-	unsigned long flags;
 	size_t len = 0;
 
 	/* only dump kmsg when no console is available */
@@ -28,16 +24,11 @@
 	if (con)
 		return;
 
-	if (!spin_trylock_irqsave(&lock, flags))
-		return;
-
 	printf("kmsg_dump:\n");
-	while (kmsg_dump_get_line(iter, true, line, sizeof(line), &len)) {
+	while (kmsg_dump_get_line(dumper, true, line, sizeof(line), &len)) {
 		line[len] = '\0';
 		printf("%s", line);
 	}
-
-	spin_unlock_irqrestore(&lock, flags);
 }
 
 static struct kmsg_dumper kmsg_dumper = {
diff --git a/kernel/arch/x86/Kconfig b/kernel/arch/x86/Kconfig
index 71b78b3..32536ff 100644
--- a/kernel/arch/x86/Kconfig
+++ b/kernel/arch/x86/Kconfig
@@ -15,7 +15,6 @@
 	select CLKSRC_I8253
 	select CLONE_BACKWARDS
 	select HAVE_DEBUG_STACKOVERFLOW
-	select KMAP_LOCAL
 	select MODULES_USE_ELF_REL
 	select OLD_SIGACTION
 	select GENERIC_VDSO_32
@@ -218,7 +217,6 @@
 	select HAVE_PCI
 	select HAVE_PERF_REGS
 	select HAVE_PERF_USER_STACK_DUMP
-	select HAVE_PREEMPT_LAZY
 	select MMU_GATHER_RCU_TABLE_FREE		if PARAVIRT
 	select HAVE_POSIX_CPU_TIMERS_TASK_WORK
 	select HAVE_REGS_AND_STACK_ACCESS_API
diff --git a/kernel/arch/x86/crypto/aesni-intel_glue.c b/kernel/arch/x86/crypto/aesni-intel_glue.c
index 29c716e..be891fd 100644
--- a/kernel/arch/x86/crypto/aesni-intel_glue.c
+++ b/kernel/arch/x86/crypto/aesni-intel_glue.c
@@ -379,14 +379,14 @@
 
 	err = skcipher_walk_virt(&walk, req, true);
 
+	kernel_fpu_begin();
 	while ((nbytes = walk.nbytes)) {
-		kernel_fpu_begin();
 		aesni_ecb_enc(ctx, walk.dst.virt.addr, walk.src.virt.addr,
 			      nbytes & AES_BLOCK_MASK);
-		kernel_fpu_end();
 		nbytes &= AES_BLOCK_SIZE - 1;
 		err = skcipher_walk_done(&walk, nbytes);
 	}
+	kernel_fpu_end();
 
 	return err;
 }
@@ -401,14 +401,14 @@
 
 	err = skcipher_walk_virt(&walk, req, true);
 
+	kernel_fpu_begin();
 	while ((nbytes = walk.nbytes)) {
-		kernel_fpu_begin();
 		aesni_ecb_dec(ctx, walk.dst.virt.addr, walk.src.virt.addr,
 			      nbytes & AES_BLOCK_MASK);
-		kernel_fpu_end();
 		nbytes &= AES_BLOCK_SIZE - 1;
 		err = skcipher_walk_done(&walk, nbytes);
 	}
+	kernel_fpu_end();
 
 	return err;
 }
@@ -423,14 +423,14 @@
 
 	err = skcipher_walk_virt(&walk, req, true);
 
+	kernel_fpu_begin();
 	while ((nbytes = walk.nbytes)) {
-		kernel_fpu_begin();
 		aesni_cbc_enc(ctx, walk.dst.virt.addr, walk.src.virt.addr,
 			      nbytes & AES_BLOCK_MASK, walk.iv);
-		kernel_fpu_end();
 		nbytes &= AES_BLOCK_SIZE - 1;
 		err = skcipher_walk_done(&walk, nbytes);
 	}
+	kernel_fpu_end();
 
 	return err;
 }
@@ -445,14 +445,14 @@
 
 	err = skcipher_walk_virt(&walk, req, true);
 
+	kernel_fpu_begin();
 	while ((nbytes = walk.nbytes)) {
-		kernel_fpu_begin();
 		aesni_cbc_dec(ctx, walk.dst.virt.addr, walk.src.virt.addr,
 			      nbytes & AES_BLOCK_MASK, walk.iv);
-		kernel_fpu_end();
 		nbytes &= AES_BLOCK_SIZE - 1;
 		err = skcipher_walk_done(&walk, nbytes);
 	}
+	kernel_fpu_end();
 
 	return err;
 }
@@ -500,20 +500,18 @@
 
 	err = skcipher_walk_virt(&walk, req, true);
 
+	kernel_fpu_begin();
 	while ((nbytes = walk.nbytes) >= AES_BLOCK_SIZE) {
-		kernel_fpu_begin();
 		aesni_ctr_enc_tfm(ctx, walk.dst.virt.addr, walk.src.virt.addr,
 			              nbytes & AES_BLOCK_MASK, walk.iv);
-		kernel_fpu_end();
 		nbytes &= AES_BLOCK_SIZE - 1;
 		err = skcipher_walk_done(&walk, nbytes);
 	}
 	if (walk.nbytes) {
-		kernel_fpu_begin();
 		ctr_crypt_final(ctx, &walk);
-		kernel_fpu_end();
 		err = skcipher_walk_done(&walk, 0);
 	}
+	kernel_fpu_end();
 
 	return err;
 }
diff --git a/kernel/arch/x86/crypto/cast5_avx_glue.c b/kernel/arch/x86/crypto/cast5_avx_glue.c
index 2f8df8e..384ccb0 100644
--- a/kernel/arch/x86/crypto/cast5_avx_glue.c
+++ b/kernel/arch/x86/crypto/cast5_avx_glue.c
@@ -46,7 +46,7 @@
 
 static int ecb_crypt(struct skcipher_request *req, bool enc)
 {
-	bool fpu_enabled;
+	bool fpu_enabled = false;
 	struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req);
 	struct cast5_ctx *ctx = crypto_skcipher_ctx(tfm);
 	struct skcipher_walk walk;
@@ -61,7 +61,7 @@
 		u8 *wsrc = walk.src.virt.addr;
 		u8 *wdst = walk.dst.virt.addr;
 
-		fpu_enabled = cast5_fpu_begin(false, &walk, nbytes);
+		fpu_enabled = cast5_fpu_begin(fpu_enabled, &walk, nbytes);
 
 		/* Process multi-block batch */
 		if (nbytes >= bsize * CAST5_PARALLEL_BLOCKS) {
@@ -90,9 +90,10 @@
 		} while (nbytes >= bsize);
 
 done:
-		cast5_fpu_end(fpu_enabled);
 		err = skcipher_walk_done(&walk, nbytes);
 	}
+
+	cast5_fpu_end(fpu_enabled);
 	return err;
 }
 
@@ -196,7 +197,7 @@
 {
 	struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req);
 	struct cast5_ctx *ctx = crypto_skcipher_ctx(tfm);
-	bool fpu_enabled;
+	bool fpu_enabled = false;
 	struct skcipher_walk walk;
 	unsigned int nbytes;
 	int err;
@@ -204,11 +205,12 @@
 	err = skcipher_walk_virt(&walk, req, false);
 
 	while ((nbytes = walk.nbytes)) {
-		fpu_enabled = cast5_fpu_begin(false, &walk, nbytes);
+		fpu_enabled = cast5_fpu_begin(fpu_enabled, &walk, nbytes);
 		nbytes = __cbc_decrypt(ctx, &walk);
-		cast5_fpu_end(fpu_enabled);
 		err = skcipher_walk_done(&walk, nbytes);
 	}
+
+	cast5_fpu_end(fpu_enabled);
 	return err;
 }
 
@@ -275,7 +277,7 @@
 {
 	struct crypto_skcipher *tfm = crypto_skcipher_reqtfm(req);
 	struct cast5_ctx *ctx = crypto_skcipher_ctx(tfm);
-	bool fpu_enabled;
+	bool fpu_enabled = false;
 	struct skcipher_walk walk;
 	unsigned int nbytes;
 	int err;
@@ -283,12 +285,13 @@
 	err = skcipher_walk_virt(&walk, req, false);
 
 	while ((nbytes = walk.nbytes) >= CAST5_BLOCK_SIZE) {
-		fpu_enabled = cast5_fpu_begin(false, &walk, nbytes);
+		fpu_enabled = cast5_fpu_begin(fpu_enabled, &walk, nbytes);
 		nbytes = __ctr_crypt(&walk, ctx);
-		cast5_fpu_end(fpu_enabled);
 		err = skcipher_walk_done(&walk, nbytes);
 	}
 
+	cast5_fpu_end(fpu_enabled);
+
 	if (walk.nbytes) {
 		ctr_crypt_final(&walk, ctx);
 		err = skcipher_walk_done(&walk, 0);
diff --git a/kernel/arch/x86/crypto/glue_helper.c b/kernel/arch/x86/crypto/glue_helper.c
index 6d07747..d3d91a0 100644
--- a/kernel/arch/x86/crypto/glue_helper.c
+++ b/kernel/arch/x86/crypto/glue_helper.c
@@ -24,7 +24,7 @@
 	void *ctx = crypto_skcipher_ctx(crypto_skcipher_reqtfm(req));
 	const unsigned int bsize = 128 / 8;
 	struct skcipher_walk walk;
-	bool fpu_enabled;
+	bool fpu_enabled = false;
 	unsigned int nbytes;
 	int err;
 
@@ -37,7 +37,7 @@
 		unsigned int i;
 
 		fpu_enabled = glue_fpu_begin(bsize, gctx->fpu_blocks_limit,
-					     &walk, false, nbytes);
+					     &walk, fpu_enabled, nbytes);
 		for (i = 0; i < gctx->num_funcs; i++) {
 			func_bytes = bsize * gctx->funcs[i].num_blocks;
 
@@ -55,9 +55,10 @@
 			if (nbytes < bsize)
 				break;
 		}
-		glue_fpu_end(fpu_enabled);
 		err = skcipher_walk_done(&walk, nbytes);
 	}
+
+	glue_fpu_end(fpu_enabled);
 	return err;
 }
 EXPORT_SYMBOL_GPL(glue_ecb_req_128bit);
@@ -100,7 +101,7 @@
 	void *ctx = crypto_skcipher_ctx(crypto_skcipher_reqtfm(req));
 	const unsigned int bsize = 128 / 8;
 	struct skcipher_walk walk;
-	bool fpu_enabled;
+	bool fpu_enabled = false;
 	unsigned int nbytes;
 	int err;
 
@@ -114,7 +115,7 @@
 		u128 last_iv;
 
 		fpu_enabled = glue_fpu_begin(bsize, gctx->fpu_blocks_limit,
-					     &walk, false, nbytes);
+					     &walk, fpu_enabled, nbytes);
 		/* Start of the last block. */
 		src += nbytes / bsize - 1;
 		dst += nbytes / bsize - 1;
@@ -147,10 +148,10 @@
 done:
 		u128_xor(dst, dst, (u128 *)walk.iv);
 		*(u128 *)walk.iv = last_iv;
-		glue_fpu_end(fpu_enabled);
 		err = skcipher_walk_done(&walk, nbytes);
 	}
 
+	glue_fpu_end(fpu_enabled);
 	return err;
 }
 EXPORT_SYMBOL_GPL(glue_cbc_decrypt_req_128bit);
@@ -161,7 +162,7 @@
 	void *ctx = crypto_skcipher_ctx(crypto_skcipher_reqtfm(req));
 	const unsigned int bsize = 128 / 8;
 	struct skcipher_walk walk;
-	bool fpu_enabled;
+	bool fpu_enabled = false;
 	unsigned int nbytes;
 	int err;
 
@@ -175,7 +176,7 @@
 		le128 ctrblk;
 
 		fpu_enabled = glue_fpu_begin(bsize, gctx->fpu_blocks_limit,
-					     &walk, false, nbytes);
+					     &walk, fpu_enabled, nbytes);
 
 		be128_to_le128(&ctrblk, (be128 *)walk.iv);
 
@@ -201,9 +202,10 @@
 		}
 
 		le128_to_be128((be128 *)walk.iv, &ctrblk);
-		glue_fpu_end(fpu_enabled);
 		err = skcipher_walk_done(&walk, nbytes);
 	}
+
+	glue_fpu_end(fpu_enabled);
 
 	if (nbytes) {
 		le128 ctrblk;
@@ -304,13 +306,7 @@
 	tweak_fn(tweak_ctx, walk.iv, walk.iv);
 
 	while (nbytes) {
-		fpu_enabled = glue_fpu_begin(bsize, gctx->fpu_blocks_limit,
-					     &walk, fpu_enabled,
-					     nbytes < bsize ? bsize : nbytes);
 		nbytes = __glue_xts_req_128bit(gctx, crypt_ctx, &walk);
-
-		glue_fpu_end(fpu_enabled);
-		fpu_enabled = false;
 
 		err = skcipher_walk_done(&walk, nbytes);
 		nbytes = walk.nbytes;
diff --git a/kernel/arch/x86/include/asm/fixmap.h b/kernel/arch/x86/include/asm/fixmap.h
index 8eba66a..77217bd 100644
--- a/kernel/arch/x86/include/asm/fixmap.h
+++ b/kernel/arch/x86/include/asm/fixmap.h
@@ -31,7 +31,7 @@
 #include <asm/pgtable_types.h>
 #ifdef CONFIG_X86_32
 #include <linux/threads.h>
-#include <asm/kmap_size.h>
+#include <asm/kmap_types.h>
 #else
 #include <uapi/asm/vsyscall.h>
 #endif
@@ -94,7 +94,7 @@
 #endif
 #ifdef CONFIG_X86_32
 	FIX_KMAP_BEGIN,	/* reserved pte's for temporary kernel mappings */
-	FIX_KMAP_END = FIX_KMAP_BEGIN + (KM_MAX_IDX * NR_CPUS) - 1,
+	FIX_KMAP_END = FIX_KMAP_BEGIN+(KM_TYPE_NR*NR_CPUS)-1,
 #ifdef CONFIG_PCI_MMCONFIG
 	FIX_PCIE_MCFG,
 #endif
@@ -151,6 +151,7 @@
 
 extern int fixmaps_set;
 
+extern pte_t *kmap_pte;
 extern pte_t *pkmap_page_table;
 
 void __native_set_fixmap(enum fixed_addresses idx, pte_t pte);
diff --git a/kernel/arch/x86/include/asm/fpu/api.h b/kernel/arch/x86/include/asm/fpu/api.h
index d31b088..8b9bfaa 100644
--- a/kernel/arch/x86/include/asm/fpu/api.h
+++ b/kernel/arch/x86/include/asm/fpu/api.h
@@ -28,7 +28,6 @@
 extern void kernel_fpu_end(void);
 extern bool irq_fpu_usable(void);
 extern void fpregs_mark_activate(void);
-extern void kernel_fpu_resched(void);
 
 /* Code that is unaware of kernel_fpu_begin_mask() can use this */
 static inline void kernel_fpu_begin(void)
@@ -41,32 +40,17 @@
  * A context switch will (and softirq might) save CPU's FPU registers to
  * fpu->state and set TIF_NEED_FPU_LOAD leaving CPU's FPU registers in
  * a random state.
- *
- * local_bh_disable() protects against both preemption and soft interrupts
- * on !RT kernels.
- *
- * On RT kernels local_bh_disable() is not sufficient because it only
- * serializes soft interrupt related sections via a local lock, but stays
- * preemptible. Disabling preemption is the right choice here as bottom
- * half processing is always in thread context on RT kernels so it
- * implicitly prevents bottom half processing as well.
- *
- * Disabling preemption also serializes against kernel_fpu_begin().
  */
 static inline void fpregs_lock(void)
 {
-	if (!IS_ENABLED(CONFIG_PREEMPT_RT))
-		local_bh_disable();
-	else
-		preempt_disable();
+	preempt_disable();
+	local_bh_disable();
 }
 
 static inline void fpregs_unlock(void)
 {
-	if (!IS_ENABLED(CONFIG_PREEMPT_RT))
-		local_bh_enable();
-	else
-		preempt_enable();
+	local_bh_enable();
+	preempt_enable();
 }
 
 #ifdef CONFIG_X86_DEBUG_FPU
diff --git a/kernel/arch/x86/include/asm/highmem.h b/kernel/arch/x86/include/asm/highmem.h
index 032e020..0f420b2 100644
--- a/kernel/arch/x86/include/asm/highmem.h
+++ b/kernel/arch/x86/include/asm/highmem.h
@@ -23,6 +23,7 @@
 
 #include <linux/interrupt.h>
 #include <linux/threads.h>
+#include <asm/kmap_types.h>
 #include <asm/tlbflush.h>
 #include <asm/paravirt.h>
 #include <asm/fixmap.h>
@@ -57,16 +58,10 @@
 #define PKMAP_NR(virt)  ((virt-PKMAP_BASE) >> PAGE_SHIFT)
 #define PKMAP_ADDR(nr)  (PKMAP_BASE + ((nr) << PAGE_SHIFT))
 
+void *kmap_atomic_pfn(unsigned long pfn);
+void *kmap_atomic_prot_pfn(unsigned long pfn, pgprot_t prot);
+
 #define flush_cache_kmaps()	do { } while (0)
-
-#define	arch_kmap_local_post_map(vaddr, pteval)		\
-	arch_flush_lazy_mmu_mode()
-
-#define	arch_kmap_local_post_unmap(vaddr)		\
-	do {						\
-		flush_tlb_one_kernel((vaddr));		\
-		arch_flush_lazy_mmu_mode();		\
-	} while (0)
 
 extern void add_highpages_with_active_regions(int nid, unsigned long start_pfn,
 					unsigned long end_pfn);
diff --git a/kernel/arch/x86/include/asm/iomap.h b/kernel/arch/x86/include/asm/iomap.h
index e2de092..bacf68c 100644
--- a/kernel/arch/x86/include/asm/iomap.h
+++ b/kernel/arch/x86/include/asm/iomap.h
@@ -9,14 +9,19 @@
 #include <linux/fs.h>
 #include <linux/mm.h>
 #include <linux/uaccess.h>
-#include <linux/highmem.h>
 #include <asm/cacheflush.h>
 #include <asm/tlbflush.h>
 
-void __iomem *__iomap_local_pfn_prot(unsigned long pfn, pgprot_t prot);
+void __iomem *
+iomap_atomic_prot_pfn(unsigned long pfn, pgprot_t prot);
 
-int iomap_create_wc(resource_size_t base, unsigned long size, pgprot_t *prot);
+void
+iounmap_atomic(void __iomem *kvaddr);
 
-void iomap_free(resource_size_t base, unsigned long size);
+int
+iomap_create_wc(resource_size_t base, unsigned long size, pgprot_t *prot);
+
+void
+iomap_free(resource_size_t base, unsigned long size);
 
 #endif /* _ASM_X86_IOMAP_H */
diff --git a/kernel/arch/x86/include/asm/kmap_types.h b/kernel/arch/x86/include/asm/kmap_types.h
new file mode 100644
index 0000000..04ab826
--- /dev/null
+++ b/kernel/arch/x86/include/asm/kmap_types.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _ASM_X86_KMAP_TYPES_H
+#define _ASM_X86_KMAP_TYPES_H
+
+#if defined(CONFIG_X86_32) && defined(CONFIG_DEBUG_HIGHMEM)
+#define  __WITH_KM_FENCE
+#endif
+
+#include <asm-generic/kmap_types.h>
+
+#undef __WITH_KM_FENCE
+
+#endif /* _ASM_X86_KMAP_TYPES_H */
diff --git a/kernel/arch/x86/include/asm/paravirt_types.h b/kernel/arch/x86/include/asm/paravirt_types.h
index 130f428..903d718 100644
--- a/kernel/arch/x86/include/asm/paravirt_types.h
+++ b/kernel/arch/x86/include/asm/paravirt_types.h
@@ -41,6 +41,7 @@
 #ifndef __ASSEMBLY__
 
 #include <asm/desc_defs.h>
+#include <asm/kmap_types.h>
 #include <asm/pgtable_types.h>
 #include <asm/nospec-branch.h>
 
diff --git a/kernel/arch/x86/include/asm/preempt.h b/kernel/arch/x86/include/asm/preempt.h
index afe37a8..a334dd0 100644
--- a/kernel/arch/x86/include/asm/preempt.h
+++ b/kernel/arch/x86/include/asm/preempt.h
@@ -89,24 +89,9 @@
  * a decrement which hits zero means we have no preempt_count and should
  * reschedule.
  */
-static __always_inline bool ____preempt_count_dec_and_test(void)
-{
-	return GEN_UNARY_RMWcc("decl", __preempt_count, e, __percpu_arg([var]));
-}
-
 static __always_inline bool __preempt_count_dec_and_test(void)
 {
-	if (____preempt_count_dec_and_test())
-		return true;
-#ifdef CONFIG_PREEMPT_LAZY
-	if (preempt_count())
-		return false;
-	if (current_thread_info()->preempt_lazy_count)
-		return false;
-	return test_thread_flag(TIF_NEED_RESCHED_LAZY);
-#else
-	return false;
-#endif
+	return GEN_UNARY_RMWcc("decl", __preempt_count, e, __percpu_arg([var]));
 }
 
 /*
@@ -114,29 +99,10 @@
  */
 static __always_inline bool should_resched(int preempt_offset)
 {
-#ifdef CONFIG_PREEMPT_LAZY
-	u32 tmp;
-	tmp = raw_cpu_read_4(__preempt_count);
-	if (tmp == preempt_offset)
-		return true;
-
-	/* preempt count == 0 ? */
-	tmp &= ~PREEMPT_NEED_RESCHED;
-	if (tmp != preempt_offset)
-		return false;
-	/* XXX PREEMPT_LOCK_OFFSET */
-	if (current_thread_info()->preempt_lazy_count)
-		return false;
-	return test_thread_flag(TIF_NEED_RESCHED_LAZY);
-#else
 	return unlikely(raw_cpu_read_4(__preempt_count) == preempt_offset);
-#endif
 }
 
 #ifdef CONFIG_PREEMPTION
-#ifdef CONFIG_PREEMPT_RT
-  extern void preempt_schedule_lock(void);
-#endif
   extern asmlinkage void preempt_schedule_thunk(void);
 # define __preempt_schedule() \
 	asm volatile ("call preempt_schedule_thunk" : ASM_CALL_CONSTRAINT)
diff --git a/kernel/arch/x86/include/asm/signal.h b/kernel/arch/x86/include/asm/signal.h
index f3bf2f5..6fd8410 100644
--- a/kernel/arch/x86/include/asm/signal.h
+++ b/kernel/arch/x86/include/asm/signal.h
@@ -28,19 +28,6 @@
 #define SA_IA32_ABI	0x02000000u
 #define SA_X32_ABI	0x01000000u
 
-/*
- * Because some traps use the IST stack, we must keep preemption
- * disabled while calling do_trap(), but do_trap() may call
- * force_sig_info() which will grab the signal spin_locks for the
- * task, which in PREEMPT_RT are mutexes.  By defining
- * ARCH_RT_DELAYS_SIGNAL_SEND the force_sig_info() will set
- * TIF_NOTIFY_RESUME and set up the signal to be sent on exit of the
- * trap.
- */
-#if defined(CONFIG_PREEMPT_RT)
-#define ARCH_RT_DELAYS_SIGNAL_SEND
-#endif
-
 #ifndef CONFIG_COMPAT
 typedef sigset_t compat_sigset_t;
 #endif
diff --git a/kernel/arch/x86/include/asm/stackprotector.h b/kernel/arch/x86/include/asm/stackprotector.h
index 3df0a95..7fb482f 100644
--- a/kernel/arch/x86/include/asm/stackprotector.h
+++ b/kernel/arch/x86/include/asm/stackprotector.h
@@ -65,7 +65,7 @@
  */
 static __always_inline void boot_init_stack_canary(void)
 {
-	u64 canary = 0;
+	u64 canary;
 	u64 tsc;
 
 #ifdef CONFIG_X86_64
@@ -76,14 +76,8 @@
 	 * of randomness. The TSC only matters for very early init,
 	 * there it already has some randomness on most systems. Later
 	 * on during the bootup the random pool has true entropy too.
-	 * For preempt-rt we need to weaken the randomness a bit, as
-	 * we can't call into the random generator from atomic context
-	 * due to locking constraints. We just leave canary
-	 * uninitialized and use the TSC based randomness on top of it.
 	 */
-#ifndef CONFIG_PREEMPT_RT
 	get_random_bytes(&canary, sizeof(canary));
-#endif
 	tsc = rdtsc();
 	canary += tsc + (tsc << 32UL);
 	canary &= CANARY_MASK;
diff --git a/kernel/arch/x86/include/asm/thread_info.h b/kernel/arch/x86/include/asm/thread_info.h
index d77e99b..012c8ee 100644
--- a/kernel/arch/x86/include/asm/thread_info.h
+++ b/kernel/arch/x86/include/asm/thread_info.h
@@ -56,23 +56,16 @@
 struct thread_info {
 	unsigned long		flags;		/* low level flags */
 	u32			status;		/* thread synchronous flags */
-	int			preempt_lazy_count;	/* 0 => lazy preemptable
-							  <0 => BUG */
 };
 
 #define INIT_THREAD_INFO(tsk)			\
 {						\
 	.flags		= 0,			\
-	.preempt_lazy_count = 0,		\
 }
 
 #else /* !__ASSEMBLY__ */
 
 #include <asm/asm-offsets.h>
-
-#define GET_THREAD_INFO(reg) \
-	_ASM_MOV PER_CPU_VAR(cpu_current_top_of_stack),reg ; \
-	_ASM_SUB $(THREAD_SIZE),reg ;
 
 #endif
 
@@ -110,7 +103,6 @@
 #define TIF_SYSCALL_TRACEPOINT	28	/* syscall tracepoint instrumentation */
 #define TIF_ADDR32		29	/* 32-bit address space on 64 bits */
 #define TIF_X32			30	/* 32-bit native x86-64 binary */
-#define TIF_NEED_RESCHED_LAZY	31	/* lazy rescheduling necessary */
 
 #define _TIF_SYSCALL_TRACE	(1 << TIF_SYSCALL_TRACE)
 #define _TIF_NOTIFY_RESUME	(1 << TIF_NOTIFY_RESUME)
@@ -132,7 +124,6 @@
 #define _TIF_IA32		(1 << TIF_IA32)
 #define _TIF_NOTIFY_SIGNAL	(1 << TIF_NOTIFY_SIGNAL)
 #define _TIF_SLD		(1 << TIF_SLD)
-#define _TIF_NEED_RESCHED_LAZY	(1 << TIF_NEED_RESCHED_LAZY)
 #define _TIF_POLLING_NRFLAG	(1 << TIF_POLLING_NRFLAG)
 #define _TIF_IO_BITMAP		(1 << TIF_IO_BITMAP)
 #define _TIF_FORCED_TF		(1 << TIF_FORCED_TF)
@@ -164,8 +155,6 @@
 #endif
 
 #define _TIF_WORK_CTXSW_NEXT	(_TIF_WORK_CTXSW)
-
-#define _TIF_NEED_RESCHED_MASK	(_TIF_NEED_RESCHED | _TIF_NEED_RESCHED_LAZY)
 
 #define STACK_WARN		(THREAD_SIZE/8)
 
diff --git a/kernel/arch/x86/kernel/crash_dump_32.c b/kernel/arch/x86/kernel/crash_dump_32.c
index 5fcac46..33ee476 100644
--- a/kernel/arch/x86/kernel/crash_dump_32.c
+++ b/kernel/arch/x86/kernel/crash_dump_32.c
@@ -13,6 +13,8 @@
 
 #include <linux/uaccess.h>
 
+static void *kdump_buf_page;
+
 static inline bool is_crashed_pfn_valid(unsigned long pfn)
 {
 #ifndef CONFIG_X86_PAE
@@ -39,11 +41,15 @@
  * @userbuf: if set, @buf is in user address space, use copy_to_user(),
  *	otherwise @buf is in kernel address space, use memcpy().
  *
- * Copy a page from "oldmem". For this page, there might be no pte mapped
- * in the current kernel.
+ * Copy a page from "oldmem". For this page, there is no pte mapped
+ * in the current kernel. We stitch up a pte, similar to kmap_atomic.
+ *
+ * Calling copy_to_user() in atomic context is not desirable. Hence first
+ * copying the data to a pre-allocated kernel page and then copying to user
+ * space in non-atomic context.
  */
-ssize_t copy_oldmem_page(unsigned long pfn, char *buf, size_t csize,
-			 unsigned long offset, int userbuf)
+ssize_t copy_oldmem_page(unsigned long pfn, char *buf,
+                               size_t csize, unsigned long offset, int userbuf)
 {
 	void  *vaddr;
 
@@ -53,16 +59,38 @@
 	if (!is_crashed_pfn_valid(pfn))
 		return -EFAULT;
 
-	vaddr = kmap_local_pfn(pfn);
+	vaddr = kmap_atomic_pfn(pfn);
 
 	if (!userbuf) {
-		memcpy(buf, vaddr + offset, csize);
+		memcpy(buf, (vaddr + offset), csize);
+		kunmap_atomic(vaddr);
 	} else {
-		if (copy_to_user(buf, vaddr + offset, csize))
-			csize = -EFAULT;
+		if (!kdump_buf_page) {
+			printk(KERN_WARNING "Kdump: Kdump buffer page not"
+				" allocated\n");
+			kunmap_atomic(vaddr);
+			return -EFAULT;
+		}
+		copy_page(kdump_buf_page, vaddr);
+		kunmap_atomic(vaddr);
+		if (copy_to_user(buf, (kdump_buf_page + offset), csize))
+			return -EFAULT;
 	}
-
-	kunmap_local(vaddr);
 
 	return csize;
 }
+
+static int __init kdump_buf_page_init(void)
+{
+	int ret = 0;
+
+	kdump_buf_page = kmalloc(PAGE_SIZE, GFP_KERNEL);
+	if (!kdump_buf_page) {
+		printk(KERN_WARNING "Kdump: Failed to allocate kdump buffer"
+			 " page\n");
+		ret = -ENOMEM;
+	}
+
+	return ret;
+}
+arch_initcall(kdump_buf_page_init);
diff --git a/kernel/arch/x86/kernel/fpu/core.c b/kernel/arch/x86/kernel/fpu/core.c
index d315d45..571220a 100644
--- a/kernel/arch/x86/kernel/fpu/core.c
+++ b/kernel/arch/x86/kernel/fpu/core.c
@@ -159,18 +159,6 @@
 }
 EXPORT_SYMBOL_GPL(kernel_fpu_end);
 
-void kernel_fpu_resched(void)
-{
-	WARN_ON_FPU(!this_cpu_read(in_kernel_fpu));
-
-	if (should_resched(PREEMPT_OFFSET)) {
-		kernel_fpu_end();
-		cond_resched();
-		kernel_fpu_begin();
-	}
-}
-EXPORT_SYMBOL_GPL(kernel_fpu_resched);
-
 /*
  * Save the FPU state (mark it for reload if necessary):
  *
diff --git a/kernel/arch/x86/kernel/irq_32.c b/kernel/arch/x86/kernel/irq_32.c
index 93c6b88..0b79efc 100644
--- a/kernel/arch/x86/kernel/irq_32.c
+++ b/kernel/arch/x86/kernel/irq_32.c
@@ -131,7 +131,6 @@
 	return 0;
 }
 
-#ifndef CONFIG_PREEMPT_RT
 void do_softirq_own_stack(void)
 {
 	struct irq_stack *irqstk;
@@ -148,7 +147,6 @@
 
 	call_on_stack(__do_softirq, isp);
 }
-#endif
 
 void __handle_irq(struct irq_desc *desc, struct pt_regs *regs)
 {
diff --git a/kernel/arch/x86/kernel/irq_64.c b/kernel/arch/x86/kernel/irq_64.c
index 7cfc4e6..440eed5 100644
--- a/kernel/arch/x86/kernel/irq_64.c
+++ b/kernel/arch/x86/kernel/irq_64.c
@@ -72,9 +72,7 @@
 	return map_irq_stack(cpu);
 }
 
-#ifndef CONFIG_PREEMPT_RT
 void do_softirq_own_stack(void)
 {
 	run_on_irqstack_cond(__do_softirq, NULL);
 }
-#endif
diff --git a/kernel/arch/x86/kvm/x86.c b/kernel/arch/x86/kvm/x86.c
index 4c62278..23d7c56 100644
--- a/kernel/arch/x86/kvm/x86.c
+++ b/kernel/arch/x86/kvm/x86.c
@@ -8096,14 +8096,6 @@
 		goto out;
 	}
 
-#ifdef CONFIG_PREEMPT_RT
-	if (!boot_cpu_has(X86_FEATURE_CONSTANT_TSC)) {
-		pr_err("RT requires X86_FEATURE_CONSTANT_TSC\n");
-		r = -EOPNOTSUPP;
-		goto out;
-	}
-#endif
-
 	r = -ENOMEM;
 	x86_fpu_cache = kmem_cache_create("x86_fpu", sizeof(struct fpu),
 					  __alignof__(struct fpu), SLAB_ACCOUNT,
diff --git a/kernel/arch/x86/mm/highmem_32.c b/kernel/arch/x86/mm/highmem_32.c
index 2c54b76..075fe51 100644
--- a/kernel/arch/x86/mm/highmem_32.c
+++ b/kernel/arch/x86/mm/highmem_32.c
@@ -4,6 +4,65 @@
 #include <linux/swap.h> /* for totalram_pages */
 #include <linux/memblock.h>
 
+void *kmap_atomic_high_prot(struct page *page, pgprot_t prot)
+{
+	unsigned long vaddr;
+	int idx, type;
+
+	type = kmap_atomic_idx_push();
+	idx = type + KM_TYPE_NR*smp_processor_id();
+	vaddr = __fix_to_virt(FIX_KMAP_BEGIN + idx);
+	BUG_ON(!pte_none(*(kmap_pte-idx)));
+	set_pte(kmap_pte-idx, mk_pte(page, prot));
+	arch_flush_lazy_mmu_mode();
+
+	return (void *)vaddr;
+}
+EXPORT_SYMBOL(kmap_atomic_high_prot);
+
+/*
+ * This is the same as kmap_atomic() but can map memory that doesn't
+ * have a struct page associated with it.
+ */
+void *kmap_atomic_pfn(unsigned long pfn)
+{
+	return kmap_atomic_prot_pfn(pfn, kmap_prot);
+}
+EXPORT_SYMBOL_GPL(kmap_atomic_pfn);
+
+void kunmap_atomic_high(void *kvaddr)
+{
+	unsigned long vaddr = (unsigned long) kvaddr & PAGE_MASK;
+
+	if (vaddr >= __fix_to_virt(FIX_KMAP_END) &&
+	    vaddr <= __fix_to_virt(FIX_KMAP_BEGIN)) {
+		int idx, type;
+
+		type = kmap_atomic_idx();
+		idx = type + KM_TYPE_NR * smp_processor_id();
+
+#ifdef CONFIG_DEBUG_HIGHMEM
+		WARN_ON_ONCE(vaddr != __fix_to_virt(FIX_KMAP_BEGIN + idx));
+#endif
+		/*
+		 * Force other mappings to Oops if they'll try to access this
+		 * pte without first remap it.  Keeping stale mappings around
+		 * is a bad idea also, in case the page changes cacheability
+		 * attributes or becomes a protected page in a hypervisor.
+		 */
+		kpte_clear_flush(kmap_pte-idx, vaddr);
+		kmap_atomic_idx_pop();
+		arch_flush_lazy_mmu_mode();
+	}
+#ifdef CONFIG_DEBUG_HIGHMEM
+	else {
+		BUG_ON(vaddr < PAGE_OFFSET);
+		BUG_ON(vaddr >= (unsigned long)high_memory);
+	}
+#endif
+}
+EXPORT_SYMBOL(kunmap_atomic_high);
+
 void __init set_highmem_pages_init(void)
 {
 	struct zone *zone;
diff --git a/kernel/arch/x86/mm/init_32.c b/kernel/arch/x86/mm/init_32.c
index da31c26..7c05525 100644
--- a/kernel/arch/x86/mm/init_32.c
+++ b/kernel/arch/x86/mm/init_32.c
@@ -394,6 +394,19 @@
 	return last_map_addr;
 }
 
+pte_t *kmap_pte;
+
+static void __init kmap_init(void)
+{
+	unsigned long kmap_vstart;
+
+	/*
+	 * Cache the first kmap pte:
+	 */
+	kmap_vstart = __fix_to_virt(FIX_KMAP_BEGIN);
+	kmap_pte = virt_to_kpte(kmap_vstart);
+}
+
 #ifdef CONFIG_HIGHMEM
 static void __init permanent_kmaps_init(pgd_t *pgd_base)
 {
@@ -699,6 +712,8 @@
 
 	__flush_tlb_all();
 
+	kmap_init();
+
 	/*
 	 * NOTE: at this point the bootmem allocator is fully available.
 	 */
diff --git a/kernel/arch/x86/mm/iomap_32.c b/kernel/arch/x86/mm/iomap_32.c
index 9aaa756..f60398a 100644
--- a/kernel/arch/x86/mm/iomap_32.c
+++ b/kernel/arch/x86/mm/iomap_32.c
@@ -44,7 +44,28 @@
 }
 EXPORT_SYMBOL_GPL(iomap_free);
 
-void __iomem *__iomap_local_pfn_prot(unsigned long pfn, pgprot_t prot)
+void *kmap_atomic_prot_pfn(unsigned long pfn, pgprot_t prot)
+{
+	unsigned long vaddr;
+	int idx, type;
+
+	preempt_disable();
+	pagefault_disable();
+
+	type = kmap_atomic_idx_push();
+	idx = type + KM_TYPE_NR * smp_processor_id();
+	vaddr = __fix_to_virt(FIX_KMAP_BEGIN + idx);
+	set_pte(kmap_pte - idx, pfn_pte(pfn, prot));
+	arch_flush_lazy_mmu_mode();
+
+	return (void *)vaddr;
+}
+
+/*
+ * Map 'pfn' using protections 'prot'
+ */
+void __iomem *
+iomap_atomic_prot_pfn(unsigned long pfn, pgprot_t prot)
 {
 	/*
 	 * For non-PAT systems, translate non-WB request to UC- just in
@@ -60,6 +81,36 @@
 	/* Filter out unsupported __PAGE_KERNEL* bits: */
 	pgprot_val(prot) &= __default_kernel_pte_mask;
 
-	return (void __force __iomem *)__kmap_local_pfn_prot(pfn, prot);
+	return (void __force __iomem *) kmap_atomic_prot_pfn(pfn, prot);
 }
-EXPORT_SYMBOL_GPL(__iomap_local_pfn_prot);
+EXPORT_SYMBOL_GPL(iomap_atomic_prot_pfn);
+
+void
+iounmap_atomic(void __iomem *kvaddr)
+{
+	unsigned long vaddr = (unsigned long) kvaddr & PAGE_MASK;
+
+	if (vaddr >= __fix_to_virt(FIX_KMAP_END) &&
+	    vaddr <= __fix_to_virt(FIX_KMAP_BEGIN)) {
+		int idx, type;
+
+		type = kmap_atomic_idx();
+		idx = type + KM_TYPE_NR * smp_processor_id();
+
+#ifdef CONFIG_DEBUG_HIGHMEM
+		WARN_ON_ONCE(vaddr != __fix_to_virt(FIX_KMAP_BEGIN + idx));
+#endif
+		/*
+		 * Force other mappings to Oops if they'll try to access this
+		 * pte without first remap it.  Keeping stale mappings around
+		 * is a bad idea also, in case the page changes cacheability
+		 * attributes or becomes a protected page in a hypervisor.
+		 */
+		kpte_clear_flush(kmap_pte-idx, vaddr);
+		kmap_atomic_idx_pop();
+	}
+
+	pagefault_enable();
+	preempt_enable();
+}
+EXPORT_SYMBOL_GPL(iounmap_atomic);
diff --git a/kernel/arch/xtensa/Kconfig b/kernel/arch/xtensa/Kconfig
index 03cbf6b..87e08ad 100644
--- a/kernel/arch/xtensa/Kconfig
+++ b/kernel/arch/xtensa/Kconfig
@@ -666,7 +666,6 @@
 config HIGHMEM
 	bool "High Memory Support"
 	depends on MMU
-	select KMAP_LOCAL
 	help
 	  Linux can use the full amount of RAM in the system by
 	  default. However, the default MMUv2 setup only maps the
diff --git a/kernel/arch/xtensa/include/asm/fixmap.h b/kernel/arch/xtensa/include/asm/fixmap.h
index 92049b6..a06ffb0 100644
--- a/kernel/arch/xtensa/include/asm/fixmap.h
+++ b/kernel/arch/xtensa/include/asm/fixmap.h
@@ -16,7 +16,7 @@
 #ifdef CONFIG_HIGHMEM
 #include <linux/threads.h>
 #include <linux/pgtable.h>
-#include <asm/kmap_size.h>
+#include <asm/kmap_types.h>
 #endif
 
 /*
@@ -39,7 +39,7 @@
 	/* reserved pte's for temporary kernel mappings */
 	FIX_KMAP_BEGIN,
 	FIX_KMAP_END = FIX_KMAP_BEGIN +
-		(KM_MAX_IDX * NR_CPUS * DCACHE_N_COLORS) - 1,
+		(KM_TYPE_NR * NR_CPUS * DCACHE_N_COLORS) - 1,
 #endif
 	__end_of_fixed_addresses
 };
diff --git a/kernel/arch/xtensa/include/asm/highmem.h b/kernel/arch/xtensa/include/asm/highmem.h
index 0fc3b1c..eac5032 100644
--- a/kernel/arch/xtensa/include/asm/highmem.h
+++ b/kernel/arch/xtensa/include/asm/highmem.h
@@ -16,8 +16,9 @@
 #include <linux/pgtable.h>
 #include <asm/cacheflush.h>
 #include <asm/fixmap.h>
+#include <asm/kmap_types.h>
 
-#define PKMAP_BASE		((FIXADDR_START -			\
+#define PKMAP_BASE		((FIXADDR_START - \
 				  (LAST_PKMAP + 1) * PAGE_SIZE) & PMD_MASK)
 #define LAST_PKMAP		(PTRS_PER_PTE * DCACHE_N_COLORS)
 #define LAST_PKMAP_MASK		(LAST_PKMAP - 1)
@@ -66,15 +67,6 @@
 {
 	flush_cache_all();
 }
-
-enum fixed_addresses kmap_local_map_idx(int type, unsigned long pfn);
-#define arch_kmap_local_map_idx		kmap_local_map_idx
-
-enum fixed_addresses kmap_local_unmap_idx(int type, unsigned long addr);
-#define arch_kmap_local_unmap_idx	kmap_local_unmap_idx
-
-#define arch_kmap_local_post_unmap(vaddr)	\
-	local_flush_tlb_kernel_range(vaddr, vaddr + PAGE_SIZE)
 
 void kmap_init(void);
 
diff --git a/kernel/arch/xtensa/include/asm/spinlock_types.h b/kernel/arch/xtensa/include/asm/spinlock_types.h
index dc84632..64c9389 100644
--- a/kernel/arch/xtensa/include/asm/spinlock_types.h
+++ b/kernel/arch/xtensa/include/asm/spinlock_types.h
@@ -2,6 +2,10 @@
 #ifndef __ASM_SPINLOCK_TYPES_H
 #define __ASM_SPINLOCK_TYPES_H
 
+#if !defined(__LINUX_SPINLOCK_TYPES_H) && !defined(__ASM_SPINLOCK_H)
+# error "please don't include this file directly"
+#endif
+
 #include <asm-generic/qspinlock_types.h>
 #include <asm-generic/qrwlock_types.h>
 
diff --git a/kernel/arch/xtensa/mm/highmem.c b/kernel/arch/xtensa/mm/highmem.c
index 0735ca5..673196f 100644
--- a/kernel/arch/xtensa/mm/highmem.c
+++ b/kernel/arch/xtensa/mm/highmem.c
@@ -12,6 +12,8 @@
 #include <linux/highmem.h>
 #include <asm/tlbflush.h>
 
+static pte_t *kmap_pte;
+
 #if DCACHE_WAY_SIZE > PAGE_SIZE
 unsigned int last_pkmap_nr_arr[DCACHE_N_COLORS];
 wait_queue_head_t pkmap_map_wait_arr[DCACHE_N_COLORS];
@@ -31,25 +33,59 @@
 
 static inline enum fixed_addresses kmap_idx(int type, unsigned long color)
 {
-	return (type + KM_MAX_IDX * smp_processor_id()) * DCACHE_N_COLORS +
+	return (type + KM_TYPE_NR * smp_processor_id()) * DCACHE_N_COLORS +
 		color;
 }
 
-enum fixed_addresses kmap_local_map_idx(int type, unsigned long pfn)
+void *kmap_atomic_high_prot(struct page *page, pgprot_t prot)
 {
-	return kmap_idx(type, DCACHE_ALIAS(pfn << PAGE_SHIFT));
-}
+	enum fixed_addresses idx;
+	unsigned long vaddr;
 
-enum fixed_addresses kmap_local_unmap_idx(int type, unsigned long addr)
-{
-	return kmap_idx(type, DCACHE_ALIAS(addr));
+	idx = kmap_idx(kmap_atomic_idx_push(),
+		       DCACHE_ALIAS(page_to_phys(page)));
+	vaddr = __fix_to_virt(FIX_KMAP_BEGIN + idx);
+#ifdef CONFIG_DEBUG_HIGHMEM
+	BUG_ON(!pte_none(*(kmap_pte + idx)));
+#endif
+	set_pte(kmap_pte + idx, mk_pte(page, prot));
+
+	return (void *)vaddr;
 }
+EXPORT_SYMBOL(kmap_atomic_high_prot);
+
+void kunmap_atomic_high(void *kvaddr)
+{
+	if (kvaddr >= (void *)FIXADDR_START &&
+	    kvaddr < (void *)FIXADDR_TOP) {
+		int idx = kmap_idx(kmap_atomic_idx(),
+				   DCACHE_ALIAS((unsigned long)kvaddr));
+
+		/*
+		 * Force other mappings to Oops if they'll try to access this
+		 * pte without first remap it.  Keeping stale mappings around
+		 * is a bad idea also, in case the page changes cacheability
+		 * attributes or becomes a protected page in a hypervisor.
+		 */
+		pte_clear(&init_mm, kvaddr, kmap_pte + idx);
+		local_flush_tlb_kernel_range((unsigned long)kvaddr,
+					     (unsigned long)kvaddr + PAGE_SIZE);
+
+		kmap_atomic_idx_pop();
+	}
+}
+EXPORT_SYMBOL(kunmap_atomic_high);
 
 void __init kmap_init(void)
 {
+	unsigned long kmap_vstart;
+
 	/* Check if this memory layout is broken because PKMAP overlaps
 	 * page table.
 	 */
 	BUILD_BUG_ON(PKMAP_BASE < TLBTEMP_BASE_1 + TLBTEMP_SIZE);
+	/* cache the first kmap pte */
+	kmap_vstart = __fix_to_virt(FIX_KMAP_BEGIN);
+	kmap_pte = virt_to_kpte(kmap_vstart);
 	kmap_waitqueues_init();
 }
diff --git a/kernel/block/blk-mq.c b/kernel/block/blk-mq.c
index ba16e1d..5d520b1 100644
--- a/kernel/block/blk-mq.c
+++ b/kernel/block/blk-mq.c
@@ -43,7 +43,7 @@
 
 #include <trace/hooks/block.h>
 
-static DEFINE_PER_CPU(struct llist_head, blk_cpu_done);
+static DEFINE_PER_CPU(struct list_head, blk_cpu_done);
 
 static void blk_mq_poll_stats_start(struct request_queue *q);
 static void blk_mq_poll_stats_fn(struct blk_stat_callback *cb);
@@ -571,29 +571,80 @@
 }
 EXPORT_SYMBOL(blk_mq_end_request);
 
-static void blk_complete_reqs(struct llist_head *list)
-{
-	struct llist_node *entry = llist_reverse_order(llist_del_all(list));
-	struct request *rq, *next;
-
-	llist_for_each_entry_safe(rq, next, entry, ipi_list)
-		rq->q->mq_ops->complete(rq);
-}
-
+/*
+ * Softirq action handler - move entries to local list and loop over them
+ * while passing them to the queue registered handler.
+ */
 static __latent_entropy void blk_done_softirq(struct softirq_action *h)
 {
-	blk_complete_reqs(this_cpu_ptr(&blk_cpu_done));
+	struct list_head *cpu_list, local_list;
+
+	local_irq_disable();
+	cpu_list = this_cpu_ptr(&blk_cpu_done);
+	list_replace_init(cpu_list, &local_list);
+	local_irq_enable();
+
+	while (!list_empty(&local_list)) {
+		struct request *rq;
+
+		rq = list_entry(local_list.next, struct request, ipi_list);
+		list_del_init(&rq->ipi_list);
+		rq->q->mq_ops->complete(rq);
+	}
+}
+
+static void blk_mq_trigger_softirq(struct request *rq)
+{
+	struct list_head *list;
+	unsigned long flags;
+
+	local_irq_save(flags);
+	list = this_cpu_ptr(&blk_cpu_done);
+	list_add_tail(&rq->ipi_list, list);
+
+	/*
+	 * If the list only contains our just added request, signal a raise of
+	 * the softirq.  If there are already entries there, someone already
+	 * raised the irq but it hasn't run yet.
+	 */
+	if (list->next == &rq->ipi_list)
+		raise_softirq_irqoff(BLOCK_SOFTIRQ);
+	local_irq_restore(flags);
 }
 
 static int blk_softirq_cpu_dead(unsigned int cpu)
 {
-	blk_complete_reqs(&per_cpu(blk_cpu_done, cpu));
+	/*
+	 * If a CPU goes away, splice its entries to the current CPU
+	 * and trigger a run of the softirq
+	 */
+	local_irq_disable();
+	list_splice_init(&per_cpu(blk_cpu_done, cpu),
+			 this_cpu_ptr(&blk_cpu_done));
+	raise_softirq_irqoff(BLOCK_SOFTIRQ);
+	local_irq_enable();
+
 	return 0;
 }
 
+
 static void __blk_mq_complete_request_remote(void *data)
 {
-	__raise_softirq_irqoff(BLOCK_SOFTIRQ);
+	struct request *rq = data;
+
+	/*
+	 * For most of single queue controllers, there is only one irq vector
+	 * for handling I/O completion, and the only irq's affinity is set
+	 * to all possible CPUs.  On most of ARCHs, this affinity means the irq
+	 * is handled on one specific CPU.
+	 *
+	 * So complete I/O requests in softirq context in case of single queue
+	 * devices to avoid degrading I/O performance due to irqsoff latency.
+	 */
+	if (rq->q->nr_hw_queues == 1)
+		blk_mq_trigger_softirq(rq);
+	else
+		rq->q->mq_ops->complete(rq);
 }
 
 static inline bool blk_mq_complete_need_ipi(struct request *rq)
@@ -602,14 +653,6 @@
 
 	if (!IS_ENABLED(CONFIG_SMP) ||
 	    !test_bit(QUEUE_FLAG_SAME_COMP, &rq->q->queue_flags))
-		return false;
-	/*
-	 * With force threaded interrupts enabled, raising softirq from an SMP
-	 * function call will always result in waking the ksoftirqd thread.
-	 * This is probably worse than completing the request on a different
-	 * cache domain.
-	 */
-	if (force_irqthreads)
 		return false;
 
 	/* same CPU or cache domain?  Complete locally */
@@ -620,32 +663,6 @@
 
 	/* don't try to IPI to an offline CPU */
 	return cpu_online(rq->mq_ctx->cpu);
-}
-
-static void blk_mq_complete_send_ipi(struct request *rq)
-{
-	struct llist_head *list;
-	unsigned int cpu;
-
-	cpu = rq->mq_ctx->cpu;
-	list = &per_cpu(blk_cpu_done, cpu);
-	if (llist_add(&rq->ipi_list, list)) {
-		rq->csd.func = __blk_mq_complete_request_remote;
-		rq->csd.info = rq;
-		rq->csd.flags = 0;
-		smp_call_function_single_async(cpu, &rq->csd);
-	}
-}
-
-static void blk_mq_raise_softirq(struct request *rq)
-{
-	struct llist_head *list;
-
-	preempt_disable();
-	list = this_cpu_ptr(&blk_cpu_done);
-	if (llist_add(&rq->ipi_list, list))
-		raise_softirq(BLOCK_SOFTIRQ);
-	preempt_enable();
 }
 
 bool blk_mq_complete_request_remote(struct request *rq)
@@ -660,15 +677,17 @@
 		return false;
 
 	if (blk_mq_complete_need_ipi(rq)) {
-		blk_mq_complete_send_ipi(rq);
-		return true;
+		rq->csd.func = __blk_mq_complete_request_remote;
+		rq->csd.info = rq;
+		rq->csd.flags = 0;
+		smp_call_function_single_async(rq->mq_ctx->cpu, &rq->csd);
+	} else {
+		if (rq->q->nr_hw_queues > 1)
+			return false;
+		blk_mq_trigger_softirq(rq);
 	}
 
-	if (rq->q->nr_hw_queues == 1) {
-		blk_mq_raise_softirq(rq);
-		return true;
-	}
-	return false;
+	return true;
 }
 EXPORT_SYMBOL_GPL(blk_mq_complete_request_remote);
 
@@ -1577,14 +1596,14 @@
 		return;
 
 	if (!async && !(hctx->flags & BLK_MQ_F_BLOCKING)) {
-		int cpu = get_cpu_light();
+		int cpu = get_cpu();
 		if (cpumask_test_cpu(cpu, hctx->cpumask)) {
 			__blk_mq_run_hw_queue(hctx);
-			put_cpu_light();
+			put_cpu();
 			return;
 		}
 
-		put_cpu_light();
+		put_cpu();
 	}
 
 	kblockd_mod_delayed_work_on(blk_mq_hctx_next_cpu(hctx), &hctx->run_work,
@@ -4019,7 +4038,7 @@
 	int i;
 
 	for_each_possible_cpu(i)
-		init_llist_head(&per_cpu(blk_cpu_done, i));
+		INIT_LIST_HEAD(&per_cpu(blk_cpu_done, i));
 	open_softirq(BLOCK_SOFTIRQ, blk_done_softirq);
 
 	cpuhp_setup_state_nocalls(CPUHP_BLOCK_SOFTIRQ_DEAD,
diff --git a/kernel/drivers/ata/libahci.c b/kernel/drivers/ata/libahci.c
index fec2e97..055439e 100644
--- a/kernel/drivers/ata/libahci.c
+++ b/kernel/drivers/ata/libahci.c
@@ -1454,7 +1454,7 @@
 		*class = ahci_dev_classify(ap);
 
 	/* re-enable FBS if disabled before */
-	if (fbs_disabled)
+	if (fbs_disabled || (!ata_is_host_link(link) && pp->fbs_supported))
 		ahci_enable_fbs(ap);
 
 	DPRINTK("EXIT, class=%u\n", *class);
diff --git a/kernel/drivers/atm/eni.c b/kernel/drivers/atm/eni.c
index a31ffe1..9fcc49b 100644
--- a/kernel/drivers/atm/eni.c
+++ b/kernel/drivers/atm/eni.c
@@ -2056,7 +2056,7 @@
 	}
 	submitted++;
 	ATM_SKB(skb)->vcc = vcc;
-	tasklet_disable_in_atomic(&ENI_DEV(vcc->dev)->task);
+	tasklet_disable(&ENI_DEV(vcc->dev)->task);
 	res = do_tx(skb);
 	tasklet_enable(&ENI_DEV(vcc->dev)->task);
 	if (res == enq_ok) return 0;
diff --git a/kernel/drivers/block/zram/zram_drv.c b/kernel/drivers/block/zram/zram_drv.c
index f7e8f25..a8b8538 100644
--- a/kernel/drivers/block/zram/zram_drv.c
+++ b/kernel/drivers/block/zram/zram_drv.c
@@ -59,40 +59,6 @@
 static int zram_bvec_read(struct zram *zram, struct bio_vec *bvec,
 				u32 index, int offset, struct bio *bio);
 
-#ifdef CONFIG_PREEMPT_RT
-static void zram_meta_init_table_locks(struct zram *zram, size_t num_pages)
-{
-	size_t index;
-
-	for (index = 0; index < num_pages; index++)
-		spin_lock_init(&zram->table[index].lock);
-}
-
-static int zram_slot_trylock(struct zram *zram, u32 index)
-{
-	int ret;
-
-	ret = spin_trylock(&zram->table[index].lock);
-	if (ret)
-		__set_bit(ZRAM_LOCK, &zram->table[index].flags);
-	return ret;
-}
-
-static void zram_slot_lock(struct zram *zram, u32 index)
-{
-	spin_lock(&zram->table[index].lock);
-	__set_bit(ZRAM_LOCK, &zram->table[index].flags);
-}
-
-static void zram_slot_unlock(struct zram *zram, u32 index)
-{
-	__clear_bit(ZRAM_LOCK, &zram->table[index].flags);
-	spin_unlock(&zram->table[index].lock);
-}
-
-#else
-
-static void zram_meta_init_table_locks(struct zram *zram, size_t num_pages) { }
 
 static int zram_slot_trylock(struct zram *zram, u32 index)
 {
@@ -108,7 +74,6 @@
 {
 	bit_spin_unlock(ZRAM_LOCK, &zram->table[index].flags);
 }
-#endif
 
 static inline bool init_done(struct zram *zram)
 {
@@ -1213,7 +1178,6 @@
 
 	if (!huge_class_size)
 		huge_class_size = zs_huge_class_size(zram->mem_pool);
-	zram_meta_init_table_locks(zram, num_pages);
 	return true;
 }
 
diff --git a/kernel/drivers/block/zram/zram_drv.h b/kernel/drivers/block/zram/zram_drv.h
index 7e4dd44..f2fd46d 100644
--- a/kernel/drivers/block/zram/zram_drv.h
+++ b/kernel/drivers/block/zram/zram_drv.h
@@ -63,7 +63,6 @@
 		unsigned long element;
 	};
 	unsigned long flags;
-	spinlock_t lock;
 #ifdef CONFIG_ZRAM_MEMORY_TRACKING
 	ktime_t ac_time;
 #endif
diff --git a/kernel/drivers/char/tpm/tpm-dev-common.c b/kernel/drivers/char/tpm/tpm-dev-common.c
index dc4c0a0..b99e194 100644
--- a/kernel/drivers/char/tpm/tpm-dev-common.c
+++ b/kernel/drivers/char/tpm/tpm-dev-common.c
@@ -20,6 +20,7 @@
 #include "tpm-dev.h"
 
 static struct workqueue_struct *tpm_dev_wq;
+static DEFINE_MUTEX(tpm_dev_wq_lock);
 
 static ssize_t tpm_dev_transmit(struct tpm_chip *chip, struct tpm_space *space,
 				u8 *buf, size_t bufsiz)
diff --git a/kernel/drivers/char/tpm/tpm_tis.c b/kernel/drivers/char/tpm/tpm_tis.c
index c2bd0d4..4ed6e66 100644
--- a/kernel/drivers/char/tpm/tpm_tis.c
+++ b/kernel/drivers/char/tpm/tpm_tis.c
@@ -50,31 +50,6 @@
 	return container_of(data, struct tpm_tis_tcg_phy, priv);
 }
 
-#ifdef CONFIG_PREEMPT_RT
-/*
- * Flushes previous write operations to chip so that a subsequent
- * ioread*()s won't stall a cpu.
- */
-static inline void tpm_tis_flush(void __iomem *iobase)
-{
-	ioread8(iobase + TPM_ACCESS(0));
-}
-#else
-#define tpm_tis_flush(iobase) do { } while (0)
-#endif
-
-static inline void tpm_tis_iowrite8(u8 b, void __iomem *iobase, u32 addr)
-{
-	iowrite8(b, iobase + addr);
-	tpm_tis_flush(iobase);
-}
-
-static inline void tpm_tis_iowrite32(u32 b, void __iomem *iobase, u32 addr)
-{
-	iowrite32(b, iobase + addr);
-	tpm_tis_flush(iobase);
-}
-
 static int interrupts = -1;
 module_param(interrupts, int, 0444);
 MODULE_PARM_DESC(interrupts, "Enable interrupts");
@@ -194,7 +169,7 @@
 	struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(data);
 
 	while (len--)
-		tpm_tis_iowrite8(*value++, phy->iobase, addr);
+		iowrite8(*value++, phy->iobase + addr);
 
 	return 0;
 }
@@ -221,7 +196,7 @@
 {
 	struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(data);
 
-	tpm_tis_iowrite32(value, phy->iobase, addr);
+	iowrite32(value, phy->iobase + addr);
 
 	return 0;
 }
diff --git a/kernel/drivers/clk/rockchip/clk-pll.c b/kernel/drivers/clk/rockchip/clk-pll.c
index 91a0674..8008ee9 100644
--- a/kernel/drivers/clk/rockchip/clk-pll.c
+++ b/kernel/drivers/clk/rockchip/clk-pll.c
@@ -332,6 +332,64 @@
 	return rate_table;
 }
 
+static u32
+rockchip_rk3588_pll_frac_get(u32 m, u32 p, u32 s, u64 fin_hz, u64 fvco)
+{
+	u64 fref, fout, ffrac;
+	u32 k = 0;
+
+	fref = fin_hz / p;
+	ffrac = fvco - (m * fref);
+	fout = ffrac * 65536;
+	k = fout / fref;
+	if (k > 32767) {
+		fref = fin_hz / p;
+		ffrac = ((m + 1) * fref) - fvco;
+		fout = ffrac * 65536;
+		k = ((fout * 10 / fref) + 7) / 10;
+		if (k > 32767)
+			k = 0;
+		else
+			k = ~k + 1;
+	}
+	return k;
+}
+
+static struct rockchip_pll_rate_table *
+rockchip_rk3588_pll_frac_by_auto(unsigned long fin_hz,  unsigned long fout_hz)
+{
+	struct rockchip_pll_rate_table *rate_table = rk_pll_rate_table_get();
+	u64 fvco_min = 2250 * MHZ, fvco_max = 4500 * MHZ;
+	u32 p, m, s, k;
+	u64 fvco;
+
+	for (s = 0; s <= 6; s++) {
+		fvco = (u64)fout_hz << s;
+		if (fvco < fvco_min || fvco > fvco_max)
+			continue;
+		for (p = 1; p <= 4; p++) {
+			for (m = 64; m <= 1023; m++) {
+				if ((fvco >= m * fin_hz / p) && (fvco < (m + 1) * fin_hz / p)) {
+					k = rockchip_rk3588_pll_frac_get(m, p, s,
+									 (u64)fin_hz,
+									 fvco);
+					if (!k)
+						continue;
+					rate_table->p = p;
+					rate_table->s = s;
+					rate_table->k = k;
+					if (k > 32767)
+						rate_table->m = m + 1;
+					else
+						rate_table->m = m;
+					return rate_table;
+				}
+			}
+		}
+	}
+	return NULL;
+}
+
 static struct rockchip_pll_rate_table *
 rockchip_rk3588_pll_clk_set_by_auto(struct rockchip_clk_pll *pll,
 				    unsigned long fin_hz,
@@ -341,7 +399,7 @@
 	u64 fvco_min = 2250 * MHZ, fvco_max = 4500 * MHZ;
 	u64 fout_min = 37 * MHZ, fout_max = 4500 * MHZ;
 	u32 p, m, s;
-	u64 fvco, fref, fout, ffrac;
+	u64 fvco;
 
 	if (fin_hz == 0 || fout_hz == 0 || fout_hz == fin_hz)
 		return NULL;
@@ -368,26 +426,11 @@
 		}
 		pr_err("CANNOT FIND Fout by auto,fout = %lu\n", fout_hz);
 	} else {
-		for (s = 0; s <= 6; s++) {
-			fvco = (u64)fout_hz << s;
-			if (fvco < fvco_min || fvco > fvco_max)
-				continue;
-			for (p = 1; p <= 4; p++) {
-				for (m = 64; m <= 1023; m++) {
-					if ((fvco >= m * fin_hz / p) && (fvco < (m + 1) * fin_hz / p)) {
-						rate_table->p = p;
-						rate_table->m = m;
-						rate_table->s = s;
-						fref = fin_hz / p;
-						ffrac = fvco - (m * fref);
-						fout = ffrac * 65536;
-						rate_table->k = fout / fref;
-						return rate_table;
-					}
-				}
-			}
-		}
-		pr_err("CANNOT FIND Fout by auto,fout = %lu\n", fout_hz);
+		rate_table = rockchip_rk3588_pll_frac_by_auto(fin_hz, fout_hz);
+		if (!rate_table)
+			pr_err("CANNOT FIND Fout by auto,fout = %lu\n", fout_hz);
+		else
+			return rate_table;
 	}
 	return NULL;
 }
@@ -1345,7 +1388,17 @@
 	rate64 *= cur.m;
 	do_div(rate64, cur.p);
 
-	if (cur.k) {
+	if (cur.k & BIT(15)) {
+		/* fractional mode */
+		u64 frac_rate64;
+
+		cur.k = (~(cur.k - 1)) & RK3588_PLLCON2_K_MASK;
+		frac_rate64 = prate * cur.k;
+		postdiv = cur.p;
+		postdiv *= 65536;
+		do_div(frac_rate64, postdiv);
+		rate64 -= frac_rate64;
+	} else {
 		/* fractional mode */
 		u64 frac_rate64 = prate * cur.k;
 
@@ -1516,7 +1569,7 @@
 {
 	struct clk *parent = clk_get_parent(clk);
 	struct rockchip_clk_pll *pll;
-	static u32 frac, fbdiv;
+	static u32 frac, fbdiv, s, p;
 	bool negative;
 	u32 pllcon, pllcon0, pllcon2, fbdiv_mask, frac_mask, frac_shift;
 	u64 fracdiv, m, n;
@@ -1567,11 +1620,6 @@
 	negative = !!(ppm & BIT(31));
 	ppm = negative ? ~ppm + 1 : ppm;
 
-	if (!frac) {
-		frac = readl_relaxed(pll->reg_base + pllcon2) & frac_mask;
-		fbdiv = readl_relaxed(pll->reg_base + pllcon0) & fbdiv_mask;
-	}
-
 	switch (pll->type) {
 	case pll_rk3036:
 	case pll_rk3328:
@@ -1583,6 +1631,10 @@
 		 *    1 << 24                 1 << 24       1000000
 		 *
 		 */
+		if (!frac) {
+			frac = readl_relaxed(pll->reg_base + pllcon2) & frac_mask;
+			fbdiv = readl_relaxed(pll->reg_base + pllcon0) & fbdiv_mask;
+		}
 		m = div64_u64((uint64_t)frac * ppm, 1000000);
 		n = div64_u64((uint64_t)ppm << 24, 1000000) * fbdiv;
 
@@ -1597,13 +1649,65 @@
 		writel_relaxed(pllcon, pll->reg_base + pllcon2);
 		break;
 	case pll_rk3588:
-		m = div64_u64((uint64_t)frac * ppm, 100000);
-		n = div64_u64((uint64_t)ppm * 65535 * fbdiv, 100000);
+		if (!fbdiv) {
+			frac = readl_relaxed(pll->reg_base + pllcon2) & frac_mask;
+			fbdiv = readl_relaxed(pll->reg_base + pllcon0) & fbdiv_mask;
+		}
+		if (!frac) {
+			pllcon = readl_relaxed(pll->reg_base + RK3588_PLLCON(1));
+			s = ((pllcon >> RK3588_PLLCON1_S_SHIFT)
+				& RK3588_PLLCON1_S_MASK);
+			p = ((pllcon >> RK3588_PLLCON1_P_SHIFT)
+				& RK3588_PLLCON1_P_MASK);
+			m = div64_u64((uint64_t)clk_get_rate(clk) * ppm, 24000000);
+			n = div64_u64((uint64_t)m * 65536 * p * (1 << s), 1000000);
 
-		fracdiv = negative ? frac - (div64_u64(m + n, 10)) : frac + (div64_u64(m + n, 10));
-
-		if (!frac || fracdiv > frac_mask)
-			return -EINVAL;
+			if (n > 32767)
+				return -EINVAL;
+			fracdiv = negative ? ~n + 1 : n;
+		} else if (frac & BIT(15)) {
+			frac = (~(frac - 1)) & RK3588_PLLCON2_K_MASK;
+			m = div64_u64((uint64_t)frac * ppm, 100000);
+			n = div64_u64((uint64_t)ppm * 65536 * fbdiv, 100000);
+			if (negative) {
+				fracdiv = frac + (div64_u64(m + n, 10));
+				if (fracdiv > 32767)
+					return -EINVAL;
+				fracdiv = ~fracdiv + 1;
+			} else {
+				s = div64_u64(m + n, 10);
+				if (frac >= s) {
+					fracdiv = frac - s;
+					if (fracdiv > 32767)
+						return -EINVAL;
+					fracdiv = ~fracdiv + 1;
+				} else {
+					fracdiv = s - frac;
+					if (fracdiv > 32767)
+						return -EINVAL;
+				}
+			}
+		} else {
+			m = div64_u64((uint64_t)frac * ppm, 100000);
+			n = div64_u64((uint64_t)ppm * 65536 * fbdiv, 100000);
+			if (!negative) {
+				fracdiv = frac + (div64_u64(m + n, 10));
+				if (fracdiv > 32767)
+					return -EINVAL;
+			} else {
+				s = div64_u64(m + n, 10);
+				if (frac >= s) {
+					fracdiv = frac - s;
+					if (fracdiv > 32767)
+						return -EINVAL;
+				} else {
+					fracdiv = s - frac;
+					if (fracdiv > 32767)
+						return -EINVAL;
+					fracdiv = ~fracdiv + 1;
+				}
+			}
+		}
 
 		writel_relaxed(HIWORD_UPDATE(fracdiv, frac_mask, frac_shift),
 			       pll->reg_base + pllcon2);
diff --git a/kernel/drivers/clk/rockchip/clk-rk3328.c b/kernel/drivers/clk/rockchip/clk-rk3328.c
index 8de60c4..8ec63aa 100644
--- a/kernel/drivers/clk/rockchip/clk-rk3328.c
+++ b/kernel/drivers/clk/rockchip/clk-rk3328.c
@@ -317,14 +317,15 @@
 			RK3328_CLKGATE_CON(14), 1, GFLAGS),
 
 	/* PD_DDR */
-	COMPOSITE(0, "clk_ddr", mux_ddrphy_p, CLK_IS_CRITICAL,
-			RK3328_CLKSEL_CON(3), 8, 2, MFLAGS, 0, 3, DFLAGS | CLK_DIVIDER_POWER_OF_TWO,
-			RK3328_CLKGATE_CON(0), 4, GFLAGS),
-	GATE(0, "clk_ddrmsch", "clk_ddr", CLK_IS_CRITICAL,
+	COMPOSITE_DDRCLK(SCLK_DDRCLK, "sclk_ddrc", mux_ddrphy_p, 0,
+			 RK3328_CLKSEL_CON(3), 8, 2, 0, 3,
+			 ROCKCHIP_DDRCLK_SIP_V2),
+
+	GATE(0, "clk_ddrmsch", "sclk_ddrc", CLK_IGNORE_UNUSED,
 			RK3328_CLKGATE_CON(18), 6, GFLAGS),
-	GATE(0, "clk_ddrupctl", "clk_ddr", CLK_IS_CRITICAL,
+	GATE(0, "clk_ddrupctl", "sclk_ddrc", CLK_IGNORE_UNUSED,
 			RK3328_CLKGATE_CON(18), 5, GFLAGS),
-	GATE(0, "aclk_ddrupctl", "clk_ddr", CLK_IGNORE_UNUSED,
+	GATE(0, "aclk_ddrupctl", "sclk_ddrc", CLK_IGNORE_UNUSED,
 			RK3328_CLKGATE_CON(18), 4, GFLAGS),
 	GATE(0, "clk_ddrmon", "xin24m", CLK_IGNORE_UNUSED,
 			RK3328_CLKGATE_CON(0), 6, GFLAGS),
diff --git a/kernel/drivers/clk/rockchip/clk-rk3399.c b/kernel/drivers/clk/rockchip/clk-rk3399.c
index f02f459..6112951 100644
--- a/kernel/drivers/clk/rockchip/clk-rk3399.c
+++ b/kernel/drivers/clk/rockchip/clk-rk3399.c
@@ -651,9 +651,9 @@
 	MUX(SCLK_RMII_SRC, "clk_rmii_src", mux_rmii_p, CLK_SET_RATE_PARENT,
 			RK3399_CLKSEL_CON(19), 4, 1, MFLAGS),
 	GATE(SCLK_MACREF_OUT, "clk_mac_refout", "clk_rmii_src", 0,
-			RK3399_CLKGATE_CON(5), 6, GFLAGS),
-	GATE(SCLK_MACREF, "clk_mac_ref", "clk_rmii_src", 0,
 			RK3399_CLKGATE_CON(5), 7, GFLAGS),
+	GATE(SCLK_MACREF, "clk_mac_ref", "clk_rmii_src", 0,
+			RK3399_CLKGATE_CON(5), 6, GFLAGS),
 	GATE(SCLK_MAC_RX, "clk_rmii_rx", "clk_rmii_src", 0,
 			RK3399_CLKGATE_CON(5), 8, GFLAGS),
 	GATE(SCLK_MAC_TX, "clk_rmii_tx", "clk_rmii_src", 0,
diff --git a/kernel/drivers/clk/rockchip/clk-rk3568.c b/kernel/drivers/clk/rockchip/clk-rk3568.c
index 5c10de9..79365eb 100644
--- a/kernel/drivers/clk/rockchip/clk-rk3568.c
+++ b/kernel/drivers/clk/rockchip/clk-rk3568.c
@@ -1618,6 +1618,16 @@
 	}
 }
 
+static int protect_clocks[] = {
+	ACLK_VO,
+	HCLK_VO,
+	ACLK_VOP,
+	HCLK_VOP,
+	DCLK_VOP0,
+	DCLK_VOP1,
+	DCLK_VOP2,
+};
+
 static void __init rk3568_pmu_clk_init(struct device_node *np)
 {
 	struct rockchip_clk_provider *ctx;
@@ -1695,6 +1705,8 @@
 
 	if (!rk_dump_cru)
 		rk_dump_cru = rk3568_dump_cru;
+
+	rockchip_clk_protect(ctx, protect_clocks, ARRAY_SIZE(protect_clocks));
 }
 
 CLK_OF_DECLARE(rk3568_cru, "rockchip,rk3568-cru", rk3568_clk_init);
diff --git a/kernel/drivers/clk/rockchip/clk-rk3588.c b/kernel/drivers/clk/rockchip/clk-rk3588.c
index 28c23e9..4b03505 100644
--- a/kernel/drivers/clk/rockchip/clk-rk3588.c
+++ b/kernel/drivers/clk/rockchip/clk-rk3588.c
@@ -79,16 +79,16 @@
 	RK3588_PLL_RATE(1008000000, 2, 336, 2, 0),
 	RK3588_PLL_RATE(1000000000, 3, 500, 2, 0),
 	RK3588_PLL_RATE(983040000, 4, 655, 2, 23592),
-	RK3588_PLL_RATE(955520000, 3, 477, 2, 49806),
+	RK3588_PLL_RATE(955520000, 3, 478, 2, 49807),
 	RK3588_PLL_RATE(903168000, 6, 903, 2, 11009),
 	RK3588_PLL_RATE(900000000, 2, 300, 2, 0),
 	RK3588_PLL_RATE(816000000, 2, 272, 2, 0),
 	RK3588_PLL_RATE(786432000, 2, 262, 2, 9437),
 	RK3588_PLL_RATE(786000000, 1, 131, 2, 0),
-	RK3588_PLL_RATE(785560000, 3, 392, 2, 51117),
+	RK3588_PLL_RATE(785560000, 3, 393, 2, 51119),
 	RK3588_PLL_RATE(722534400, 8, 963, 2, 24850),
 	RK3588_PLL_RATE(600000000, 2, 200, 2, 0),
-	RK3588_PLL_RATE(594000000, 2, 198, 2, 0),
+	RK3588_PLL_RATE(594000000, 1, 99, 2, 0),
 	RK3588_PLL_RATE(408000000, 2, 272, 3, 0),
 	RK3588_PLL_RATE(312000000, 2, 208, 3, 0),
 	RK3588_PLL_RATE(216000000, 2, 288, 4, 0),
diff --git a/kernel/drivers/cpufreq/cpufreq-dt-platdev.c b/kernel/drivers/cpufreq/cpufreq-dt-platdev.c
index 2243cc9..75a2460 100644
--- a/kernel/drivers/cpufreq/cpufreq-dt-platdev.c
+++ b/kernel/drivers/cpufreq/cpufreq-dt-platdev.c
@@ -145,6 +145,7 @@
 	{ .compatible = "rockchip,rk3528", },
 	{ .compatible = "rockchip,rk3562", },
 	{ .compatible = "rockchip,rk3566", },
+	{ .compatible = "rockchip,rk3567", },
 	{ .compatible = "rockchip,rk3568", },
 	{ .compatible = "rockchip,rk3588", },
 	{ .compatible = "rockchip,rv1103", },
diff --git a/kernel/drivers/cpufreq/cpufreq_interactive.c b/kernel/drivers/cpufreq/cpufreq_interactive.c
index c05eb4b..a486993 100644
--- a/kernel/drivers/cpufreq/cpufreq_interactive.c
+++ b/kernel/drivers/cpufreq/cpufreq_interactive.c
@@ -513,8 +513,7 @@
 			sampling_rate = icpu->ipolicy->tunables->sampling_rate;
 			icpu->last_sample_time = local_clock();
 			icpu->next_sample_jiffies = usecs_to_jiffies(sampling_rate) + jiffies;
-			icpu->work_in_progress = true;
-			irq_work_queue_on(&icpu->irq_work, icpu->cpu);
+			cpufreq_interactive_update(icpu);
 		}
 	}
 
diff --git a/kernel/drivers/cpufreq/cpufreq_times.c b/kernel/drivers/cpufreq/cpufreq_times.c
index 60ee537..47dc34c 100644
--- a/kernel/drivers/cpufreq/cpufreq_times.c
+++ b/kernel/drivers/cpufreq/cpufreq_times.c
@@ -23,7 +23,7 @@
 #include <linux/threads.h>
 #include <trace/hooks/cpufreq.h>
 
-static DEFINE_RAW_SPINLOCK(task_time_in_state_lock); /* task->time_in_state */
+static DEFINE_SPINLOCK(task_time_in_state_lock); /* task->time_in_state */
 
 /**
  * struct cpu_freqs - per-cpu frequency information
@@ -47,9 +47,9 @@
 {
 	unsigned long flags;
 
-	raw_spin_lock_irqsave(&task_time_in_state_lock, flags);
+	spin_lock_irqsave(&task_time_in_state_lock, flags);
 	p->time_in_state = NULL;
-	raw_spin_unlock_irqrestore(&task_time_in_state_lock, flags);
+	spin_unlock_irqrestore(&task_time_in_state_lock, flags);
 	p->max_state = 0;
 }
 
@@ -64,9 +64,9 @@
 	if (!temp)
 		return;
 
-	raw_spin_lock_irqsave(&task_time_in_state_lock, flags);
+	spin_lock_irqsave(&task_time_in_state_lock, flags);
 	p->time_in_state = temp;
-	raw_spin_unlock_irqrestore(&task_time_in_state_lock, flags);
+	spin_unlock_irqrestore(&task_time_in_state_lock, flags);
 	p->max_state = max_state;
 }
 
@@ -94,10 +94,10 @@
 	if (!p->time_in_state)
 		return;
 
-	raw_spin_lock_irqsave(&task_time_in_state_lock, flags);
+	spin_lock_irqsave(&task_time_in_state_lock, flags);
 	temp = p->time_in_state;
 	p->time_in_state = NULL;
-	raw_spin_unlock_irqrestore(&task_time_in_state_lock, flags);
+	spin_unlock_irqrestore(&task_time_in_state_lock, flags);
 	kfree(temp);
 }
 
@@ -110,7 +110,7 @@
 	struct cpu_freqs *freqs;
 	struct cpu_freqs *last_freqs = NULL;
 
-	raw_spin_lock_irqsave(&task_time_in_state_lock, flags);
+	spin_lock_irqsave(&task_time_in_state_lock, flags);
 	for_each_possible_cpu(cpu) {
 		freqs = all_freqs[cpu];
 		if (!freqs || freqs == last_freqs)
@@ -127,7 +127,7 @@
 				   (unsigned long)nsec_to_clock_t(cputime));
 		}
 	}
-	raw_spin_unlock_irqrestore(&task_time_in_state_lock, flags);
+	spin_unlock_irqrestore(&task_time_in_state_lock, flags);
 	return 0;
 }
 
@@ -142,11 +142,11 @@
 
 	state = freqs->offset + READ_ONCE(freqs->last_index);
 
-	raw_spin_lock_irqsave(&task_time_in_state_lock, flags);
+	spin_lock_irqsave(&task_time_in_state_lock, flags);
 	if ((state < p->max_state || !cpufreq_task_times_realloc_locked(p)) &&
 	    p->time_in_state)
 		p->time_in_state[state] += cputime;
-	raw_spin_unlock_irqrestore(&task_time_in_state_lock, flags);
+	spin_unlock_irqrestore(&task_time_in_state_lock, flags);
 
 	trace_android_vh_cpufreq_acct_update_power(cputime, p, state);
 }
diff --git a/kernel/drivers/cpufreq/rockchip-cpufreq.c b/kernel/drivers/cpufreq/rockchip-cpufreq.c
index ce3e08a..804fcfd 100644
--- a/kernel/drivers/cpufreq/rockchip-cpufreq.c
+++ b/kernel/drivers/cpufreq/rockchip-cpufreq.c
@@ -605,11 +605,13 @@
 	}
 	if (opp_info->data && opp_info->data->get_soc_info)
 		opp_info->data->get_soc_info(dev, np, &bin, &process);
+	rockchip_get_soc_info(dev, np, &bin, &process);
 	rockchip_get_scale_volt_sel(dev, "cpu_leakage", reg_name, bin, process,
 				    &cluster->scale, &volt_sel);
 	if (opp_info->data && opp_info->data->set_soc_info)
 		opp_info->data->set_soc_info(dev, np, bin, process, volt_sel);
 	pname_table = rockchip_set_opp_prop_name(dev, process, volt_sel);
+	rockchip_set_opp_supported_hw(dev, np, bin, volt_sel);
 
 	if (of_find_property(dev->of_node, "cpu-supply", NULL) &&
 	    of_find_property(dev->of_node, "mem-supply", NULL)) {
diff --git a/kernel/drivers/crypto/rockchip/rk_crypto_core.c b/kernel/drivers/crypto/rockchip/rk_crypto_core.c
index 56a50d5..2a9cf2d 100644
--- a/kernel/drivers/crypto/rockchip/rk_crypto_core.c
+++ b/kernel/drivers/crypto/rockchip/rk_crypto_core.c
@@ -272,9 +272,17 @@
 static void rk_crypto_irq_timer_handle(struct timer_list *t)
 {
 	struct rk_crypto_dev *rk_dev = from_timer(rk_dev, t, timer);
+	unsigned long flags;
+
+	spin_lock_irqsave(&rk_dev->lock, flags);
 
 	rk_dev->err = -ETIMEDOUT;
 	rk_dev->stat.timeout_cnt++;
+
+	rk_unload_data(rk_dev);
+
+	spin_unlock_irqrestore(&rk_dev->lock, flags);
+
 	tasklet_schedule(&rk_dev->done_task);
 }
 
@@ -282,8 +290,12 @@
 {
 	struct rk_crypto_dev *rk_dev  = platform_get_drvdata(dev_id);
 	struct rk_alg_ctx *alg_ctx;
+	unsigned long flags;
 
-	spin_lock(&rk_dev->lock);
+	spin_lock_irqsave(&rk_dev->lock, flags);
+
+	/* reset timeout timer */
+	start_irq_timer(rk_dev);
 
 	alg_ctx = rk_alg_ctx_cast(rk_dev->async_req);
 
@@ -292,9 +304,14 @@
 	if (alg_ctx->ops.irq_handle)
 		alg_ctx->ops.irq_handle(irq, dev_id);
 
-	tasklet_schedule(&rk_dev->done_task);
+	/* already trigger timeout */
+	if (rk_dev->err != -ETIMEDOUT) {
+		spin_unlock_irqrestore(&rk_dev->lock, flags);
+		tasklet_schedule(&rk_dev->done_task);
+	} else {
+		spin_unlock_irqrestore(&rk_dev->lock, flags);
+	}
 
-	spin_unlock(&rk_dev->lock);
 	return IRQ_HANDLED;
 }
 
diff --git a/kernel/drivers/crypto/rockchip/rk_crypto_utils.c b/kernel/drivers/crypto/rockchip/rk_crypto_utils.c
index 5db73ab..5758e0e 100644
--- a/kernel/drivers/crypto/rockchip/rk_crypto_utils.c
+++ b/kernel/drivers/crypto/rockchip/rk_crypto_utils.c
@@ -72,14 +72,18 @@
 {
 	int in, out, align;
 
+	/* The last piece has no need for length alignment */
 	in = IS_ALIGNED((u32)sg_src->offset, 4) &&
-	     IS_ALIGNED((u32)sg_src->length, align_mask) &&
+	     (!sg_next(sg_src) ||
+	      IS_ALIGNED((u32)sg_src->length, align_mask)) &&
 	     (sg_phys(sg_src) < SZ_4G);
 	if (!sg_dst)
 		return in;
 
+	/* The last piece has no need for length alignment */
 	out = IS_ALIGNED((u32)sg_dst->offset, 4) &&
-	      IS_ALIGNED((u32)sg_dst->length, align_mask) &&
+	      (!sg_next(sg_dst) ||
+	       IS_ALIGNED((u32)sg_dst->length, align_mask)) &&
 	      (sg_phys(sg_dst) < SZ_4G);
 	align = in && out;
 
diff --git a/kernel/drivers/crypto/rockchip/rk_crypto_v2_ahash.c b/kernel/drivers/crypto/rockchip/rk_crypto_v2_ahash.c
index dd9ea24..919603f 100644
--- a/kernel/drivers/crypto/rockchip/rk_crypto_v2_ahash.c
+++ b/kernel/drivers/crypto/rockchip/rk_crypto_v2_ahash.c
@@ -58,6 +58,10 @@
 			pool_timeout_us);
 
 	CRYPTO_WRITE(rk_dev, CRYPTO_HASH_CTL, 0xffff0000);
+
+	/* clear dma int status */
+	tmp = CRYPTO_READ(rk_dev, CRYPTO_DMA_INT_ST);
+	CRYPTO_WRITE(rk_dev, CRYPTO_DMA_INT_ST, tmp);
 }
 
 static int rk_crypto_irq_handle(int irq, void *dev_id)
diff --git a/kernel/drivers/crypto/rockchip/rk_crypto_v2_skcipher.c b/kernel/drivers/crypto/rockchip/rk_crypto_v2_skcipher.c
index 2a4628f..2bfff0d 100644
--- a/kernel/drivers/crypto/rockchip/rk_crypto_v2_skcipher.c
+++ b/kernel/drivers/crypto/rockchip/rk_crypto_v2_skcipher.c
@@ -197,6 +197,10 @@
 			pool_timeout_us);
 
 	CRYPTO_WRITE(rk_dev, CRYPTO_BC_CTL, 0xffff0000);
+
+	/* clear dma int status */
+	tmp = CRYPTO_READ(rk_dev, CRYPTO_DMA_INT_ST);
+	CRYPTO_WRITE(rk_dev, CRYPTO_DMA_INT_ST, tmp);
 }
 
 static void rk_crypto_complete(struct crypto_async_request *base, int err)
diff --git a/kernel/drivers/crypto/rockchip/rk_crypto_v3_ahash.c b/kernel/drivers/crypto/rockchip/rk_crypto_v3_ahash.c
index f39026d..0c91b45 100644
--- a/kernel/drivers/crypto/rockchip/rk_crypto_v3_ahash.c
+++ b/kernel/drivers/crypto/rockchip/rk_crypto_v3_ahash.c
@@ -63,6 +63,10 @@
 			pool_timeout_us);
 
 	CRYPTO_WRITE(rk_dev, CRYPTO_HASH_CTL, 0xffff0000);
+
+	/* clear dma int status */
+	tmp = CRYPTO_READ(rk_dev, CRYPTO_DMA_INT_ST);
+	CRYPTO_WRITE(rk_dev, CRYPTO_DMA_INT_ST, tmp);
 }
 
 static int rk_hash_mid_data_store(struct rk_crypto_dev *rk_dev, struct rk_hash_mid_data *mid_data)
diff --git a/kernel/drivers/crypto/rockchip/rk_crypto_v3_skcipher.c b/kernel/drivers/crypto/rockchip/rk_crypto_v3_skcipher.c
index 26d2b71..4220e6c 100644
--- a/kernel/drivers/crypto/rockchip/rk_crypto_v3_skcipher.c
+++ b/kernel/drivers/crypto/rockchip/rk_crypto_v3_skcipher.c
@@ -196,6 +196,10 @@
 			pool_timeout_us);
 
 	CRYPTO_WRITE(rk_dev, CRYPTO_BC_CTL, 0xffff0000);
+
+	/* clear dma int status */
+	tmp = CRYPTO_READ(rk_dev, CRYPTO_DMA_INT_ST);
+	CRYPTO_WRITE(rk_dev, CRYPTO_DMA_INT_ST, tmp);
 }
 
 static void rk_crypto_complete(struct crypto_async_request *base, int err)
diff --git a/kernel/drivers/devfreq/rockchip_dmc.c b/kernel/drivers/devfreq/rockchip_dmc.c
index 01edb77..7b957db 100644
--- a/kernel/drivers/devfreq/rockchip_dmc.c
+++ b/kernel/drivers/devfreq/rockchip_dmc.c
@@ -3052,6 +3052,10 @@
 	if (rockchip_get_rl_map_talbe(np, "vop-pn-msch-readlatency",
 				      &dmcfreq->info.vop_pn_rl_tbl))
 		dev_err(dev, "failed to get vop pn to msch rl\n");
+	if (dmcfreq->video_4k_rate)
+		dmcfreq->info.vop_4k_rate = dmcfreq->video_4k_rate;
+	else if (dmcfreq->video_4k_10b_rate)
+		dmcfreq->info.vop_4k_rate = dmcfreq->video_4k_10b_rate;
 
 	of_property_read_u32(np, "touchboost_duration",
 			     (u32 *)&dmcfreq->touchboostpulse_duration_val);
diff --git a/kernel/drivers/devfreq/rockchip_dmc_common.c b/kernel/drivers/devfreq/rockchip_dmc_common.c
index cacc7d0..bb658eb 100644
--- a/kernel/drivers/devfreq/rockchip_dmc_common.c
+++ b/kernel/drivers/devfreq/rockchip_dmc_common.c
@@ -85,9 +85,9 @@
 	if (!common_info)
 		return;
 
-	dev_dbg(common_info->dev, "line bw=%u, frame bw=%u, pn=%u\n",
+	dev_dbg(common_info->dev, "line bw=%u, frame bw=%u, pn=%u, pn_4k=%u\n",
 		vop_info->line_bw_mbyte, vop_info->frame_bw_mbyte,
-		vop_info->plane_num);
+		vop_info->plane_num, vop_info->plane_num_4k);
 
 	if (!common_info->vop_pn_rl_tbl || !common_info->set_msch_readlatency)
 		goto vop_bw_tbl;
@@ -129,6 +129,9 @@
 	}
 
 next:
+	if (vop_info->plane_num_4k && target < common_info->vop_4k_rate)
+		target = common_info->vop_4k_rate;
+
 	vop_last_rate = common_info->vop_req_rate;
 	common_info->vop_req_rate = target;
 
diff --git a/kernel/drivers/dma/pl330.c b/kernel/drivers/dma/pl330.c
index 9159280..b1d6fae 100644
--- a/kernel/drivers/dma/pl330.c
+++ b/kernel/drivers/dma/pl330.c
@@ -3191,6 +3191,7 @@
 	struct resource *res;
 	int i, ret, irq;
 	int num_chan;
+	int val;
 	struct device_node *np = adev->dev.of_node;
 
 	ret = dma_set_mask_and_coherent(&adev->dev, DMA_BIT_MASK(32));
@@ -3205,7 +3206,12 @@
 	pd = &pl330->ddma;
 	pd->dev = &adev->dev;
 
-	pl330->mcbufsz = 0;
+	if (!device_property_read_u32(&adev->dev, "arm,pl330-mcbufsz-bytes", &val)) {
+		if ((val > 0) && (val <= PAGE_SIZE))
+			pl330->mcbufsz = val;
+
+		dev_info(&adev->dev, "mcbufsz: %d bytes\n", pl330->mcbufsz);
+	}
 
 	/* get quirk */
 	for (i = 0; i < ARRAY_SIZE(of_quirks); i++)
diff --git a/kernel/drivers/edac/Kconfig b/kernel/drivers/edac/Kconfig
index 7a47680..68a58c1 100644
--- a/kernel/drivers/edac/Kconfig
+++ b/kernel/drivers/edac/Kconfig
@@ -537,4 +537,11 @@
 	  Support for error detection and correction on the
 	  SoCs with ARM DMC-520 DRAM controller.
 
+config EDAC_ROCKCHIP
+	tristate "Rockchip DDR ECC"
+	depends on ARCH_ROCKCHIP && HAVE_ARM_SMCCC
+	help
+	  Support for error detection and correction on the
+	  rockchip family of SOCs.
+
 endif # EDAC
diff --git a/kernel/drivers/edac/Makefile b/kernel/drivers/edac/Makefile
index 3a84916..e25719f 100644
--- a/kernel/drivers/edac/Makefile
+++ b/kernel/drivers/edac/Makefile
@@ -89,3 +89,4 @@
 obj-$(CONFIG_EDAC_ASPEED)		+= aspeed_edac.o
 obj-$(CONFIG_EDAC_BLUEFIELD)		+= bluefield_edac.o
 obj-$(CONFIG_EDAC_DMC520)		+= dmc520_edac.o
+obj-$(CONFIG_EDAC_ROCKCHIP)		+= rockchip_edac.o
diff --git a/kernel/drivers/edac/rockchip_edac.c b/kernel/drivers/edac/rockchip_edac.c
new file mode 100644
index 0000000..4b1317b
--- /dev/null
+++ b/kernel/drivers/edac/rockchip_edac.c
@@ -0,0 +1,358 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2023 Rockchip Electronics Co., Ltd.
+ */
+
+#include <linux/edac.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/rockchip/rockchip_sip.h>
+#include <soc/rockchip/rockchip_sip.h>
+
+#include "edac_module.h"
+
+#define MAX_CS					(4)
+
+#define MAX_CH					(1)
+
+#define	RK_EDAC_MOD				"1"
+
+/* ECCCADDR0 */
+#define ECC_CORR_RANK_SHIFT			(24)
+#define ECC_CORR_RANK_MASK			(0x3)
+#define ECC_CORR_ROW_MASK			(0x3ffff)
+/* ECCCADDR1 */
+#define ECC_CORR_CID_SHIFT			(28)
+#define ECC_CORR_CID_MASK			(0x3)
+#define ECC_CORR_BG_SHIFT			(24)
+#define ECC_CORR_BG_MASK			(0x3)
+#define ECC_CORR_BANK_SHIFT			(16)
+#define ECC_CORR_BANK_MASK			(0x7)
+#define ECC_CORR_COL_MASK			(0xfff)
+/* ECCUADDR0 */
+#define ECC_UNCORR_RANK_SHIFT			(24)
+#define ECC_UNCORR_RANK_MASK			(0x3)
+#define ECC_UNCORR_ROW_MASK			(0x3ffff)
+/* ECCUADDR1 */
+#define ECC_UNCORR_CID_SHIFT			(28)
+#define ECC_UNCORR_CID_MASK			(0x3)
+#define ECC_UNCORR_BG_SHIFT			(24)
+#define ECC_UNCORR_BG_MASK			(0x3)
+#define ECC_UNCORR_BANK_SHIFT			(16)
+#define ECC_UNCORR_BANK_MASK			(0x7)
+#define ECC_UNCORR_COL_MASK			(0xfff)
+
+/**
+ * struct ddr_ecc_error_info - DDR ECC error log information
+ * @err_cnt:	error count
+ * @rank:	Rank number
+ * @row:	Row number
+ * @chip_id:	Chip id number
+ * @bank_group:	Bank Group number
+ * @bank:	Bank number
+ * @col:	Column number
+ * @bitpos:	Bit position
+ */
+struct ddr_ecc_error_info {
+	u32 err_cnt;
+	u32 rank;
+	u32 row;
+	u32 chip_id;
+	u32 bank_group;
+	u32 bank;
+	u32 col;
+	u32 bitpos;
+};
+
+/**
+ * struct ddr_ecc_status - DDR ECC status information to report
+ * @ceinfo:	Correctable error log information
+ * @ueinfo:	Uncorrectable error log information
+ */
+struct ddr_ecc_status {
+	struct ddr_ecc_error_info ceinfo;
+	struct ddr_ecc_error_info ueinfo;
+};
+
+/**
+ * struct rk_edac_priv - RK DDR memory controller private instance data
+ * @name:	EDAC name
+ * @stat:	DDR ECC status information
+ * @ce_cnt:	Correctable Error count
+ * @ue_cnt:	Uncorrectable Error count
+ * @irq_ce:	Corrected interrupt number
+ * @irq_ue:	Uncorrected interrupt number
+ */
+struct rk_edac_priv {
+	char *name;
+	struct ddr_ecc_status stat;
+	u32 ce_cnt;
+	u32 ue_cnt;
+	int irq_ce;
+	int irq_ue;
+};
+
+static struct ddr_ecc_status *ddr_edac_info;
+
+static inline void opstate_init_int(void)
+{
+	switch (edac_op_state) {
+	case EDAC_OPSTATE_POLL:
+	case EDAC_OPSTATE_INT:
+		break;
+	default:
+		edac_op_state = EDAC_OPSTATE_INT;
+		break;
+	}
+}
+
+static void rockchip_edac_handle_ce_error(struct mem_ctl_info *mci,
+					  struct ddr_ecc_status *p)
+{
+	struct ddr_ecc_error_info *pinf;
+
+	if (p->ceinfo.err_cnt) {
+		pinf = &p->ceinfo;
+		edac_mc_printk(mci, KERN_ERR,
+			       "DDR ECC CE error: CS%d, Row 0x%x, Bg 0x%x, Bk 0x%x, Col 0x%x bit 0x%x\n",
+			       pinf->rank, pinf->row, pinf->bank_group,
+			       pinf->bank, pinf->col,
+			       pinf->bitpos);
+		edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,
+				     p->ceinfo.err_cnt, 0, 0, 0, 0, 0, -1,
+				     mci->ctl_name, "");
+	}
+}
+
+static void rockchip_edac_handle_ue_error(struct mem_ctl_info *mci,
+					  struct ddr_ecc_status *p)
+{
+	struct ddr_ecc_error_info *pinf;
+
+	if (p->ueinfo.err_cnt) {
+		pinf = &p->ueinfo;
+		edac_mc_printk(mci, KERN_ERR,
+			       "DDR ECC UE error: CS%d, Row 0x%x, Bg 0x%x, Bk 0x%x, Col 0x%x\n",
+			       pinf->rank, pinf->row,
+			       pinf->bank_group, pinf->bank, pinf->col);
+		edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,
+				     p->ueinfo.err_cnt, 0, 0, 0, 0, 0, -1,
+				     mci->ctl_name, "");
+	}
+}
+
+static int rockchip_edac_get_error_info(struct mem_ctl_info *mci)
+{
+	struct arm_smccc_res res;
+
+	res = sip_smc_dram(SHARE_PAGE_TYPE_DDRECC, 0,
+			   ROCKCHIP_SIP_CONFIG_DRAM_ECC);
+	if ((res.a0) || (res.a1)) {
+		edac_mc_printk(mci, KERN_ERR, "ROCKCHIP_SIP_CONFIG_DRAM_ECC not support: 0x%lx\n",
+			       res.a0);
+		return -ENXIO;
+	}
+
+	return 0;
+}
+
+static void rockchip_edac_check(struct mem_ctl_info *mci)
+{
+	struct rk_edac_priv *priv = mci->pvt_info;
+	int ret;
+
+	ret = rockchip_edac_get_error_info(mci);
+	if (ret)
+		return;
+
+	priv->ce_cnt += ddr_edac_info->ceinfo.err_cnt;
+	priv->ue_cnt += ddr_edac_info->ceinfo.err_cnt;
+	rockchip_edac_handle_ce_error(mci, ddr_edac_info);
+	rockchip_edac_handle_ue_error(mci, ddr_edac_info);
+}
+
+static irqreturn_t rockchip_edac_mc_ce_isr(int irq, void *dev_id)
+{
+	struct mem_ctl_info *mci = dev_id;
+	struct rk_edac_priv *priv = mci->pvt_info;
+	int ret;
+
+	ret = rockchip_edac_get_error_info(mci);
+	if (ret)
+		return IRQ_NONE;
+
+	priv->ce_cnt += ddr_edac_info->ceinfo.err_cnt;
+
+	rockchip_edac_handle_ce_error(mci, ddr_edac_info);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t rockchip_edac_mc_ue_isr(int irq, void *dev_id)
+{
+	struct mem_ctl_info *mci = dev_id;
+	struct rk_edac_priv *priv = mci->pvt_info;
+	int ret;
+
+	ret = rockchip_edac_get_error_info(mci);
+	if (ret)
+		return IRQ_NONE;
+
+	priv->ue_cnt += ddr_edac_info->ueinfo.err_cnt;
+
+	rockchip_edac_handle_ue_error(mci, ddr_edac_info);
+
+	return IRQ_HANDLED;
+}
+
+static int rockchip_edac_mc_init(struct mem_ctl_info *mci,
+			   struct platform_device *pdev)
+{
+	struct rk_edac_priv *priv = mci->pvt_info;
+	struct arm_smccc_res res;
+	int ret;
+
+	mci->pdev = &pdev->dev;
+	dev_set_drvdata(mci->pdev, mci);
+	mci->mtype_cap = MEM_FLAG_DDR3 | MEM_FLAG_DDR4;
+	mci->edac_ctl_cap = EDAC_FLAG_SECDED;
+	mci->scrub_cap = SCRUB_NONE;
+	mci->scrub_mode = SCRUB_NONE;
+
+	mci->edac_cap = EDAC_FLAG_SECDED;
+	mci->ctl_name = priv->name;
+	mci->dev_name = priv->name;
+	mci->mod_name = RK_EDAC_MOD;
+
+	if (edac_op_state == EDAC_OPSTATE_POLL)
+		mci->edac_check = rockchip_edac_check;
+	mci->ctl_page_to_phys = NULL;
+
+	res = sip_smc_request_share_mem(1, SHARE_PAGE_TYPE_DDRECC);
+	if (res.a0 != 0) {
+		dev_err(&pdev->dev, "no ATF memory for init, ret 0x%lx\n", res.a0);
+		return -ENOMEM;
+	}
+	ddr_edac_info = (struct ddr_ecc_status *)res.a1;
+	memset(ddr_edac_info, 0, sizeof(struct ddr_ecc_status));
+
+	ret = rockchip_edac_get_error_info(mci);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int rockchip_edac_probe(struct platform_device *pdev)
+{
+	struct mem_ctl_info *mci;
+	struct edac_mc_layer layers[2];
+	struct rk_edac_priv *priv;
+	int ret;
+
+	opstate_init_int();
+	layers[0].type = EDAC_MC_LAYER_CHIP_SELECT;
+	layers[0].size = MAX_CS;
+	layers[0].is_virt_csrow = true;
+	layers[1].type = EDAC_MC_LAYER_CHANNEL;
+	layers[1].size = MAX_CH;
+	layers[1].is_virt_csrow = false;
+
+	mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers,
+			    sizeof(struct rk_edac_priv));
+	if (!mci) {
+		edac_printk(KERN_ERR, EDAC_MC,
+			    "Failed memory allocation for mc instance\n");
+		return -ENOMEM;
+	}
+
+	priv = mci->pvt_info;
+	priv->name = "rk_edac_ecc";
+	ret = rockchip_edac_mc_init(mci, pdev);
+	if (ret) {
+		edac_printk(KERN_ERR, EDAC_MC,
+			    "Failed to initialize instance\n");
+		goto free_edac_mc;
+	}
+
+	ret = edac_mc_add_mc(mci);
+	if (ret) {
+		edac_printk(KERN_ERR, EDAC_MC,
+			    "Failed edac_mc_add_mc()\n");
+		goto free_edac_mc;
+	}
+
+	if (edac_op_state == EDAC_OPSTATE_INT) {
+		/* register interrupts */
+		priv->irq_ce = platform_get_irq_byname(pdev, "ce");
+		ret = devm_request_irq(&pdev->dev, priv->irq_ce,
+				       rockchip_edac_mc_ce_isr,
+				       0,
+				       "[EDAC] MC err", mci);
+		if (ret < 0) {
+			edac_printk(KERN_ERR, EDAC_MC,
+				    "%s: Unable to request ce irq %d for RK EDAC\n",
+				    __func__, priv->irq_ce);
+			goto del_mc;
+		}
+
+		edac_printk(KERN_INFO, EDAC_MC,
+			    "acquired ce irq %d for MC\n",
+			    priv->irq_ce);
+
+		priv->irq_ue = platform_get_irq_byname(pdev, "ue");
+		ret = devm_request_irq(&pdev->dev, priv->irq_ue,
+				       rockchip_edac_mc_ue_isr,
+				       0,
+				       "[EDAC] MC err", mci);
+		if (ret < 0) {
+			edac_printk(KERN_ERR, EDAC_MC,
+				    "%s: Unable to request ue irq %d for RK EDAC\n",
+				    __func__, priv->irq_ue);
+			goto del_mc;
+		}
+
+		edac_printk(KERN_INFO, EDAC_MC,
+			    "acquired ue irq %d for MC\n",
+			    priv->irq_ue);
+	}
+
+	return 0;
+
+del_mc:
+	edac_mc_del_mc(&pdev->dev);
+free_edac_mc:
+	edac_mc_free(mci);
+
+	return -ENODEV;
+}
+
+static int rockchip_edac_remove(struct platform_device *pdev)
+{
+	struct mem_ctl_info *mci = dev_get_drvdata(&pdev->dev);
+
+	edac_mc_del_mc(&pdev->dev);
+	edac_mc_free(mci);
+
+	return 0;
+}
+
+static const struct of_device_id rk_ddr_mc_err_of_match[] = {
+	{ .compatible = "rockchip,rk3568-edac", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, rk_ddr_mc_err_of_match);
+
+static struct platform_driver rockchip_edac_driver = {
+	.probe = rockchip_edac_probe,
+	.remove = rockchip_edac_remove,
+	.driver = {
+		.name = "rk_edac",
+		.of_match_table = rk_ddr_mc_err_of_match,
+	},
+};
+module_platform_driver(rockchip_edac_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("He Zhihuan <huan.he@rock-chips.com>\n");
+MODULE_DESCRIPTION("ROCKCHIP EDAC kernel module");
diff --git a/kernel/drivers/firewire/ohci.c b/kernel/drivers/firewire/ohci.c
index 17c9d82..9811c40 100644
--- a/kernel/drivers/firewire/ohci.c
+++ b/kernel/drivers/firewire/ohci.c
@@ -2545,7 +2545,7 @@
 	struct driver_data *driver_data = packet->driver_data;
 	int ret = -ENOENT;
 
-	tasklet_disable_in_atomic(&ctx->tasklet);
+	tasklet_disable(&ctx->tasklet);
 
 	if (packet->ack != 0)
 		goto out;
@@ -3465,7 +3465,7 @@
 	struct iso_context *ctx = container_of(base, struct iso_context, base);
 	int ret = 0;
 
-	tasklet_disable_in_atomic(&ctx->context.tasklet);
+	tasklet_disable(&ctx->context.tasklet);
 
 	if (!test_and_set_bit_lock(0, &ctx->flushing_completions)) {
 		context_tasklet((unsigned long)&ctx->context);
diff --git a/kernel/drivers/firmware/efi/efi.c b/kernel/drivers/firmware/efi/efi.c
index 311e3c0..70be9c8 100644
--- a/kernel/drivers/firmware/efi/efi.c
+++ b/kernel/drivers/firmware/efi/efi.c
@@ -66,7 +66,7 @@
 
 struct workqueue_struct *efi_rts_wq;
 
-static bool disable_runtime = IS_ENABLED(CONFIG_PREEMPT_RT);
+static bool disable_runtime;
 static int __init setup_noefi(char *arg)
 {
 	disable_runtime = true;
@@ -96,9 +96,6 @@
 
 	if (parse_option_str(str, "noruntime"))
 		disable_runtime = true;
-
-	if (parse_option_str(str, "runtime"))
-		disable_runtime = false;
 
 	if (parse_option_str(str, "nosoftreserve"))
 		set_bit(EFI_MEM_NO_SOFT_RESERVE, &efi.flags);
diff --git a/kernel/drivers/firmware/rockchip_sip.c b/kernel/drivers/firmware/rockchip_sip.c
index 186011f..99f3d2c 100644
--- a/kernel/drivers/firmware/rockchip_sip.c
+++ b/kernel/drivers/firmware/rockchip_sip.c
@@ -627,6 +627,16 @@
 }
 EXPORT_SYMBOL_GPL(sip_hdcpkey_init);
 
+int sip_smc_mcu_config(unsigned long mcu_id,
+		       unsigned long func,
+		       unsigned long arg2)
+{
+	struct arm_smccc_res res;
+
+	res = __invoke_sip_fn_smc(SIP_MCU_CFG, mcu_id, func, arg2);
+	return res.a0;
+}
+EXPORT_SYMBOL_GPL(sip_smc_mcu_config);
 /******************************************************************************/
 #ifdef CONFIG_ARM
 static __init int sip_firmware_init(void)
diff --git a/kernel/drivers/gpio/Kconfig b/kernel/drivers/gpio/Kconfig
index 20bff29..9c5778b 100644
--- a/kernel/drivers/gpio/Kconfig
+++ b/kernel/drivers/gpio/Kconfig
@@ -984,6 +984,14 @@
 	help
 	  Select this to enable the MC9S08DZ60 GPIO driver
 
+config GPIO_NCA9539
+	tristate "NCA9539 I2C GPIO expander"
+	depends on I2C || COMPILE_TEST
+	select REGMAP_I2C
+	help
+	  Say yes here to support the NCA9539 series of I2C Expanders.
+	  GPIO expanders used for additional digital outputs or inputs.
+
 config GPIO_PCA953X
 	tristate "PCA95[357]x, PCA9698, TCA64xx, and MAX7310 I/O ports"
 	select REGMAP_I2C
diff --git a/kernel/drivers/gpio/Makefile b/kernel/drivers/gpio/Makefile
index 0a905ed..b42c5d0 100644
--- a/kernel/drivers/gpio/Makefile
+++ b/kernel/drivers/gpio/Makefile
@@ -107,6 +107,7 @@
 obj-$(CONFIG_GPIO_MVEBU)		+= gpio-mvebu.o
 obj-$(CONFIG_GPIO_MXC)			+= gpio-mxc.o
 obj-$(CONFIG_GPIO_MXS)			+= gpio-mxs.o
+obj-$(CONFIG_GPIO_NCA9539)		+= gpio-nca9539.o
 obj-$(CONFIG_GPIO_OCTEON)		+= gpio-octeon.o
 obj-$(CONFIG_GPIO_OMAP)			+= gpio-omap.o
 obj-$(CONFIG_GPIO_PALMAS)		+= gpio-palmas.o
diff --git a/kernel/drivers/gpio/gpio-nca9539.c b/kernel/drivers/gpio/gpio-nca9539.c
new file mode 100644
index 0000000..50fff6d
--- /dev/null
+++ b/kernel/drivers/gpio/gpio-nca9539.c
@@ -0,0 +1,332 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ *  NCA9539 I2C Port Expander I/O
+ *
+ *  Copyright (C) 2023 Cody Xie <cody.xie@rock-chips.com>
+ *
+ */
+
+#include <linux/compiler_types.h>
+#include <linux/bitfield.h>
+#include <linux/gpio/driver.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/string.h>
+#include <linux/types.h>
+
+#define NCA9539_REG_INPUT_PORT_BASE 0x00
+#define NCA9539_REG_INPUT_PORT0 (NCA9539_REG_INPUT_PORT_BASE + 0x0)
+#define NCA9539_REG_INPUT_PORT1 (NCA9539_REG_INPUT_PORT_BASE + 0x1)
+#define NCA9539_REG_OUTPUT_PORT_BASE 0x02
+#define NCA9539_REG_OUTPUT_PORT0 (NCA9539_REG_OUTPUT_PORT_BASE + 0x0)
+#define NCA9539_REG_OUTPUT_PORT1 (NCA9539_REG_OUTPUT_PORT_BASE + 0x1)
+#define NCA9539_REG_POLARITY_BASE 0x04
+#define NCA9539_REG_POLARITY_PORT0 (NCA9539_REG_POLARITY_BASE + 0x0)
+#define NCA9539_REG_POLARITY_PORT1 (NCA9539_REG_POLARITY_BASE + 0x1)
+#define NCA9539_REG_CONFIG_BASE 0x06
+#define NCA9539_REG_CONFIG_PORT0 (NCA9539_REG_CONFIG_BASE + 0x0)
+#define NCA9539_REG_CONFIG_PORT1 (NCA9539_REG_CONFIG_BASE + 0x1)
+
+struct nca9539_chip {
+	struct gpio_chip gpio_chip;
+	struct regmap *regmap;
+	struct regulator *regulator;
+	unsigned int ngpio;
+};
+
+static int nca9539_gpio_get_direction(struct gpio_chip *gc, unsigned int offset)
+{
+	struct nca9539_chip *priv = gpiochip_get_data(gc);
+	unsigned int port = offset / 8;
+	unsigned int pin = offset % 8;
+	unsigned int value;
+	int ret;
+
+	dev_dbg(gc->parent, "%s offset(%d)", __func__, offset);
+	ret = regmap_read(priv->regmap, NCA9539_REG_CONFIG_BASE + port, &value);
+	if (ret < 0) {
+		dev_err(gc->parent, "%s offset(%d) read config failed",
+			__func__, offset);
+		return ret;
+	}
+
+	if (value & BIT(pin))
+		return GPIO_LINE_DIRECTION_IN;
+
+	return GPIO_LINE_DIRECTION_OUT;
+}
+
+static int nca9539_gpio_direction_input(struct gpio_chip *gc, unsigned int offset)
+{
+	struct nca9539_chip *priv = gpiochip_get_data(gc);
+	unsigned int port = offset / 8;
+	unsigned int pin = offset % 8;
+	int ret;
+
+	dev_dbg(gc->parent, "%s offset(%d)", __func__, offset);
+	ret = regmap_update_bits(priv->regmap, NCA9539_REG_CONFIG_BASE + port,
+				 BIT(pin), BIT(pin));
+	if (ret < 0) {
+		dev_err(gc->parent, "%s offset(%d) read config failed",
+			__func__, offset);
+	}
+
+	return ret;
+}
+
+static int nca9539_gpio_direction_output(struct gpio_chip *gc, unsigned int offset,
+					 int val)
+{
+	struct nca9539_chip *priv = gpiochip_get_data(gc);
+	unsigned int port = offset / 8;
+	unsigned int pin = offset % 8;
+	int ret;
+
+	dev_dbg(gc->parent, "%s offset(%d) val(%d)", __func__, offset, val);
+	ret = regmap_update_bits(priv->regmap, NCA9539_REG_CONFIG_BASE + port,
+				 BIT(pin), 0);
+	if (ret < 0) {
+		dev_err(gc->parent,
+			"%s offset(%d) val(%d) update config failed", __func__,
+			offset, val);
+		return ret;
+	}
+
+	ret = regmap_update_bits(priv->regmap,
+				 NCA9539_REG_OUTPUT_PORT_BASE + port, BIT(pin),
+				 val ? BIT(pin) : 0);
+	if (ret < 0) {
+		dev_err(gc->parent,
+			"%s offset(%d) val(%d) update output failed", __func__,
+			offset, val);
+		return ret;
+	}
+
+	return ret;
+}
+
+static int nca9539_gpio_get(struct gpio_chip *gc, unsigned int offset)
+{
+	struct nca9539_chip *priv = gpiochip_get_data(gc);
+	unsigned int port = offset / 8;
+	unsigned int pin = offset % 8;
+	unsigned int reg;
+	unsigned int value;
+	int ret;
+
+	dev_dbg(gc->parent, "%s offset(%d)", __func__, offset);
+	ret = regmap_read(priv->regmap, NCA9539_REG_CONFIG_BASE + port, &value);
+	if (ret < 0) {
+		dev_err(gc->parent, "%s offset(%d) check config failed",
+			__func__, offset);
+		return ret;
+	}
+	if (!(BIT(pin) & value))
+		reg = NCA9539_REG_OUTPUT_PORT_BASE + port;
+	else
+		reg = NCA9539_REG_INPUT_PORT_BASE + port;
+	ret = regmap_read(priv->regmap, reg, &value);
+	if (ret < 0) {
+		dev_err(gc->parent, "%s offset(%d) read value failed", __func__,
+			offset);
+		return -EIO;
+	}
+
+	return !!(BIT(pin) & value);
+}
+
+static void nca9539_gpio_set(struct gpio_chip *gc, unsigned int offset, int val)
+{
+	struct nca9539_chip *priv = gpiochip_get_data(gc);
+	unsigned int port = offset / 8;
+	unsigned int pin = offset % 8;
+	unsigned int value;
+	int ret;
+
+	dev_dbg(gc->parent, "%s offset(%d) val(%d)", __func__, offset, val);
+	ret = regmap_read(priv->regmap, NCA9539_REG_CONFIG_BASE + port, &value);
+	if (ret < 0 || !!(BIT(pin) & value)) {
+		dev_err(gc->parent, "%s offset(%d) val(%d) check config failed",
+			__func__, offset, val);
+	}
+
+	ret = regmap_update_bits(priv->regmap,
+				 NCA9539_REG_OUTPUT_PORT_BASE + port, BIT(pin),
+				 val ? BIT(pin) : 0);
+	if (ret < 0) {
+		dev_err(gc->parent, "%s offset(%d) val(%d) read input failed",
+			__func__, offset, val);
+	}
+}
+
+static bool nca9539_is_writeable_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case NCA9539_REG_OUTPUT_PORT0:
+	case NCA9539_REG_OUTPUT_PORT1:
+	case NCA9539_REG_POLARITY_PORT0:
+	case NCA9539_REG_POLARITY_PORT1:
+	case NCA9539_REG_CONFIG_PORT0:
+	case NCA9539_REG_CONFIG_PORT1:
+		return true;
+	}
+	return false;
+}
+
+static bool nca9539_is_readable_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case NCA9539_REG_INPUT_PORT0:
+	case NCA9539_REG_INPUT_PORT1:
+	case NCA9539_REG_OUTPUT_PORT0:
+	case NCA9539_REG_OUTPUT_PORT1:
+	case NCA9539_REG_POLARITY_PORT0:
+	case NCA9539_REG_POLARITY_PORT1:
+	case NCA9539_REG_CONFIG_PORT0:
+	case NCA9539_REG_CONFIG_PORT1:
+		return true;
+	}
+	return false;
+}
+
+static bool nca9539_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+	return true;
+}
+
+static const struct reg_default nca9539_regmap_default[] = {
+	{ NCA9539_REG_INPUT_PORT0, 0xFF },
+	{ NCA9539_REG_INPUT_PORT1, 0xFF },
+	{ NCA9539_REG_OUTPUT_PORT0, 0xFF },
+	{ NCA9539_REG_OUTPUT_PORT1, 0xFF },
+	{ NCA9539_REG_POLARITY_PORT0, 0x00 },
+	{ NCA9539_REG_POLARITY_PORT1, 0x00 },
+	{ NCA9539_REG_CONFIG_PORT0, 0xFF },
+	{ NCA9539_REG_CONFIG_PORT1, 0xFF },
+};
+
+static const struct regmap_config nca9539_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+	.max_register = 7,
+	.writeable_reg = nca9539_is_writeable_reg,
+	.readable_reg = nca9539_is_readable_reg,
+	.volatile_reg = nca9539_is_volatile_reg,
+	.reg_defaults = nca9539_regmap_default,
+	.num_reg_defaults = ARRAY_SIZE(nca9539_regmap_default),
+	.cache_type = REGCACHE_FLAT,
+};
+
+static const struct gpio_chip template_chip = {
+	.label = "nca9539-gpio",
+	.owner = THIS_MODULE,
+	.get_direction = nca9539_gpio_get_direction,
+	.direction_input = nca9539_gpio_direction_input,
+	.direction_output = nca9539_gpio_direction_output,
+	.get = nca9539_gpio_get,
+	.set = nca9539_gpio_set,
+	.base = -1,
+	.can_sleep = true,
+};
+
+static int nca9539_probe(struct i2c_client *client)
+{
+	struct nca9539_chip *chip;
+	struct regulator *reg;
+	int ret;
+
+	chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
+	if (!chip)
+		return -ENOMEM;
+
+	chip->gpio_chip = template_chip;
+	chip->gpio_chip.label = "nca9539-gpio";
+	chip->gpio_chip.parent = &client->dev;
+	chip->ngpio = (uintptr_t)of_device_get_match_data(&client->dev);
+	chip->gpio_chip.ngpio = chip->ngpio;
+
+	reg = devm_regulator_get(&client->dev, "vdd");
+	if (IS_ERR(reg))
+		return dev_err_probe(&client->dev, PTR_ERR(reg),
+				     "reg get err\n");
+
+	ret = regulator_enable(reg);
+	if (ret) {
+		dev_err(&client->dev, "reg en err: %d\n", ret);
+		return ret;
+	}
+	chip->regulator = reg;
+
+	chip->regmap = devm_regmap_init_i2c(client, &nca9539_regmap_config);
+	if (IS_ERR(chip->regmap)) {
+		ret = PTR_ERR(chip->regmap);
+		dev_err(&client->dev, "Failed to allocate register map: %d\n",
+			ret);
+		goto err_exit;
+	}
+	regcache_mark_dirty(chip->regmap);
+	ret = regcache_sync(chip->regmap);
+	if (ret) {
+		dev_err(&client->dev, "Failed to sync register map: %d\n", ret);
+		goto err_exit;
+	}
+
+	// TODO(Cody): irq_chip setup
+
+	ret = devm_gpiochip_add_data(&client->dev, &chip->gpio_chip, chip);
+	if (ret < 0) {
+		dev_err(&client->dev, "Unable to register gpiochip\n");
+		goto err_exit;
+	}
+
+	i2c_set_clientdata(client, chip);
+
+	return 0;
+
+err_exit:
+	regulator_disable(chip->regulator);
+	return ret;
+}
+
+static int nca9539_remove(struct i2c_client *client)
+{
+	struct nca9539_chip *chip = i2c_get_clientdata(client);
+
+	regulator_disable(chip->regulator);
+
+	return 0;
+}
+
+static const struct of_device_id nca9539_gpio_of_match_table[] = {
+	{
+		.compatible = "novo,nca9539-gpio",
+		.data = (void *)16,
+	},
+	{ /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, nca9539_gpio_of_match_table);
+
+static const struct i2c_device_id nca9539_gpio_id_table[] = {
+	{ "nca9539-gpio" },
+	{ /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(i2c, nca9539_gpio_id_table);
+
+static struct i2c_driver nca9539_driver = {
+	.driver = {
+		.name		= "nca9539-gpio",
+		.of_match_table	= nca9539_gpio_of_match_table,
+	},
+	.probe_new	= nca9539_probe,
+	.remove	= nca9539_remove,
+	.id_table	= nca9539_gpio_id_table,
+};
+module_i2c_driver(nca9539_driver);
+
+MODULE_AUTHOR("Cody Xie <cody.xie@rock-chips.com>");
+MODULE_DESCRIPTION("GPIO expander driver for Novosense nca9539");
+MODULE_LICENSE("GPL");
diff --git a/kernel/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/kernel/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
index 1a76671..53264c7 100644
--- a/kernel/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
+++ b/kernel/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c
@@ -32,6 +32,7 @@
 
 #include "analogix_dp_core.h"
 #include "analogix_dp_reg.h"
+#include "../../rockchip/rockchip_drm_drv.h"
 
 #define to_dp(nm)	container_of(nm, struct analogix_dp_device, nm)
 
@@ -51,6 +52,9 @@
 	struct i2c_client *client;
 	struct device_node *node;
 };
+
+static void analogix_dp_bridge_mode_set(struct drm_bridge *bridge,
+				const struct drm_display_mode *adj_mode);
 
 static bool analogix_dp_bandwidth_ok(struct analogix_dp_device *dp,
 				     const struct drm_display_mode *mode,
@@ -1442,6 +1446,32 @@
 		extcon_set_state_sync(dp->extcon, EXTCON_DISP_DP, false);
 }
 
+static int
+analogix_dp_atomic_connector_get_property(struct drm_connector *connector,
+					  const struct drm_connector_state *state,
+					  struct drm_property *property,
+					  uint64_t *val)
+{
+	struct rockchip_drm_private *private = connector->dev->dev_private;
+	struct analogix_dp_device *dp = to_dp(connector);
+
+	if (property == private->split_area_prop) {
+		switch (dp->split_area) {
+		case 1:
+			*val = ROCKCHIP_DRM_SPLIT_LEFT_SIDE;
+			break;
+		case 2:
+			*val = ROCKCHIP_DRM_SPLIT_RIGHT_SIDE;
+			break;
+		default:
+			*val = ROCKCHIP_DRM_SPLIT_UNSET;
+			break;
+		}
+	}
+
+	return 0;
+}
+
 static const struct drm_connector_funcs analogix_dp_connector_funcs = {
 	.fill_modes = drm_helper_probe_single_connector_modes,
 	.detect = analogix_dp_connector_detect,
@@ -1450,6 +1480,7 @@
 	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
 	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
 	.force = analogix_dp_connector_force,
+	.atomic_get_property = analogix_dp_atomic_connector_get_property,
 };
 
 static int analogix_dp_bridge_attach(struct drm_bridge *bridge,
@@ -1480,6 +1511,7 @@
 
 	if (!dp->plat_data->skip_connector) {
 		int connector_type = DRM_MODE_CONNECTOR_eDP;
+		struct rockchip_drm_private *private;
 
 		if (dp->plat_data->bridge &&
 		    dp->plat_data->bridge->type != DRM_MODE_CONNECTOR_Unknown)
@@ -1498,6 +1530,13 @@
 			DRM_ERROR("Failed to initialize connector with drm\n");
 			return ret;
 		}
+
+		private = connector->dev->dev_private;
+
+		if (dp->split_area)
+			drm_object_attach_property(&connector->base,
+						   private->split_area_prop,
+						   dp->split_area);
 
 		drm_connector_helper_add(connector,
 					 &analogix_dp_connector_helper_funcs);
@@ -1575,13 +1614,17 @@
 	struct drm_atomic_state *old_state = old_bridge_state->base.state;
 	struct analogix_dp_device *dp = bridge->driver_private;
 	struct drm_crtc *crtc;
-	struct drm_crtc_state *old_crtc_state;
+	struct drm_crtc_state *old_crtc_state, *new_crtc_state;
 
 	crtc = analogix_dp_get_new_crtc(dp, old_state);
 	if (!crtc)
 		return;
 
 	old_crtc_state = drm_atomic_get_old_crtc_state(old_state, crtc);
+
+	new_crtc_state = drm_atomic_get_new_crtc_state(old_state, crtc);
+	analogix_dp_bridge_mode_set(bridge, &new_crtc_state->adjusted_mode);
+
 	/* Don't touch the panel if we're coming back from PSR */
 	if (old_crtc_state && old_crtc_state->self_refresh_active)
 		return;
@@ -1790,7 +1833,6 @@
 }
 
 static void analogix_dp_bridge_mode_set(struct drm_bridge *bridge,
-				const struct drm_display_mode *orig_mode,
 				const struct drm_display_mode *adj_mode)
 {
 	struct analogix_dp_device *dp = bridge->driver_private;
@@ -1910,7 +1952,7 @@
 
 	drm_mode_copy(&m, mode);
 
-	if (dp->plat_data->split_mode)
+	if (dp->plat_data->split_mode || dp->plat_data->dual_connector_split)
 		dp->plat_data->convert_to_origin_mode(&m);
 
 	max_link_rate = min_t(u32, dp->video_info.max_link_rate,
@@ -1934,7 +1976,6 @@
 	.atomic_enable = analogix_dp_bridge_atomic_enable,
 	.atomic_disable = analogix_dp_bridge_atomic_disable,
 	.atomic_post_disable = analogix_dp_bridge_atomic_post_disable,
-	.mode_set = analogix_dp_bridge_mode_set,
 	.attach = analogix_dp_bridge_attach,
 	.detach = analogix_dp_bridge_detach,
 	.mode_valid = analogix_dp_bridge_mode_valid,
@@ -2073,6 +2114,9 @@
 		return ret;
 	}
 
+	if (device_property_read_u32(dp->dev, "split-area", &dp->split_area))
+		dp->split_area = 0;
+
 	return 0;
 }
 
diff --git a/kernel/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h b/kernel/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
index a843a73..f744058 100644
--- a/kernel/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
+++ b/kernel/drivers/gpu/drm/bridge/analogix/analogix_dp_core.h
@@ -195,6 +195,8 @@
 	struct analogix_dp_plat_data *plat_data;
 	struct extcon_dev *extcon;
 	struct analogix_dp_compliance compliance;
+
+	u32 split_area;
 };
 
 /* analogix_dp_reg.c */
diff --git a/kernel/drivers/gpu/drm/bridge/maxim-max96755f.c b/kernel/drivers/gpu/drm/bridge/maxim-max96755f.c
index bfabb61..95dcd52 100644
--- a/kernel/drivers/gpu/drm/bridge/maxim-max96755f.c
+++ b/kernel/drivers/gpu/drm/bridge/maxim-max96755f.c
@@ -41,6 +41,7 @@
 	bool dv_swp_ab;
 	bool dpi_deskew_en;
 	bool split_mode;
+	bool bridge_dual_link;
 	u32 dsi_lane_map[4];
 
 	struct {
@@ -283,7 +284,7 @@
 
 static void max96755f_bridge_reset_oneshot(struct max96755f_bridge *ser)
 {
-	regmap_update_bits(ser->regmap, 0x10, RESET_ONESHOT,
+	regmap_update_bits(ser->regmap, 0x0010, RESET_ONESHOT,
 			   FIELD_PREP(RESET_ONESHOT, 1));
 
 	mdelay(100);
@@ -324,6 +325,12 @@
 				   FIELD_PREP(START_PORTAY, 1));
 		regmap_update_bits(ser->regmap, 0x02, VID_TX_EN_X,
 				   FIELD_PREP(VID_TX_EN_X, 1));
+		if (ser->bridge_dual_link) {
+			regmap_update_bits(ser->regmap, 0x0010,
+				   AUTO_LINK | LINK_CFG,
+				   FIELD_PREP(AUTO_LINK, 0) |
+				   FIELD_PREP(LINK_CFG, DUAL_LINK));
+		}
 	}
 
 	max96755f_bridge_reset_oneshot(ser);
@@ -358,7 +365,7 @@
 			   FIELD_PREP(VID_TX_EN_X, 0) |
 			   FIELD_PREP(VID_TX_EN_Y, 0));
 
-	if (ser->split_mode)
+	if (ser->split_mode || ser->bridge_dual_link)
 		regmap_update_bits(ser->regmap, 0x0010,
 				   AUTO_LINK | LINK_CFG,
 				   FIELD_PREP(AUTO_LINK, 1) |
@@ -487,6 +494,7 @@
 static int max96755f_bridge_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
+	struct device_node *np = dev->of_node;
 	struct max96755f_bridge *ser;
 	int ret;
 
@@ -529,6 +537,8 @@
 	if (ret)
 		return dev_err_probe(dev, ret, "failed to request lock IRQ\n");
 
+	ser->bridge_dual_link = of_property_read_bool(np, "bridge_dual_link");
+
 	ser->bridge.funcs = &max96755f_bridge_funcs;
 	ser->bridge.of_node = dev->of_node;
 	ser->bridge.ops = DRM_BRIDGE_OP_DETECT;
diff --git a/kernel/drivers/gpu/drm/bridge/synopsys/Makefile b/kernel/drivers/gpu/drm/bridge/synopsys/Makefile
index 4e788db..757f552 100644
--- a/kernel/drivers/gpu/drm/bridge/synopsys/Makefile
+++ b/kernel/drivers/gpu/drm/bridge/synopsys/Makefile
@@ -1,6 +1,6 @@
 # SPDX-License-Identifier: GPL-2.0-only
 obj-$(CONFIG_DRM_DW_HDMI) += dw-hdmi.o dw-hdmi-hdcp.o \
-			dw-hdmi-qp.o
+			dw-hdmi-qp.o dw-hdmi-qp-hdcp.o
 obj-$(CONFIG_DRM_DW_HDMI_AHB_AUDIO) += dw-hdmi-ahb-audio.o
 obj-$(CONFIG_DRM_DW_HDMI_I2S_AUDIO) += dw-hdmi-i2s-audio.o dw-hdmi-qp-i2s-audio.o
 obj-$(CONFIG_DRM_DW_HDMI_CEC) += dw-hdmi-cec.o dw-hdmi-qp-cec.o
diff --git a/kernel/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c b/kernel/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c
index f7db2e8..95b0cae 100644
--- a/kernel/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c
+++ b/kernel/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c
@@ -132,6 +132,23 @@
 	return 0;
 }
 
+static int dw_hdmi_i2s_prepare(struct device *dev, void *data,
+			       struct hdmi_codec_daifmt *fmt,
+			       struct hdmi_codec_params *hparms)
+{
+	struct dw_hdmi_i2s_audio_data *audio = data;
+	struct dw_hdmi *hdmi = audio->hdmi;
+
+	dw_hdmi_audio_disable(hdmi);
+
+	hdmi_write(audio, HDMI_AUD_CONF0_SW_RESET, HDMI_AUD_CONF0);
+	hdmi_write(audio, (u8)~HDMI_MC_SWRSTZ_I2SSWRST_REQ, HDMI_MC_SWRSTZ);
+
+	dw_hdmi_audio_enable(hdmi);
+
+	return 0;
+}
+
 static int dw_hdmi_i2s_audio_startup(struct device *dev, void *data)
 {
 	struct dw_hdmi_i2s_audio_data *audio = data;
@@ -198,6 +215,7 @@
 
 static struct hdmi_codec_ops dw_hdmi_i2s_ops = {
 	.hw_params	= dw_hdmi_i2s_hw_params,
+	.prepare	= dw_hdmi_i2s_prepare,
 	.audio_startup  = dw_hdmi_i2s_audio_startup,
 	.audio_shutdown	= dw_hdmi_i2s_audio_shutdown,
 	.get_eld	= dw_hdmi_i2s_get_eld,
diff --git a/kernel/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp-hdcp.c b/kernel/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp-hdcp.c
new file mode 100644
index 0000000..7f55f72
--- /dev/null
+++ b/kernel/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp-hdcp.c
@@ -0,0 +1,650 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) Rockchip Electronics Co.Ltd
+ * Author:
+ *      Algea Cao <algea.cao@rock-chips.com>
+ */
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/hdmi.h>
+#include <linux/iopoll.h>
+#include <linux/irq.h>
+#include <linux/kthread.h>
+#include <linux/mutex.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/spinlock.h>
+#include <linux/soc/rockchip/rk_vendor_storage.h>
+#include <crypto/sha.h>
+#include <drm/bridge/dw_hdmi.h>
+
+#include "dw-hdmi-qp.h"
+#include "dw-hdmi-qp-hdcp.h"
+
+#define HDCP_KEY_SIZE		308
+#define HDCP_KEY_SEED_SIZE	2
+
+#define KSV_LEN			5
+#define HEADER			10
+#define SHAMAX			20
+
+#define MAX_DOWNSTREAM_DEVICE_NUM	5
+#define DPK_WR_OK_TIMEOUT_US		30000
+#define HDMI_HDCP1X_ID			5
+
+/* HDCP Registers */
+#define HDMI_HDCPREG_RMCTL	0x780e
+#define HDMI_HDCPREG_RMSTS	0x780f
+#define HDMI_HDCPREG_SEED0	0x7810
+#define HDMI_HDCPREG_SEED1	0x7811
+#define HDMI_HDCPREG_DPK0	0x7812
+#define HDMI_HDCPREG_DPK1	0x7813
+#define HDMI_HDCPREG_DPK2	0x7814
+#define HDMI_HDCPREG_DPK3	0x7815
+#define HDMI_HDCPREG_DPK4	0x7816
+#define HDMI_HDCPREG_DPK5	0x7817
+#define HDMI_HDCPREG_DPK6	0x7818
+#define HDMI_HDCP2REG_CTRL	0x7904
+#define HDMI_HDCP2REG_MASK	0x790c
+#define HDMI_HDCP2REG_MUTE	0x790e
+
+enum dw_hdmi_hdcp_state {
+	DW_HDCP_DISABLED,
+	DW_HDCP_AUTH_START,
+	DW_HDCP_AUTH_SUCCESS,
+	DW_HDCP_AUTH_FAIL,
+};
+
+enum {
+	DW_HDMI_HDCP_KSV_LEN = 8,
+	DW_HDMI_HDCP_SHA_LEN = 20,
+	DW_HDMI_HDCP_DPK_LEN = 280,
+	DW_HDMI_HDCP_KEY_LEN = 308,
+	DW_HDMI_HDCP_SEED_LEN = 2,
+};
+
+enum {
+	HDCP14_R0_TIMER_OVR_EN_MASK = 0x01,
+	HDCP14_R0_TIMER_OVR_EN = 0x01,
+	HDCP14_R0_TIMER_OVR_DISABLE = 0x00,
+
+	HDCP14_RI_TIMER_OVR_EN_MASK = 0x80,
+	HDCP14_RI_TIMER_OVR_EN = 0x80,
+	HDCP14_RI_TIMER_OVR_DISABLE = 0x00,
+
+	HDCP14_R0_TIMER_OVR_VALUE_MASK = 0x1e,
+	HDCP14_RI_TIMER_OVR_VALUE_MASK = 0xff00,
+
+	HDCP14_KEY_WR_OK = 0x100,
+
+	HDCP14_HPD_MASK = 0x01,
+	HDCP14_HPD_EN = 0x01,
+	HDCP14_HPD_DISABLE = 0x00,
+
+	HDCP14_ENCRYPTION_ENABLE_MASK = 0x04,
+	HDCP14_ENCRYPTION_ENABLE = 0x04,
+	HDCP14_ENCRYPTION_DISABLE = 0x04,
+
+	HDCP14_KEY_DECRYPT_EN_MASK = 0x400,
+	HDCP14_KEY_DECRYPT_EN = 0x400,
+	HDCP14_KEY_DECRYPT_DISABLE = 0x00,
+
+	HDMI_A_SRMCTRL_SHA1_FAIL_MASK = 0X08,
+	HDMI_A_SRMCTRL_SHA1_FAIL_DISABLE = 0X00,
+	HDMI_A_SRMCTRL_SHA1_FAIL_ENABLE = 0X08,
+
+	HDMI_A_SRMCTRL_KSV_UPDATE_MASK = 0X04,
+	HDMI_A_SRMCTRL_KSV_UPDATE_DISABLE = 0X00,
+	HDMI_A_SRMCTRL_KSV_UPDATE_ENABLE = 0X04,
+
+	HDMI_A_SRMCTRL_KSV_MEM_REQ_MASK = 0X01,
+	HDMI_A_SRMCTRL_KSV_MEM_REQ_DISABLE = 0X00,
+	HDMI_A_SRMCTRL_KSV_MEM_REQ_ENABLE = 0X01,
+
+	HDMI_A_SRMCTRL_KSV_MEM_ACCESS_MASK = 0X02,
+	HDMI_A_SRMCTRL_KSV_MEM_ACCESS_DISABLE = 0X00,
+	HDMI_A_SRMCTRL_KSV_MEM_ACCESS_ENABLE = 0X02,
+
+	HDMI_A_SRM_BASE_MAX_DEVS_EXCEEDED = 0x80,
+	HDMI_A_SRM_BASE_DEVICE_COUNT = 0x7f,
+
+	HDMI_A_SRM_BASE_MAX_CASCADE_EXCEEDED = 0x08,
+
+	HDMI_A_APIINTSTAT_KSVSHA1_CALC_INT = 0x02,
+
+	/* HDCPREG_RMSTS field values */
+	DPK_WR_OK_STS = 0x40,
+
+	HDMI_A_HDCP22_MASK = 0x40,
+
+	HDMI_HDCP2_OVR_EN_MASK = 0x02,
+	HDMI_HDCP2_OVR_ENABLE = 0x02,
+	HDMI_HDCP2_OVR_DISABLE = 0x00,
+
+	HDMI_HDCP2_FORCE_MASK = 0x04,
+	HDMI_HDCP2_FORCE_ENABLE = 0x04,
+	HDMI_HDCP2_FORCE_DISABLE = 0x00,
+};
+
+struct sha_t {
+	u8 mlength[8];
+	u8 mblock[64];
+	int mindex;
+	int mcomputed;
+	int mcorrupted;
+	unsigned int mdigest[5];
+};
+
+static inline unsigned int shacircularshift(unsigned int bits,
+					    unsigned int word)
+{
+	return (((word << bits) & 0xFFFFFFFF) | (word >> (32 - bits)));
+}
+
+static void hdcp_modb(struct dw_qp_hdcp *hdcp, u32 data, u32 mask, u32 reg)
+{
+	struct dw_hdmi_qp *hdmi = hdcp->hdmi;
+	u32 val = hdcp->read(hdmi, reg) & ~mask;
+
+	val |= data & mask;
+	hdcp->write(hdmi, val, reg);
+}
+
+static int hdcp_load_keys_cb(struct dw_qp_hdcp *hdcp)
+{
+	u32 size;
+	u8 hdcp_vendor_data[320];
+
+	hdcp->keys = kmalloc(HDCP_KEY_SIZE, GFP_KERNEL);
+	if (!hdcp->keys)
+		return -ENOMEM;
+
+	hdcp->seeds = kmalloc(HDCP_KEY_SEED_SIZE, GFP_KERNEL);
+	if (!hdcp->seeds) {
+		kfree(hdcp->keys);
+		return -ENOMEM;
+	}
+
+	size = rk_vendor_read(HDMI_HDCP1X_ID, hdcp_vendor_data, 314);
+	if (size < (HDCP_KEY_SIZE + HDCP_KEY_SEED_SIZE)) {
+		dev_err(hdcp->dev, "HDCP: read size %d\n", size);
+		memset(hdcp->keys, 0, HDCP_KEY_SIZE);
+		memset(hdcp->seeds, 0, HDCP_KEY_SEED_SIZE);
+	} else {
+		memcpy(hdcp->keys, hdcp_vendor_data, HDCP_KEY_SIZE);
+		memcpy(hdcp->seeds, hdcp_vendor_data + HDCP_KEY_SIZE,
+		       HDCP_KEY_SEED_SIZE);
+	}
+
+	return 0;
+}
+
+static int dw_hdcp_qp_hdcp_load_key(struct dw_qp_hdcp *hdcp)
+{
+	int i, j;
+	int ret, val;
+	void __iomem *reg_rmsts_addr;
+	struct dw_hdmi_qp_hdcp_keys *hdcp_keys;
+	struct dw_hdmi_qp *hdmi = hdcp->hdmi;
+	u32 ksv, dkl, dkh;
+
+	if (!hdcp->keys) {
+		ret = hdcp_load_keys_cb(hdcp);
+		if (ret)
+			return ret;
+	}
+	hdcp_keys = hdcp->keys;
+
+	reg_rmsts_addr = hdcp->regs + HDCP14_KEY_STATUS;
+
+	/* hdcp key has been written */
+	if (hdcp->read(hdmi, HDCP14_KEY_STATUS) & 0x3f) {
+		dev_info(hdcp->dev, "hdcp key has been written\n");
+		return 0;
+	}
+
+	ksv = hdcp_keys->KSV[0] | hdcp_keys->KSV[1] << 8 |
+		hdcp_keys->KSV[2] << 16 | hdcp_keys->KSV[3] << 24;
+	hdcp->write(hdmi, ksv, HDCP14_AKSV_L);
+
+	ksv = hdcp_keys->KSV[4];
+	hdcp->write(hdmi, ksv, HDCP14_AKSV_H);
+
+	if (hdcp->seeds) {
+		hdcp_modb(hdcp, HDCP14_KEY_DECRYPT_EN,
+			  HDCP14_KEY_DECRYPT_EN_MASK,
+			  HDCP14_CONFIG0);
+		hdcp->write(hdmi, (hdcp->seeds[0] << 8) | hdcp->seeds[1],
+			    HDCP14_KEY_SEED);
+	} else {
+		hdcp_modb(hdcp, HDCP14_KEY_DECRYPT_DISABLE,
+			  HDCP14_KEY_DECRYPT_EN_MASK,
+			  HDCP14_CONFIG0);
+	}
+
+	for (i = 0; i < DW_HDMI_HDCP_DPK_LEN - 6; i += 7) {
+		dkl = 0;
+		dkh = 0;
+		for (j = 0; j < 4; j++)
+			dkl |= hdcp_keys->devicekey[i + j] << (j * 8);
+		for (j = 4; j < 7; j++)
+			dkh |= hdcp_keys->devicekey[i + j] << ((j - 4) * 8);
+
+		hdcp->write(hdmi, dkh, HDCP14_KEY_H);
+		hdcp->write(hdmi, dkl, HDCP14_KEY_L);
+
+		ret = readx_poll_timeout(readl, reg_rmsts_addr, val,
+					 val & HDCP14_KEY_WR_OK, 1000,
+					 DPK_WR_OK_TIMEOUT_US);
+		if (ret) {
+			dev_err(hdcp->dev, "hdcp key write err\n");
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static void dw_hdcp_qp_hdcp_restart(struct dw_qp_hdcp *hdcp)
+{
+	mutex_lock(&hdcp->mutex);
+
+	if (!hdcp->remaining_times) {
+		mutex_unlock(&hdcp->mutex);
+		return;
+	}
+
+	hdcp_modb(hdcp, 0, HDCP14_ENCRYPTION_ENABLE_MASK | HDCP14_HPD_MASK,
+		   HDCP14_CONFIG0);
+
+	hdcp->write(hdcp->hdmi, 1, HDCP14_CONFIG1);
+	mdelay(50);
+	hdcp->write(hdcp->hdmi, HDCP14_AUTH_CHG_MASK_N | HDCP14_KSV_LIST_DONE_MASK_N,
+		    AVP_1_INT_CLEAR);
+	hdcp_modb(hdcp, HDCP14_AUTH_CHG_MASK_N | HDCP14_KSV_LIST_DONE_MASK_N,
+		  HDCP14_AUTH_CHG_MASK_N | HDCP14_KSV_LIST_DONE_MASK_N, AVP_1_INT_MASK_N);
+
+	hdcp_modb(hdcp, HDCP14_ENCRYPTION_ENABLE_MASK | HDCP14_HPD_MASK,
+		  HDCP14_ENCRYPTION_ENABLE_MASK | HDCP14_HPD_MASK,
+		   HDCP14_CONFIG0);
+
+	hdcp->remaining_times--;
+	mutex_unlock(&hdcp->mutex);
+}
+
+static int dw_hdcp_qp_hdcp_start(struct dw_qp_hdcp *hdcp)
+{
+	struct dw_hdmi_qp *hdmi = hdcp->hdmi;
+
+	dw_hdcp_qp_hdcp_load_key(hdcp);
+
+	mutex_lock(&hdcp->mutex);
+	hdcp->remaining_times = hdcp->retry_times;
+
+	hdcp->write(hdmi, HDCP14_AUTH_CHG_MASK_N | HDCP14_KSV_LIST_DONE_MASK_N, AVP_1_INT_CLEAR);
+	hdcp_modb(hdcp, HDCP14_AUTH_CHG_MASK_N | HDCP14_KSV_LIST_DONE_MASK_N,
+		  HDCP14_AUTH_CHG_MASK_N | HDCP14_KSV_LIST_DONE_MASK_N, AVP_1_INT_MASK_N);
+
+	mdelay(50);
+
+	hdcp_modb(hdcp, HDCP14_ENCRYPTION_ENABLE | HDCP14_HPD_EN,
+		  HDCP14_ENCRYPTION_ENABLE_MASK | HDCP14_HPD_MASK,
+		  HDCP14_CONFIG0);
+
+	hdcp->status = DW_HDCP_AUTH_START;
+	dev_info(hdcp->dev, "start hdcp\n");
+	mutex_unlock(&hdcp->mutex);
+
+	queue_work(hdcp->workqueue, &hdcp->work);
+	return 0;
+}
+
+static int dw_hdcp_qp_hdcp_stop(struct dw_qp_hdcp *hdcp)
+{
+	mutex_lock(&hdcp->mutex);
+	hdcp_modb(hdcp, 0, HDCP14_ENCRYPTION_ENABLE_MASK | HDCP14_HPD_MASK,
+		  HDCP14_CONFIG0);
+
+	hdcp_modb(hdcp, 0, HDCP14_AUTH_CHG_MASK_N | HDCP14_KSV_LIST_DONE_MASK_N, AVP_1_INT_MASK_N);
+	hdcp->write(hdcp->hdmi, 0, HDCP14_CONFIG1);
+	hdcp->status = DW_HDCP_DISABLED;
+	mutex_unlock(&hdcp->mutex);
+	return 0;
+}
+
+static void sha_reset(struct sha_t *sha)
+{
+	u32 i = 0;
+
+	sha->mindex = 0;
+	sha->mcomputed = false;
+	sha->mcorrupted = false;
+	for (i = 0; i < sizeof(sha->mlength); i++)
+		sha->mlength[i] = 0;
+
+	sha1_init(sha->mdigest);
+}
+
+static void sha_processblock(struct sha_t *sha)
+{
+	u32 array[SHA1_WORKSPACE_WORDS];
+
+	sha1_transform(sha->mdigest, sha->mblock, array);
+	sha->mindex = 0;
+}
+
+static void sha_padmessage(struct sha_t *sha)
+{
+	/*
+	 *  Check to see if the current message block is too small to hold
+	 *  the initial padding bits and length.  If so, we will pad the
+	 *  block, process it, and then continue padding into a second
+	 *  block.
+	 */
+	if (sha->mindex > 55) {
+		sha->mblock[sha->mindex++] = 0x80;
+		while (sha->mindex < 64)
+			sha->mblock[sha->mindex++] = 0;
+
+		sha_processblock(sha);
+		while (sha->mindex < 56)
+			sha->mblock[sha->mindex++] = 0;
+	} else {
+		sha->mblock[sha->mindex++] = 0x80;
+		while (sha->mindex < 56)
+			sha->mblock[sha->mindex++] = 0;
+	}
+
+	/* Store the message length as the last 8 octets */
+	sha->mblock[56] = sha->mlength[7];
+	sha->mblock[57] = sha->mlength[6];
+	sha->mblock[58] = sha->mlength[5];
+	sha->mblock[59] = sha->mlength[4];
+	sha->mblock[60] = sha->mlength[3];
+	sha->mblock[61] = sha->mlength[2];
+	sha->mblock[62] = sha->mlength[1];
+	sha->mblock[63] = sha->mlength[0];
+
+	sha_processblock(sha);
+}
+
+static int sha_result(struct sha_t *sha)
+{
+	if (sha->mcorrupted)
+		return false;
+
+	if (sha->mcomputed == 0) {
+		sha_padmessage(sha);
+		sha->mcomputed = true;
+	}
+	return true;
+}
+
+static void sha_input(struct sha_t *sha, const u8 *data, u32 size)
+{
+	int i = 0;
+	unsigned int j = 0;
+	int rc = true;
+
+	if (data == 0 || size == 0)
+		return;
+
+	if (sha->mcomputed || sha->mcorrupted) {
+		sha->mcorrupted = true;
+		return;
+	}
+	while (size-- && !sha->mcorrupted) {
+		sha->mblock[sha->mindex++] = *data;
+
+		for (i = 0; i < 8; i++) {
+			rc = true;
+			for (j = 0; j < sizeof(sha->mlength); j++) {
+				sha->mlength[j]++;
+				if (sha->mlength[j] != 0) {
+					rc = false;
+					break;
+				}
+			}
+			sha->mcorrupted = (sha->mcorrupted  ||
+					   rc) ? true : false;
+		}
+		/* if corrupted then message is too long */
+		if (sha->mindex == 64)
+			sha_processblock(sha);
+		data++;
+	}
+}
+
+static int hdcp_verify_ksv(const u8 *data, u32 size)
+{
+	u32 i = 0;
+	struct sha_t sha;
+
+	if ((!data) || (size < (HEADER + SHAMAX)))
+		return false;
+
+	sha_reset(&sha);
+	sha_input(&sha, data, size - SHAMAX);
+	if (sha_result(&sha) == false)
+		return false;
+
+	for (i = 0; i < SHAMAX; i++) {
+		if (data[size - SHAMAX + i] != (u8)(sha.mdigest[i / 4] >> ((i % 4) * 8)))
+			return false;
+	}
+	return true;
+}
+
+static void dw_hdcp_qp_hdcp_2nd_auth(struct dw_qp_hdcp *hdcp)
+{
+	u8 *data;
+	u32 len;
+
+	len = (hdcp->read(hdcp->hdmi, HDCP14_STATUS0) & HDCP14_RPT_DEVICE_COUNT) >> 9;
+	len = len * KSV_LEN + BSTATUS_LEN + M0_LEN + SHAMAX;
+
+	data = kmalloc(len, GFP_KERNEL);
+	if (!data)
+		return;
+
+	hdcp->get_mem(hdcp->hdmi, data, len);
+
+	if (hdcp_verify_ksv(data, len))
+		hdcp->write(hdcp->hdmi, HDCP14_SHA1_MSG_CORRECT_P, HDCP14_CONFIG1);
+	else
+		dw_hdcp_qp_hdcp_restart(hdcp);
+}
+
+static void dw_hdcp_qp_hdcp_auth(struct dw_qp_hdcp *hdcp, u32 hdcp_status)
+{
+	if (!(hdcp_status & BIT(2))) {
+		mutex_lock(&hdcp->mutex);
+		if (hdcp->status == DW_HDCP_DISABLED) {
+			mutex_unlock(&hdcp->mutex);
+			return;
+		}
+		dev_err(hdcp->dev, "hdcp auth failed\n");
+		hdcp_modb(hdcp, 0, HDCP14_ENCRYPTION_ENABLE_MASK | HDCP14_HPD_MASK,
+			  HDCP14_CONFIG0);
+		hdcp->status = DW_HDCP_AUTH_FAIL;
+		mutex_unlock(&hdcp->mutex);
+
+		dw_hdcp_qp_hdcp_restart(hdcp);
+	} else {
+		mutex_lock(&hdcp->mutex);
+		dev_info(hdcp->dev, "hdcp auth success\n");
+		hdcp->status = DW_HDCP_AUTH_SUCCESS;
+		mutex_unlock(&hdcp->mutex);
+	}
+}
+
+static void dw_hdcp_qp_hdcp_isr(struct dw_qp_hdcp *hdcp, u32 avp_int, u32 hdcp_status)
+{
+	if (hdcp->status == DW_HDCP_DISABLED)
+		return;
+
+	dev_info(hdcp->dev, "hdcp_int is 0x%02x\n", hdcp_status);
+
+	if (avp_int & HDCP14_KSV_LIST_DONE_MASK_N)
+		dw_hdcp_qp_hdcp_2nd_auth(hdcp);
+
+	if (avp_int & HDCP14_AUTH_CHG_MASK_N)
+		dw_hdcp_qp_hdcp_auth(hdcp, hdcp_status);
+}
+
+static ssize_t trytimes_show(struct device *device,
+			     struct device_attribute *attr, char *buf)
+{
+	int trytimes = 0;
+	struct dw_qp_hdcp *hdcp = dev_get_drvdata(device);
+
+	if (hdcp)
+		trytimes = hdcp->retry_times;
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", trytimes);
+}
+
+static ssize_t trytimes_store(struct device *device,
+			      struct device_attribute *attr,
+			      const char *buf, size_t count)
+{
+	int trytimes;
+	struct dw_qp_hdcp *hdcp = dev_get_drvdata(device);
+
+	if (!hdcp)
+		return -EINVAL;
+
+	if (kstrtoint(buf, 0, &trytimes))
+		return -EINVAL;
+
+	if (hdcp->retry_times != trytimes) {
+		hdcp->retry_times = trytimes;
+		hdcp->remaining_times = hdcp->retry_times;
+	}
+
+	return count;
+}
+
+static DEVICE_ATTR_RW(trytimes);
+
+static ssize_t status_show(struct device *device,
+			   struct device_attribute *attr, char *buf)
+{
+	int status = DW_HDCP_DISABLED;
+	struct dw_qp_hdcp *hdcp = dev_get_drvdata(device);
+
+	if (hdcp)
+		status = hdcp->status;
+
+	if (status == DW_HDCP_DISABLED)
+		return snprintf(buf, PAGE_SIZE, "hdcp disable\n");
+	else if (status == DW_HDCP_AUTH_START)
+		return snprintf(buf, PAGE_SIZE, "hdcp_auth_start\n");
+	else if (status == DW_HDCP_AUTH_SUCCESS)
+		return snprintf(buf, PAGE_SIZE, "hdcp_auth_success\n");
+	else if (status == DW_HDCP_AUTH_FAIL)
+		return snprintf(buf, PAGE_SIZE, "hdcp_auth_fail\n");
+	else
+		return snprintf(buf, PAGE_SIZE, "unknown status\n");
+}
+
+static DEVICE_ATTR_RO(status);
+
+static struct attribute *dw_hdmi_qp_hdcp_attrs[] = {
+	&dev_attr_trytimes.attr,
+	&dev_attr_status.attr,
+	NULL
+};
+ATTRIBUTE_GROUPS(dw_hdmi_qp_hdcp);
+
+/* If sink is a repeater, we need to wait ksv list ready */
+static void dw_hdmi_qp_hdcp(struct work_struct *p_work)
+{
+	struct dw_qp_hdcp *hdcp = container_of(p_work, struct dw_qp_hdcp, work);
+	u32 val;
+	int i = 500;
+
+	while (i--) {
+		usleep_range(7000, 8000);
+
+		mutex_lock(&hdcp->mutex);
+		if (hdcp->status == DW_HDCP_DISABLED) {
+			dev_dbg(hdcp->dev, "hdcp is disabled, don't wait repeater ready\n");
+			mutex_unlock(&hdcp->mutex);
+			return;
+		}
+
+		val = hdcp->read(hdcp->hdmi, HDCP14_STATUS1);
+
+		/* sink isn't repeater or ksv fifo ready, stop waiting */
+		if (!(val & HDCP14_RCV_REPEATER) || (val & HDCP14_RCV_KSV_FIFO_READY)) {
+			dev_dbg(hdcp->dev, "wait ksv fifo finished\n");
+			mutex_unlock(&hdcp->mutex);
+			return;
+		}
+
+		mutex_unlock(&hdcp->mutex);
+	}
+
+	if (i < 0) {
+		dev_err(hdcp->dev, "wait repeater ready time out\n");
+		dw_hdcp_qp_hdcp_restart(hdcp);
+	}
+}
+
+static int dw_hdcp_qp_hdcp_probe(struct platform_device *pdev)
+{
+	int ret = 0;
+	struct dw_qp_hdcp *hdcp = pdev->dev.platform_data;
+
+	/* retry time if hdcp auth fail. unlimited time if set 0 */
+	hdcp->dev = &pdev->dev;
+	hdcp->hdcp_start = dw_hdcp_qp_hdcp_start;
+	hdcp->hdcp_stop = dw_hdcp_qp_hdcp_stop;
+	hdcp->hdcp_isr = dw_hdcp_qp_hdcp_isr;
+
+	ret = device_add_groups(hdcp->dev, dw_hdmi_qp_hdcp_groups);
+	if (ret) {
+		dev_err(hdcp->dev, "Failed to add sysfs files group\n");
+		return ret;
+	}
+
+	platform_set_drvdata(pdev, hdcp);
+
+	hdcp->workqueue = create_workqueue("hdcp_queue");
+	INIT_WORK(&hdcp->work, dw_hdmi_qp_hdcp);
+
+	hdcp->retry_times = 3;
+	mutex_init(&hdcp->mutex);
+
+	dev_info(hdcp->dev, "%s success\n", __func__);
+	return 0;
+}
+
+static int dw_hdcp_qp_hdcp_remove(struct platform_device *pdev)
+{
+	struct dw_qp_hdcp *hdcp = pdev->dev.platform_data;
+
+	cancel_work_sync(&hdcp->work);
+	flush_workqueue(hdcp->workqueue);
+	destroy_workqueue(hdcp->workqueue);
+
+	device_remove_groups(hdcp->dev, dw_hdmi_qp_hdcp_groups);
+	kfree(hdcp->keys);
+	kfree(hdcp->seeds);
+
+	return 0;
+}
+
+static struct platform_driver dw_hdcp_qp_hdcp_driver = {
+	.probe  = dw_hdcp_qp_hdcp_probe,
+	.remove = dw_hdcp_qp_hdcp_remove,
+	.driver = {
+		.name = DW_HDCP_QP_DRIVER_NAME,
+	},
+};
+
+module_platform_driver(dw_hdcp_qp_hdcp_driver);
+MODULE_DESCRIPTION("DW HDMI QP transmitter HDCP driver");
+MODULE_LICENSE("GPL");
diff --git a/kernel/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp-hdcp.h b/kernel/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp-hdcp.h
new file mode 100644
index 0000000..48c3a48
--- /dev/null
+++ b/kernel/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp-hdcp.h
@@ -0,0 +1,55 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) Rockchip Electronics Co.Ltd
+ * Author:
+ *      Algea Cao <algea.cao@rock-chips.com>
+ */
+#ifndef DW_HDMI_QP_HDCP_H
+#define DW_HDMI_QP_HDCP_H
+
+#include <linux/miscdevice.h>
+
+#define DW_HDCP_QP_DRIVER_NAME "dw-hdmi-qp-hdcp"
+#define PRIVATE_KEY_SIZE	280
+#define KEY_SHA_SIZE		20
+
+#define KSV_LEN			5
+#define BSTATUS_LEN		2
+#define M0_LEN			8
+#define SHAMAX			20
+
+struct dw_hdmi_qp_hdcp_keys {
+	u8 KSV[8];
+	u8 devicekey[PRIVATE_KEY_SIZE];
+	u8 sha1[KEY_SHA_SIZE];
+};
+
+struct dw_qp_hdcp {
+	int retry_times;
+	int remaining_times;
+	char *seeds;
+	int invalidkey;
+	char *invalidkeys;
+	int hdcp2_enable;
+	int status;
+	u32 reg_io_width;
+
+	struct dw_hdmi_qp_hdcp_keys *keys;
+	struct device *dev;
+	struct dw_hdmi_qp *hdmi;
+	void __iomem *regs;
+
+	struct mutex mutex;
+
+	struct work_struct work;
+	struct workqueue_struct *workqueue;
+
+	void (*write)(struct dw_hdmi_qp *hdmi, u32 val, int offset);
+	u32 (*read)(struct dw_hdmi_qp *hdmi, int offset);
+	void (*get_mem)(struct dw_hdmi_qp *hdmi, u8 *data, u32 len);
+	int (*hdcp_start)(struct dw_qp_hdcp *hdcp);
+	int (*hdcp_stop)(struct dw_qp_hdcp *hdcp);
+	void (*hdcp_isr)(struct dw_qp_hdcp *hdcp, u32 avp_int, u32 hdcp_status);
+};
+
+#endif
diff --git a/kernel/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c b/kernel/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c
index 7ea4a54..906f0f6 100644
--- a/kernel/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c
+++ b/kernel/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c
@@ -4,6 +4,7 @@
  * Author:
  *      Algea Cao <algea.cao@rock-chips.com>
  */
+#include <linux/bitfield.h>
 #include <linux/clk.h>
 #include <linux/delay.h>
 #include <linux/dma-mapping.h>
@@ -25,6 +26,7 @@
 #include <drm/drm_dsc.h>
 #include <drm/drm_edid.h>
 #include <drm/drm_encoder_slave.h>
+#include <drm/drm_hdcp.h>
 #include <drm/drm_of.h>
 #include <drm/drm_panel.h>
 #include <drm/drm_print.h>
@@ -38,6 +40,7 @@
 #include "dw-hdmi-qp-audio.h"
 #include "dw-hdmi-qp.h"
 #include "dw-hdmi-qp-cec.h"
+#include "dw-hdmi-qp-hdcp.h"
 
 #include <media/cec-notifier.h>
 
@@ -51,6 +54,19 @@
 
 #define HDMI14_MAX_TMDSCLK	340000000
 #define HDMI20_MAX_TMDSCLK_KHZ	600000
+
+#define HDMI_VH0		0x20
+#define HDMI_HDCP_ADDR		0x3a
+#define HDMI_BCAPS		0x40
+#define HDMI_HDCP14_SUPPORT	BIT(7)
+#define HDMI_HDCP2_VERSION	0x50
+#define HDMI_HDCP2_SUPPORT	BIT(2)
+
+#define SINK_CAP_HDCP14		BIT(0)
+#define SINK_CAP_HDCP2		BIT(1)
+
+#define HDMI_HDCP2_AUTH		BIT(1)
+#define HDMI_HDCP14_AUTH	BIT(0)
 
 static const unsigned int dw_hdmi_cable[] = {
 	EXTCON_DISP_HDMI,
@@ -221,6 +237,7 @@
 struct dw_hdmi_qp {
 	struct drm_connector connector;
 	struct drm_bridge bridge;
+	struct drm_bridge *next_bridge;
 	struct drm_panel *panel;
 	struct platform_device *hdcp_dev;
 	struct platform_device *audio;
@@ -230,7 +247,7 @@
 
 	struct hdmi_qp_data_info hdmi_data;
 	const struct dw_hdmi_plat_data *plat_data;
-
+	struct dw_qp_hdcp *hdcp;
 	int vic;
 	int main_irq;
 	int avp_irq;
@@ -249,6 +266,7 @@
 
 	struct i2c_adapter *ddc;
 	void __iomem *regs;
+	void __iomem *hdcp14_mem;
 	bool sink_is_hdmi;
 	bool sink_has_audio;
 	bool dclk_en;
@@ -256,6 +274,7 @@
 	bool cec_enable;
 	bool allm_enable;
 	bool support_hdmi;
+	bool skip_connector;
 	int force_output;
 	int vp_id;
 	int old_vp_id;
@@ -268,6 +287,8 @@
 	bool rxsense;			/* rxsense state */
 	u8 phy_mask;			/* desired phy int mask settings */
 	u8 mc_clkdis;			/* clock disable register */
+	u8 hdcp_caps;
+	u8 hdcp_status;
 
 	bool update;
 	bool hdr2sdr;
@@ -944,8 +965,6 @@
 {
 	/* Software reset */
 	hdmi_writel(hdmi, 0x01, I2CM_CONTROL0);
-
-	hdmi_writel(hdmi, 0x085c085c, I2CM_FM_SCL_CONFIG0);
 
 	hdmi_modb(hdmi, 0, I2CM_FM_EN, I2CM_INTERFACE_CONTROL0);
 
@@ -1877,6 +1896,58 @@
 	return tmdsclock;
 }
 
+static void dw_hdmi_qp_hdcp_enable(struct dw_hdmi_qp *hdmi,
+				   struct drm_connector *connector)
+{
+	int ret, val;
+	const struct drm_connector_state *conn_state = connector->state;
+	void *data = hdmi->plat_data->phy_data;
+
+	if (conn_state->content_protection != DRM_MODE_CONTENT_PROTECTION_DESIRED)
+		return;
+
+	/* sink support hdcp2.x */
+	if (hdmi->hdcp_caps & SINK_CAP_HDCP2) {
+		hdmi_writel(hdmi, HDCP2_ESM_P0_GPIO_OUT_2_CHG_IRQ, AVP_3_INT_CLEAR);
+		hdmi_modb(hdmi, HDCP2_ESM_P0_GPIO_OUT_2_CHG_IRQ,
+			  HDCP2_ESM_P0_GPIO_OUT_2_CHG_IRQ, AVP_3_INT_MASK_N);
+
+		hdmi_writel(hdmi, 0x35, HDCP2LOGIC_ESM_GPIO_IN);
+		hdmi_modb(hdmi, 0, HDCP2_BYPASS, HDCP2LOGIC_CONFIG0);
+		if (hdmi->plat_data->set_hdcp2_enable)
+			hdmi->plat_data->set_hdcp2_enable(data, true);
+
+		/* wait hdcp2.X auth success */
+		ret = regmap_read_poll_timeout(hdmi->regm, HDCP2LOGIC_ESM_GPIO_OUT, val,
+					       FIELD_GET(HDCP2_AUTHENTICATION_SUCCESS, val),
+					       10000, 2000000);
+		if (ret) {
+			hdmi->hdcp_status &= ~HDMI_HDCP2_AUTH;
+			dev_info(hdmi->dev, "hdcp2 auth failed,start hdcp1.4\n");
+
+			hdmi_writel(hdmi, 0, HDCP2LOGIC_ESM_GPIO_IN);
+			hdmi_modb(hdmi, HDCP2_BYPASS, HDCP2_BYPASS, HDCP2LOGIC_CONFIG0);
+
+			if (hdmi->plat_data->set_hdcp2_enable)
+				hdmi->plat_data->set_hdcp2_enable(data, false);
+
+			if (hdmi->hdcp && hdmi->hdcp->hdcp_start)
+				hdmi->hdcp->hdcp_start(hdmi->hdcp);
+			goto exit;
+		}
+
+		hdmi->hdcp_status |= HDMI_HDCP2_AUTH;
+		drm_hdcp_update_content_protection(connector, DRM_MODE_CONTENT_PROTECTION_ENABLED);
+		dev_info(hdmi->dev, "HDCP2 authentication succeed\n");
+	} else {
+		if (hdmi->hdcp && hdmi->hdcp->hdcp_start)
+			hdmi->hdcp->hdcp_start(hdmi->hdcp);
+	}
+exit:
+	if (hdmi->plat_data->set_hdcp_status)
+		hdmi->plat_data->set_hdcp_status(data, hdmi->hdcp_status);
+}
+
 static int dw_hdmi_qp_setup(struct dw_hdmi_qp *hdmi,
 			    const struct drm_connector *connector,
 			    struct drm_display_mode *mode)
@@ -2038,6 +2109,7 @@
 		dev_info(hdmi->dev, "%s DVI mode\n", __func__);
 	}
 
+	dw_hdmi_qp_hdcp_enable(hdmi, hdmi->curr_conn);
 	hdmi->frl_switch = false;
 	return 0;
 }
@@ -2056,6 +2128,9 @@
 
 	if (hdmi->panel)
 		return connector_status_connected;
+
+	if (hdmi->next_bridge && hdmi->next_bridge->ops & DRM_BRIDGE_OP_DETECT)
+		return drm_bridge_detect(hdmi->next_bridge);
 
 	if (hdmi->plat_data->left)
 		secondary = hdmi->plat_data->left;
@@ -2124,6 +2199,58 @@
 	return false;
 }
 
+static ssize_t hdcp_ddc_read(struct i2c_adapter *adapter, u8 address,
+			     u8 offset, void *buffer)
+{
+	int ret;
+	struct i2c_msg msgs[2] = {
+		{
+			.addr = address,
+			.flags = 0,
+			.len = 1,
+			.buf = &offset,
+		}, {
+			.addr = address,
+			.flags = I2C_M_RD,
+			.len = 1,
+			.buf = buffer,
+		}
+	};
+
+	ret = i2c_transfer(adapter, msgs, ARRAY_SIZE(msgs));
+	if (ret < 0)
+		return ret;
+	if (ret != ARRAY_SIZE(msgs))
+		return -EPROTO;
+
+	return 0;
+}
+
+static u8 dw_hdmi_qp_hdcp_capable(struct dw_hdmi_qp *hdmi)
+{
+	u8 version = 0;
+	u8 bcaps;
+	int ret;
+
+	ret = hdcp_ddc_read(hdmi->ddc, HDMI_HDCP_ADDR, HDMI_BCAPS, &bcaps);
+	if (ret < 0) {
+		dev_err(hdmi->dev, "get hdcp1.4 capable failed:%d\n", ret);
+		return 0;
+	}
+	if (bcaps & HDMI_HDCP14_SUPPORT)
+		version |= SINK_CAP_HDCP14;
+
+	ret = hdcp_ddc_read(hdmi->ddc, HDMI_HDCP_ADDR, HDMI_HDCP2_VERSION, &bcaps);
+	if (ret < 0) {
+		dev_err(hdmi->dev, "get hdcp2.x capable failed:%d\n", ret);
+		return 0;
+	}
+	if (bcaps & HDMI_HDCP2_SUPPORT)
+		version |= SINK_CAP_HDCP2;
+
+	return version;
+}
+
 static int dw_hdmi_connector_get_modes(struct drm_connector *connector)
 {
 	struct dw_hdmi_qp *hdmi =
@@ -2134,16 +2261,39 @@
 	struct drm_display_mode *mode;
 	struct drm_display_info *info = &connector->display_info;
 	void *data = hdmi->plat_data->phy_data;
+	struct drm_property_blob *edid_blob_ptr = connector->edid_blob_ptr;
 	int i, ret = 0;
+
+	if (hdmi->plat_data->right && hdmi->plat_data->right->next_bridge) {
+		struct drm_bridge *bridge = hdmi->plat_data->right->next_bridge;
+
+		if (bridge->ops & DRM_BRIDGE_OP_MODES) {
+			if (!drm_bridge_get_modes(bridge, connector))
+				return 0;
+		}
+	}
 
 	if (hdmi->panel)
 		return drm_panel_get_modes(hdmi->panel, connector);
+
+	if (hdmi->next_bridge && hdmi->next_bridge->ops & DRM_BRIDGE_OP_MODES)
+		return drm_bridge_get_modes(hdmi->next_bridge, connector);
 
 	if (!hdmi->ddc)
 		return 0;
 
 	memset(metedata, 0, sizeof(*metedata));
-	edid = drm_get_edid(connector, hdmi->ddc);
+
+	if (edid_blob_ptr && edid_blob_ptr->length) {
+		edid = kmalloc(edid_blob_ptr->length, GFP_KERNEL);
+		if (!edid)
+			return -ENOMEM;
+		memcpy(edid, edid_blob_ptr->data, edid_blob_ptr->length);
+	} else {
+		edid = drm_get_edid(connector, hdmi->ddc);
+		hdmi->hdcp_caps = dw_hdmi_qp_hdcp_capable(hdmi);
+	}
+
 	if (edid) {
 		dev_dbg(hdmi->dev, "got edid: width[%d] x height[%d]\n",
 			edid->width_cm, edid->height_cm);
@@ -2161,6 +2311,7 @@
 		if (hdmi->plat_data->get_yuv422_format)
 			hdmi->plat_data->get_yuv422_format(connector, edid);
 		dw_hdmi_update_hdr_property(connector);
+		hdmi->hdcp_caps = dw_hdmi_qp_hdcp_capable(hdmi);
 		if (ret > 0 && hdmi->plat_data->split_mode) {
 			struct dw_hdmi_qp *secondary = NULL;
 			void *secondary_data;
@@ -2170,8 +2321,10 @@
 			else if (hdmi->plat_data->right)
 				secondary = hdmi->plat_data->right;
 
-			if (!secondary)
+			if (!secondary) {
+				kfree(edid);
 				return -ENOMEM;
+			}
 			secondary_data = secondary->plat_data->phy_data;
 
 			list_for_each_entry(mode, &connector->probed_modes, head)
@@ -2224,7 +2377,6 @@
 
 		dev_info(hdmi->dev, "failed to get edid\n");
 	}
-	dw_hdmi_qp_check_output_type_changed(hdmi);
 
 	return ret;
 }
@@ -2444,6 +2596,37 @@
 	return false;
 }
 
+static bool check_dw_hdcp_state_changed(struct drm_connector *conn,
+					struct drm_atomic_state *state)
+{
+	struct drm_connector_state *old_state, *new_state;
+	u64 old_cp, new_cp;
+
+	old_state = drm_atomic_get_old_connector_state(state, conn);
+	new_state = drm_atomic_get_new_connector_state(state, conn);
+	old_cp = old_state->content_protection;
+	new_cp = new_state->content_protection;
+
+	if (old_state->hdcp_content_type != new_state->hdcp_content_type &&
+	    new_cp != DRM_MODE_CONTENT_PROTECTION_UNDESIRED) {
+		new_state->content_protection = DRM_MODE_CONTENT_PROTECTION_DESIRED;
+		return true;
+	}
+
+	if (!new_state->crtc) {
+		if (old_cp == DRM_MODE_CONTENT_PROTECTION_ENABLED)
+			new_state->content_protection = DRM_MODE_CONTENT_PROTECTION_DESIRED;
+		return false;
+	}
+
+	if (old_cp == new_cp ||
+	    (old_cp == DRM_MODE_CONTENT_PROTECTION_DESIRED &&
+	     new_cp == DRM_MODE_CONTENT_PROTECTION_ENABLED))
+		return false;
+
+	return true;
+}
+
 static int dw_hdmi_connector_atomic_check(struct drm_connector *connector,
 					  struct drm_atomic_state *state)
 {
@@ -2546,7 +2729,8 @@
 	}
 
 	if (check_hdr_color_change(old_state, new_state, hdmi) || hdmi->logo_plug_out ||
-	    dw_hdmi_color_changed(connector, state)) {
+	    dw_hdmi_color_changed(connector, state) ||
+	    dw_hdmi_qp_check_output_type_changed(hdmi)) {
 		u32 mtmdsclk;
 
 		crtc_state = drm_atomic_get_crtc_state(state, crtc);
@@ -2580,13 +2764,16 @@
 			hdmi_modb(hdmi, PKTSCHED_GCP_TX_EN, PKTSCHED_GCP_TX_EN, PKTSCHED_PKT_EN);
 			mdelay(50);
 		} else if (!hdmi->disabled) {
-			if (mode.clock > 600000)
+			if (hdmi->previous_mode.clock > 600000 && mode.clock > 600000)
 				hdmi->frl_switch = true;
 			hdmi->update = false;
 			crtc_state->mode_changed = true;
 			hdmi->logo_plug_out = false;
 		}
 	}
+
+	if (check_dw_hdcp_state_changed(connector, state))
+		crtc_state->mode_changed = true;
 
 	return 0;
 }
@@ -2608,17 +2795,6 @@
 void dw_hdmi_qp_set_output_type(struct dw_hdmi_qp *hdmi, u64 val)
 {
 	hdmi->force_output = val;
-
-	if (!dw_hdmi_qp_check_output_type_changed(hdmi))
-		return;
-
-	if (hdmi->disabled)
-		return;
-
-	if (!hdmi->sink_is_hdmi)
-		hdmi_modb(hdmi, OPMODE_DVI, OPMODE_DVI, LINK_CONFIG0);
-	else
-		hdmi_modb(hdmi, 0, OPMODE_DVI, LINK_CONFIG0);
 }
 EXPORT_SYMBOL_GPL(dw_hdmi_qp_set_output_type);
 
@@ -2682,13 +2858,31 @@
 	struct drm_connector *connector = &hdmi->connector;
 	struct cec_connector_info conn_info;
 	struct cec_notifier *notifier;
+	bool skip_connector = false;
 
-	if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)
+	if (hdmi->next_bridge) {
+		struct drm_bridge *next_bridge = hdmi->next_bridge;
+		int ret;
+
+		ret = drm_bridge_attach(bridge->encoder, next_bridge, bridge,
+					next_bridge->ops & DRM_BRIDGE_OP_MODES ?
+					DRM_BRIDGE_ATTACH_NO_CONNECTOR : 0);
+		if (ret) {
+			DRM_ERROR("failed to attach next bridge: %d\n", ret);
+			return ret;
+		}
+
+		skip_connector = !(next_bridge->ops & DRM_BRIDGE_OP_MODES);
+	}
+
+	hdmi->skip_connector = skip_connector;
+	if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR || skip_connector)
 		return 0;
 
 	connector->interlace_allowed = 1;
 	connector->polled = DRM_CONNECTOR_POLL_HPD;
-
+	if (hdmi->next_bridge && hdmi->next_bridge->ops & DRM_BRIDGE_OP_DETECT)
+		connector->polled = DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT;
 	drm_connector_helper_add(connector, &dw_hdmi_connector_helper_funcs);
 
 	drm_connector_init(bridge->dev, connector, &dw_hdmi_connector_funcs,
@@ -2728,8 +2922,18 @@
 			     const struct drm_display_info *info,
 			     const struct drm_display_mode *mode)
 {
+	struct dw_hdmi_qp *hdmi = bridge->driver_private;
+	const struct dw_hdmi_plat_data *pdata = hdmi->plat_data;
+
 	if (mode->clock <= 25000)
 		return MODE_CLOCK_RANGE;
+
+	if (!hdmi->sink_is_hdmi && mode->clock > 340000)
+		return MODE_BAD;
+
+	if (pdata->mode_valid)
+		return pdata->mode_valid(NULL, pdata->priv_data, info,
+					 mode);
 
 	return MODE_OK;
 }
@@ -2757,6 +2961,7 @@
 {
 	struct dw_hdmi_qp *hdmi = bridge->driver_private;
 	void *data = hdmi->plat_data->phy_data;
+	const struct drm_connector_state *conn_state = hdmi->curr_conn->state;
 
 	if (hdmi->panel)
 		drm_panel_disable(hdmi->panel);
@@ -2764,6 +2969,19 @@
 	/* set avmute */
 	hdmi_writel(hdmi, 1, PKTSCHED_PKT_CONTROL0);
 	mdelay(50);
+
+	hdmi_modb(hdmi, 0, HDCP2_ESM_P0_GPIO_OUT_2_CHG_IRQ,
+		  AVP_3_INT_MASK_N);
+	if (hdmi->hdcp && hdmi->hdcp->hdcp_stop)
+		hdmi->hdcp->hdcp_stop(hdmi->hdcp);
+
+	hdmi_writel(hdmi, 0, HDCP2LOGIC_ESM_GPIO_IN);
+	if (conn_state->content_protection != DRM_MODE_CONTENT_PROTECTION_UNDESIRED)
+		drm_hdcp_update_content_protection(hdmi->curr_conn,
+						   DRM_MODE_CONTENT_PROTECTION_DESIRED);
+
+	if (hdmi->plat_data->set_hdcp_status)
+		hdmi->plat_data->set_hdcp_status(data, hdmi->hdcp_status);
 
 	extcon_set_state_sync(hdmi->extcon, EXTCON_DISP_HDMI, false);
 	handle_plugged_change(hdmi, false);
@@ -2781,7 +2999,7 @@
 		hdmi_writel(hdmi, 0, FLT_CONFIG0);
 		hdmi_writel(hdmi, 0, SCRAMB_CONFIG0);
 		/* set sink frl mode disable */
-		if (hdmi->curr_conn && dw_hdmi_support_scdc(hdmi, &hdmi->curr_conn->display_info))
+		if (dw_hdmi_support_scdc(hdmi, &hdmi->curr_conn->display_info))
 			drm_scdc_writeb(hdmi->ddc, 0x31, 0);
 
 		hdmi->phy.ops->disable(hdmi, hdmi->phy.data);
@@ -2913,17 +3131,77 @@
 static irqreturn_t dw_hdmi_qp_avp_hardirq(int irq, void *dev_id)
 {
 	struct dw_hdmi_qp *hdmi = dev_id;
-	u32 stat;
+	u32 stat1, stat3;
 
-	stat = hdmi_readl(hdmi, AVP_1_INT_STATUS);
-	if (stat) {
-		dev_dbg(hdmi->dev, "HDCP irq %#x\n", stat);
-		stat &= ~stat;
-		hdmi_writel(hdmi, stat, AVP_1_INT_MASK_N);
-		return IRQ_WAKE_THREAD;
+	stat1 = hdmi_readl(hdmi, AVP_1_INT_STATUS);
+	stat3 = hdmi_readl(hdmi, AVP_3_INT_STATUS);
+
+	if (!stat1 && !stat3)
+		return IRQ_NONE;
+
+	return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t dw_hdmi_qp_avp_irq(int irq, void *dev_id)
+{
+	struct dw_hdmi_qp *hdmi = dev_id;
+	struct drm_connector_state *conn_state;
+	void *data = hdmi->plat_data->phy_data;
+	u32 stat1, stat3, val;
+
+	stat1 = hdmi_readl(hdmi, AVP_1_INT_STATUS);
+	stat3 = hdmi_readl(hdmi, AVP_3_INT_STATUS);
+
+	hdmi_writel(hdmi, stat1, AVP_1_INT_CLEAR);
+	hdmi_writel(hdmi, stat3, AVP_3_INT_CLEAR);
+
+	if (!hdmi->curr_conn || !hdmi->curr_conn->state)
+		return IRQ_HANDLED;
+
+	conn_state = hdmi->curr_conn->state;
+	val = conn_state->content_protection;
+
+	if (hdmi->hdcp && hdmi->hdcp->hdcp_isr) {
+		u32 hdcp_status = hdmi_readl(hdmi, HDCP14_STATUS0);
+
+		if (stat1 & HDCP14_AUTH_CHG_MASK_N) {
+			/* hdcp14 auth success */
+			if (hdcp_status & BIT(2)) {
+				hdmi->hdcp_status |= HDMI_HDCP14_AUTH;
+				if (conn_state->content_protection !=
+				    DRM_MODE_CONTENT_PROTECTION_UNDESIRED)
+					val = DRM_MODE_CONTENT_PROTECTION_ENABLED;
+			} else if (!(hdcp_status & BIT(2))) {
+				hdmi->hdcp_status &= ~HDMI_HDCP14_AUTH;
+				if (conn_state->content_protection !=
+				    DRM_MODE_CONTENT_PROTECTION_UNDESIRED)
+					val = DRM_MODE_CONTENT_PROTECTION_DESIRED;
+			}
+			conn_state->content_protection = val;
+		}
+		hdmi->hdcp->hdcp_isr(hdmi->hdcp, stat1, hdcp_status);
 	}
 
-	return IRQ_NONE;
+	if (stat3 & HDCP2_ESM_P0_GPIO_OUT_2_CHG_IRQ) {
+		stat3 = hdmi_readl(hdmi, HDCP2LOGIC_ESM_GPIO_OUT);
+		if (stat3 & HDCP2_AUTHENTICATION_SUCCESS) {
+			hdmi->hdcp_status |= HDMI_HDCP2_AUTH;
+			if (conn_state->content_protection !=
+			    DRM_MODE_CONTENT_PROTECTION_UNDESIRED)
+				val = DRM_MODE_CONTENT_PROTECTION_ENABLED;
+		} else if (!(stat3 & HDCP2_AUTHENTICATION_SUCCESS)) {
+			hdmi->hdcp_status &= ~HDMI_HDCP2_AUTH;
+			if (conn_state->content_protection !=
+			    DRM_MODE_CONTENT_PROTECTION_UNDESIRED)
+				val = DRM_MODE_CONTENT_PROTECTION_DESIRED;
+		}
+		conn_state->content_protection = val;
+	}
+
+	if (hdmi->plat_data->set_hdcp_status)
+		hdmi->plat_data->set_hdcp_status(data, hdmi->hdcp_status);
+
+	return IRQ_HANDLED;
 }
 
 static irqreturn_t dw_hdmi_qp_earc_hardirq(int irq, void *dev_id)
@@ -2940,21 +3218,6 @@
 	}
 
 	return IRQ_NONE;
-}
-
-static irqreturn_t dw_hdmi_qp_avp_irq(int irq, void *dev_id)
-{
-	struct dw_hdmi_qp *hdmi = dev_id;
-	u32 stat;
-
-	stat = hdmi_readl(hdmi, AVP_1_INT_STATUS);
-
-	if (!stat)
-		return IRQ_NONE;
-
-	hdmi_writel(hdmi, stat, AVP_1_INT_CLEAR);
-
-	return IRQ_HANDLED;
 }
 
 static irqreturn_t dw_hdmi_qp_earc_irq(int irq, void *dev_id)
@@ -3124,6 +3387,11 @@
 	struct dw_hdmi_qp *hdmi = s->private;
 	u32 i = 0, j = 0, val = 0;
 
+	if (hdmi->disabled) {
+		dev_err(hdmi->dev, "hdmi is disabled\n");
+		return -EACCES;
+	}
+
 	seq_puts(s, "\n---------------------------------------------------");
 
 	for (i = 0; i < ARRAY_SIZE(hdmi_reg_table); i++) {
@@ -3154,6 +3422,11 @@
 		((struct seq_file *)file->private_data)->private;
 	u32 reg, val;
 	char kbuf[25];
+
+	if (hdmi->disabled) {
+		dev_err(hdmi->dev, "hdmi is disabled\n");
+		return -EACCES;
+	}
 
 	if (count > 24) {
 		dev_err(hdmi->dev, "out of buf range\n");
@@ -3339,6 +3612,70 @@
 			    hdmi, &dw_hdmi_ctrl_fops);
 }
 
+static void dw_hdmi_qp_hdcp14_get_mem(struct dw_hdmi_qp *hdmi, u8 *data, u32 len)
+{
+	u32 ksv_len, i, val;
+	void *hdmi_data = hdmi->plat_data->phy_data;
+
+	if (hdmi->plat_data->set_hdcp14_mem)
+		hdmi->plat_data->set_hdcp14_mem(hdmi_data, true);
+
+	ksv_len = len - BSTATUS_LEN - M0_LEN - SHAMAX;
+	for (i = 0; i < len; i++) {
+		/* read ksv list */
+		if (i < ksv_len)
+			val = readl(hdmi->hdcp14_mem + HDMI_HDCP14_MEM_KSV0 + i * 4);
+		/* read bstatus */
+		else if (i < len - SHAMAX - M0_LEN)
+			val = readl(hdmi->hdcp14_mem + HDMI_HDCP14_MEM_BSTATUS0 +
+				    (i - ksv_len) * 4);
+		/* read M0 */
+		else if (i < len - SHAMAX)
+			val = readl(hdmi->hdcp14_mem + HDMI_HDCP14_MEM_M0_1 +
+				    (i - ksv_len - BSTATUS_LEN) * 4);
+		else
+			/* VH0 save in external memory is error, we need to read VH0 via ddc */
+			hdcp_ddc_read(hdmi->ddc, HDMI_HDCP_ADDR, HDMI_VH0 + i - (len - SHAMAX),
+				      &val);
+
+		data[i] = val;
+	}
+
+	if (hdmi->plat_data->set_hdcp14_mem)
+		hdmi->plat_data->set_hdcp14_mem(hdmi_data, false);
+}
+
+static int dw_hdmi_qp_register_hdcp(struct device *dev,
+				    struct dw_hdmi_qp *hdmi)
+{
+	struct dw_qp_hdcp hdmi_hdcp = {
+		.hdmi = hdmi,
+		.write = hdmi_writel,
+		.read = hdmi_readl,
+		.regs = hdmi->regs,
+		.get_mem = dw_hdmi_qp_hdcp14_get_mem,
+	};
+	struct platform_device_info hdcp_device_info = {
+		.parent = dev,
+		.id = PLATFORM_DEVID_AUTO,
+		.res = NULL,
+		.num_res = 0,
+		.name = DW_HDCP_QP_DRIVER_NAME,
+		.data = &hdmi_hdcp,
+		.size_data = sizeof(hdmi_hdcp),
+		.dma_mask = DMA_BIT_MASK(32),
+	};
+	hdmi->hdcp_dev = platform_device_register_full(&hdcp_device_info);
+	if (IS_ERR(hdmi->hdcp_dev)) {
+		dev_err(dev, "failed to register hdcp!\n");
+		return -ENOMEM;
+	}
+
+	hdmi->hdcp = hdmi->hdcp_dev->dev.platform_data;
+
+	return 0;
+}
+
 static struct dw_hdmi_qp *
 __dw_hdmi_probe(struct platform_device *pdev,
 		const struct dw_hdmi_plat_data *plat_data)
@@ -3352,10 +3689,11 @@
 	struct dw_hdmi_qp_cec_data cec;
 	struct resource *iores = NULL;
 	struct drm_panel *panel = NULL;
+	struct drm_bridge *bridge = NULL;
 	int irq;
 	int ret;
 
-	ret = drm_of_find_panel_or_bridge(np, 1, -1, &panel, NULL);
+	ret = drm_of_find_panel_or_bridge(np, 1, -1, &panel, &bridge);
 	if (ret < 0 && ret != -ENODEV)
 		return ERR_PTR(ret);
 
@@ -3364,6 +3702,7 @@
 		return ERR_PTR(-ENOMEM);
 
 	hdmi->panel = panel;
+	hdmi->next_bridge = bridge;
 	hdmi->connector.stereo_allowed = 1;
 	hdmi->plat_data = plat_data;
 	hdmi->dev = dev;
@@ -3512,7 +3851,7 @@
 	hdmi->avp_irq = irq;
 	ret = devm_request_threaded_irq(dev, hdmi->avp_irq,
 					dw_hdmi_qp_avp_hardirq,
-					dw_hdmi_qp_avp_irq, IRQF_SHARED,
+					dw_hdmi_qp_avp_irq, IRQF_ONESHOT,
 					dev_name(dev), hdmi);
 	if (ret)
 		goto err_aud;
@@ -3564,6 +3903,20 @@
 		goto err_cec;
 
 	dw_hdmi_register_debugfs(dev, hdmi);
+
+	if (hdmi_readl(hdmi, CONFIG_REG) & CONFIG_HDCP14) {
+		iores = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+		hdmi->hdcp14_mem = devm_ioremap_resource(dev, iores);
+
+		if (IS_ERR(hdmi->hdcp14_mem)) {
+			ret = PTR_ERR(hdmi->hdcp14_mem);
+			goto err_cec;
+		}
+
+		ret = dw_hdmi_qp_register_hdcp(dev, hdmi);
+		if (ret)
+			goto err_cec;
+	}
 
 	return hdmi;
 
@@ -3617,6 +3970,8 @@
 		hdmi->bridge.encoder->funcs->destroy(hdmi->bridge.encoder);
 	if (!IS_ERR(hdmi->cec))
 		platform_device_unregister(hdmi->cec);
+	if (!IS_ERR(hdmi->hdcp_dev))
+		platform_device_unregister(hdmi->hdcp_dev);
 	if (hdmi->i2c)
 		i2c_del_adapter(&hdmi->i2c->adap);
 	else
@@ -3646,6 +4001,10 @@
 		}
 
 		plat_data->connector = &hdmi->connector;
+		if (hdmi->skip_connector && hdmi->next_bridge)
+			plat_data->bridge = hdmi->next_bridge;
+		else
+			plat_data->bridge = NULL;
 	}
 
 	if (plat_data->split_mode && !hdmi->plat_data->first_screen) {
@@ -3703,6 +4062,7 @@
 		disable_irq(hdmi->earc_irq);
 
 	pinctrl_pm_select_sleep_state(dev);
+	drm_connector_update_edid_property(&hdmi->connector, NULL);
 }
 EXPORT_SYMBOL_GPL(dw_hdmi_qp_suspend);
 
diff --git a/kernel/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.h b/kernel/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.h
index 225bfaa..e9b5e19 100644
--- a/kernel/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.h
+++ b/kernel/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.h
@@ -13,6 +13,7 @@
 #define CONFIG_REG					0xc
 #define CONFIG_CEC					BIT(28)
 #define CONFIG_AUD_UD					BIT(23)
+#define CONFIG_HDCP14					BIT(8)
 #define CORE_TIMESTAMP_HHMM				0x14
 #define CORE_TIMESTAMP_MMDD				0x18
 #define CORE_TIMESTAMP_YYYY				0x1c
@@ -139,7 +140,7 @@
 #define FRAME_COMPOSER_CONFIG8				0x860
 #define FRAME_COMPOSER_CONFIG9				0x864
 #define KEEPOUT_REKEY_CFG				GENMASK(9, 8)
-#define KEEPOUT_REKEY_ALWAYS				0x2
+#define KEEPOUT_REKEY_ALWAYS				(0x2 << 8)
 #define FRAME_COMPOSER_CONTROL0				0x86c
 /* Video Monitor Registers */
 #define VIDEO_MONITOR_CONFIG0				0x880
@@ -155,9 +156,13 @@
 #define HDCP2_BYPASS					BIT(0)
 #define HDCP2LOGIC_ESM_GPIO_IN				0x8e4
 #define HDCP2LOGIC_ESM_GPIO_OUT				0x8e8
+#define HDCP2_AUTHENTICATION_SUCCESS			BIT(6)
 /* HDCP14 Registers */
 #define HDCP14_CONFIG0					0x900
+#define HDCP14_OESS_ESSS_OVR_VALUE                      BIT(14)
+#define HDCP14_OESS_ESSS_OVR_EN                         BIT(13)
 #define HDCP14_CONFIG1					0x904
+#define HDCP14_SHA1_MSG_CORRECT_P                       BIT(3)
 #define HDCP14_CONFIG2					0x908
 #define HDCP14_CONFIG3					0x90c
 #define HDCP14_KEY_SEED					0x914
@@ -169,7 +174,10 @@
 #define HDCP14_AN_H					0x92c
 #define HDCP14_AN_L					0x930
 #define HDCP14_STATUS0					0x934
+#define HDCP14_RPT_DEVICE_COUNT                         0xFE00
 #define HDCP14_STATUS1					0x938
+#define HDCP14_RCV_REPEATER                             BIT(6)
+#define HDCP14_RCV_KSV_FIFO_READY                       BIT(5)
 /* Scrambler Registers */
 #define SCRAMB_CONFIG0					0x960
 /* Video Configuration Registers */
@@ -792,6 +800,7 @@
 #define AVP_1_INT_STATUS				0x3820
 #define AVP_1_INT_MASK_N				0x3824
 #define HDCP14_AUTH_CHG_MASK_N				BIT(6)
+#define HDCP14_KSV_LIST_DONE_MASK_N		        BIT(1)
 #define AVP_1_INT_CLEAR					0x3828
 #define AVP_1_INT_FORCE					0x382c
 #define AVP_2_INT_STATUS				0x3830
@@ -802,6 +811,7 @@
 #define AVP_3_INT_MASK_N				0x3844
 #define AVP_3_INT_CLEAR					0x3848
 #define AVP_3_INT_FORCE					0x384c
+#define HDCP2_ESM_P0_GPIO_OUT_2_CHG_IRQ			BIT(17)
 #define AVP_4_INT_STATUS				0x3850
 #define AVP_4_INT_MASK_N				0x3854
 #define AVP_4_INT_CLEAR					0x3858
@@ -832,4 +842,9 @@
 #define EARCRX_1_INT_CLEAR				0x4828
 #define EARCRX_1_INT_FORCE				0x482c
 
+#define HDMI_HDCP14_MEM_KSV0				0x4f08
+#define HDMI_HDCP14_MEM_BSTATUS0			0x5958
+#define HDMI_HDCP14_MEM_M0_1				0x5960
+#define HDMI_HDCP14_MEM_M0_7				0x597c
+
 #endif /* __DW_HDMI_QP_H__ */
diff --git a/kernel/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/kernel/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
index f395a69..c36513c 100644
--- a/kernel/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
+++ b/kernel/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
@@ -2328,7 +2328,7 @@
 
 	/* Dynamic Range and Mastering Infoframe is introduced in v2.11a. */
 	if (hdmi->version < 0x211a) {
-		DRM_ERROR("Not support DRM Infoframe\n");
+		dev_dbg(hdmi->dev, "Not support DRM Infoframe\n");
 		return;
 	}
 
@@ -3093,7 +3093,6 @@
 	int i,  ret = 0;
 
 	memset(metedata, 0, sizeof(*metedata));
-#if 0
 	edid = dw_hdmi_get_edid(hdmi, connector);
 	if (edid) {
 		int vic = 0;
@@ -3121,7 +3120,6 @@
 
 		kfree(edid);
 	} else {
-#endif
 		hdmi->support_hdmi = true;
 		hdmi->sink_has_audio = true;
 		for (i = 0; i < ARRAY_SIZE(dw_hdmi_default_modes); i++) {
@@ -3141,7 +3139,7 @@
 		info->color_formats = 0;
 
 		dev_info(hdmi->dev, "failed to get edid\n");
-//	}
+	}
 	dw_hdmi_update_hdr_property(connector);
 	dw_hdmi_check_output_type_changed(hdmi);
 
diff --git a/kernel/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c b/kernel/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
index a680731..71f0daa 100644
--- a/kernel/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
+++ b/kernel/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
@@ -876,19 +876,27 @@
 static void dw_mipi_dsi_bridge_post_disable(struct drm_bridge *bridge)
 {
 	struct dw_mipi_dsi *dsi = bridge_to_dsi(bridge);
+	const struct dw_mipi_dsi_plat_data *pdata = dsi->plat_data;
 
 	if (dsi->panel)
 		drm_panel_unprepare(dsi->panel);
 
 	dw_mipi_dsi_post_disable(dsi);
+
+	if (pdata->stream_standby)
+		pdata->stream_standby(pdata->priv_data, 0);
 }
 
 static void dw_mipi_dsi_bridge_disable(struct drm_bridge *bridge)
 {
 	struct dw_mipi_dsi *dsi = bridge_to_dsi(bridge);
+	const struct dw_mipi_dsi_plat_data *pdata = dsi->plat_data;
 
 	if (dsi->panel)
 		drm_panel_disable(dsi->panel);
+
+	if (pdata->stream_standby)
+		pdata->stream_standby(pdata->priv_data, 1);
 
 	dw_mipi_dsi_disable(dsi);
 }
@@ -975,6 +983,10 @@
 static void dw_mipi_dsi_bridge_pre_enable(struct drm_bridge *bridge)
 {
 	struct dw_mipi_dsi *dsi = bridge_to_dsi(bridge);
+	const struct dw_mipi_dsi_plat_data *pdata = dsi->plat_data;
+
+	if (pdata->stream_standby)
+		pdata->stream_standby(pdata->priv_data, 1);
 
 	dw_mipi_dsi_pre_enable(dsi);
 
@@ -1006,9 +1018,13 @@
 static void dw_mipi_dsi_bridge_enable(struct drm_bridge *bridge)
 {
 	struct dw_mipi_dsi *dsi = bridge_to_dsi(bridge);
+	const struct dw_mipi_dsi_plat_data *pdata = dsi->plat_data;
 
 	dw_mipi_dsi_enable(dsi);
 
+	if (pdata->stream_standby)
+		pdata->stream_standby(pdata->priv_data, 0);
+
 	if (dsi->panel)
 		drm_panel_enable(dsi->panel);
 
diff --git a/kernel/drivers/gpu/drm/drm_atomic_helper.c b/kernel/drivers/gpu/drm/drm_atomic_helper.c
index 33768dd..7fc8e70 100644
--- a/kernel/drivers/gpu/drm/drm_atomic_helper.c
+++ b/kernel/drivers/gpu/drm/drm_atomic_helper.c
@@ -3554,9 +3554,6 @@
 	replaced  = drm_property_replace_blob(&crtc_state->degamma_lut, NULL);
 	replaced |= drm_property_replace_blob(&crtc_state->ctm, NULL);
 	replaced |= drm_property_replace_blob(&crtc_state->gamma_lut, blob);
-#if defined(CONFIG_ROCKCHIP_DRM_CUBIC_LUT)
-	replaced |= drm_property_replace_blob(&crtc_state->cubic_lut, NULL);
-#endif
 	crtc_state->color_mgmt_changed |= replaced;
 
 	ret = drm_atomic_commit(state);
diff --git a/kernel/drivers/gpu/drm/drm_atomic_state_helper.c b/kernel/drivers/gpu/drm/drm_atomic_state_helper.c
index c29183d..9ad7404 100644
--- a/kernel/drivers/gpu/drm/drm_atomic_state_helper.c
+++ b/kernel/drivers/gpu/drm/drm_atomic_state_helper.c
@@ -141,10 +141,6 @@
 		drm_property_blob_get(state->ctm);
 	if (state->gamma_lut)
 		drm_property_blob_get(state->gamma_lut);
-#if defined(CONFIG_ROCKCHIP_DRM_CUBIC_LUT)
-	if (state->cubic_lut)
-		drm_property_blob_get(state->cubic_lut);
-#endif
 	state->mode_changed = false;
 	state->active_changed = false;
 	state->planes_changed = false;
@@ -217,9 +213,6 @@
 	drm_property_blob_put(state->degamma_lut);
 	drm_property_blob_put(state->ctm);
 	drm_property_blob_put(state->gamma_lut);
-#if defined(CONFIG_ROCKCHIP_DRM_CUBIC_LUT)
-	drm_property_blob_put(state->cubic_lut);
-#endif
 }
 EXPORT_SYMBOL(__drm_atomic_helper_crtc_destroy_state);
 
diff --git a/kernel/drivers/gpu/drm/drm_atomic_uapi.c b/kernel/drivers/gpu/drm/drm_atomic_uapi.c
index 975ece7..25c269b 100644
--- a/kernel/drivers/gpu/drm/drm_atomic_uapi.c
+++ b/kernel/drivers/gpu/drm/drm_atomic_uapi.c
@@ -459,16 +459,6 @@
 					&replaced);
 		state->color_mgmt_changed |= replaced;
 		return ret;
-#if defined(CONFIG_ROCKCHIP_DRM_CUBIC_LUT)
-	} else if (property == config->cubic_lut_property) {
-		ret = drm_atomic_replace_property_blob_from_id(dev,
-					&state->cubic_lut,
-					val,
-					-1, sizeof(struct drm_color_lut),
-					&replaced);
-		state->color_mgmt_changed |= replaced;
-		return ret;
-#endif
 	} else if (property == config->prop_out_fence_ptr) {
 		s32 __user *fence_ptr = u64_to_user_ptr(val);
 
@@ -511,10 +501,6 @@
 		*val = (state->ctm) ? state->ctm->base.id : 0;
 	else if (property == config->gamma_lut_property)
 		*val = (state->gamma_lut) ? state->gamma_lut->base.id : 0;
-#if defined(CONFIG_ROCKCHIP_DRM_CUBIC_LUT)
-	else if (property == config->cubic_lut_property)
-		*val = (state->cubic_lut) ? state->cubic_lut->base.id : 0;
-#endif
 	else if (property == config->prop_out_fence_ptr)
 		*val = 0;
 	else if (crtc->funcs->atomic_get_property)
diff --git a/kernel/drivers/gpu/drm/drm_color_mgmt.c b/kernel/drivers/gpu/drm/drm_color_mgmt.c
index 7b270b6..138ff34 100644
--- a/kernel/drivers/gpu/drm/drm_color_mgmt.c
+++ b/kernel/drivers/gpu/drm/drm_color_mgmt.c
@@ -33,7 +33,7 @@
 /**
  * DOC: overview
  *
- * Color management or color space adjustments is supported through a set of 7
+ * Color management or color space adjustments is supported through a set of 5
  * properties on the &drm_crtc object. They are set up by calling
  * drm_crtc_enable_color_mgmt().
  *
@@ -60,7 +60,7 @@
  * “CTM”:
  *	Blob property to set the current transformation matrix (CTM) apply to
  *	pixel data after the lookup through the degamma LUT and before the
- *	lookup through the cubic LUT. The data is interpreted as a struct
+ *	lookup through the gamma LUT. The data is interpreted as a struct
  *	&drm_color_ctm.
  *
  *	Setting this to NULL (blob property value set to 0) means a
@@ -68,40 +68,13 @@
  *	boot-up state too. Drivers can access the blob for the color conversion
  *	matrix through &drm_crtc_state.ctm.
  *
- * ”CUBIC_LUT”:
- *	Blob property to set the cubic (3D) lookup table performing color
- *	mapping after the transformation matrix and before the lookup through
- *	the gamma LUT. Unlike the degamma and gamma LUTs that map color
- *	components independently, the 3D LUT converts an input color to an
- *	output color by indexing into the 3D table using the color components
- *	as a 3D coordinate. The LUT is subsampled as 8-bit (or more) precision
- *	would require too much storage space in the hardware, so the precision
- *	of the color components is reduced before the look up, and the low
- *	order bits may be used to interpolate between the nearest points in 3D
- *	space.
- *
- *	The data is interpreted as an array of &struct drm_color_lut elements.
- *	Hardware might choose not to use the full precision of the LUT
- *	elements.
- *
- *	Setting this to NULL (blob property value set to 0) means the output
- *	color is identical to the input color. This is generally the driver
- *	boot-up state too. Drivers can access this blob through
- *	&drm_crtc_state.cubic_lut.
- *
- * ”CUBIC_LUT_SIZE”:
- *	Unsigned range property to give the size of the lookup table to be set
- *	on the CUBIC_LUT property (the size depends on the underlying hardware).
- *	If drivers support multiple LUT sizes then they should publish the
- *	largest size, and sub-sample smaller sized LUTs appropriately.
- *
  * “GAMMA_LUT”:
  *	Blob property to set the gamma lookup table (LUT) mapping pixel data
- *	after the cubic LUT to data sent to the connector. The data is
- *	interpreted as an array of &struct drm_color_lut elements. Hardware
- *	might choose not to use the full precision of the LUT elements nor use
- *	all the elements of the LUT (for example the hardware might choose to
- *	interpolate between LUT[0] and LUT[4]).
+ *	after the transformation matrix to data sent to the connector. The
+ *	data is interpreted as an array of &struct drm_color_lut elements.
+ *	Hardware might choose not to use the full precision of the LUT elements
+ *	nor use all the elements of the LUT (for example the hardware might
+ *	choose to interpolate between LUT[0] and LUT[4]).
  *
  *	Setting this to NULL (blob property value set to 0) means a
  *	linear/pass-thru gamma table should be used. This is generally the
diff --git a/kernel/drivers/gpu/drm/drm_mode_config.c b/kernel/drivers/gpu/drm/drm_mode_config.c
index f7f21df..f1affc1 100644
--- a/kernel/drivers/gpu/drm/drm_mode_config.c
+++ b/kernel/drivers/gpu/drm/drm_mode_config.c
@@ -364,22 +364,6 @@
 		return -ENOMEM;
 	dev->mode_config.gamma_lut_size_property = prop;
 
-#if defined(CONFIG_ROCKCHIP_DRM_CUBIC_LUT)
-	prop = drm_property_create(dev,
-			DRM_MODE_PROP_BLOB,
-			"CUBIC_LUT", 0);
-	if (!prop)
-		return -ENOMEM;
-	dev->mode_config.cubic_lut_property = prop;
-
-	prop = drm_property_create_range(dev,
-			DRM_MODE_PROP_IMMUTABLE,
-			"CUBIC_LUT_SIZE", 0, UINT_MAX);
-	if (!prop)
-		return -ENOMEM;
-	dev->mode_config.cubic_lut_size_property = prop;
-#endif
-
 	prop = drm_property_create(dev,
 				   DRM_MODE_PROP_IMMUTABLE | DRM_MODE_PROP_BLOB,
 				   "IN_FORMATS", 0);
diff --git a/kernel/drivers/gpu/drm/i915/display/intel_sprite.c b/kernel/drivers/gpu/drm/i915/display/intel_sprite.c
index a65061e..12f7128 100644
--- a/kernel/drivers/gpu/drm/i915/display/intel_sprite.c
+++ b/kernel/drivers/gpu/drm/i915/display/intel_sprite.c
@@ -118,8 +118,7 @@
 			"PSR idle timed out 0x%x, atomic update may fail\n",
 			psr_status);
 
-	if (!IS_ENABLED(CONFIG_PREEMPT_RT))
-		local_irq_disable();
+	local_irq_disable();
 
 	crtc->debug.min_vbl = min;
 	crtc->debug.max_vbl = max;
@@ -144,13 +143,11 @@
 			break;
 		}
 
-		if (!IS_ENABLED(CONFIG_PREEMPT_RT))
-			local_irq_enable();
+		local_irq_enable();
 
 		timeout = schedule_timeout(timeout);
 
-		if (!IS_ENABLED(CONFIG_PREEMPT_RT))
-			local_irq_disable();
+		local_irq_disable();
 	}
 
 	finish_wait(wq, &wait);
@@ -183,8 +180,7 @@
 	return;
 
 irq_disable:
-	if (!IS_ENABLED(CONFIG_PREEMPT_RT))
-		local_irq_disable();
+	local_irq_disable();
 }
 
 /**
@@ -222,8 +218,7 @@
 		new_crtc_state->uapi.event = NULL;
 	}
 
-	if (!IS_ENABLED(CONFIG_PREEMPT_RT))
-		local_irq_enable();
+	local_irq_enable();
 
 	if (intel_vgpu_active(dev_priv))
 		return;
diff --git a/kernel/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c b/kernel/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
index 2abf043..0c083af 100644
--- a/kernel/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
+++ b/kernel/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c
@@ -1080,7 +1080,7 @@
 		struct i915_ggtt *ggtt = cache_to_ggtt(cache);
 
 		intel_gt_flush_ggtt_writes(ggtt->vm.gt);
-		io_mapping_unmap_local((void __iomem *)vaddr);
+		io_mapping_unmap_atomic((void __iomem *)vaddr);
 
 		if (drm_mm_node_allocated(&cache->node)) {
 			ggtt->vm.clear_range(&ggtt->vm,
@@ -1146,7 +1146,7 @@
 
 	if (cache->vaddr) {
 		intel_gt_flush_ggtt_writes(ggtt->vm.gt);
-		io_mapping_unmap_local((void __force __iomem *) unmask_page(cache->vaddr));
+		io_mapping_unmap_atomic((void __force __iomem *) unmask_page(cache->vaddr));
 	} else {
 		struct i915_vma *vma;
 		int err;
@@ -1194,7 +1194,8 @@
 		offset += page << PAGE_SHIFT;
 	}
 
-	vaddr = (void __force *)io_mapping_map_local_wc(&ggtt->iomap, offset);
+	vaddr = (void __force *)io_mapping_map_atomic_wc(&ggtt->iomap,
+							 offset);
 	cache->page = page;
 	cache->vaddr = (unsigned long)vaddr;
 
diff --git a/kernel/drivers/gpu/drm/i915/gt/intel_breadcrumbs.c b/kernel/drivers/gpu/drm/i915/gt/intel_breadcrumbs.c
index 3f4f854..0040b47 100644
--- a/kernel/drivers/gpu/drm/i915/gt/intel_breadcrumbs.c
+++ b/kernel/drivers/gpu/drm/i915/gt/intel_breadcrumbs.c
@@ -342,9 +342,10 @@
 	/* Kick the work once more to drain the signalers */
 	irq_work_sync(&b->irq_work);
 	while (unlikely(READ_ONCE(b->irq_armed))) {
-		irq_work_queue(&b->irq_work);
+		local_irq_disable();
+		signal_irq_work(&b->irq_work);
+		local_irq_enable();
 		cond_resched();
-		irq_work_sync(&b->irq_work);
 	}
 	GEM_BUG_ON(!list_empty(&b->signalers));
 }
diff --git a/kernel/drivers/gpu/drm/i915/gt/intel_engine_pm.c b/kernel/drivers/gpu/drm/i915/gt/intel_engine_pm.c
index 313d8a2..f7b2e07 100644
--- a/kernel/drivers/gpu/drm/i915/gt/intel_engine_pm.c
+++ b/kernel/drivers/gpu/drm/i915/gt/intel_engine_pm.c
@@ -60,10 +60,9 @@
 
 static inline unsigned long __timeline_mark_lock(struct intel_context *ce)
 {
-	unsigned long flags = 0;
+	unsigned long flags;
 
-	if (!force_irqthreads)
-		local_irq_save(flags);
+	local_irq_save(flags);
 	mutex_acquire(&ce->timeline->mutex.dep_map, 2, 0, _THIS_IP_);
 
 	return flags;
@@ -73,8 +72,7 @@
 					  unsigned long flags)
 {
 	mutex_release(&ce->timeline->mutex.dep_map, _THIS_IP_);
-	if (!force_irqthreads)
-		local_irq_restore(flags);
+	local_irq_restore(flags);
 }
 
 #else
diff --git a/kernel/drivers/gpu/drm/i915/i915_gem.c b/kernel/drivers/gpu/drm/i915/i915_gem.c
index 88944c3..5827669 100644
--- a/kernel/drivers/gpu/drm/i915/i915_gem.c
+++ b/kernel/drivers/gpu/drm/i915/i915_gem.c
@@ -355,15 +355,22 @@
 	      char __user *user_data, int length)
 {
 	void __iomem *vaddr;
-	bool fail = false;
+	unsigned long unwritten;
 
 	/* We can use the cpu mem copy function because this is X86. */
-	vaddr = io_mapping_map_local_wc(mapping, base);
-	if (copy_to_user(user_data, (void __force *)vaddr + offset, length))
-		fail = true;
-	io_mapping_unmap_local(vaddr);
-
-	return fail;
+	vaddr = io_mapping_map_atomic_wc(mapping, base);
+	unwritten = __copy_to_user_inatomic(user_data,
+					    (void __force *)vaddr + offset,
+					    length);
+	io_mapping_unmap_atomic(vaddr);
+	if (unwritten) {
+		vaddr = io_mapping_map_wc(mapping, base, PAGE_SIZE);
+		unwritten = copy_to_user(user_data,
+					 (void __force *)vaddr + offset,
+					 length);
+		io_mapping_unmap(vaddr);
+	}
+	return unwritten;
 }
 
 static int
@@ -532,14 +539,21 @@
 	   char __user *user_data, int length)
 {
 	void __iomem *vaddr;
-	bool fail = false;
+	unsigned long unwritten;
 
 	/* We can use the cpu mem copy function because this is X86. */
-	vaddr = io_mapping_map_local_wc(mapping, base);
-	if (copy_from_user((void __force *)vaddr + offset, user_data, length))
-		fail = true;
-	io_mapping_unmap_local(vaddr);
-	return fail;
+	vaddr = io_mapping_map_atomic_wc(mapping, base);
+	unwritten = __copy_from_user_inatomic_nocache((void __force *)vaddr + offset,
+						      user_data, length);
+	io_mapping_unmap_atomic(vaddr);
+	if (unwritten) {
+		vaddr = io_mapping_map_wc(mapping, base, PAGE_SIZE);
+		unwritten = copy_from_user((void __force *)vaddr + offset,
+					   user_data, length);
+		io_mapping_unmap(vaddr);
+	}
+
+	return unwritten;
 }
 
 /**
diff --git a/kernel/drivers/gpu/drm/i915/i915_irq.c b/kernel/drivers/gpu/drm/i915/i915_irq.c
index 7339a42..759f523 100644
--- a/kernel/drivers/gpu/drm/i915/i915_irq.c
+++ b/kernel/drivers/gpu/drm/i915/i915_irq.c
@@ -847,7 +847,6 @@
 	spin_lock_irqsave(&dev_priv->uncore.lock, irqflags);
 
 	/* preempt_disable_rt() should go right here in PREEMPT_RT patchset. */
-	preempt_disable_rt();
 
 	/* Get optional system timestamp before query. */
 	if (stime)
@@ -899,7 +898,6 @@
 		*etime = ktime_get();
 
 	/* preempt_enable_rt() should go right here in PREEMPT_RT patchset. */
-	preempt_enable_rt();
 
 	spin_unlock_irqrestore(&dev_priv->uncore.lock, irqflags);
 
diff --git a/kernel/drivers/gpu/drm/i915/i915_trace.h b/kernel/drivers/gpu/drm/i915/i915_trace.h
index 396b659..a4addcc 100644
--- a/kernel/drivers/gpu/drm/i915/i915_trace.h
+++ b/kernel/drivers/gpu/drm/i915/i915_trace.h
@@ -2,10 +2,6 @@
 #if !defined(_I915_TRACE_H_) || defined(TRACE_HEADER_MULTI_READ)
 #define _I915_TRACE_H_
 
-#ifdef CONFIG_PREEMPT_RT
-#define NOTRACE
-#endif
-
 #include <linux/stringify.h>
 #include <linux/types.h>
 #include <linux/tracepoint.h>
@@ -782,7 +778,7 @@
 	    TP_ARGS(rq)
 );
 
-#if defined(CONFIG_DRM_I915_LOW_LEVEL_TRACEPOINTS) && !defined(NOTRACE)
+#if defined(CONFIG_DRM_I915_LOW_LEVEL_TRACEPOINTS)
 DEFINE_EVENT(i915_request, i915_request_submit,
 	     TP_PROTO(struct i915_request *rq),
 	     TP_ARGS(rq)
diff --git a/kernel/drivers/gpu/drm/i915/selftests/i915_gem.c b/kernel/drivers/gpu/drm/i915/selftests/i915_gem.c
index 4324931..412e216 100644
--- a/kernel/drivers/gpu/drm/i915/selftests/i915_gem.c
+++ b/kernel/drivers/gpu/drm/i915/selftests/i915_gem.c
@@ -57,12 +57,12 @@
 
 		ggtt->vm.insert_page(&ggtt->vm, dma, slot, I915_CACHE_NONE, 0);
 
-		s = io_mapping_map_local_wc(&ggtt->iomap, slot);
+		s = io_mapping_map_atomic_wc(&ggtt->iomap, slot);
 		for (x = 0; x < PAGE_SIZE / sizeof(u32); x++) {
 			prng = next_pseudo_random32(prng);
 			iowrite32(prng, &s[x]);
 		}
-		io_mapping_unmap_local(s);
+		io_mapping_unmap_atomic(s);
 	}
 
 	ggtt->vm.clear_range(&ggtt->vm, slot, PAGE_SIZE);
diff --git a/kernel/drivers/gpu/drm/i915/selftests/i915_gem_gtt.c b/kernel/drivers/gpu/drm/i915/selftests/i915_gem_gtt.c
index a68bed4..713770f 100644
--- a/kernel/drivers/gpu/drm/i915/selftests/i915_gem_gtt.c
+++ b/kernel/drivers/gpu/drm/i915/selftests/i915_gem_gtt.c
@@ -1200,9 +1200,9 @@
 		u64 offset = tmp.start + order[n] * PAGE_SIZE;
 		u32 __iomem *vaddr;
 
-		vaddr = io_mapping_map_local_wc(&ggtt->iomap, offset);
+		vaddr = io_mapping_map_atomic_wc(&ggtt->iomap, offset);
 		iowrite32(n, vaddr + n);
-		io_mapping_unmap_local(vaddr);
+		io_mapping_unmap_atomic(vaddr);
 	}
 	intel_gt_flush_ggtt_writes(ggtt->vm.gt);
 
@@ -1212,9 +1212,9 @@
 		u32 __iomem *vaddr;
 		u32 val;
 
-		vaddr = io_mapping_map_local_wc(&ggtt->iomap, offset);
+		vaddr = io_mapping_map_atomic_wc(&ggtt->iomap, offset);
 		val = ioread32(vaddr + n);
-		io_mapping_unmap_local(vaddr);
+		io_mapping_unmap_atomic(vaddr);
 
 		if (val != n) {
 			pr_err("insert page failed: found %d, expected %d\n",
diff --git a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/fbmem.h b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/fbmem.h
index 411f91e..6c5bbff 100644
--- a/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/fbmem.h
+++ b/kernel/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/fbmem.h
@@ -60,19 +60,19 @@
 static inline u32
 fbmem_peek(struct io_mapping *fb, u32 off)
 {
-	u8 __iomem *p = io_mapping_map_local_wc(fb, off & PAGE_MASK);
+	u8 __iomem *p = io_mapping_map_atomic_wc(fb, off & PAGE_MASK);
 	u32 val = ioread32(p + (off & ~PAGE_MASK));
-	io_mapping_unmap_local(p);
+	io_mapping_unmap_atomic(p);
 	return val;
 }
 
 static inline void
 fbmem_poke(struct io_mapping *fb, u32 off, u32 val)
 {
-	u8 __iomem *p = io_mapping_map_local_wc(fb, off & PAGE_MASK);
+	u8 __iomem *p = io_mapping_map_atomic_wc(fb, off & PAGE_MASK);
 	iowrite32(val, p + (off & ~PAGE_MASK));
 	wmb();
-	io_mapping_unmap_local(p);
+	io_mapping_unmap_atomic(p);
 }
 
 static inline bool
diff --git a/kernel/drivers/gpu/drm/panel/Kconfig b/kernel/drivers/gpu/drm/panel/Kconfig
index 2794dd3..c04e091 100644
--- a/kernel/drivers/gpu/drm/panel/Kconfig
+++ b/kernel/drivers/gpu/drm/panel/Kconfig
@@ -236,6 +236,14 @@
 	  Say Y if you want to enable support for panels based on the
 	  Maxim MAX96752F.
 
+config DRM_PANEL_MAXIM_MAX96772
+	tristate "Maxim MAX96772-based panels"
+	depends on OF
+	depends on BACKLIGHT_CLASS_DEVICE
+	help
+	  Say Y if you want to enable support for panels based on the
+	  Maxim MAX96772.
+
 config DRM_PANEL_OLIMEX_LCD_OLINUXINO
 	tristate "Olimex LCD-OLinuXino panel"
 	depends on OF
diff --git a/kernel/drivers/gpu/drm/panel/Makefile b/kernel/drivers/gpu/drm/panel/Makefile
index d99b1ea..f28d982 100644
--- a/kernel/drivers/gpu/drm/panel/Makefile
+++ b/kernel/drivers/gpu/drm/panel/Makefile
@@ -22,6 +22,7 @@
 obj-$(CONFIG_DRM_PANEL_NOVATEK_NT39016) += panel-novatek-nt39016.o
 obj-$(CONFIG_DRM_PANEL_MANTIX_MLAF057WE51) += panel-mantix-mlaf057we51.o
 obj-$(CONFIG_DRM_PANEL_MAXIM_MAX96752F) += panel-maxim-max96752f.o
+obj-$(CONFIG_DRM_PANEL_MAXIM_MAX96772) += panel-maxim-max96772.o
 obj-$(CONFIG_DRM_PANEL_OLIMEX_LCD_OLINUXINO) += panel-olimex-lcd-olinuxino.o
 obj-$(CONFIG_DRM_PANEL_ORISETECH_OTM8009A) += panel-orisetech-otm8009a.o
 obj-$(CONFIG_DRM_PANEL_OSD_OSD101T2587_53TS) += panel-osd-osd101t2587-53ts.o
diff --git a/kernel/drivers/gpu/drm/panel/panel-maxim-max96772.c b/kernel/drivers/gpu/drm/panel/panel-maxim-max96772.c
new file mode 100644
index 0000000..2e16d03
--- /dev/null
+++ b/kernel/drivers/gpu/drm/panel/panel-maxim-max96772.c
@@ -0,0 +1,543 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2022 Rockchip Electronics Co. Ltd.
+ */
+
+#include <linux/backlight.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/of_platform.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/of.h>
+
+#include <video/videomode.h>
+#include <video/of_display_timing.h>
+#include <video/display_timing.h>
+#include <uapi/linux/media-bus-format.h>
+
+#include <drm/drm_device.h>
+#include <drm/drm_dp_helper.h>
+#include <drm/drm_modes.h>
+#include <drm/drm_panel.h>
+
+struct max96772_panel;
+
+struct panel_desc {
+	const char *name;
+	u32 width_mm;
+	u32 height_mm;
+	u32 link_rate;
+	u32 lane_count;
+	bool ssc;
+
+	int (*prepare)(struct max96772_panel *p);
+	int (*unprepare)(struct max96772_panel *p);
+	int (*enable)(struct max96772_panel *p);
+	int (*disable)(struct max96772_panel *p);
+	int (*backlight_enable)(struct max96772_panel *p);
+	int (*backlight_disable)(struct max96772_panel *p);
+};
+
+struct max96772_panel {
+	struct drm_panel panel;
+	struct device *dev;
+	struct {
+		struct regmap *serializer;
+		struct regmap *deserializer;
+	} regmap;
+	struct backlight_device *backlight;
+	struct drm_display_mode mode;
+	const struct panel_desc *desc;
+	u32 link_rate;
+	u32 lane_count;
+	bool ssc;
+	bool panel_dual_link;
+};
+
+#define maxim_serializer_write(p, reg, val) do {			\
+		int ret;						\
+		ret = regmap_write(p->regmap.serializer, reg, val);	\
+		if (ret)						\
+			return ret;					\
+	} while (0)
+
+#define maxim_serializer_read(p, reg, val) do {				\
+		int ret;						\
+		ret = regmap_read(p->regmap.serializer, reg, val);	\
+		if (ret)						\
+			return ret;					\
+	} while (0)
+
+#define maxim_deserializer_write(p, reg, val) do {			\
+		int ret;						\
+		ret = regmap_write(p->regmap.deserializer, reg, val);	\
+		if (ret)						\
+			return ret;					\
+	} while (0)
+
+#define maxim_deserializer_read(p, reg, val) do {			\
+		int ret;						\
+		ret = regmap_read(p->regmap.deserializer, reg, val);	\
+		if (ret)						\
+			return ret;					\
+	} while (0)
+
+static const struct reg_sequence max96772_clk_ref[3][14] = {
+	{
+		{ 0xe7b2, 0x50 },
+		{ 0xe7b3, 0x00 },
+		{ 0xe7b4, 0xcc },
+		{ 0xe7b5, 0x44 },
+		{ 0xe7b6, 0x81 },
+		{ 0xe7b7, 0x30 },
+		{ 0xe7b8, 0x07 },
+		{ 0xe7b9, 0x10 },
+		{ 0xe7ba, 0x01 },
+		{ 0xe7bb, 0x00 },
+		{ 0xe7bc, 0x00 },
+		{ 0xe7bd, 0x00 },
+		{ 0xe7be, 0x52 },
+		{ 0xe7bf, 0x00 },
+	}, {
+		{ 0xe7b2, 0x50 },
+		{ 0xe7b3, 0x00 },
+		{ 0xe7b4, 0x00 },
+		{ 0xe7b5, 0x40 },
+		{ 0xe7b6, 0x6c },
+		{ 0xe7b7, 0x20 },
+		{ 0xe7b8, 0x07 },
+		{ 0xe7b9, 0x00 },
+		{ 0xe7ba, 0x01 },
+		{ 0xe7bb, 0x00 },
+		{ 0xe7bc, 0x00 },
+		{ 0xe7bd, 0x00 },
+		{ 0xe7be, 0x52 },
+		{ 0xe7bf, 0x00 },
+	}, {
+		{ 0xe7b2, 0x30 },
+		{ 0xe7b3, 0x00 },
+		{ 0xe7b4, 0x00 },
+		{ 0xe7b5, 0x40 },
+		{ 0xe7b6, 0x6c },
+		{ 0xe7b7, 0x20 },
+		{ 0xe7b8, 0x14 },
+		{ 0xe7b9, 0x00 },
+		{ 0xe7ba, 0x2e },
+		{ 0xe7bb, 0x00 },
+		{ 0xe7bc, 0x00 },
+		{ 0xe7bd, 0x01 },
+		{ 0xe7be, 0x32 },
+		{ 0xe7bf, 0x00 },
+	}
+};
+
+static int max96772_aux_dpcd_read(struct max96772_panel *p, u32 reg, u32 *value)
+{
+	maxim_deserializer_write(p, 0xe778, reg & 0xff);
+	maxim_deserializer_write(p, 0xe779, (reg >> 8) & 0xff);
+	maxim_deserializer_write(p, 0xe77c, (reg >> 16) & 0xff);
+	maxim_deserializer_write(p, 0xe776, 0x10);
+	maxim_deserializer_write(p, 0xe777, 0x80);
+	/* FIXME */
+	msleep(50);
+	maxim_deserializer_read(p, 0xe77a, value);
+
+	return 0;
+}
+
+static int max96772_prepare(struct max96772_panel *p)
+{
+	const struct drm_display_mode *mode = &p->mode;
+	u32 hfp, hsa, hbp, hact;
+	u32 vact, vsa, vfp, vbp;
+	u64 hwords, mvid;
+	bool hsync_pol, vsync_pol;
+
+	if (p->panel_dual_link) {
+		maxim_deserializer_write(p, 0x0010, 0x00);
+	}
+
+	maxim_deserializer_write(p, 0xe790, p->link_rate);
+	maxim_deserializer_write(p, 0xe792, p->lane_count);
+
+	if (p->ssc) {
+		maxim_deserializer_write(p, 0xe7b0, 0x01);
+		maxim_deserializer_write(p, 0xe7b1, 0x10);
+	} else {
+		maxim_deserializer_write(p, 0xe7b1, 0x00);
+	}
+
+	dev_info(p->dev, "link_rate=0x%02x, lane_count=0x%02x, ssc=%d\n",
+		 p->link_rate, p->lane_count, p->ssc);
+
+	switch (p->link_rate) {
+	case DP_LINK_BW_5_4:
+		regmap_multi_reg_write(p->regmap.deserializer, max96772_clk_ref[2],
+				       ARRAY_SIZE(max96772_clk_ref[2]));
+		break;
+	case DP_LINK_BW_2_7:
+		regmap_multi_reg_write(p->regmap.deserializer, max96772_clk_ref[1],
+				       ARRAY_SIZE(max96772_clk_ref[1]));
+		break;
+	case DP_LINK_BW_1_62:
+	default:
+		regmap_multi_reg_write(p->regmap.deserializer, max96772_clk_ref[0],
+				       ARRAY_SIZE(max96772_clk_ref[0]));
+		break;
+	}
+
+	vact = mode->vdisplay;
+	vsa = mode->vsync_end - mode->vsync_start;
+	vfp = mode->vsync_start - mode->vdisplay;
+	vbp = mode->vtotal - mode->vsync_end;
+	hact = mode->hdisplay;
+	hsa = mode->hsync_end - mode->hsync_start;
+	hfp = mode->hsync_start - mode->hdisplay;
+	hbp = mode->htotal - mode->hsync_end;
+
+	maxim_deserializer_write(p, 0xe794, hact & 0xff);
+	maxim_deserializer_write(p, 0xe795, (hact >> 8) & 0xff);
+	maxim_deserializer_write(p, 0xe796, hfp & 0xff);
+	maxim_deserializer_write(p, 0xe797, (hfp >> 8) & 0xff);
+	maxim_deserializer_write(p, 0xe798, hsa & 0xff);
+	maxim_deserializer_write(p, 0xe799, (hsa >> 8) & 0xff);
+	maxim_deserializer_write(p, 0xe79a, hbp & 0xff);
+	maxim_deserializer_write(p, 0xe79b, (hbp >> 8) & 0xff);
+	maxim_deserializer_write(p, 0xe79c, vact & 0xff);
+	maxim_deserializer_write(p, 0xe79d, (vact >> 8) & 0xff);
+	maxim_deserializer_write(p, 0xe79e, vfp & 0xff);
+	maxim_deserializer_write(p, 0xe79f, (vfp >> 8) & 0xff);
+	maxim_deserializer_write(p, 0xe7a0, vsa & 0xff);
+	maxim_deserializer_write(p, 0xe7a1, (vsa >> 8) & 0xff);
+	maxim_deserializer_write(p, 0xe7a2, vbp & 0xff);
+	maxim_deserializer_write(p, 0xe7a3, (vbp >> 8) & 0xff);
+
+	hsync_pol = !!(mode->flags & DRM_MODE_FLAG_NHSYNC);
+	vsync_pol = !!(mode->flags & DRM_MODE_FLAG_NVSYNC);
+	maxim_deserializer_write(p, 0xe7ac, hsync_pol | (vsync_pol << 1));
+
+	/* NVID should always be set to 0x8000 */
+	maxim_deserializer_write(p, 0xe7a8, 0);
+	maxim_deserializer_write(p, 0xe7a9, 0x80);
+
+	/* HWORDS = ((HRES x bits / pixel) / 16) - LANE_COUNT */
+	hwords = DIV_ROUND_CLOSEST_ULL(hact * 24, 16) - p->lane_count;
+	maxim_deserializer_write(p, 0xe7a4, hwords);
+	maxim_deserializer_write(p, 0xe7a5, hwords >> 8);
+
+	/* MVID = (PCLK x NVID) x 10 / Link Rate */
+	mvid = DIV_ROUND_CLOSEST_ULL((u64)mode->clock * 32768,
+				     drm_dp_bw_code_to_link_rate(p->link_rate));
+	maxim_deserializer_write(p, 0xe7a6, mvid & 0xff);
+	maxim_deserializer_write(p, 0xe7a7, (mvid >> 8) & 0xff);
+
+	maxim_deserializer_write(p, 0xe7aa, 0x40);
+	maxim_deserializer_write(p, 0xe7ab, 0x00);
+
+	/* set AUD_TX_EN = 0 */
+	maxim_deserializer_write(p, 0x02, 0xf3);
+	/* set AUD_EN_RX = 0 */
+	maxim_deserializer_write(p, 0x158, 0x20);
+	/* set MFP2 GPIO_TX_EN */
+	maxim_deserializer_write(p, 0x2b6, 0x03);
+
+	return 0;
+}
+
+static int max96776_enable(struct max96772_panel *p)
+{
+	u32 status[2];
+	u32 val;
+	int ret;
+
+	/* Run link training */
+	maxim_deserializer_write(p, 0xe776, 0x02);
+	maxim_deserializer_write(p, 0xe777, 0x80);
+
+	ret = regmap_read_poll_timeout(p->regmap.deserializer, 0x07f0, val,
+				       val & 0x01, MSEC_PER_SEC,
+				       500 * MSEC_PER_SEC);
+	if (!ret)
+		return 0;
+
+	ret = max96772_aux_dpcd_read(p, DP_LANE0_1_STATUS, &status[0]);
+	if (ret)
+		return ret;
+
+	ret = max96772_aux_dpcd_read(p, DP_LANE2_3_STATUS, &status[1]);
+	if (ret)
+		return ret;
+
+	dev_err(p->dev, "Link Training failed: LANE0_1_STATUS=0x%02x, LANE2_3_STATUS=0x%02x\n",
+		status[0], status[1]);
+
+	return 0;
+}
+
+static inline struct max96772_panel *to_max96772_panel(struct drm_panel *panel)
+{
+	return container_of(panel, struct max96772_panel, panel);
+}
+
+static int max96772_panel_prepare(struct drm_panel *panel)
+{
+	struct max96772_panel *p = to_max96772_panel(panel);
+
+	pinctrl_pm_select_default_state(p->dev);
+
+	if (p->desc->prepare)
+		p->desc->prepare(p);
+
+	if (!p->desc->link_rate || !p->desc->lane_count) {
+		u32 dpcd;
+		int ret;
+
+		ret = max96772_aux_dpcd_read(p, DP_MAX_LANE_COUNT, &dpcd);
+		if (ret) {
+			dev_err(p->dev, "failed to read max lane count\n");
+			return ret;
+		}
+
+		p->lane_count = min_t(int, 4, dpcd & DP_MAX_LANE_COUNT_MASK);
+
+		ret = max96772_aux_dpcd_read(p, DP_MAX_LINK_RATE, &dpcd);
+		if (ret) {
+			dev_err(p->dev, "failed to read max link rate\n");
+			return ret;
+		}
+
+		p->link_rate = min_t(int, dpcd, DP_LINK_BW_5_4);
+
+		ret = max96772_aux_dpcd_read(p, DP_MAX_DOWNSPREAD, &dpcd);
+		if (ret) {
+			dev_err(p->dev, "failed to read max downspread\n");
+			return ret;
+		}
+
+		p->ssc = !!(dpcd & DP_MAX_DOWNSPREAD_0_5);
+	} else {
+		p->link_rate = p->desc->link_rate;
+		p->lane_count = p->desc->lane_count;
+		p->ssc = p->desc->ssc;
+	}
+
+	return max96772_prepare(p);
+}
+
+static int max96772_panel_unprepare(struct drm_panel *panel)
+{
+	struct max96772_panel *p = to_max96772_panel(panel);
+
+	if (p->desc->unprepare)
+		p->desc->unprepare(p);
+
+	pinctrl_pm_select_sleep_state(p->dev);
+
+	return 0;
+}
+
+static int max96772_panel_enable(struct drm_panel *panel)
+{
+	struct max96772_panel *p = to_max96772_panel(panel);
+
+	max96776_enable(p);
+
+	if (p->desc->enable)
+		p->desc->enable(p);
+
+	backlight_enable(p->backlight);
+
+	if (p->desc->backlight_enable)
+		p->desc->backlight_enable(p);
+
+	return 0;
+}
+
+static int max96772_panel_disable(struct drm_panel *panel)
+{
+	struct max96772_panel *p = to_max96772_panel(panel);
+
+	if (p->desc->backlight_disable)
+		p->desc->backlight_disable(p);
+
+	backlight_disable(p->backlight);
+
+	if (p->desc->disable)
+		p->desc->disable(p);
+
+	return 0;
+}
+
+static int max96772_panel_get_modes(struct drm_panel *panel,
+				    struct drm_connector *connector)
+{
+	struct max96772_panel *p = to_max96772_panel(panel);
+	struct drm_display_mode *mode;
+	u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24;
+
+	connector->display_info.width_mm = p->desc->width_mm;
+	connector->display_info.height_mm = p->desc->height_mm;
+	drm_display_info_set_bus_formats(&connector->display_info, &bus_format, 1);
+
+	mode = drm_mode_duplicate(connector->dev, &p->mode);
+	mode->width_mm = p->desc->width_mm;
+	mode->height_mm = p->desc->height_mm;
+	mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
+
+	drm_mode_set_name(mode);
+	drm_mode_probed_add(connector, mode);
+
+	return 1;
+}
+
+static const struct drm_panel_funcs max96772_panel_funcs = {
+	.prepare = max96772_panel_prepare,
+	.unprepare = max96772_panel_unprepare,
+	.enable = max96772_panel_enable,
+	.disable = max96772_panel_disable,
+	.get_modes = max96772_panel_get_modes,
+};
+
+static int max96772_panel_parse_dt(struct max96772_panel *p)
+{
+	struct device *dev = p->dev;
+	struct display_timing dt;
+	struct videomode vm;
+	int ret;
+
+	ret = of_get_display_timing(dev->of_node, "panel-timing", &dt);
+	if (ret < 0) {
+		dev_err(dev, "%pOF: no panel-timing node found\n", dev->of_node);
+		return ret;
+	}
+
+	videomode_from_timing(&dt, &vm);
+	drm_display_mode_from_videomode(&vm, &p->mode);
+	p->panel_dual_link = of_property_read_bool(dev->of_node, "panel_dual_link");
+
+	return 0;
+}
+
+static const struct regmap_range max96772_readable_ranges[] = {
+	regmap_reg_range(0x0000, 0x0800),
+	regmap_reg_range(0x1700, 0x1700),
+	regmap_reg_range(0x4100, 0x4100),
+	regmap_reg_range(0x6230, 0x6230),
+	regmap_reg_range(0xe75e, 0xe75e),
+	regmap_reg_range(0xe776, 0xe7bf),
+};
+
+static const struct regmap_access_table max96772_readable_table = {
+	.yes_ranges = max96772_readable_ranges,
+	.n_yes_ranges = ARRAY_SIZE(max96772_readable_ranges),
+};
+
+static const struct regmap_config max96772_regmap_config = {
+	.name = "max96772",
+	.reg_bits = 16,
+	.val_bits = 8,
+	.max_register = 0xffff,
+	.rd_table = &max96772_readable_table,
+};
+
+static int max96772_panel_probe(struct i2c_client *client)
+{
+	struct device *dev = &client->dev;
+	struct max96772_panel *p;
+	struct i2c_client *parent;
+	int ret;
+
+	p = devm_kzalloc(dev, sizeof(*p), GFP_KERNEL);
+	if (!p)
+		return -ENOMEM;
+
+	p->dev = dev;
+	p->desc = of_device_get_match_data(dev);
+	i2c_set_clientdata(client, p);
+
+	ret = max96772_panel_parse_dt(p);
+	if (ret)
+		return dev_err_probe(dev, ret, "failed to parse DT\n");
+
+	p->backlight = devm_of_find_backlight(dev);
+	if (IS_ERR(p->backlight))
+		return dev_err_probe(dev, PTR_ERR(p->backlight),
+				     "failed to get backlight\n");
+
+	p->regmap.deserializer =
+		devm_regmap_init_i2c(client, &max96772_regmap_config);
+	if (IS_ERR(p->regmap.deserializer))
+		return dev_err_probe(dev, PTR_ERR(p->regmap.deserializer),
+				     "failed to initialize deserializer regmap\n");
+
+	parent = of_find_i2c_device_by_node(dev->of_node->parent->parent);
+	if (!parent)
+		return dev_err_probe(dev, -ENODEV, "failed to find parent\n");
+
+	p->regmap.serializer = dev_get_regmap(&parent->dev, NULL);
+	if (!p->regmap.serializer)
+		return dev_err_probe(dev, -ENODEV,
+				     "failed to initialize serializer regmap\n");
+
+	drm_panel_init(&p->panel, dev, &max96772_panel_funcs,
+		       DRM_MODE_CONNECTOR_eDP);
+	drm_panel_add(&p->panel);
+
+	return 0;
+}
+
+static int max96772_panel_remove(struct i2c_client *client)
+{
+	struct max96772_panel *p = i2c_get_clientdata(client);
+
+	drm_panel_remove(&p->panel);
+
+	return 0;
+}
+
+static int boe_ae146m1t_l10_prepare(struct max96772_panel *p)
+{
+	return 0;
+}
+
+static int boe_ae146m1t_l10_unprepare(struct max96772_panel *p)
+{
+	return 0;
+}
+
+
+static const struct panel_desc boe_ae146m1t_l10 = {
+	.name = "boe,ae146mit0-l10",
+	.width_mm = 323,
+	.height_mm = 182,
+	.link_rate = DP_LINK_BW_2_7,
+	.lane_count = 4,
+	.ssc = 0,
+	.prepare = boe_ae146m1t_l10_prepare,
+	.unprepare = boe_ae146m1t_l10_unprepare,
+
+};
+
+static const struct of_device_id max96772_panel_of_match[] = {
+	{ .compatible = "boe,ae146m1t-l10", &boe_ae146m1t_l10 },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, max96772_panel_of_match);
+
+static struct i2c_driver max96772_panel_driver = {
+	.driver = {
+		.name = "max96772-panel",
+		.of_match_table = max96772_panel_of_match,
+	},
+	.probe_new = max96772_panel_probe,
+	.remove = max96772_panel_remove,
+};
+
+module_i2c_driver(max96772_panel_driver);
+
+MODULE_AUTHOR("Wyon Bi <bivvy.bi@rock-chips.com>");
+MODULE_DESCRIPTION("Maxim MAX96772 based panel driver");
+MODULE_LICENSE("GPL");
diff --git a/kernel/drivers/gpu/drm/panel/panel-simple.c b/kernel/drivers/gpu/drm/panel/panel-simple.c
index 5f4ce17..5d8a58b 100644
--- a/kernel/drivers/gpu/drm/panel/panel-simple.c
+++ b/kernel/drivers/gpu/drm/panel/panel-simple.c
@@ -28,6 +28,7 @@
 #include <linux/of_platform.h>
 #include <linux/platform_device.h>
 #include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
 
 #include <video/display_timing.h>
 #include <video/mipi_display.h>
@@ -41,6 +42,11 @@
 #include <drm/drm_dsc.h>
 
 #include "panel-simple.h"
+
+enum panel_simple_cmd_type {
+	CMD_TYPE_DEFAULT,
+	CMD_TYPE_SPI
+};
 
 struct panel_cmd_header {
 	u8 data_type;
@@ -124,6 +130,11 @@
 
 	struct panel_cmd_seq *init_seq;
 	struct panel_cmd_seq *exit_seq;
+
+	enum panel_simple_cmd_type cmd_type;
+
+	int (*spi_read)(struct device *dev, const u8 cmd, u8 *val);
+	int (*spi_write)(struct device *dev, const u8 *data, size_t len, u8 type);
 };
 
 struct panel_simple {
@@ -266,7 +277,30 @@
 			dev_err(dev, "failed to write dcs cmd: %d\n", err);
 
 		if (cmd->header.delay)
-			msleep(cmd->header.delay);
+			usleep_range(cmd->header.delay * 1000, cmd->header.delay * 1000 + 100);
+	}
+
+	return 0;
+}
+
+static int panel_simple_xfer_spi_cmd_seq(struct panel_simple *panel, struct panel_cmd_seq *cmds)
+{
+	int i;
+	int ret;
+
+	if (!cmds)
+		return -EINVAL;
+
+	for (i = 0; i < cmds->cmd_cnt; i++) {
+		struct panel_cmd_desc *cmd = &cmds->cmds[i];
+
+		ret = panel->desc->spi_write(panel->base.dev, cmd->payload,
+					     cmd->header.payload_length, cmd->header.data_type);
+		if (ret)
+			return ret;
+
+		if (cmd->header.delay)
+			usleep_range(cmd->header.delay * 1000, cmd->header.delay * 1000 + 100);
 	}
 
 	return 0;
@@ -444,7 +478,7 @@
 		return 0;
 
 	if (p->desc->delay.disable)
-		msleep(p->desc->delay.disable);
+		usleep_range(p->desc->delay.disable * 1000, p->desc->delay.disable * 1000 + 100);
 
 	p->enabled = false;
 
@@ -458,9 +492,17 @@
 	if (!p->prepared)
 		return 0;
 
-	if (p->desc->exit_seq)
-		if (p->dsi)
-			panel_simple_xfer_dsi_cmd_seq(p, p->desc->exit_seq);
+	if (p->desc->exit_seq) {
+		if (p->desc->cmd_type == CMD_TYPE_SPI) {
+			if (panel_simple_xfer_spi_cmd_seq(p, p->desc->exit_seq)) {
+				dev_err(panel->dev, "failed to send exit spi cmds seq\n");
+				return -EINVAL;
+			}
+		} else {
+			if (p->dsi)
+				panel_simple_xfer_dsi_cmd_seq(p, p->desc->exit_seq);
+		}
+	}
 
 	gpiod_direction_output(p->reset_gpio, 1);
 	gpiod_direction_output(p->enable_gpio, 0);
@@ -468,7 +510,7 @@
 	panel_simple_regulator_disable(p);
 
 	if (p->desc->delay.unprepare)
-		msleep(p->desc->delay.unprepare);
+		usleep_range(p->desc->delay.unprepare * 1000, p->desc->delay.unprepare * 1000 + 100);
 
 	p->prepared = false;
 
@@ -522,7 +564,7 @@
 	if (p->no_hpd)
 		delay += p->desc->delay.hpd_absent_delay;
 	if (delay)
-		msleep(delay);
+		usleep_range(delay * 1000, delay * 1000 + 100);
 
 	if (p->hpd_gpio) {
 		if (IS_ERR(p->hpd_gpio)) {
@@ -547,16 +589,24 @@
 	gpiod_direction_output(p->reset_gpio, 1);
 
 	if (p->desc->delay.reset)
-		msleep(p->desc->delay.reset);
+		usleep_range(p->desc->delay.reset * 1000, p->desc->delay.reset * 1000 + 100);
 
 	gpiod_direction_output(p->reset_gpio, 0);
 
 	if (p->desc->delay.init)
-		msleep(p->desc->delay.init);
+		usleep_range(p->desc->delay.init * 1000, p->desc->delay.init * 1000 + 100);
 
-	if (p->desc->init_seq)
-		if (p->dsi)
-			panel_simple_xfer_dsi_cmd_seq(p, p->desc->init_seq);
+	if (p->desc->init_seq) {
+		if (p->desc->cmd_type == CMD_TYPE_SPI) {
+			if (panel_simple_xfer_spi_cmd_seq(p, p->desc->init_seq)) {
+				dev_err(panel->dev, "failed to send init spi cmds seq\n");
+				return -EINVAL;
+			}
+		} else {
+			if (p->dsi)
+				panel_simple_xfer_dsi_cmd_seq(p, p->desc->init_seq);
+		}
+	}
 
 	p->prepared = true;
 
@@ -571,7 +621,7 @@
 		return 0;
 
 	if (p->desc->delay.enable)
-		msleep(p->desc->delay.enable);
+		usleep_range(p->desc->delay.enable * 1000, p->desc->delay.enable * 1000 + 100);
 
 	p->enabled = true;
 
@@ -5109,6 +5159,113 @@
 	.shutdown = panel_simple_dsi_shutdown,
 };
 
+static int panel_simple_spi_read(struct device *dev, const u8 cmd, u8 *data)
+{
+	return 0;
+}
+
+static int panel_simple_spi_write_word(struct device *dev, u16 data)
+{
+	struct spi_device *spi = to_spi_device(dev);
+	struct spi_transfer xfer = {
+		.len	= 2,
+		.tx_buf = &data,
+	};
+	struct spi_message msg;
+
+	spi_message_init(&msg);
+	spi_message_add_tail(&xfer, &msg);
+
+	return spi_sync(spi, &msg);
+}
+
+static int panel_simple_spi_write(struct device *dev, const u8 *data, size_t len, u8 type)
+{
+	int ret = 0;
+	int i;
+	u16 mask = type ? 0x100 : 0;
+
+	for (i = 0; i < len; i++) {
+		ret = panel_simple_spi_write_word(dev, *data | mask);
+		if (ret) {
+			dev_err(dev, "failed to write spi seq: %*ph\n", (int)len, data);
+			return ret;
+		}
+		data++;
+	}
+
+	return ret;
+}
+
+static const struct of_device_id panel_simple_spi_of_match[] = {
+	{ .compatible = "simple-panel-spi", .data = NULL },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, panel_simple_spi_of_match);
+
+static int panel_simple_spi_probe(struct spi_device *spi)
+{
+	struct device *dev = &spi->dev;
+	const struct of_device_id *id;
+	const struct panel_desc *desc;
+	struct panel_desc *d;
+	int ret;
+
+	id = of_match_node(panel_simple_spi_of_match, dev->of_node);
+	if (!id)
+		return -ENODEV;
+
+	if (!id->data) {
+		d = devm_kzalloc(dev, sizeof(*d), GFP_KERNEL);
+		if (!d)
+			return -ENOMEM;
+
+		ret = panel_simple_of_get_desc_data(dev, d);
+		if (ret) {
+			dev_err(dev, "failed to get desc data: %d\n", ret);
+			return ret;
+		}
+
+		d->spi_write = panel_simple_spi_write;
+		d->spi_read = panel_simple_spi_read;
+		d->cmd_type = CMD_TYPE_SPI;
+	}
+	desc = id->data ? id->data : d;
+
+	/*
+	 * Set spi to 3 lines and 9bits/word mode.
+	 */
+	spi->bits_per_word = 9;
+	spi->mode = SPI_MODE_3;
+	ret = spi_setup(spi);
+	if (ret < 0) {
+		dev_err(dev, "spi setup failed.\n");
+		return ret;
+	}
+
+	return panel_simple_probe(dev, desc);
+}
+
+static int panel_simple_spi_remove(struct spi_device *spi)
+{
+	return panel_simple_remove(&spi->dev);
+}
+
+static void panel_simple_spi_shutdown(struct spi_device *spi)
+{
+	panel_simple_shutdown(&spi->dev);
+}
+
+static struct spi_driver panel_simple_spi_driver = {
+	.driver	= {
+		.name		= "panel-simple-spi",
+		.of_match_table = panel_simple_spi_of_match,
+	},
+	.probe			= panel_simple_spi_probe,
+	.remove			= panel_simple_spi_remove,
+	.shutdown		= panel_simple_spi_shutdown,
+};
+
 static int __init panel_simple_init(void)
 {
 	int err;
@@ -5116,6 +5273,12 @@
 	err = platform_driver_register(&panel_simple_platform_driver);
 	if (err < 0)
 		return err;
+
+	if (IS_ENABLED(CONFIG_SPI_MASTER)) {
+		err = spi_register_driver(&panel_simple_spi_driver);
+		if (err < 0)
+			return err;
+	}
 
 	if (IS_ENABLED(CONFIG_DRM_MIPI_DSI)) {
 		err = mipi_dsi_driver_register(&panel_simple_dsi_driver);
@@ -5132,6 +5295,9 @@
 	if (IS_ENABLED(CONFIG_DRM_MIPI_DSI))
 		mipi_dsi_driver_unregister(&panel_simple_dsi_driver);
 
+	if (IS_ENABLED(CONFIG_SPI_MASTER))
+		spi_unregister_driver(&panel_simple_spi_driver);
+
 	platform_driver_unregister(&panel_simple_platform_driver);
 }
 module_exit(panel_simple_exit);
diff --git a/kernel/drivers/gpu/drm/qxl/qxl_image.c b/kernel/drivers/gpu/drm/qxl/qxl_image.c
index 93f92cc..60ab715 100644
--- a/kernel/drivers/gpu/drm/qxl/qxl_image.c
+++ b/kernel/drivers/gpu/drm/qxl/qxl_image.c
@@ -124,12 +124,12 @@
 				  wrong (check the bitmaps are sent correctly
 				  first) */
 
-	ptr = qxl_bo_kmap_local_page(qdev, chunk_bo, 0);
+	ptr = qxl_bo_kmap_atomic_page(qdev, chunk_bo, 0);
 	chunk = ptr;
 	chunk->data_size = height * chunk_stride;
 	chunk->prev_chunk = 0;
 	chunk->next_chunk = 0;
-	qxl_bo_kunmap_local_page(qdev, chunk_bo, ptr);
+	qxl_bo_kunmap_atomic_page(qdev, chunk_bo, ptr);
 
 	{
 		void *k_data, *i_data;
@@ -143,7 +143,7 @@
 			i_data = (void *)data;
 
 			while (remain > 0) {
-				ptr = qxl_bo_kmap_local_page(qdev, chunk_bo, page << PAGE_SHIFT);
+				ptr = qxl_bo_kmap_atomic_page(qdev, chunk_bo, page << PAGE_SHIFT);
 
 				if (page == 0) {
 					chunk = ptr;
@@ -157,7 +157,7 @@
 
 				memcpy(k_data, i_data, size);
 
-				qxl_bo_kunmap_local_page(qdev, chunk_bo, ptr);
+				qxl_bo_kunmap_atomic_page(qdev, chunk_bo, ptr);
 				i_data += size;
 				remain -= size;
 				page++;
@@ -175,10 +175,10 @@
 					page_offset = offset_in_page(out_offset);
 					size = min((int)(PAGE_SIZE - page_offset), remain);
 
-					ptr = qxl_bo_kmap_local_page(qdev, chunk_bo, page_base);
+					ptr = qxl_bo_kmap_atomic_page(qdev, chunk_bo, page_base);
 					k_data = ptr + page_offset;
 					memcpy(k_data, i_data, size);
-					qxl_bo_kunmap_local_page(qdev, chunk_bo, ptr);
+					qxl_bo_kunmap_atomic_page(qdev, chunk_bo, ptr);
 					remain -= size;
 					i_data += size;
 					out_offset += size;
@@ -189,7 +189,7 @@
 	qxl_bo_kunmap(chunk_bo);
 
 	image_bo = dimage->bo;
-	ptr = qxl_bo_kmap_local_page(qdev, image_bo, 0);
+	ptr = qxl_bo_kmap_atomic_page(qdev, image_bo, 0);
 	image = ptr;
 
 	image->descriptor.id = 0;
@@ -212,7 +212,7 @@
 		break;
 	default:
 		DRM_ERROR("unsupported image bit depth\n");
-		qxl_bo_kunmap_local_page(qdev, image_bo, ptr);
+		qxl_bo_kunmap_atomic_page(qdev, image_bo, ptr);
 		return -EINVAL;
 	}
 	image->u.bitmap.flags = QXL_BITMAP_TOP_DOWN;
@@ -222,7 +222,7 @@
 	image->u.bitmap.palette = 0;
 	image->u.bitmap.data = qxl_bo_physical_address(qdev, chunk_bo, 0);
 
-	qxl_bo_kunmap_local_page(qdev, image_bo, ptr);
+	qxl_bo_kunmap_atomic_page(qdev, image_bo, ptr);
 
 	return 0;
 }
diff --git a/kernel/drivers/gpu/drm/qxl/qxl_ioctl.c b/kernel/drivers/gpu/drm/qxl/qxl_ioctl.c
index 7850230..5cea6ee 100644
--- a/kernel/drivers/gpu/drm/qxl/qxl_ioctl.c
+++ b/kernel/drivers/gpu/drm/qxl/qxl_ioctl.c
@@ -89,11 +89,11 @@
 {
 	void *reloc_page;
 
-	reloc_page = qxl_bo_kmap_local_page(qdev, info->dst_bo, info->dst_offset & PAGE_MASK);
+	reloc_page = qxl_bo_kmap_atomic_page(qdev, info->dst_bo, info->dst_offset & PAGE_MASK);
 	*(uint64_t *)(reloc_page + (info->dst_offset & ~PAGE_MASK)) = qxl_bo_physical_address(qdev,
 											      info->src_bo,
 											      info->src_offset);
-	qxl_bo_kunmap_local_page(qdev, info->dst_bo, reloc_page);
+	qxl_bo_kunmap_atomic_page(qdev, info->dst_bo, reloc_page);
 }
 
 static void
@@ -105,9 +105,9 @@
 	if (info->src_bo && !info->src_bo->is_primary)
 		id = info->src_bo->surface_id;
 
-	reloc_page = qxl_bo_kmap_local_page(qdev, info->dst_bo, info->dst_offset & PAGE_MASK);
+	reloc_page = qxl_bo_kmap_atomic_page(qdev, info->dst_bo, info->dst_offset & PAGE_MASK);
 	*(uint32_t *)(reloc_page + (info->dst_offset & ~PAGE_MASK)) = id;
-	qxl_bo_kunmap_local_page(qdev, info->dst_bo, reloc_page);
+	qxl_bo_kunmap_atomic_page(qdev, info->dst_bo, reloc_page);
 }
 
 /* return holding the reference to this object */
@@ -149,6 +149,7 @@
 	struct qxl_bo *cmd_bo;
 	void *fb_cmd;
 	int i, ret, num_relocs;
+	int unwritten;
 
 	switch (cmd->type) {
 	case QXL_CMD_DRAW:
@@ -184,21 +185,21 @@
 		goto out_free_reloc;
 
 	/* TODO copy slow path code from i915 */
-	fb_cmd = qxl_bo_kmap_local_page(qdev, cmd_bo, (release->release_offset & PAGE_MASK));
+	fb_cmd = qxl_bo_kmap_atomic_page(qdev, cmd_bo, (release->release_offset & PAGE_MASK));
+	unwritten = __copy_from_user_inatomic_nocache
+		(fb_cmd + sizeof(union qxl_release_info) + (release->release_offset & ~PAGE_MASK),
+		 u64_to_user_ptr(cmd->command), cmd->command_size);
 
-	if (copy_from_user(fb_cmd + sizeof(union qxl_release_info) +
-			   (release->release_offset & ~PAGE_MASK),
-			   u64_to_user_ptr(cmd->command), cmd->command_size)) {
-		ret = -EFAULT;
-	} else {
+	{
 		struct qxl_drawable *draw = fb_cmd;
 
 		draw->mm_time = qdev->rom->mm_clock;
 	}
 
-	qxl_bo_kunmap_local_page(qdev, cmd_bo, fb_cmd);
-	if (ret) {
-		DRM_ERROR("copy from user failed %d\n", ret);
+	qxl_bo_kunmap_atomic_page(qdev, cmd_bo, fb_cmd);
+	if (unwritten) {
+		DRM_ERROR("got unwritten %d\n", unwritten);
+		ret = -EFAULT;
 		goto out_free_release;
 	}
 
diff --git a/kernel/drivers/gpu/drm/qxl/qxl_object.c b/kernel/drivers/gpu/drm/qxl/qxl_object.c
index 5ee5171..544a9e4 100644
--- a/kernel/drivers/gpu/drm/qxl/qxl_object.c
+++ b/kernel/drivers/gpu/drm/qxl/qxl_object.c
@@ -173,8 +173,8 @@
 	return 0;
 }
 
-void *qxl_bo_kmap_local_page(struct qxl_device *qdev,
-			     struct qxl_bo *bo, int page_offset)
+void *qxl_bo_kmap_atomic_page(struct qxl_device *qdev,
+			      struct qxl_bo *bo, int page_offset)
 {
 	unsigned long offset;
 	void *rptr;
@@ -189,7 +189,7 @@
 		goto fallback;
 
 	offset = bo->tbo.mem.start << PAGE_SHIFT;
-	return io_mapping_map_local_wc(map, offset + page_offset);
+	return io_mapping_map_atomic_wc(map, offset + page_offset);
 fallback:
 	if (bo->kptr) {
 		rptr = bo->kptr + (page_offset * PAGE_SIZE);
@@ -215,14 +215,14 @@
 	ttm_bo_kunmap(&bo->kmap);
 }
 
-void qxl_bo_kunmap_local_page(struct qxl_device *qdev,
-			      struct qxl_bo *bo, void *pmap)
+void qxl_bo_kunmap_atomic_page(struct qxl_device *qdev,
+			       struct qxl_bo *bo, void *pmap)
 {
 	if ((bo->tbo.mem.mem_type != TTM_PL_VRAM) &&
 	    (bo->tbo.mem.mem_type != TTM_PL_PRIV))
 		goto fallback;
 
-	io_mapping_unmap_local(pmap);
+	io_mapping_unmap_atomic(pmap);
 	return;
  fallback:
 	qxl_bo_kunmap(bo);
diff --git a/kernel/drivers/gpu/drm/qxl/qxl_object.h b/kernel/drivers/gpu/drm/qxl/qxl_object.h
index 6ae89b1..5762ea4 100644
--- a/kernel/drivers/gpu/drm/qxl/qxl_object.h
+++ b/kernel/drivers/gpu/drm/qxl/qxl_object.h
@@ -89,8 +89,8 @@
 			 struct qxl_bo **bo_ptr);
 extern int qxl_bo_kmap(struct qxl_bo *bo, void **ptr);
 extern void qxl_bo_kunmap(struct qxl_bo *bo);
-void *qxl_bo_kmap_local_page(struct qxl_device *qdev, struct qxl_bo *bo, int page_offset);
-void qxl_bo_kunmap_local_page(struct qxl_device *qdev, struct qxl_bo *bo, void *map);
+void *qxl_bo_kmap_atomic_page(struct qxl_device *qdev, struct qxl_bo *bo, int page_offset);
+void qxl_bo_kunmap_atomic_page(struct qxl_device *qdev, struct qxl_bo *bo, void *map);
 extern struct qxl_bo *qxl_bo_ref(struct qxl_bo *bo);
 extern void qxl_bo_unref(struct qxl_bo **bo);
 extern int qxl_bo_pin(struct qxl_bo *bo);
diff --git a/kernel/drivers/gpu/drm/qxl/qxl_release.c b/kernel/drivers/gpu/drm/qxl/qxl_release.c
index b665a33..b2a475a 100644
--- a/kernel/drivers/gpu/drm/qxl/qxl_release.c
+++ b/kernel/drivers/gpu/drm/qxl/qxl_release.c
@@ -414,7 +414,7 @@
 	union qxl_release_info *info;
 	struct qxl_bo *bo = release->release_bo;
 
-	ptr = qxl_bo_kmap_local_page(qdev, bo, release->release_offset & PAGE_MASK);
+	ptr = qxl_bo_kmap_atomic_page(qdev, bo, release->release_offset & PAGE_MASK);
 	if (!ptr)
 		return NULL;
 	info = ptr + (release->release_offset & ~PAGE_MASK);
@@ -429,7 +429,7 @@
 	void *ptr;
 
 	ptr = ((void *)info) - (release->release_offset & ~PAGE_MASK);
-	qxl_bo_kunmap_local_page(qdev, bo, ptr);
+	qxl_bo_kunmap_atomic_page(qdev, bo, ptr);
 }
 
 void qxl_release_fence_buffer_objects(struct qxl_release *release)
diff --git a/kernel/drivers/gpu/drm/radeon/radeon_display.c b/kernel/drivers/gpu/drm/radeon/radeon_display.c
index 95ce311..71bdafa 100644
--- a/kernel/drivers/gpu/drm/radeon/radeon_display.c
+++ b/kernel/drivers/gpu/drm/radeon/radeon_display.c
@@ -1823,7 +1823,6 @@
 	struct radeon_device *rdev = dev->dev_private;
 
 	/* preempt_disable_rt() should go right here in PREEMPT_RT patchset. */
-	preempt_disable_rt();
 
 	/* Get optional system timestamp before query. */
 	if (stime)
@@ -1916,7 +1915,6 @@
 		*etime = ktime_get();
 
 	/* preempt_enable_rt() should go right here in PREEMPT_RT patchset. */
-	preempt_enable_rt();
 
 	/* Decode into vertical and horizontal scanout position. */
 	*vpos = position & 0x1fff;
diff --git a/kernel/drivers/gpu/drm/rockchip/Kconfig b/kernel/drivers/gpu/drm/rockchip/Kconfig
index 8d5dda5..3154597 100644
--- a/kernel/drivers/gpu/drm/rockchip/Kconfig
+++ b/kernel/drivers/gpu/drm/rockchip/Kconfig
@@ -20,13 +20,6 @@
 
 if DRM_ROCKCHIP
 
-config ROCKCHIP_DRM_CUBIC_LUT
-	bool "Support 3D cubic LUT"
-	depends on NO_GKI
-	help
-	  This add properties to support provision of a 3D cubic
-	  look up table, allowing for color specific adjustments.
-
 config ROCKCHIP_DRM_DEBUG
 	bool "Rockchip DRM debug"
 	depends on DEBUG_FS
diff --git a/kernel/drivers/gpu/drm/rockchip/Makefile b/kernel/drivers/gpu/drm/rockchip/Makefile
index e443270..7a42624 100644
--- a/kernel/drivers/gpu/drm/rockchip/Makefile
+++ b/kernel/drivers/gpu/drm/rockchip/Makefile
@@ -16,7 +16,8 @@
 						rockchip_drm_self_test.o
 
 rockchipdrm-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o
-rockchipdrm-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o cdn-dp-reg.o
+rockchipdrm-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o cdn-dp-reg.o \
+					 cdn-dp-link-training.o
 rockchipdrm-$(CONFIG_ROCKCHIP_DRM_TVE) += rockchip_drm_tve.o
 rockchipdrm-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o
 rockchipdrm-$(CONFIG_ROCKCHIP_DW_MIPI_DSI) += dw-mipi-dsi-rockchip.o \
diff --git a/kernel/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c b/kernel/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
index 236b0dd..e15c50e 100644
--- a/kernel/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
+++ b/kernel/drivers/gpu/drm/rockchip/analogix_dp-rockchip.c
@@ -437,6 +437,16 @@
 	} else {
 		s->output_if |= dp->id ? VOP_OUTPUT_IF_eDP1 : VOP_OUTPUT_IF_eDP0;
 	}
+
+	if (dp->plat_data.dual_connector_split) {
+		s->output_flags |= ROCKCHIP_OUTPUT_DUAL_CONNECTOR_SPLIT_MODE;
+
+		if (dp->plat_data.left_display)
+			s->output_if_left_panel |= dp->id ?
+						   VOP_OUTPUT_IF_eDP1 :
+						   VOP_OUTPUT_IF_eDP0;
+	}
+
 	s->output_bpc = di->bpc;
 	s->bus_flags = di->bus_flags;
 	s->tv_state = &conn_state->tv;
@@ -678,6 +688,12 @@
 	device_property_read_u32(dev, "min-refresh-rate", &dp->min_refresh_rate);
 	device_property_read_u32(dev, "max-refresh-rate", &dp->max_refresh_rate);
 
+	if (dp->data->split_mode && device_property_read_bool(dev, "dual-connector-split")) {
+		dp->plat_data.dual_connector_split = true;
+		if (device_property_read_bool(dev, "left-display"))
+			dp->plat_data.left_display = true;
+	}
+
 	ret = component_add(dev, &rockchip_dp_component_ops);
 	if (ret)
 		goto err_dp_remove;
diff --git a/kernel/drivers/gpu/drm/rockchip/cdn-dp-core.c b/kernel/drivers/gpu/drm/rockchip/cdn-dp-core.c
index 3333003..bae50c5 100644
--- a/kernel/drivers/gpu/drm/rockchip/cdn-dp-core.c
+++ b/kernel/drivers/gpu/drm/rockchip/cdn-dp-core.c
@@ -151,8 +151,8 @@
 	u8 value;
 
 	*sink_count = 0;
-	ret = cdn_dp_dpcd_read(dp, DP_SINK_COUNT, &value, 1);
-	if (ret)
+	ret = drm_dp_dpcd_read(&dp->aux, DP_SINK_COUNT, &value, 1);
+	if (ret < 0)
 		return ret;
 
 	*sink_count = DP_GET_SINK_COUNT(value);
@@ -351,9 +351,9 @@
 	if (!cdn_dp_check_sink_connection(dp))
 		return -ENODEV;
 
-	ret = cdn_dp_dpcd_read(dp, DP_DPCD_REV, dp->dpcd,
-			       DP_RECEIVER_CAP_SIZE);
-	if (ret) {
+	ret = drm_dp_dpcd_read(&dp->aux, DP_DPCD_REV, dp->dpcd,
+			       sizeof(dp->dpcd));
+	if (ret < 0) {
 		DRM_DEV_ERROR(dp->dev, "Failed to get caps %d\n", ret);
 		return ret;
 	}
@@ -551,8 +551,8 @@
 	if (!port || !dp->max_rate || !dp->max_lanes)
 		return false;
 
-	if (cdn_dp_dpcd_read(dp, DP_LANE0_1_STATUS, link_status,
-			     DP_LINK_STATUS_SIZE)) {
+	if (drm_dp_dpcd_read_link_status(&dp->aux, link_status) !=
+	    DP_LINK_STATUS_SIZE) {
 		DRM_ERROR("Failed to get link status\n");
 		return false;
 	}
@@ -598,11 +598,13 @@
 			goto out;
 		}
 	}
-
-	ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_IDLE);
-	if (ret) {
-		DRM_DEV_ERROR(dp->dev, "Failed to idle video %d\n", ret);
-		goto out;
+	if (dp->use_fw_training) {
+		ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_IDLE);
+		if (ret) {
+			DRM_DEV_ERROR(dp->dev,
+				      "Failed to idle video %d\n", ret);
+			goto out;
+		}
 	}
 
 	ret = cdn_dp_config_video(dp);
@@ -611,11 +613,15 @@
 		goto out;
 	}
 
-	ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_VALID);
-	if (ret) {
-		DRM_DEV_ERROR(dp->dev, "Failed to valid video %d\n", ret);
-		goto out;
+	if (dp->use_fw_training) {
+		ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_VALID);
+		if (ret) {
+			DRM_DEV_ERROR(dp->dev,
+				"Failed to valid video %d\n", ret);
+			goto out;
+		}
 	}
+
 out:
 	mutex_unlock(&dp->lock);
 }
@@ -962,6 +968,40 @@
 		drm_kms_helper_hotplug_event(dp->drm_dev);
 }
 
+static ssize_t cdn_dp_aux_transfer(struct drm_dp_aux *aux,
+				   struct drm_dp_aux_msg *msg)
+{
+	struct cdn_dp_device *dp = container_of(aux, struct cdn_dp_device, aux);
+	int ret;
+	u8 status;
+
+	switch (msg->request & ~DP_AUX_I2C_MOT) {
+	case DP_AUX_NATIVE_WRITE:
+	case DP_AUX_I2C_WRITE:
+	case DP_AUX_I2C_WRITE_STATUS_UPDATE:
+		ret = cdn_dp_dpcd_write(dp, msg->address, msg->buffer,
+					msg->size);
+		break;
+	case DP_AUX_NATIVE_READ:
+	case DP_AUX_I2C_READ:
+		ret = cdn_dp_dpcd_read(dp, msg->address, msg->buffer,
+				       msg->size);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	status = cdn_dp_get_aux_status(dp);
+	if (status == AUX_STATUS_ACK)
+		msg->reply = DP_AUX_NATIVE_REPLY_ACK;
+	else if (status == AUX_STATUS_NACK)
+		msg->reply = DP_AUX_NATIVE_REPLY_NACK;
+	else if (status == AUX_STATUS_DEFER)
+		msg->reply = DP_AUX_NATIVE_REPLY_DEFER;
+
+	return ret;
+}
+
 static int cdn_dp_bind(struct device *dev, struct device *master, void *data)
 {
 	struct cdn_dp_device *dp = dev_get_drvdata(dev);
@@ -979,6 +1019,13 @@
 	dp->active = false;
 	dp->active_port = -1;
 	dp->fw_loaded = false;
+	dp->aux.name = "DP-AUX";
+	dp->aux.transfer = cdn_dp_aux_transfer;
+	dp->aux.dev = dev;
+
+	ret = drm_dp_aux_register(&dp->aux);
+	if (ret)
+		return ret;
 
 	INIT_DELAYED_WORK(&dp->event_work, cdn_dp_pd_event_work);
 
diff --git a/kernel/drivers/gpu/drm/rockchip/cdn-dp-core.h b/kernel/drivers/gpu/drm/rockchip/cdn-dp-core.h
index 519900c..60a8c09 100644
--- a/kernel/drivers/gpu/drm/rockchip/cdn-dp-core.h
+++ b/kernel/drivers/gpu/drm/rockchip/cdn-dp-core.h
@@ -68,12 +68,14 @@
 	struct platform_device *audio_pdev;
 	struct delayed_work event_work;
 	struct edid *edid;
+	struct drm_dp_aux aux;
 	struct rockchip_drm_sub_dev sub_dev;
 
 	struct mutex lock;
 	bool connected;
 	bool active;
 	bool suspended;
+	bool use_fw_training;
 
 	const struct firmware *fw;	/* cdn dp firmware */
 	unsigned int fw_version;	/* cdn fw version */
@@ -97,6 +99,7 @@
 	unsigned int max_rate;
 	u8 lanes;
 	int active_port;
+	u8 train_set[4];
 
 	u8 dpcd[DP_RECEIVER_CAP_SIZE];
 	bool sink_has_audio;
diff --git a/kernel/drivers/gpu/drm/rockchip/cdn-dp-link-training.c b/kernel/drivers/gpu/drm/rockchip/cdn-dp-link-training.c
index 08962e9..ec2f001 100644
--- a/kernel/drivers/gpu/drm/rockchip/cdn-dp-link-training.c
+++ b/kernel/drivers/gpu/drm/rockchip/cdn-dp-link-training.c
@@ -14,14 +14,24 @@
 static void cdn_dp_set_signal_levels(struct cdn_dp_device *dp)
 {
 	struct cdn_dp_port *port = dp->port[dp->active_port];
-	int rate = drm_dp_bw_code_to_link_rate(dp->link.rate);
+	union phy_configure_opts phy_cfg = {0};
 	u8 swing = (dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK) >>
 		   DP_TRAIN_VOLTAGE_SWING_SHIFT;
 	u8 pre_emphasis = (dp->train_set[0] & DP_TRAIN_PRE_EMPHASIS_MASK)
 			  >> DP_TRAIN_PRE_EMPHASIS_SHIFT;
+	unsigned int lane;
 
-	tcphy_dp_set_phy_config(port->phy, rate, dp->link.num_lanes,
-				swing, pre_emphasis);
+	for (lane = 0; lane < dp->max_lanes; lane++) {
+		phy_cfg.dp.voltage[lane] = swing;
+		phy_cfg.dp.pre[lane] = pre_emphasis;
+	}
+
+	phy_cfg.dp.lanes = dp->max_lanes;
+	phy_cfg.dp.link_rate = drm_dp_bw_code_to_link_rate(dp->max_rate) / 100;
+	phy_cfg.dp.set_lanes = false;
+	phy_cfg.dp.set_rate = false;
+	phy_cfg.dp.set_voltages = true;
+	phy_configure(port->phy, &phy_cfg);
 }
 
 static int cdn_dp_set_pattern(struct cdn_dp_device *dp, uint8_t dp_train_pat)
@@ -30,7 +40,7 @@
 	int ret;
 	uint8_t pattern = dp_train_pat & DP_TRAINING_PATTERN_MASK;
 
-	global_config = NUM_LANES(dp->link.num_lanes - 1) | SST_MODE |
+	global_config = NUM_LANES(dp->max_lanes - 1) | SST_MODE |
 			GLOBAL_EN | RG_EN | ENC_RST_DIS | WR_VHSYNC_FALL;
 
 	phy_config = DP_TX_PHY_ENCODER_BYPASS(0) |
@@ -63,7 +73,7 @@
 		return ret;
 	}
 
-	ret = cdn_dp_reg_write(dp, DPTX_LANE_EN, BIT(dp->link.num_lanes) - 1);
+	ret = cdn_dp_reg_write(dp, DPTX_LANE_EN, BIT(dp->max_lanes) - 1);
 	if (ret) {
 		DRM_ERROR("fail to set DPTX_LANE_EN, error: %d\n", ret);
 		return ret;
@@ -106,7 +116,7 @@
 	uint8_t v = 0, p = 0;
 	uint8_t preemph_max;
 
-	for (i = 0; i < dp->link.num_lanes; i++) {
+	for (i = 0; i < dp->max_lanes; i++) {
 		v = max(v, drm_dp_get_adjust_request_voltage(link_status, i));
 		p = max(p, drm_dp_get_adjust_request_pre_emphasis(link_status,
 								  i));
@@ -119,7 +129,7 @@
 	if (p >= preemph_max)
 		p = preemph_max | DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
 
-	for (i = 0; i < dp->link.num_lanes; i++)
+	for (i = 0; i < dp->max_lanes; i++)
 		dp->train_set[i] = v | p;
 }
 
@@ -149,7 +159,7 @@
 {
 	int lane;
 
-	for (lane = 0; lane < dp->link.num_lanes; lane++)
+	for (lane = 0; lane < dp->max_lanes; lane++)
 		if ((dp->train_set[lane] & DP_TRAIN_MAX_SWING_REACHED) == 0)
 			return false;
 
@@ -163,8 +173,8 @@
 	cdn_dp_set_signal_levels(dp);
 
 	ret = drm_dp_dpcd_write(&dp->aux, DP_TRAINING_LANE0_SET,
-				dp->train_set, dp->link.num_lanes);
-	if (ret != dp->link.num_lanes)
+				dp->train_set, dp->max_lanes);
+	if (ret != dp->max_lanes)
 		return -EINVAL;
 
 	return 0;
@@ -183,8 +193,8 @@
 		len = 1;
 	} else {
 		/* DP_TRAINING_LANEx_SET follow DP_TRAINING_PATTERN_SET */
-		memcpy(buf + 1, dp->train_set, dp->link.num_lanes);
-		len = dp->link.num_lanes + 1;
+		memcpy(buf + 1, dp->train_set, dp->max_lanes);
+		len = dp->max_lanes + 1;
 	}
 
 	ret = drm_dp_dpcd_write(&dp->aux, DP_TRAINING_PATTERN_SET,
@@ -237,7 +247,7 @@
 			return -EINVAL;
 		}
 
-		if (drm_dp_clock_recovery_ok(link_status, dp->link.num_lanes)) {
+		if (drm_dp_clock_recovery_ok(link_status, dp->max_lanes)) {
 			DRM_DEBUG_KMS("clock recovery OK\n");
 			return 0;
 		}
@@ -301,12 +311,12 @@
 
 		/* Make sure clock is still ok */
 		if (!drm_dp_clock_recovery_ok(link_status,
-					      dp->link.num_lanes)) {
+					      dp->max_lanes)) {
 			DRM_DEBUG_KMS("Clock recovery check failed\n");
 			break;
 		}
 
-		if (drm_dp_channel_eq_ok(link_status,  dp->link.num_lanes)) {
+		if (drm_dp_channel_eq_ok(link_status,  dp->max_lanes)) {
 			DRM_DEBUG_KMS("Channel EQ done\n");
 			return 0;
 		}
@@ -338,17 +348,17 @@
 
 static int cdn_dp_get_lower_link_rate(struct cdn_dp_device *dp)
 {
-	switch (dp->link.rate) {
+	switch (dp->max_rate) {
 	case DP_LINK_BW_1_62:
 		return -EINVAL;
 	case DP_LINK_BW_2_7:
-		dp->link.rate = DP_LINK_BW_1_62;
+		dp->max_rate = DP_LINK_BW_1_62;
 		break;
 	case DP_LINK_BW_5_4:
-		dp->link.rate = DP_LINK_BW_2_7;
+		dp->max_rate = DP_LINK_BW_2_7;
 		break;
 	default:
-		dp->link.rate = DP_LINK_BW_5_4;
+		dp->max_rate = DP_LINK_BW_5_4;
 		break;
 	}
 
@@ -372,12 +382,12 @@
 
 	source_max = dp->lanes;
 	sink_max = drm_dp_max_lane_count(dp->dpcd);
-	dp->link.num_lanes = min(source_max, sink_max);
+	dp->max_lanes = min(source_max, sink_max);
 
 	source_max = drm_dp_bw_code_to_link_rate(CDN_DP_MAX_LINK_RATE);
 	sink_max = drm_dp_max_link_rate(dp->dpcd);
 	rate = min(source_max, sink_max);
-	dp->link.rate = drm_dp_link_rate_to_bw_code(rate);
+	dp->max_rate = drm_dp_link_rate_to_bw_code(rate);
 
 	ssc_on = !!(dp->dpcd[DP_MAX_DOWNSPREAD] & DP_MAX_DOWNSPREAD_0_5);
 	link_config[0] = ssc_on ? DP_SPREAD_AMP_0_5 : 0;
@@ -387,23 +397,21 @@
 	drm_dp_dpcd_write(&dp->aux, DP_DOWNSPREAD_CTRL, link_config, 2);
 
 	while (true) {
-		ret = tcphy_dp_set_link_rate(port->phy,
-				drm_dp_bw_code_to_link_rate(dp->link.rate),
-				ssc_on);
-		if (ret) {
-			DRM_ERROR("failed to set link rate: %d\n", ret);
-			return ret;
-		}
+		union phy_configure_opts phy_cfg = {0};
 
-		ret = tcphy_dp_set_lane_count(port->phy, dp->link.num_lanes);
-		if (ret) {
-			DRM_ERROR("failed to set lane count: %d\n", ret);
+		phy_cfg.dp.lanes = dp->max_lanes;
+		phy_cfg.dp.link_rate = drm_dp_bw_code_to_link_rate(dp->max_rate) / 100;
+		phy_cfg.dp.ssc = ssc_on;
+		phy_cfg.dp.set_lanes = true;
+		phy_cfg.dp.set_rate = true;
+		phy_cfg.dp.set_voltages = false;
+		ret = phy_configure(port->phy, &phy_cfg);
+		if (ret)
 			return ret;
-		}
 
 		/* Write the link configuration data */
-		link_config[0] = dp->link.rate;
-		link_config[1] = dp->link.num_lanes;
+		link_config[0] = dp->max_rate;
+		link_config[1] = dp->max_lanes;
 		if (drm_dp_enhanced_frame_cap(dp->dpcd))
 			link_config[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
 		drm_dp_dpcd_write(&dp->aux, DP_LINK_BW_SET, link_config, 2);
diff --git a/kernel/drivers/gpu/drm/rockchip/cdn-dp-reg.c b/kernel/drivers/gpu/drm/rockchip/cdn-dp-reg.c
index 33fb4d0..2a54486 100644
--- a/kernel/drivers/gpu/drm/rockchip/cdn-dp-reg.c
+++ b/kernel/drivers/gpu/drm/rockchip/cdn-dp-reg.c
@@ -181,7 +181,7 @@
 	return 0;
 }
 
-static int cdn_dp_reg_write(struct cdn_dp_device *dp, u16 addr, u32 val)
+int cdn_dp_reg_write(struct cdn_dp_device *dp, u16 addr, u32 val)
 {
 	u8 msg[6];
 
@@ -213,7 +213,12 @@
 				   sizeof(field), field);
 }
 
-int cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr, u8 *data, u16 len)
+/*
+ * Returns the number of bytes transferred on success, or a negative
+ * error code on failure. -ETIMEDOUT is returned if mailbox message was
+ * not send successfully;
+ */
+ssize_t cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr, u8 *data, u16 len)
 {
 	u8 msg[5], reg[5];
 	int ret;
@@ -239,24 +244,41 @@
 		goto err_dpcd_read;
 
 	ret = cdn_dp_mailbox_read_receive(dp, data, len);
+	if (!ret)
+		return len;
 
 err_dpcd_read:
+	DRM_DEV_ERROR(dp->dev, "dpcd read failed: %d\n", ret);
 	return ret;
 }
 
-int cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr, u8 value)
+#define CDN_AUX_HEADER_SIZE	5
+#define CDN_AUX_MSG_SIZE	20
+/*
+ * Returns the number of bytes transferred on success, or a negative error
+ * code on failure. -ETIMEDOUT is returned if mailbox message was not send
+ * success; -EINVAL is returned if get the wrong data size after message
+ * is sent
+ */
+ssize_t cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr, u8 *data, u16 len)
 {
-	u8 msg[6], reg[5];
+	u8 msg[CDN_AUX_MSG_SIZE + CDN_AUX_HEADER_SIZE];
+	u8 reg[CDN_AUX_HEADER_SIZE];
 	int ret;
 
-	msg[0] = 0;
-	msg[1] = 1;
+	if (WARN_ON(len > CDN_AUX_MSG_SIZE) || WARN_ON(len <= 0))
+		return -EINVAL;
+
+	msg[0] = (len >> 8) & 0xff;
+	msg[1] = len & 0xff;
 	msg[2] = (addr >> 16) & 0xff;
 	msg[3] = (addr >> 8) & 0xff;
 	msg[4] = addr & 0xff;
-	msg[5] = value;
+
+	memcpy(msg + CDN_AUX_HEADER_SIZE, data, len);
+
 	ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX, DPTX_WRITE_DPCD,
-				  sizeof(msg), msg);
+				  CDN_AUX_HEADER_SIZE + len, msg);
 	if (ret)
 		goto err_dpcd_write;
 
@@ -269,12 +291,43 @@
 	if (ret)
 		goto err_dpcd_write;
 
-	if (addr != (reg[2] << 16 | reg[3] << 8 | reg[4]))
+	if ((len != (reg[0] << 8 | reg[1])) ||
+	    (addr != (reg[2] << 16 | reg[3] << 8 | reg[4]))) {
 		ret = -EINVAL;
+	} else {
+		return len;
+	}
 
 err_dpcd_write:
 	if (ret)
 		DRM_DEV_ERROR(dp->dev, "dpcd write failed: %d\n", ret);
+	return ret;
+}
+
+int cdn_dp_get_aux_status(struct cdn_dp_device *dp)
+{
+	u8 status;
+	int ret;
+
+	ret = cdn_dp_mailbox_send(dp, MB_MODULE_ID_DP_TX,
+				  DPTX_GET_LAST_AUX_STAUS, 0, NULL);
+	if (ret)
+		goto err_get_hpd;
+
+	ret = cdn_dp_mailbox_validate_receive(dp, MB_MODULE_ID_DP_TX,
+					      DPTX_GET_LAST_AUX_STAUS,
+					      sizeof(status));
+	if (ret)
+		goto err_get_hpd;
+
+	ret = cdn_dp_mailbox_read_receive(dp, &status, sizeof(status));
+	if (ret)
+		goto err_get_hpd;
+
+	return status;
+
+err_get_hpd:
+	DRM_DEV_ERROR(dp->dev, "get aux status failed: %d\n", ret);
 	return ret;
 }
 
@@ -535,7 +588,7 @@
 	if (ret)
 		goto err_get_training_status;
 
-	dp->max_rate = drm_dp_bw_code_to_link_rate(status[0]);
+	dp->max_rate = status[0];
 	dp->max_lanes = status[1];
 
 err_get_training_status:
@@ -548,6 +601,31 @@
 {
 	int ret;
 
+	/*
+	 * DP firmware uses fixed phy config values to do training, but some
+	 * boards need to adjust these values to fit for their unique hardware
+	 * design. So if the phy is using custom config values, do software
+	 * link training instead of relying on firmware, if software training
+	 * fail, keep firmware training as a fallback if sw training fails.
+	 */
+	ret = cdn_dp_software_train_link(dp);
+	if (ret) {
+		DRM_DEV_ERROR(dp->dev,
+			"Failed to do software training %d\n", ret);
+		goto do_fw_training;
+	}
+	ret = cdn_dp_reg_write(dp, SOURCE_HDTX_CAR, 0xf);
+	if (ret) {
+		DRM_DEV_ERROR(dp->dev,
+			"Failed to write SOURCE_HDTX_CAR register %d\n", ret);
+		goto do_fw_training;
+	}
+	dp->use_fw_training = false;
+	return 0;
+
+do_fw_training:
+	dp->use_fw_training = true;
+	DRM_DEV_DEBUG_KMS(dp->dev, "use fw training\n");
 	ret = cdn_dp_training_start(dp);
 	if (ret) {
 		DRM_DEV_ERROR(dp->dev, "Failed to start training %d\n", ret);
@@ -639,7 +717,7 @@
 	bit_per_pix = (video->color_fmt == YCBCR_4_2_2) ?
 		      (video->color_depth * 2) : (video->color_depth * 3);
 
-	link_rate = dp->max_rate / 1000;
+	link_rate = drm_dp_bw_code_to_link_rate(dp->max_rate) / 1000;
 
 	ret = cdn_dp_reg_write(dp, BND_HSYNC2VSYNC, VIF_BYPASS_INTERLACE);
 	if (ret)
diff --git a/kernel/drivers/gpu/drm/rockchip/cdn-dp-reg.h b/kernel/drivers/gpu/drm/rockchip/cdn-dp-reg.h
index 441248b..656ec69 100644
--- a/kernel/drivers/gpu/drm/rockchip/cdn-dp-reg.h
+++ b/kernel/drivers/gpu/drm/rockchip/cdn-dp-reg.h
@@ -8,6 +8,7 @@
 #define _CDN_DP_REG_H
 
 #include <linux/bitops.h>
+#include <linux/phy/phy.h>
 
 #define ADDR_IMEM		0x10000
 #define ADDR_DMEM		0x20000
@@ -129,7 +130,7 @@
 #define HPD_EVENT_MASK			0x211c
 #define HPD_EVENT_DET			0x2120
 
-/* dpyx framer addr */
+/* dptx framer addr */
 #define DP_FRAMER_GLOBAL_CONFIG		0x2200
 #define DP_SW_RESET			0x2204
 #define DP_FRAMER_TU			0x2208
@@ -320,6 +321,13 @@
 #define GENERAL_BUS_SETTINGS            0x03
 #define GENERAL_TEST_ACCESS             0x04
 
+/* AUX status*/
+#define AUX_STATUS_ACK			0
+#define AUX_STATUS_NACK			1
+#define AUX_STATUS_DEFER			2
+#define AUX_STATUS_SINK_ERROR		3
+#define AUX_STATUS_BUS_ERROR		4
+
 #define DPTX_SET_POWER_MNG			0x00
 #define DPTX_SET_HOST_CAPABILITIES		0x01
 #define DPTX_GET_EDID				0x02
@@ -416,6 +424,40 @@
 /* Reference cycles when using lane clock as reference */
 #define LANE_REF_CYC				0x8000
 
+/* register CM_VID_CTRL */
+#define LANE_VID_REF_CYC(x)                    (((x) & (BIT(24) - 1)) << 0)
+#define NMVID_MEAS_TOLERANCE(x)                        (((x) & 0xf) << 24)
+
+/* register DP_TX_PHY_CONFIG_REG */
+#define DP_TX_PHY_TRAINING_ENABLE(x)           ((x) & 1)
+#define DP_TX_PHY_TRAINING_TYPE_PRBS7          (0 << 1)
+#define DP_TX_PHY_TRAINING_TYPE_TPS1           (1 << 1)
+#define DP_TX_PHY_TRAINING_TYPE_TPS2           (2 << 1)
+#define DP_TX_PHY_TRAINING_TYPE_TPS3           (3 << 1)
+#define DP_TX_PHY_TRAINING_TYPE_TPS4           (4 << 1)
+#define DP_TX_PHY_TRAINING_TYPE_PLTPAT         (5 << 1)
+#define DP_TX_PHY_TRAINING_TYPE_D10_2          (6 << 1)
+#define DP_TX_PHY_TRAINING_TYPE_HBR2CPAT       (8 << 1)
+#define DP_TX_PHY_TRAINING_PATTERN(x)          ((x) << 1)
+#define DP_TX_PHY_SCRAMBLER_BYPASS(x)          (((x) & 1) << 5)
+#define DP_TX_PHY_ENCODER_BYPASS(x)            (((x) & 1) << 6)
+#define DP_TX_PHY_SKEW_BYPASS(x)               (((x) & 1) << 7)
+#define DP_TX_PHY_DISPARITY_RST(x)             (((x) & 1) << 8)
+#define DP_TX_PHY_LANE0_SKEW(x)                (((x) & 7) << 9)
+#define DP_TX_PHY_LANE1_SKEW(x)                (((x) & 7) << 12)
+#define DP_TX_PHY_LANE2_SKEW(x)                (((x) & 7) << 15)
+#define DP_TX_PHY_LANE3_SKEW(x)                (((x) & 7) << 18)
+#define DP_TX_PHY_10BIT_ENABLE(x)              (((x) & 1) << 21)
+
+/* register DP_FRAMER_GLOBAL_CONFIG */
+#define NUM_LANES(x)           ((x) & 3)
+#define SST_MODE               (0 << 2)
+#define RG_EN                  (0 << 4)
+#define GLOBAL_EN              BIT(3)
+#define NO_VIDEO               BIT(5)
+#define ENC_RST_DIS            BIT(6)
+#define WR_VHSYNC_FALL         BIT(7)
+
 enum voltage_swing_level {
 	VOLTAGE_LEVEL_0,
 	VOLTAGE_LEVEL_1,
@@ -461,8 +503,12 @@
 int cdn_dp_event_config(struct cdn_dp_device *dp);
 u32 cdn_dp_get_event(struct cdn_dp_device *dp);
 int cdn_dp_get_hpd_status(struct cdn_dp_device *dp);
-int cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr, u8 value);
-int cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr, u8 *data, u16 len);
+int cdn_dp_reg_write(struct cdn_dp_device *dp, u16 addr, u32 val);
+ssize_t cdn_dp_dpcd_write(struct cdn_dp_device *dp, u32 addr,
+			  u8 *data, u16 len);
+ssize_t cdn_dp_dpcd_read(struct cdn_dp_device *dp, u32 addr,
+			 u8 *data, u16 len);
+int cdn_dp_get_aux_status(struct cdn_dp_device *dp);
 int cdn_dp_get_edid_block(void *dp, u8 *edid,
 			  unsigned int block, size_t length);
 int cdn_dp_train_link(struct cdn_dp_device *dp);
@@ -471,4 +517,5 @@
 int cdn_dp_audio_stop(struct cdn_dp_device *dp, struct audio_info *audio);
 int cdn_dp_audio_mute(struct cdn_dp_device *dp, bool enable);
 int cdn_dp_audio_config(struct cdn_dp_device *dp, struct audio_info *audio);
+int cdn_dp_software_train_link(struct cdn_dp_device *dp);
 #endif /* _CDN_DP_REG_H */
diff --git a/kernel/drivers/gpu/drm/rockchip/dw-dp.c b/kernel/drivers/gpu/drm/rockchip/dw-dp.c
index 1f39998..31136d4 100644
--- a/kernel/drivers/gpu/drm/rockchip/dw-dp.c
+++ b/kernel/drivers/gpu/drm/rockchip/dw-dp.c
@@ -239,8 +239,14 @@
 #define DPTX_HDCP22GPIOCHNGSTS			0x362c
 #define DPTX_HDCPREG_DPK_CRC			0x3630
 
+#define HDCP_KEY_SIZE				308
+#define HDCP_KEY_SEED_SIZE			2
+
 #define HDCP_DATA_SIZE				330
 #define DP_HDCP1X_ID				6
+
+#define HDCP_SIG_MAGIC				0x4B534541	/* "AESK" */
+#define HDCP_FLG_AES				1
 
 #define DPTX_MAX_REGISTER			DPTX_HDCPREG_DPK_CRC
 
@@ -407,6 +413,14 @@
 	int color_format;
 };
 
+struct hdcp_key_data_t {
+	unsigned int signature;
+	unsigned int length;
+	unsigned int crc;
+	unsigned int flags;
+	unsigned char data[];
+};
+
 enum {
 	DPTX_VM_RGB_6BIT,
 	DPTX_VM_RGB_8BIT,
@@ -499,6 +513,8 @@
 	u8 hdcp_vendor_data[HDCP_DATA_SIZE + 1];
 	void __iomem *base;
 	struct arm_smccc_res res;
+	struct hdcp_key_data_t *key_data;
+	bool aes_encrypt;
 
 	regmap_read(dp->regmap, DPTX_HDCPREG_RMLSTS, &val);
 	if (FIELD_GET(IDPK_DATA_INDEX, val) == 40) {
@@ -507,10 +523,16 @@
 	}
 
 	size = rk_vendor_read(DP_HDCP1X_ID, hdcp_vendor_data, HDCP_DATA_SIZE);
-	if (size < HDCP_DATA_SIZE)  {
-		dev_info(dp->dev, "HDCP: read size %d\n", size);
+	if (size < (HDCP_KEY_SIZE + HDCP_KEY_SEED_SIZE))  {
+		dev_info(dp->dev, "HDCP key read error, size: %d\n", size);
 		return -EINVAL;
 	}
+
+	key_data = (struct hdcp_key_data_t *)hdcp_vendor_data;
+	if ((key_data->signature != HDCP_SIG_MAGIC) || !(key_data->flags & HDCP_FLG_AES))
+		aes_encrypt = false;
+	else
+		aes_encrypt = true;
 
 	base = sip_hdcp_request_share_memory(dp->id ? DP_TX1 : DP_TX0);
 	if (!base)
@@ -518,7 +540,7 @@
 
 	memcpy_toio(base, hdcp_vendor_data, size);
 
-	res = sip_hdcp_config(HDCP_FUNC_KEY_LOAD, dp->id ? DP_TX1 : DP_TX0, 0);
+	res = sip_hdcp_config(HDCP_FUNC_KEY_LOAD, dp->id ? DP_TX1 : DP_TX0, !aes_encrypt);
 	if (IS_SIP_ERROR(res.a0)) {
 		dev_err(dp->dev, "load hdcp key failed\n");
 		return -EBUSY;
diff --git a/kernel/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c b/kernel/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c
index badbdb1..abae085 100644
--- a/kernel/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c
+++ b/kernel/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c
@@ -1101,6 +1101,15 @@
 	return 0;
 }
 
+static void
+dw_mipi_dsi_rockchip_stream_standby(void *priv_data, bool standby)
+{
+	struct dw_mipi_dsi_rockchip *dsi = priv_data;
+	struct drm_encoder *encoder = &dsi->encoder;
+
+	rockchip_drm_crtc_standby(encoder->crtc, standby);
+}
+
 static int dw_mipi_dsi_rockchip_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
@@ -1209,6 +1218,10 @@
 	dsi->pdata.max_data_lanes = dsi->cdata->max_data_lanes;
 	dsi->pdata.phy_ops = &dw_mipi_dsi_rockchip_phy_ops;
 	dsi->pdata.priv_data = dsi;
+
+	if (dsi->cdata->soc_type == RK3568)
+		dsi->pdata.stream_standby = dw_mipi_dsi_rockchip_stream_standby;
+
 	platform_set_drvdata(pdev, dsi);
 
 	dsi->dmd = dw_mipi_dsi_probe(pdev, &dsi->pdata);
diff --git a/kernel/drivers/gpu/drm/rockchip/dw-mipi-dsi2-rockchip.c b/kernel/drivers/gpu/drm/rockchip/dw-mipi-dsi2-rockchip.c
index 75c9739..46ace54 100644
--- a/kernel/drivers/gpu/drm/rockchip/dw-mipi-dsi2-rockchip.c
+++ b/kernel/drivers/gpu/drm/rockchip/dw-mipi-dsi2-rockchip.c
@@ -275,8 +275,11 @@
 	struct rockchip_drm_sub_dev sub_dev;
 
 	struct gpio_desc *te_gpio;
-	bool user_split_mode;
-	struct drm_property *user_split_mode_prop;
+
+	/* split with other display interface */
+	bool dual_connector_split;
+	bool left_display;
+	u32 split_area;
 };
 
 static inline struct dw_mipi_dsi2 *host_to_dsi2(struct mipi_dsi_host *host)
@@ -450,7 +453,8 @@
 		dw_mipi_dsi2_post_disable(dsi2->slave);
 }
 
-static void dw_mipi_dsi2_encoder_disable(struct drm_encoder *encoder)
+static void dw_mipi_dsi2_encoder_atomic_disable(struct drm_encoder *encoder,
+						struct drm_atomic_state *state)
 {
 	struct dw_mipi_dsi2 *dsi2 = encoder_to_dsi2(encoder);
 	struct drm_crtc *crtc = encoder->crtc;
@@ -837,9 +841,53 @@
 		dw_mipi_dsi2_enable(dsi2->slave);
 }
 
-static void dw_mipi_dsi2_encoder_enable(struct drm_encoder *encoder)
+static int dw_mipi_dsi2_encoder_mode_set(struct dw_mipi_dsi2 *dsi2,
+					 struct drm_atomic_state *state)
+{
+	struct drm_encoder *encoder = &dsi2->encoder;
+	struct drm_connector *connector;
+	struct drm_connector_state *conn_state;
+	struct drm_crtc_state *crtc_state;
+	const struct drm_display_mode *adjusted_mode;
+	struct drm_display_mode *mode = &dsi2->mode;
+
+	connector = drm_atomic_get_new_connector_for_encoder(state, encoder);
+	if (!connector)
+		return -ENODEV;
+
+	conn_state = drm_atomic_get_new_connector_state(state, connector);
+	if (!conn_state)
+		return -ENODEV;
+
+	crtc_state = drm_atomic_get_new_crtc_state(state, conn_state->crtc);
+	if (!crtc_state) {
+		dev_err(dsi2->dev, "failed to get crtc state\n");
+		return -ENODEV;
+	}
+
+	adjusted_mode = &crtc_state->adjusted_mode;
+	drm_mode_copy(mode, adjusted_mode);
+
+	if (dsi2->dual_connector_split)
+		drm_mode_convert_to_origin_mode(mode);
+
+	if (dsi2->slave)
+		drm_mode_copy(&dsi2->slave->mode, mode);
+
+	return 0;
+}
+
+static void dw_mipi_dsi2_encoder_atomic_enable(struct drm_encoder *encoder,
+					       struct drm_atomic_state *state)
 {
 	struct dw_mipi_dsi2 *dsi2 = encoder_to_dsi2(encoder);
+	int ret;
+
+	ret = dw_mipi_dsi2_encoder_mode_set(dsi2, state);
+	if (ret) {
+		dev_err(dsi2->dev, "failed to set dsi2 mode\n");
+		return;
+	}
 
 	dw_mipi_dsi2_get_lane_rate(dsi2);
 
@@ -867,8 +915,8 @@
 
 static int
 dw_mipi_dsi2_encoder_atomic_check(struct drm_encoder *encoder,
-				 struct drm_crtc_state *crtc_state,
-				 struct drm_connector_state *conn_state)
+				  struct drm_crtc_state *crtc_state,
+				  struct drm_connector_state *conn_state)
 {
 
 	struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state);
@@ -917,6 +965,15 @@
 		s->output_if |= VOP_OUTPUT_IF_MIPI1;
 	}
 
+	if (dsi2->dual_connector_split) {
+		s->output_flags |= ROCKCHIP_OUTPUT_DUAL_CONNECTOR_SPLIT_MODE;
+
+		if (dsi2->left_display)
+			s->output_if_left_panel |= dsi2->id ?
+						   VOP_OUTPUT_IF_MIPI1 :
+						   VOP_OUTPUT_IF_MIPI0;
+	}
+
 	if (dsi2->dsc_enable) {
 		s->dsc_enable = 1;
 		s->dsc_sink_cap.version_major = dsi2->version_major;
@@ -931,18 +988,6 @@
 	}
 
 	return 0;
-}
-
-static void
-dw_mipi_dsi2_encoder_atomic_mode_set(struct drm_encoder *encoder,
-				    struct drm_crtc_state *crtc_state,
-				    struct drm_connector_state *connector_state)
-{
-	struct dw_mipi_dsi2 *dsi2 = encoder_to_dsi2(encoder);
-
-	drm_mode_copy(&dsi2->mode, &crtc_state->adjusted_mode);
-	if (dsi2->slave)
-		drm_mode_copy(&dsi2->slave->mode, &crtc_state->adjusted_mode);
 }
 
 static void dw_mipi_dsi2_loader_protect(struct dw_mipi_dsi2 *dsi2, bool on)
@@ -980,10 +1025,9 @@
 
 static const struct drm_encoder_helper_funcs
 dw_mipi_dsi2_encoder_helper_funcs = {
-	.enable = dw_mipi_dsi2_encoder_enable,
-	.disable = dw_mipi_dsi2_encoder_disable,
+	.atomic_enable = dw_mipi_dsi2_encoder_atomic_enable,
+	.atomic_disable = dw_mipi_dsi2_encoder_atomic_disable,
 	.atomic_check = dw_mipi_dsi2_encoder_atomic_check,
-	.atomic_mode_set = dw_mipi_dsi2_encoder_atomic_mode_set,
 };
 
 static int dw_mipi_dsi2_connector_get_modes(struct drm_connector *connector)
@@ -1065,6 +1109,32 @@
 	drm_connector_cleanup(connector);
 }
 
+static int
+dw_mipi_dsi2_atomic_connector_get_property(struct drm_connector *connector,
+					   const struct drm_connector_state *state,
+					   struct drm_property *property,
+					   uint64_t *val)
+{
+	struct rockchip_drm_private *private = connector->dev->dev_private;
+	struct dw_mipi_dsi2 *dsi2 = con_to_dsi2(connector);
+
+	if (property == private->split_area_prop) {
+		switch (dsi2->split_area) {
+		case 1:
+			*val = ROCKCHIP_DRM_SPLIT_LEFT_SIDE;
+			break;
+		case 2:
+			*val = ROCKCHIP_DRM_SPLIT_RIGHT_SIDE;
+			break;
+		default:
+			*val = ROCKCHIP_DRM_SPLIT_UNSET;
+			break;
+		}
+	}
+
+	return 0;
+}
+
 static const struct drm_connector_funcs dw_mipi_dsi2_atomic_connector_funcs = {
 	.fill_modes = drm_helper_probe_single_connector_modes,
 	.detect = dw_mipi_dsi2_connector_detect,
@@ -1072,6 +1142,7 @@
 	.reset = drm_atomic_helper_connector_reset,
 	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
 	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+	.atomic_get_property = dw_mipi_dsi2_atomic_connector_get_property,
 };
 
 static int dw_mipi_dsi2_dual_channel_probe(struct dw_mipi_dsi2 *dsi2)
@@ -1143,6 +1214,9 @@
 		dsi2->slave->dsc_enable = dsi2->dsc_enable;
 	}
 
+	if (!dsi2->dsc_enable)
+		return 0;
+
 	of_property_read_u32(np, "slice-width", &dsi2->slice_width);
 	of_property_read_u32(np, "slice-height", &dsi2->slice_height);
 	of_property_read_u8(np, "version-major", &dsi2->version_major);
@@ -1178,7 +1252,20 @@
 		len -= header->payload_length;
 	}
 
+	if (!pps) {
+		dev_err(dsi2->dev, "not found dsc pps definition\n");
+		return -EINVAL;
+	}
+
 	dsi2->pps = pps;
+
+	if (dsi2->slave) {
+		u16 pic_width = be16_to_cpu(pps->pic_width) / 2;
+
+		dsi2->pps->pic_width = cpu_to_be16(pic_width);
+		dev_info(dsi2->dev, "dsc pic_width change from %d to %d\n",
+			 pic_width * 2, pic_width);
+	}
 
 	return 0;
 }
@@ -1218,22 +1305,15 @@
 static int dw_mipi_dsi2_register_sub_dev(struct dw_mipi_dsi2 *dsi2,
 					 struct drm_connector *connector)
 {
+	struct rockchip_drm_private *private;
 	struct device *dev = dsi2->dev;
-	struct drm_property *prop;
-	int ret;
 
-	prop = drm_property_create_bool(dsi2->drm_dev, DRM_MODE_PROP_IMMUTABLE,
-					"USER_SPLIT_MODE");
-	if (!prop) {
-		ret = -EINVAL;
-		DRM_DEV_ERROR(dev, "create user split mode prop failed\n");
-		goto connector_cleanup;
-	}
+	private = connector->dev->dev_private;
 
-	dsi2->user_split_mode_prop = prop;
-	drm_object_attach_property(&connector->base,
-				   dsi2->user_split_mode_prop,
-				   dsi2->user_split_mode ? 1 : 0);
+	if (dsi2->split_area)
+		drm_object_attach_property(&connector->base,
+					   private->split_area_prop,
+					   dsi2->split_area);
 
 	dsi2->sub_dev.connector = connector;
 	dsi2->sub_dev.of_node = dev->of_node;
@@ -1241,11 +1321,6 @@
 	rockchip_drm_register_sub_dev(&dsi2->sub_dev);
 
 	return 0;
-
-connector_cleanup:
-	connector->funcs->destroy(connector);
-
-	return ret;
 }
 
 static int dw_mipi_dsi2_bind(struct device *dev, struct device *master,
@@ -1563,7 +1638,16 @@
 	dsi2->id = id;
 	dsi2->pdata = of_device_get_match_data(dev);
 	platform_set_drvdata(pdev, dsi2);
-	dsi2->user_split_mode = device_property_read_bool(dev, "user-split-mode");
+
+	if (device_property_read_bool(dev, "dual-connector-split")) {
+		dsi2->dual_connector_split = true;
+
+		if (device_property_read_bool(dev, "left-display"))
+			dsi2->left_display = true;
+	}
+
+	if (device_property_read_u32(dev, "split-area", &dsi2->split_area))
+		dsi2->split_area = 0;
 
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	regs = devm_ioremap_resource(dev, res);
diff --git a/kernel/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/kernel/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
index 463dcb5..450b162 100644
--- a/kernel/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
+++ b/kernel/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c
@@ -17,6 +17,7 @@
 #include <drm/drm_crtc_helper.h>
 #include <drm/drm_dsc.h>
 #include <drm/drm_edid.h>
+#include <drm/drm_hdcp.h>
 #include <drm/bridge/dw_hdmi.h>
 #include <drm/drm_edid.h>
 #include <drm/drm_of.h>
@@ -105,6 +106,8 @@
 #define RK3588_HDMI1_LEVEL_INT		BIT(24)
 #define RK3588_HDMI1_INTR_CHANGE_CNT	(0x7 << 21)
 
+#define RK3588_GRF_VO1_CON1		0x0004
+#define HDCP1_P1_GPIO_IN		BIT(9)
 #define RK3588_GRF_VO1_CON3		0x000c
 #define RK3588_COLOR_FORMAT_MASK	0xf
 #define RK3588_RGB			0
@@ -129,6 +132,8 @@
 #define RK3588_HDMI0_GRANT_SW		BIT(11)
 #define RK3588_HDMI1_GRANT_SEL		BIT(12)
 #define RK3588_HDMI1_GRANT_SW		BIT(13)
+#define RK3588_GRF_VO1_CON4		0x0010
+#define RK3588_HDMI_HDCP14_MEM_EN	BIT(15)
 #define RK3588_GRF_VO1_CON6		0x0018
 #define RK3588_GRF_VO1_CON7		0x001c
 
@@ -197,7 +202,6 @@
 	u8 id;
 	bool hpd_stat;
 	bool is_hdmi_qp;
-	bool user_split_mode;
 
 	unsigned long bus_format;
 	unsigned long output_bus_format;
@@ -215,9 +219,9 @@
 	struct drm_property *next_hdr_sink_data_property;
 	struct drm_property *output_hdmi_dvi;
 	struct drm_property *output_type_capacity;
-	struct drm_property *user_split_mode_prop;
 	struct drm_property *allm_capacity;
 	struct drm_property *allm_enable;
+	struct drm_property *hdcp_state_property;
 
 	struct drm_property_blob *hdr_panel_blob_ptr;
 	struct drm_property_blob *next_hdr_data_ptr;
@@ -234,6 +238,7 @@
 	u8 max_lanes;
 	u8 add_func;
 	u8 edid_colorimetry;
+	u8 hdcp_status;
 	struct rockchip_drm_dsc_cap dsc_cap;
 	struct next_hdr_sink_data next_hdr_data;
 	struct dw_hdmi_link_config link_cfg;
@@ -1592,14 +1597,6 @@
 	struct drm_crtc *crtc;
 	struct rockchip_hdmi *hdmi;
 
-	/*
-	 * Pixel clocks we support are always < 2GHz and so fit in an
-	 * int.  We should make sure source rate does too so we don't get
-	 * overflow when we multiply by 1000.
-	 */
-	if (mode->clock > INT_MAX / 1000)
-		return MODE_BAD;
-
 	if (!encoder) {
 		const struct drm_connector_helper_funcs *funcs;
 
@@ -1615,6 +1612,21 @@
 		return MODE_BAD;
 
 	hdmi = to_rockchip_hdmi(encoder);
+
+	if (hdmi->is_hdmi_qp) {
+		if (!hdmi->enable_gpio && mode->clock > 600000)
+			return MODE_BAD;
+
+		return MODE_OK;
+	}
+
+	/*
+	 * Pixel clocks we support are always < 2GHz and so fit in an
+	 * int.  We should make sure source rate does too so we don't get
+	 * overflow when we multiply by 1000.
+	 */
+	if (mode->clock > INT_MAX / 1000)
+		return MODE_BAD;
 
 	/*
 	 * If sink max TMDS clock < 340MHz, we should check the mode pixel
@@ -1668,10 +1680,14 @@
 {
 	struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder);
 	struct drm_crtc *crtc = encoder->crtc;
-	struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc->state);
+	struct rockchip_crtc_state *s;
 
-	if (WARN_ON(!crtc || !crtc->state))
+	if (!crtc || !crtc->state) {
+		dev_info(hdmi->dev, "%s old crtc state is null\n", __func__);
 		return;
+	}
+
+	s = to_rockchip_crtc_state(crtc->state);
 
 	if (crtc->state->active_changed) {
 		if (hdmi->plat_data->split_mode) {
@@ -1699,8 +1715,10 @@
 	int mux;
 	int ret;
 
-	if (WARN_ON(!crtc || !crtc->state))
+	if (!crtc || !crtc->state) {
+		dev_info(hdmi->dev, "%s old crtc state is null\n", __func__);
 		return;
+	}
 
 	if (hdmi->phy)
 		phy_set_bus_width(hdmi->phy, hdmi->phy_bus_width);
@@ -1878,6 +1896,26 @@
 		regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON6, val);
 }
 
+static void rk3588_set_hdcp_status(void *data, u8 status)
+{
+	struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
+
+	hdmi->hdcp_status = status;
+}
+
+static void rk3588_set_hdcp2_enable(void *data, bool enable)
+{
+	struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
+	u32 val;
+
+	if (enable)
+		val = HIWORD_UPDATE(HDCP1_P1_GPIO_IN, HDCP1_P1_GPIO_IN);
+	else
+		val = HIWORD_UPDATE(0, HDCP1_P1_GPIO_IN);
+
+	regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON1, val);
+}
+
 static void rk3588_set_grf_cfg(void *data)
 {
 	struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
@@ -2027,11 +2065,6 @@
 	else
 		color_depth = 8;
 
-	if (!sink_is_hdmi) {
-		*color_format = RK_IF_FORMAT_RGB;
-		color_depth = 8;
-	}
-
 	*eotf = HDMI_EOTF_TRADITIONAL_GAMMA_SDR;
 	if (conn_state->hdr_output_metadata) {
 		hdr_metadata = (struct hdr_output_metadata *)
@@ -2080,6 +2113,11 @@
 
 	if (hdmi->is_hdmi_qp && mode.clock >= 600000)
 		*color_format = RK_IF_FORMAT_YCBCR420;
+
+	if (!sink_is_hdmi) {
+		*color_format = RK_IF_FORMAT_RGB;
+		color_depth = 8;
+	}
 
 	if (*color_format == RK_IF_FORMAT_YCBCR422 || color_depth == 8)
 		tmdsclock = pixclock;
@@ -2551,6 +2589,18 @@
 	}
 }
 
+static void dw_hdmi_rockchip_set_hdcp14_mem(void *data, bool enable)
+{
+	struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
+	u32 val;
+
+	val = HIWORD_UPDATE(enable << 15, RK3588_HDMI_HDCP14_MEM_EN);
+	if (!hdmi->id)
+		regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON4, val);
+	else
+		regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON7, val);
+}
+
 static const struct drm_prop_enum_list color_depth_enum_list[] = {
 	{ 0, "Automatic" }, /* Prefer highest color depth */
 	{ 8, "24bit" },
@@ -2597,6 +2647,7 @@
 	struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
 	struct drm_property *prop;
 	struct rockchip_drm_private *private = connector->dev->dev_private;
+	int ret;
 
 	switch (color) {
 	case MEDIA_BUS_FMT_RGB101010_1X30:
@@ -2711,30 +2762,24 @@
 		drm_object_attach_property(&connector->base, prop, 0);
 	}
 
-	prop = drm_property_create_bool(connector->dev, DRM_MODE_PROP_IMMUTABLE,
-					"USER_SPLIT_MODE");
-	if (prop) {
-		hdmi->user_split_mode_prop = prop;
-		drm_object_attach_property(&connector->base, prop,
-					   hdmi->user_split_mode ? 1 : 0);
-	}
+	if (hdmi->is_hdmi_qp) {
+		prop = drm_property_create_bool(connector->dev, 0, "allm_capacity");
+		if (prop) {
+			hdmi->allm_capacity = prop;
+			drm_object_attach_property(&connector->base, prop,
+						   !!(hdmi->add_func & SUPPORT_HDMI_ALLM));
+		}
 
-	prop = drm_property_create_bool(connector->dev, 0, "allm_capacity");
-	if (prop) {
-		hdmi->allm_capacity = prop;
-		drm_object_attach_property(&connector->base, prop,
-					   !!(hdmi->add_func & SUPPORT_HDMI_ALLM));
+		prop = drm_property_create_enum(connector->dev, 0,
+						"allm_enable",
+						allm_enable_list,
+						ARRAY_SIZE(allm_enable_list));
+		if (prop) {
+			hdmi->allm_enable = prop;
+			drm_object_attach_property(&connector->base, prop, 0);
+		}
+		hdmi->enable_allm = allm_en;
 	}
-
-	prop = drm_property_create_enum(connector->dev, 0,
-					"allm_enable",
-					allm_enable_list,
-					ARRAY_SIZE(allm_enable_list));
-	if (prop) {
-		hdmi->allm_enable = prop;
-		drm_object_attach_property(&connector->base, prop, 0);
-	}
-	hdmi->enable_allm = allm_en;
 
 	prop = drm_property_create_enum(connector->dev, 0,
 					"output_hdmi_dvi",
@@ -2773,6 +2818,21 @@
 		drm_object_attach_property(&connector->base,
 					   connector->colorspace_property, 0);
 	drm_object_attach_property(&connector->base, private->connector_id_prop, hdmi->id);
+
+	ret = drm_connector_attach_content_protection_property(connector, true);
+	if (ret) {
+		dev_err(hdmi->dev, "failed to attach content protection: %d\n", ret);
+		return;
+	}
+
+	prop = drm_property_create_range(connector->dev, 0, RK_IF_PROP_ENCRYPTED,
+					 RK_IF_HDCP_ENCRYPTED_NONE, RK_IF_HDCP_ENCRYPTED_LEVEL2);
+	if (!prop) {
+		dev_err(hdmi->dev, "create hdcp encrypted prop for hdmi%d failed\n", hdmi->id);
+		return;
+	}
+	hdmi->hdcp_state_property = prop;
+	drm_object_attach_property(&connector->base, prop, RK_IF_HDCP_ENCRYPTED_NONE);
 }
 
 static void
@@ -2833,12 +2893,6 @@
 		drm_property_destroy(connector->dev,
 				     hdmi->output_type_capacity);
 		hdmi->output_type_capacity = NULL;
-	}
-
-	if (hdmi->user_split_mode_prop) {
-		drm_property_destroy(connector->dev,
-				     hdmi->user_split_mode_prop);
-		hdmi->user_split_mode_prop = NULL;
 	}
 
 	if (hdmi->allm_capacity) {
@@ -2913,6 +2967,8 @@
 		if (allm_enable != hdmi->enable_allm)
 			dw_hdmi_qp_set_allm_enable(hdmi->hdmi_qp, hdmi->enable_allm);
 		return 0;
+	} else if (property == hdmi->hdcp_state_property) {
+		return 0;
 	}
 
 	DRM_ERROR("Unknown property [PROP:%d:%s]\n",
@@ -2982,14 +3038,19 @@
 		else
 			*val = dw_hdmi_qp_get_output_type_cap(hdmi->hdmi_qp);
 		return 0;
-	} else if (property == hdmi->user_split_mode_prop) {
-		*val = hdmi->user_split_mode;
-		return 0;
 	} else if (property == hdmi->allm_capacity) {
 		*val = !!(hdmi->add_func & SUPPORT_HDMI_ALLM);
 		return 0;
 	} else if (property == hdmi->allm_enable) {
 		*val = hdmi->enable_allm;
+		return 0;
+	} else if (property == hdmi->hdcp_state_property) {
+		if (hdmi->hdcp_status & BIT(1))
+			*val = RK_IF_HDCP_ENCRYPTED_LEVEL2;
+		else if (hdmi->hdcp_status & BIT(0))
+			*val = RK_IF_HDCP_ENCRYPTED_LEVEL1;
+		else
+			*val = RK_IF_HDCP_ENCRYPTED_NONE;
 		return 0;
 	}
 
@@ -3376,6 +3437,7 @@
 };
 
 static const struct dw_hdmi_plat_data rk3588_hdmi_drv_data = {
+	.mode_valid = dw_hdmi_rockchip_mode_valid,
 	.phy_data = &rk3588_hdmi_chip_data,
 	.qp_phy_ops = &rk3588_hdmi_phy_ops,
 	.phy_name = "samsung_hdptx_phy",
@@ -3463,6 +3525,8 @@
 	plat_data->get_colorimetry =
 		dw_hdmi_rockchip_get_colorimetry;
 	plat_data->get_link_cfg = dw_hdmi_rockchip_get_link_cfg;
+	plat_data->set_hdcp2_enable = rk3588_set_hdcp2_enable;
+	plat_data->set_hdcp_status = rk3588_set_hdcp_status;
 	plat_data->set_grf_cfg = rk3588_set_grf_cfg;
 	plat_data->get_grf_color_fmt = rk3588_get_grf_color_fmt;
 	plat_data->convert_to_split_mode = drm_mode_convert_to_split_mode;
@@ -3478,6 +3542,8 @@
 		dw_hdmi_rockchip_set_prev_bus_format;
 	plat_data->set_ddc_io =
 		dw_hdmi_rockchip_set_ddc_io;
+	plat_data->set_hdcp14_mem =
+		dw_hdmi_rockchip_set_hdcp14_mem;
 	plat_data->property_ops = &dw_hdmi_rockchip_property_ops;
 
 	secondary = rockchip_hdmi_find_by_id(dev->driver, !hdmi->id);
@@ -3496,12 +3562,6 @@
 			secondary->plat_data->split_mode = true;
 			if (!secondary->plat_data->first_screen)
 				plat_data->first_screen = true;
-		}
-
-		if (device_property_read_bool(dev, "user-split-mode") ||
-		    device_property_read_bool(secondary->dev, "user-split-mode")) {
-			hdmi->user_split_mode = true;
-			secondary->user_split_mode = true;
 		}
 	}
 
@@ -3675,7 +3735,20 @@
 			drm_encoder_cleanup(&hdmi->encoder);
 		}
 
-		if (plat_data->connector) {
+		if (plat_data->bridge) {
+			struct drm_connector *connector = NULL;
+			struct list_head *connector_list =
+				&plat_data->bridge->dev->mode_config.connector_list;
+
+			list_for_each_entry(connector, connector_list, head)
+				if (drm_connector_has_possible_encoder(connector,
+							&hdmi->encoder))
+					break;
+
+			hdmi->sub_dev.connector = connector;
+			hdmi->sub_dev.of_node = dev->of_node;
+			rockchip_drm_register_sub_dev(&hdmi->sub_dev);
+		} else if (plat_data->connector) {
 			hdmi->sub_dev.connector = plat_data->connector;
 			hdmi->sub_dev.loader_protect = dw_hdmi_rockchip_encoder_loader_protect;
 			if (secondary && device_property_read_bool(secondary->dev, "split-mode"))
diff --git a/kernel/drivers/gpu/drm/rockchip/rockchip_drm_direct_show.c b/kernel/drivers/gpu/drm/rockchip/rockchip_drm_direct_show.c
index 9989820..9e7276b 100644
--- a/kernel/drivers/gpu/drm/rockchip/rockchip_drm_direct_show.c
+++ b/kernel/drivers/gpu/drm/rockchip/rockchip_drm_direct_show.c
@@ -316,3 +316,18 @@
 
 	return ret;
 }
+
+int rockchip_drm_direct_show_buf_begin_cpu_access(struct rockchip_drm_direct_show_buffer *buffer)
+{
+	struct drm_gem_object *obj = &buffer->rk_gem_obj->base;
+
+	return rockchip_gem_prime_begin_cpu_access(obj, DMA_FROM_DEVICE);
+}
+
+int rockchip_drm_direct_show_buf_end_cpu_access(struct rockchip_drm_direct_show_buffer *buffer)
+{
+	struct drm_gem_object *obj = &buffer->rk_gem_obj->base;
+
+	return rockchip_gem_prime_end_cpu_access(obj, DMA_TO_DEVICE);
+}
+
diff --git a/kernel/drivers/gpu/drm/rockchip/rockchip_drm_direct_show.h b/kernel/drivers/gpu/drm/rockchip/rockchip_drm_direct_show.h
index 939f0d4..583f760 100644
--- a/kernel/drivers/gpu/drm/rockchip/rockchip_drm_direct_show.h
+++ b/kernel/drivers/gpu/drm/rockchip/rockchip_drm_direct_show.h
@@ -61,5 +61,7 @@
 int rockchip_drm_direct_show_commit(struct drm_device *drm,
 				    struct rockchip_drm_direct_show_commit_info *commit_info);
 int rockchip_drm_direct_show_disable_plane(struct drm_device *drm, struct drm_plane *plane);
+int rockchip_drm_direct_show_buf_begin_cpu_access(struct rockchip_drm_direct_show_buffer *buffer);
+int rockchip_drm_direct_show_buf_end_cpu_access(struct rockchip_drm_direct_show_buffer *buffer);
 
 #endif
diff --git a/kernel/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/kernel/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
index d5b93d5..a3a8c44 100644
--- a/kernel/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
+++ b/kernel/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
@@ -125,6 +125,7 @@
 	hbp = mode->htotal - mode->hsync_end;
 
 	mode->clock *= 2;
+	mode->crtc_clock *= 2;
 	mode->hdisplay = hactive * 2;
 	mode->hsync_start = mode->hdisplay + hfp * 2;
 	mode->hsync_end = mode->hsync_start + hsync * 2;
@@ -143,6 +144,7 @@
 	hbp = mode->htotal - mode->hsync_end;
 
 	mode->clock /= 2;
+	mode->crtc_clock /= 2;
 	mode->hdisplay = hactive / 2;
 	mode->hsync_start = mode->hdisplay + hfp / 2;
 	mode->hsync_end = mode->hsync_start + hsync / 2;
@@ -197,6 +199,30 @@
 	return 0;
 }
 EXPORT_SYMBOL(rockchip_drm_get_bpp);
+
+uint32_t rockchip_drm_get_cycles_per_pixel(uint32_t bus_format)
+{
+	switch (bus_format) {
+	case MEDIA_BUS_FMT_RGB565_1X16:
+	case MEDIA_BUS_FMT_RGB666_1X18:
+	case MEDIA_BUS_FMT_RGB888_1X24:
+	case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
+		return 1;
+	case MEDIA_BUS_FMT_RGB565_2X8_LE:
+	case MEDIA_BUS_FMT_BGR565_2X8_LE:
+		return 2;
+	case MEDIA_BUS_FMT_RGB666_3X6:
+	case MEDIA_BUS_FMT_RGB888_3X8:
+	case MEDIA_BUS_FMT_BGR888_3X8:
+		return 3;
+	case MEDIA_BUS_FMT_RGB888_DUMMY_4X8:
+	case MEDIA_BUS_FMT_BGR888_DUMMY_4X8:
+		return 4;
+	default:
+		return 1;
+	}
+}
+EXPORT_SYMBOL(rockchip_drm_get_cycles_per_pixel);
 
 /**
  * rockchip_drm_of_find_possible_crtcs - find the possible CRTCs for an active
@@ -1289,6 +1315,12 @@
 }
 #endif
 
+static const struct drm_prop_enum_list split_area[] = {
+	{ ROCKCHIP_DRM_SPLIT_UNSET, "UNSET" },
+	{ ROCKCHIP_DRM_SPLIT_LEFT_SIDE, "LEFT" },
+	{ ROCKCHIP_DRM_SPLIT_RIGHT_SIDE, "RIGHT" },
+};
+
 static int rockchip_drm_create_properties(struct drm_device *dev)
 {
 	struct drm_property *prop;
@@ -1324,6 +1356,11 @@
 		return -ENOMEM;
 	private->connector_id_prop = prop;
 
+	prop = drm_property_create_enum(dev, DRM_MODE_PROP_ENUM, "SPLIT_AREA",
+					split_area,
+					ARRAY_SIZE(split_area));
+	private->split_area_prop = prop;
+
 	prop = drm_property_create_object(dev,
 					  DRM_MODE_PROP_ATOMIC | DRM_MODE_PROP_IMMUTABLE,
 					  "SOC_ID", DRM_MODE_OBJECT_CRTC);
@@ -1337,6 +1374,9 @@
 	private->aclk_prop = drm_property_create_range(dev, 0, "ACLK", 0, UINT_MAX);
 	private->bg_prop = drm_property_create_range(dev, 0, "BACKGROUND", 0, UINT_MAX);
 	private->line_flag_prop = drm_property_create_range(dev, 0, "LINE_FLAG1", 0, UINT_MAX);
+	private->cubic_lut_prop = drm_property_create(dev, DRM_MODE_PROP_BLOB, "CUBIC_LUT", 0);
+	private->cubic_lut_size_prop = drm_property_create_range(dev, DRM_MODE_PROP_IMMUTABLE,
+								 "CUBIC_LUT_SIZE", 0, UINT_MAX);
 
 	return drm_mode_create_tv_properties(dev, 0, NULL);
 }
diff --git a/kernel/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/kernel/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
index 4c36d9b..7b6abed 100644
--- a/kernel/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
+++ b/kernel/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
@@ -107,6 +107,12 @@
 	ROCKCHIP_COLOR_BAR_VERTICAL = 2,
 };
 
+enum rockchip_drm_split_area {
+	ROCKCHIP_DRM_SPLIT_UNSET = 0,
+	ROCKCHIP_DRM_SPLIT_LEFT_SIDE = 1,
+	ROCKCHIP_DRM_SPLIT_RIGHT_SIDE = 2,
+};
+
 struct rockchip_drm_sub_dev {
 	struct list_head list;
 	struct drm_connector *connector;
@@ -184,6 +190,37 @@
 	u16 target_bits_per_pixel_x16;
 };
 
+#define ACM_GAIN_LUT_HY_LENGTH		(9*17)
+#define ACM_GAIN_LUT_HY_TOTAL_LENGTH	(ACM_GAIN_LUT_HY_LENGTH * 3)
+#define ACM_GAIN_LUT_HS_LENGTH		(13*17)
+#define ACM_GAIN_LUT_HS_TOTAL_LENGTH	(ACM_GAIN_LUT_HS_LENGTH * 3)
+#define ACM_DELTA_LUT_H_LENGTH		65
+#define ACM_DELTA_LUT_H_TOTAL_LENGTH	(ACM_DELTA_LUT_H_LENGTH * 3)
+
+struct post_acm {
+	s16 delta_lut_h[ACM_DELTA_LUT_H_TOTAL_LENGTH];
+	s16 gain_lut_hy[ACM_GAIN_LUT_HY_TOTAL_LENGTH];
+	s16 gain_lut_hs[ACM_GAIN_LUT_HS_TOTAL_LENGTH];
+	u16 y_gain;
+	u16 h_gain;
+	u16 s_gain;
+	u16 acm_enable;
+};
+
+struct post_csc {
+	u16 hue;
+	u16 saturation;
+	u16 contrast;
+	u16 brightness;
+	u16 r_gain;
+	u16 g_gain;
+	u16 b_gain;
+	u16 r_offset;
+	u16 g_offset;
+	u16 b_offset;
+	u16 csc_enable;
+};
+
 struct rockchip_crtc_state {
 	struct drm_crtc_state base;
 	int vp_id;
@@ -227,6 +264,7 @@
 	int afbdc_win_yoffset;
 	int dsp_layer_sel;
 	u32 output_if;
+	u32 output_if_left_panel;
 	u32 bus_format;
 	u32 bus_flags;
 	int yuv_overlay;
@@ -255,6 +293,7 @@
 	struct drm_property_blob *hdr_ext_data;
 	struct drm_property_blob *acm_lut_data;
 	struct drm_property_blob *post_csc_data;
+	struct drm_property_blob *cubic_lut_data;
 
 	int request_refresh_rate;
 	int max_refresh_rate;
@@ -399,7 +438,7 @@
  * @wait_vact_end: wait the last active line.
  */
 struct rockchip_crtc_funcs {
-	int (*loader_protect)(struct drm_crtc *crtc, bool on);
+	int (*loader_protect)(struct drm_crtc *crtc, bool on, void *data);
 	int (*enable_vblank)(struct drm_crtc *crtc);
 	void (*disable_vblank)(struct drm_crtc *crtc);
 	size_t (*bandwidth)(struct drm_crtc *crtc,
@@ -452,6 +491,8 @@
 	struct drm_property *aclk_prop;
 	struct drm_property *bg_prop;
 	struct drm_property *line_flag_prop;
+	struct drm_property *cubic_lut_prop;
+	struct drm_property *cubic_lut_size_prop;
 
 	/* private plane prop */
 	struct drm_property *eotf_prop;
@@ -461,6 +502,7 @@
 
 	/* private connector prop */
 	struct drm_property *connector_id_prop;
+	struct drm_property *split_area_prop;
 
 	const struct rockchip_crtc_funcs *crtc_funcs[ROCKCHIP_MAX_CRTC];
 
@@ -525,6 +567,7 @@
 uint32_t rockchip_drm_of_find_possible_crtcs(struct drm_device *dev,
 					     struct device_node *port);
 uint32_t rockchip_drm_get_bpp(const struct drm_format_info *info);
+uint32_t rockchip_drm_get_cycles_per_pixel(uint32_t bus_format);
 int rockchip_drm_get_yuv422_format(struct drm_connector *connector,
 				   struct edid *edid);
 int rockchip_drm_parse_cea_ext(struct rockchip_drm_dsc_cap *dsc_cap,
diff --git a/kernel/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/kernel/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
index c9ed39d..91cb119 100644
--- a/kernel/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
+++ b/kernel/drivers/gpu/drm/rockchip/rockchip_drm_fb.c
@@ -163,6 +163,7 @@
 	vop_bw_info->line_bw_mbyte = 0;
 	vop_bw_info->frame_bw_mbyte = 0;
 	vop_bw_info->plane_num = 0;
+	vop_bw_info->plane_num_4k = 0;
 
 	for_each_old_crtc_in_state(state, crtc, old_crtc_state, i) {
 		funcs = priv->crtc_funcs[drm_crtc_index(crtc)];
diff --git a/kernel/drivers/gpu/drm/rockchip/rockchip_drm_logo.c b/kernel/drivers/gpu/drm/rockchip/rockchip_drm_logo.c
index 0c9d4ba..af228f4 100644
--- a/kernel/drivers/gpu/drm/rockchip/rockchip_drm_logo.c
+++ b/kernel/drivers/gpu/drm/rockchip/rockchip_drm_logo.c
@@ -376,6 +376,69 @@
 	return rockchip_drm_logo_fb_alloc(drm_dev, &mode_cmd, private->logo);
 }
 
+static void of_parse_post_csc_info(struct device_node *route, struct rockchip_drm_mode_set *set)
+{
+	int val;
+
+	if (!of_property_read_u32(route, "post-csc,enable", &val))
+		set->csc.csc_enable = val;
+	else
+		set->csc.csc_enable = 0;
+
+	if (!set->csc.csc_enable)
+		return;
+
+	if (!of_property_read_u32(route, "post-csc,hue", &val))
+		set->csc.hue = val;
+	else
+		set->csc.hue = 256;
+
+	if (!of_property_read_u32(route, "post-csc,saturation", &val))
+		set->csc.saturation = val;
+	else
+		set->csc.saturation = 256;
+
+	if (!of_property_read_u32(route, "post-csc,contrast", &val))
+		set->csc.contrast = val;
+	else
+		set->csc.contrast = 256;
+
+	if (!of_property_read_u32(route, "post-csc,brightness", &val))
+		set->csc.brightness = val;
+	else
+		set->csc.brightness = 256;
+
+	if (!of_property_read_u32(route, "post-csc,r-gain", &val))
+		set->csc.r_gain = val;
+	else
+		set->csc.r_gain = 256;
+
+	if (!of_property_read_u32(route, "post-csc,g-gain", &val))
+		set->csc.g_gain = val;
+	else
+		set->csc.g_gain = 256;
+
+	if (!of_property_read_u32(route, "post-csc,b-gain", &val))
+		set->csc.b_gain = val;
+	else
+		set->csc.b_gain = 256;
+
+	if (!of_property_read_u32(route, "post-csc,r-offset", &val))
+		set->csc.r_offset = val;
+	else
+		set->csc.r_offset = 256;
+
+	if (!of_property_read_u32(route, "post-csc,g-offset", &val))
+		set->csc.g_offset = val;
+	else
+		set->csc.g_offset = 256;
+
+	if (!of_property_read_u32(route, "post-csc,b-offset", &val))
+		set->csc.b_offset = val;
+	else
+		set->csc.b_offset = 256;
+}
+
 static struct rockchip_drm_mode_set *
 of_parse_display_resource(struct drm_device *drm_dev, struct device_node *route)
 {
@@ -469,6 +532,8 @@
 		set->hue = val;
 	else
 		set->hue = 50;
+
+	of_parse_post_csc_info(route, set);
 
 	set->force_output = of_property_read_bool(route, "force-output");
 
@@ -746,7 +811,7 @@
 
 		if (priv->crtc_funcs[pipe] &&
 		    priv->crtc_funcs[pipe]->loader_protect)
-			priv->crtc_funcs[pipe]->loader_protect(crtc, true);
+			priv->crtc_funcs[pipe]->loader_protect(crtc, true, &set->csc);
 	}
 
 	if (!set->fb) {
@@ -798,7 +863,7 @@
 
 error_crtc:
 	if (priv->crtc_funcs[pipe] && priv->crtc_funcs[pipe]->loader_protect)
-		priv->crtc_funcs[pipe]->loader_protect(crtc, false);
+		priv->crtc_funcs[pipe]->loader_protect(crtc, false, NULL);
 error_conn:
 	if (set->sub_dev->loader_protect)
 		set->sub_dev->loader_protect(conn_state->best_encoder, false);
@@ -994,11 +1059,12 @@
 									     unset);
 				if (priv->crtc_funcs[pipe] &&
 				    priv->crtc_funcs[pipe]->loader_protect)
-					priv->crtc_funcs[pipe]->loader_protect(crtc, true);
+					priv->crtc_funcs[pipe]->loader_protect(crtc, true,
+									       &unset->csc);
 				priv->crtc_funcs[pipe]->crtc_close(crtc);
 				if (priv->crtc_funcs[pipe] &&
 				    priv->crtc_funcs[pipe]->loader_protect)
-					priv->crtc_funcs[pipe]->loader_protect(crtc, false);
+					priv->crtc_funcs[pipe]->loader_protect(crtc, false, NULL);
 			}
 		}
 
diff --git a/kernel/drivers/gpu/drm/rockchip/rockchip_drm_logo.h b/kernel/drivers/gpu/drm/rockchip/rockchip_drm_logo.h
index 1b0b239..7e1b1d2 100644
--- a/kernel/drivers/gpu/drm/rockchip/rockchip_drm_logo.h
+++ b/kernel/drivers/gpu/drm/rockchip/rockchip_drm_logo.h
@@ -7,12 +7,15 @@
 #ifndef ROCKCHIP_DRM_LOGO_H
 #define ROCKCHIP_DRM_LOGO_H
 
+#include "rockchip_drm_vop.h"
+
 struct rockchip_drm_mode_set {
 	struct list_head head;
 	struct drm_framebuffer *fb;
 	struct rockchip_drm_sub_dev *sub_dev;
 	struct drm_crtc *crtc;
 	struct drm_display_mode *mode;
+	struct post_csc csc;
 	int clock;
 	int hdisplay;
 	int vdisplay;
diff --git a/kernel/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/kernel/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
index 296c866..326b571 100644
--- a/kernel/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
+++ b/kernel/drivers/gpu/drm/rockchip/rockchip_drm_vop.c
@@ -666,11 +666,13 @@
 static bool is_rb_swap(uint32_t bus_format, uint32_t output_mode)
 {
 	/*
-	 * The default component order of serial rgb3x8 formats
+	 * The default component order of serial formats
 	 * is BGR. So it is needed to enable RB swap.
 	 */
 	if (bus_format == MEDIA_BUS_FMT_RGB888_3X8 ||
-	    bus_format == MEDIA_BUS_FMT_RGB888_DUMMY_4X8)
+	    bus_format == MEDIA_BUS_FMT_RGB888_DUMMY_4X8 ||
+	    bus_format == MEDIA_BUS_FMT_RGB666_3X6 ||
+	    bus_format == MEDIA_BUS_FMT_RGB565_2X8_LE)
 		return true;
 	else
 		return false;
@@ -1871,6 +1873,9 @@
 					to_vop_plane_state(plane->state);
 #endif
 
+	rockchip_drm_dbg(vop->dev, VOP_DEBUG_PLANE, "disable win%d-area%d by %s\n",
+			 win->win_id, win->area_id, current->comm);
+
 	if (!old_state->crtc)
 		return;
 
@@ -1980,6 +1985,7 @@
 	uint32_t val;
 	bool rb_swap, global_alpha_en;
 	int is_yuv = fb->format->is_yuv;
+	struct drm_format_name_buf format_name;
 
 #if defined(CONFIG_ROCKCHIP_DRM_DEBUG)
 	bool AFBC_flag = false;
@@ -2144,6 +2150,13 @@
 	VOP_WIN_SET(vop, win, enable, 1);
 	VOP_WIN_SET(vop, win, gate, 1);
 	spin_unlock(&vop->reg_lock);
+
+	drm_get_format_name(fb->format->format, &format_name);
+	rockchip_drm_dbg(vop->dev, VOP_DEBUG_PLANE,
+			 "update win%d-area%d [%dx%d->%dx%d@(%d, %d)] zpos:%d fmt[%s%s] addr[%pad] by %s\n",
+			 win->win_id, win->area_id, actual_w, actual_h,
+			 dsp_w, dsp_h, dsp_stx, dsp_sty, vop_plane_state->zpos, format_name.str,
+			 fb->modifier ? "[AFBC]" : "", &vop_plane_state->yrgb_mst, current->comm);
 	/*
 	 * spi interface(vop_plane_state->yrgb_kvaddr, fb->pixel_format,
 	 * actual_w, actual_h)
@@ -2325,7 +2338,7 @@
 		return;
 
 	__drm_atomic_helper_plane_reset(plane, &vop_plane_state->base);
-	win->state.zpos = win->zpos;
+	vop_plane_state->base.zpos = win->zpos;
 	vop_plane_state->global_alpha = 0xff;
 }
 
@@ -2514,7 +2527,7 @@
 	spin_unlock_irqrestore(&drm->event_lock, flags);
 }
 
-static int vop_crtc_loader_protect(struct drm_crtc *crtc, bool on)
+static int vop_crtc_loader_protect(struct drm_crtc *crtc, bool on, void *data)
 {
 	struct rockchip_drm_private *private = crtc->dev->dev_private;
 	struct vop *vop = to_vop(crtc);
@@ -3078,8 +3091,8 @@
 {
 	struct vop *vop = to_vop(crtc);
 	const struct vop_data *vop_data = vop->data;
-	struct rockchip_crtc_state *s =
-			to_rockchip_crtc_state(crtc->state);
+	struct drm_crtc_state *new_crtc_state = container_of(mode, struct drm_crtc_state, mode);
+	struct rockchip_crtc_state *s = to_rockchip_crtc_state(new_crtc_state);
 
 	if (mode->hdisplay > vop_data->max_output.width)
 		return false;
@@ -3094,6 +3107,10 @@
 	    (VOP_MAJOR(vop->version) == 2 && VOP_MINOR(vop->version) >= 12 &&
 	     s->output_if & VOP_OUTPUT_IF_BT656))
 		adj_mode->crtc_clock *= 2;
+
+	if (vop->mcu_timing.mcu_pix_total)
+		adj_mode->crtc_clock *= rockchip_drm_get_cycles_per_pixel(s->bus_format) *
+					(vop->mcu_timing.mcu_pix_total + 1);
 
 	adj_mode->crtc_clock =
 		DIV_ROUND_UP(clk_round_rate(vop->dclk, adj_mode->crtc_clock * 1000),
@@ -3118,12 +3135,14 @@
 
 	switch (s->bus_format) {
 	case MEDIA_BUS_FMT_RGB565_1X16:
+	case MEDIA_BUS_FMT_RGB565_2X8_LE:
 		VOP_CTRL_SET(vop, dither_down_en, 1);
 		VOP_CTRL_SET(vop, dither_down_mode, RGB888_TO_RGB565);
 		break;
 	case MEDIA_BUS_FMT_RGB666_1X18:
 	case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
 	case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:
+	case MEDIA_BUS_FMT_RGB666_3X6:
 		VOP_CTRL_SET(vop, dither_down_en, 1);
 		VOP_CTRL_SET(vop, dither_down_mode, RGB888_TO_RGB666);
 		break;
@@ -4023,6 +4042,7 @@
 	spin_lock_irqsave(&vop->irq_lock, flags);
 	vop->pre_overlay = s->hdr.pre_overlay;
 	vop_cfg_done(vop);
+	rockchip_drm_dbg(vop->dev, VOP_DEBUG_CFG_DONE, "cfg_done\n\n");
 	/*
 	 * rk322x and rk332x odd-even field will mistake when in interlace mode.
 	 * we must switch to frame effect before switch screen and switch to
@@ -4388,6 +4408,7 @@
 		 * frame effective, but actually it's effective immediately, so
 		 * we config this register at frame start.
 		 */
+		rockchip_drm_dbg(vop->dev, VOP_DEBUG_VSYNC, "vsync\n");
 		spin_lock_irqsave(&vop->irq_lock, flags);
 		VOP_CTRL_SET(vop, level2_overlay_en, vop->pre_overlay);
 		VOP_CTRL_SET(vop, alpha_hard_calc, vop->pre_overlay);
diff --git a/kernel/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/kernel/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
index 3243b0c..b6e12d2 100644
--- a/kernel/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
+++ b/kernel/drivers/gpu/drm/rockchip/rockchip_drm_vop.h
@@ -31,11 +31,15 @@
 #define VOP_VERSION_RK3568	VOP2_VERSION(0x40, 0x15, 0x8023)
 #define VOP_VERSION_RK3588	VOP2_VERSION(0x40, 0x17, 0x6786)
 
+/* register one connector */
 #define ROCKCHIP_OUTPUT_DUAL_CHANNEL_LEFT_RIGHT_MODE	BIT(0)
+/* register one connector */
 #define ROCKCHIP_OUTPUT_DUAL_CHANNEL_ODD_EVEN_MODE	BIT(1)
 #define ROCKCHIP_OUTPUT_DATA_SWAP			BIT(2)
 /* MIPI DSI DataStream(cmd) mode on rk3588 */
 #define ROCKCHIP_OUTPUT_MIPI_DS_MODE			BIT(3)
+/* register two connector */
+#define ROCKCHIP_OUTPUT_DUAL_CONNECTOR_SPLIT_MODE	BIT(4)
 
 #define AFBDC_FMT_RGB565	0x0
 #define AFBDC_FMT_U8U8U8U8	0x5
@@ -595,37 +599,6 @@
 	RESERVED12 = 12,	/* reserved for other dynamic hdr format */
 	RESERVED13 = 13,	/* reserved for other dynamic hdr format */
 	HDR_FORMAT_MAX,
-};
-
-#define ACM_GAIN_LUT_HY_LENGTH		(9*17)
-#define ACM_GAIN_LUT_HY_TOTAL_LENGTH	(ACM_GAIN_LUT_HY_LENGTH * 3)
-#define ACM_GAIN_LUT_HS_LENGTH		(13*17)
-#define ACM_GAIN_LUT_HS_TOTAL_LENGTH (ACM_GAIN_LUT_HS_LENGTH * 3)
-#define ACM_DELTA_LUT_H_LENGTH		65
-#define ACM_DELTA_LUT_H_TOTAL_LENGTH	(ACM_DELTA_LUT_H_LENGTH * 3)
-
-struct post_acm {
-	s16 delta_lut_h[ACM_DELTA_LUT_H_TOTAL_LENGTH];
-	s16 gain_lut_hy[ACM_GAIN_LUT_HY_TOTAL_LENGTH];
-	s16 gain_lut_hs[ACM_GAIN_LUT_HS_TOTAL_LENGTH];
-	u16 y_gain;
-	u16 h_gain;
-	u16 s_gain;
-	u16 acm_enable;
-};
-
-struct post_csc {
-	u16 hue;
-	u16 saturation;
-	u16 contrast;
-	u16 brightness;
-	u16 r_gain;
-	u16 g_gain;
-	u16 b_gain;
-	u16 r_offset;
-	u16 g_offset;
-	u16 b_offset;
-	u16 csc_enable;
 };
 
 struct post_csc_coef {
@@ -1461,7 +1434,9 @@
 #define ROCKCHIP_OUT_MODE_P565		2
 #define ROCKCHIP_OUT_MODE_BT656		5
 #define ROCKCHIP_OUT_MODE_S888		8
+#define ROCKCHIP_OUT_MODE_S666		9
 #define ROCKCHIP_OUT_MODE_YUV422	9
+#define ROCKCHIP_OUT_MODE_S565		10
 #define ROCKCHIP_OUT_MODE_S888_DUMMY	12
 #define ROCKCHIP_OUT_MODE_YUV420	14
 /* for use special outface */
diff --git a/kernel/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/kernel/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
index ed50bb2..52dbcca 100644
--- a/kernel/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
+++ b/kernel/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c
@@ -643,6 +643,11 @@
 	 */
 	uint32_t win_mask;
 	/**
+	 * @enabled_win_mask: Bitmask of enabled wins attached to the video port;
+	 */
+	uint32_t enabled_win_mask;
+
+	/**
 	 * @nr_layers: active layers attached to the video port;
 	 */
 	uint8_t nr_layers;
@@ -769,6 +774,9 @@
 	 * will be used to show uboot logo and kernel logo
 	 */
 	enum vop2_layer_phy_id primary_plane_phy_id;
+
+	struct post_acm acm_info;
+	struct post_csc csc_info;
 
 	/**
 	 * @refresh_rate_change: indicate whether refresh rate change
@@ -1870,6 +1878,18 @@
 static inline uint32_t vop2_read_lut(struct vop2 *vop2, uint32_t offset)
 {
 	return readl(vop2->lut_regs + offset);
+}
+
+static bool is_linear_10bit_yuv(uint32_t format)
+{
+	switch (format) {
+	case DRM_FORMAT_NV15:
+	case DRM_FORMAT_NV20:
+	case DRM_FORMAT_NV30:
+		return true;
+	default:
+		return false;
+	}
 }
 
 static enum vop2_data_format vop2_convert_format(uint32_t format)
@@ -3383,7 +3403,6 @@
 
 	VOP_MODULE_SET(vop2, vp, dsp_lut_en, 1);
 	vop2_write_reg_uncached(vop2, &vp->regs->gamma_update_en, 1);
-	vop2_cfg_done(crtc);
 	vp->gamma_lut_active = true;
 
 	spin_unlock(&vop2->reg_lock);
@@ -3430,26 +3449,7 @@
 		rk3588_crtc_load_lut(&vp->rockchip_crtc.crtc, vp->lut);
 		if (vcstate->splice_mode)
 			rk3588_crtc_load_lut(&splice_vp->rockchip_crtc.crtc, vp->lut);
-		vop2_cfg_done(crtc);
 	}
-	/*
-	 * maybe appear the following case:
-	 * -> set gamma
-	 * -> config done
-	 * -> atomic commit
-	 *  --> update win format
-	 *  --> update win address
-	 *  ---> here maybe meet vop hardware frame start, and triggle some config take affect.
-	 *  ---> as only some config take affect, this maybe lead to iommu pagefault.
-	 *  --> update win size
-	 *  --> update win other parameters
-	 * -> config done
-	 *
-	 * so we add vop2_wait_for_fs_by_done_bit_status() to make sure the first config done take
-	 * effect and then to do next frame config.
-	 */
-	if (VOP_MODULE_GET(vop2, vp, standby) == 0)
-		vop2_wait_for_fs_by_done_bit_status(vp);
 }
 
 static void rockchip_vop2_crtc_fb_gamma_set(struct drm_crtc *crtc, u16 red,
@@ -3491,6 +3491,7 @@
 				      struct drm_modeset_acquire_ctx *ctx)
 {
 	struct vop2_video_port *vp = to_vop2_video_port(crtc);
+	struct vop2 *vop2 = vp->vop2;
 	int i;
 
 	if (!vp->lut)
@@ -3505,6 +3506,25 @@
 		rockchip_vop2_crtc_fb_gamma_set(crtc, red[i], green[i],
 						blue[i], i);
 	vop2_crtc_load_lut(crtc);
+	vop2_cfg_done(crtc);
+	/*
+	 * maybe appear the following case:
+	 * -> set gamma
+	 * -> config done
+	 * -> atomic commit
+	 *  --> update win format
+	 *  --> update win address
+	 *  ---> here maybe meet vop hardware frame start, and triggle some config take affect.
+	 *  ---> as only some config take affect, this maybe lead to iommu pagefault.
+	 *  --> update win size
+	 *  --> update win other parameters
+	 * -> config done
+	 *
+	 * so we add vop2_wait_for_fs_by_done_bit_status() to make sure the first config done take
+	 * effect and then to do next frame config.
+	 */
+	if (VOP_MODULE_GET(vop2, vp, standby) == 0)
+		vop2_wait_for_fs_by_done_bit_status(vp);
 
 	return 0;
 }
@@ -3524,7 +3544,6 @@
 	return 0;
 }
 
-#if defined(CONFIG_ROCKCHIP_DRM_CUBIC_LUT)
 static int vop2_crtc_atomic_cubic_lut_set(struct drm_crtc *crtc,
 					  struct drm_crtc_state *old_state)
 {
@@ -3597,18 +3616,12 @@
 	return 0;
 }
 
-static void drm_crtc_enable_cubic_lut(struct drm_crtc *crtc, unsigned int cubic_lut_size)
+static void vop2_attach_cubic_lut_prop(struct drm_crtc *crtc, unsigned int cubic_lut_size)
 {
-	struct drm_device *dev = crtc->dev;
-	struct drm_mode_config *config = &dev->mode_config;
+	struct rockchip_drm_private *private = crtc->dev->dev_private;
 
-	if (cubic_lut_size) {
-		drm_object_attach_property(&crtc->base,
-					   config->cubic_lut_property, 0);
-		drm_object_attach_property(&crtc->base,
-					   config->cubic_lut_size_property,
-					   cubic_lut_size);
-	}
+	drm_object_attach_property(&crtc->base, private->cubic_lut_prop, 0);
+	drm_object_attach_property(&crtc->base, private->cubic_lut_size_prop, cubic_lut_size);
 }
 
 static void vop2_cubic_lut_init(struct vop2 *vop2)
@@ -3628,12 +3641,9 @@
 		vp->cubic_lut_len = vp_data->cubic_lut_len;
 
 		if (vp->cubic_lut_len)
-			drm_crtc_enable_cubic_lut(crtc, vp->cubic_lut_len);
+			vop2_attach_cubic_lut_prop(crtc, vp->cubic_lut_len);
 	}
 }
-#else
-static void vop2_cubic_lut_init(struct vop2 *vop2) { }
-#endif
 
 static int vop2_core_clks_prepare_enable(struct vop2 *vop2)
 {
@@ -4276,13 +4286,24 @@
 	return 0;
 }
 
-static void vop2_crtc_atomic_disable_for_psr(struct drm_crtc *crtc,
-					     struct drm_crtc_state *old_state)
+static void vop2_crtc_atomic_enter_psr(struct drm_crtc *crtc, struct drm_crtc_state *old_state)
 {
 	struct vop2_video_port *vp = to_vop2_video_port(crtc);
 	struct vop2 *vop2 = vp->vop2;
+	struct vop2_win *win;
+	unsigned long win_mask = vp->enabled_win_mask;
+	int phys_id;
 
-	vop2_disable_all_planes_for_crtc(crtc);
+	for_each_set_bit(phys_id, &win_mask, ROCKCHIP_MAX_LAYER) {
+		win = vop2_find_win_by_phys_id(vop2, phys_id);
+		VOP_WIN_SET(vop2, win, enable, 0);
+
+		if (win->feature & WIN_FEATURE_CLUSTER_MAIN)
+			VOP_CLUSTER_SET(vop2, win, enable, 0);
+	}
+
+	vop2_cfg_done(crtc);
+	vop2_wait_for_fs_by_done_bit_status(vp);
 	drm_crtc_vblank_off(crtc);
 	if (hweight8(vop2->active_vp_mask) == 1) {
 		u32 adjust_aclk_rate = 0;
@@ -4305,6 +4326,30 @@
 	}
 }
 
+static void vop2_crtc_atomic_exit_psr(struct drm_crtc *crtc, struct drm_crtc_state *old_state)
+{
+	struct vop2_video_port *vp = to_vop2_video_port(crtc);
+	struct vop2 *vop2 = vp->vop2;
+	u32 phys_id;
+	struct vop2_win *win;
+	unsigned long enabled_win_mask = vp->enabled_win_mask;
+
+	drm_crtc_vblank_on(crtc);
+	if (vop2->aclk_rate_reset)
+		clk_set_rate(vop2->aclk, vop2->aclk_rate);
+	vop2->aclk_rate_reset = false;
+
+	for_each_set_bit(phys_id, &enabled_win_mask, ROCKCHIP_MAX_LAYER) {
+		win = vop2_find_win_by_phys_id(vop2, phys_id);
+		VOP_WIN_SET(vop2, win, enable, 1);
+		if (win->feature & WIN_FEATURE_CLUSTER_MAIN)
+			VOP_CLUSTER_SET(vop2, win, enable, 1);
+	}
+
+	vop2_cfg_done(crtc);
+	vop2_wait_for_fs_by_done_bit_status(vp);
+}
+
 static void vop2_crtc_atomic_disable(struct drm_crtc *crtc,
 				     struct drm_crtc_state *old_state)
 {
@@ -4319,7 +4364,7 @@
 	WARN_ON(vp->event);
 
 	if (crtc->state->self_refresh_active) {
-		vop2_crtc_atomic_disable_for_psr(crtc, old_state);
+		vop2_crtc_atomic_enter_psr(crtc, old_state);
 		goto out;
 	}
 
@@ -4370,8 +4415,11 @@
 	if (vp->output_if & VOP_OUTPUT_IF_eDP0)
 		VOP_GRF_SET(vop2, grf, grf_edp0_en, 0);
 
-	if (vp->output_if & VOP_OUTPUT_IF_eDP1)
+	if (vp->output_if & VOP_OUTPUT_IF_eDP1) {
 		VOP_GRF_SET(vop2, grf, grf_edp1_en, 0);
+		if (dual_channel)
+			VOP_CTRL_SET(vop2, edp_dual_en, 0);
+	}
 
 	if (vp->output_if & VOP_OUTPUT_IF_HDMI0) {
 		VOP_GRF_SET(vop2, grf, grf_hdmi0_dsc_en, 0);
@@ -4381,7 +4429,15 @@
 	if (vp->output_if & VOP_OUTPUT_IF_HDMI1) {
 		VOP_GRF_SET(vop2, grf, grf_hdmi1_dsc_en, 0);
 		VOP_GRF_SET(vop2, grf, grf_hdmi1_en, 0);
+		if (dual_channel)
+			VOP_CTRL_SET(vop2, hdmi_dual_en, 0);
 	}
+
+	if ((vcstate->output_if & VOP_OUTPUT_IF_DP1) && dual_channel)
+		VOP_CTRL_SET(vop2, dp_dual_en, 0);
+
+	if ((vcstate->output_if & VOP_OUTPUT_IF_MIPI1) && dual_channel)
+		VOP_CTRL_SET(vop2, mipi_dual_en, 0);
 
 	VOP_MODULE_SET(vop2, vp, dual_channel_en, 0);
 	VOP_MODULE_SET(vop2, vp, dual_channel_swap, 0);
@@ -4548,6 +4604,107 @@
 	return ret;
 }
 
+/*
+ * 1. NV12/NV16/YUYV xoffset must aligned as 2 pixel;
+ * 2. NV12/NV15 yoffset must aligned as 2 pixel;
+ * 3. NV30 xoffset must aligned as 4 pixel;
+ * 4. NV15/NV20 xoffset must aligend as 8 pixel at rk3568/rk3588/rk3528/rk3562,
+ *    others must aligned as 4 pixel;
+ */
+static int vop2_linear_yuv_format_check(struct drm_plane *plane, struct drm_plane_state *state)
+{
+	struct vop2_plane_state *vpstate = to_vop2_plane_state(state);
+	struct drm_crtc *crtc = state->crtc;
+	struct vop2_video_port *vp = to_vop2_video_port(crtc);
+	struct vop2_win *win = to_vop2_win(plane);
+	struct drm_framebuffer *fb = state->fb;
+	struct drm_rect *src = &vpstate->src;
+	u32 val = 0;
+
+	if (vpstate->afbc_en || vpstate->tiled_en || !fb->format->is_yuv)
+		return 0;
+
+	switch (fb->format->format) {
+	case DRM_FORMAT_NV12:
+	case DRM_FORMAT_NV21:
+		val = src->x1 >> 16;
+		if (val % 2) {
+			src->x1 = ALIGN(val, 2) << 16;
+			DRM_WARN("VP%d %s src x offset[%d] must aligned as 2 pixel at NV12 fmt, and adjust to: %d\n", vp->id, win->name, val, src->x1 >> 16);
+		}
+		val = src->y1 >> 16;
+		if (val % 2) {
+			src->y1 = ALIGN(val, 2) << 16;
+			DRM_WARN("VP%d %s src y offset[%d] must aligned as 2 pixel at NV12 fmt, and adjust to: %d\n", vp->id, win->name, val, src->y1 >> 16);
+		}
+		break;
+	case DRM_FORMAT_NV15:
+		val = src->y1 >> 16;
+		if (val % 2) {
+			src->y1 = ALIGN(val, 2) << 16;
+			DRM_WARN("VP%d %s src y offset[%d] must aligned as 2 pixel at NV15 fmt, and adjust to: %d\n", vp->id, win->name, val, src->y1 >> 16);
+		}
+		if (vp->vop2->version == VOP_VERSION_RK3568 ||
+		    vp->vop2->version == VOP_VERSION_RK3588 ||
+		    vp->vop2->version == VOP_VERSION_RK3528 ||
+		    vp->vop2->version == VOP_VERSION_RK3562) {
+			val = src->x1 >> 16;
+			if (val % 8) {
+				src->x1 = ALIGN(val, 8) << 16;
+				DRM_WARN("VP%d %s src x offset[%d] must aligned as 8 pixel at NV15 fmt, and adjust to: %d\n", vp->id, win->name, val, src->x1 >> 16);
+			}
+		} else {
+			val = src->x1 >> 16;
+			if (val % 4) {
+				src->x1 = ALIGN(val, 4) << 16;
+				DRM_WARN("VP%d %s src x offset[%d] must aligned as 4 pixel at NV15 fmt, and adjust to: %d\n", vp->id, win->name, val, src->x1 >> 16);
+			}
+		}
+		break;
+	case DRM_FORMAT_NV16:
+	case DRM_FORMAT_NV61:
+	case DRM_FORMAT_YUYV:
+	case DRM_FORMAT_YVYU:
+	case DRM_FORMAT_VYUY:
+	case DRM_FORMAT_UYVY:
+		val = src->x1 >> 16;
+		if (val % 2) {
+			src->x1 = ALIGN(val, 2) << 16;
+			DRM_WARN("VP%d %s src x offset[%d] must aligned as 2 pixel at YUYV fmt, and adjust to: %d\n", vp->id, win->name, val, src->x1 >> 16);
+		}
+		break;
+	case DRM_FORMAT_NV20:
+		if (vp->vop2->version == VOP_VERSION_RK3568 ||
+		    vp->vop2->version == VOP_VERSION_RK3588 ||
+		    vp->vop2->version == VOP_VERSION_RK3528 ||
+		    vp->vop2->version == VOP_VERSION_RK3562) {
+			val = src->x1 >> 16;
+			if (val % 8) {
+				src->x1 = ALIGN(val, 8) << 16;
+				DRM_WARN("VP%d %s src x offset[%d] must aligned as 8 pixel at NV20 fmt, and adjust to: %d\n", vp->id, win->name, val, src->x1 >> 16);
+			}
+		} else {
+			val = src->x1 >> 16;
+			if (val % 4) {
+				src->x1 = ALIGN(val, 4) << 16;
+				DRM_WARN("VP%d %s src x offset[%d] must aligned as 4 pixel at NV20 fmt, and adjust to: %d\n", vp->id, win->name, val, src->x1 >> 16);
+			}
+		}
+		break;
+	case DRM_FORMAT_NV30:
+		val = src->x1 >> 16;
+		if (val % 4) {
+			src->x1 = ALIGN(val, 4) << 16;
+			DRM_WARN("VP%d %s src x offset[%d] must aligned as 4 pixel at NV30 fmt, and adjust to: %d\n", vp->id, win->name, val, src->x1 >> 16);
+		}
+		break;
+	default:
+		return 0;
+	}
+
+	return 0;
+}
+
 static int vop2_plane_atomic_check(struct drm_plane *plane, struct drm_plane_state *state)
 {
 	struct vop2_plane_state *vpstate = to_vop2_plane_state(state);
@@ -4712,14 +4869,8 @@
 			return ret;
 	}
 
-	/*
-	 * Src.x1 can be odd when do clip, but yuv plane start point
-	 * need align with 2 pixel.
-	 */
-	if (fb->format->is_yuv && ((state->src.x1 >> 16) % 2)) {
-		DRM_ERROR("Invalid Source: Yuv format not support odd xpos\n");
+	if (vop2_linear_yuv_format_check(plane, state))
 		return -EINVAL;
-	}
 
 	if (fb->format->char_per_block[0] == 0)
 		offset = ALIGN_DOWN(src->x1 >> 16, tile_size) * fb->format->cpp[0] * tile_size;
@@ -4773,6 +4924,9 @@
 {
 	struct vop2_win *win = to_vop2_win(plane);
 	struct vop2 *vop2 = win->vop2;
+	struct drm_crtc *crtc;
+	struct vop2_video_port *vp;
+
 #if defined(CONFIG_ROCKCHIP_DRM_DEBUG)
 	struct vop2_plane_state *vpstate = to_vop2_plane_state(plane->state);
 #endif
@@ -4785,9 +4939,15 @@
 
 	spin_lock(&vop2->reg_lock);
 
+	crtc = old_state->crtc;
+	vp = to_vop2_video_port(crtc);
+
 	vop2_win_disable(win, false);
-	if (win->splice_win)
+	vp->enabled_win_mask &= ~BIT(win->phys_id);
+	if (win->splice_win) {
 		vop2_win_disable(win->splice_win, false);
+		vp->enabled_win_mask &= ~BIT(win->splice_win->phys_id);
+	}
 
 #if defined(CONFIG_ROCKCHIP_DRM_DEBUG)
 	kfree(vpstate->planlist);
@@ -4957,7 +5117,7 @@
 	uint32_t actual_w, actual_h, dsp_w, dsp_h;
 	uint32_t dsp_stx, dsp_sty;
 	uint32_t act_info, dsp_info, dsp_st;
-	uint32_t format;
+	uint32_t format, check_size;
 	uint32_t afbc_format;
 	uint32_t rb_swap;
 	uint32_t uv_swap;
@@ -5010,7 +5170,8 @@
 		actual_w = dsp_w * actual_w / drm_rect_width(dst);
 	}
 	dsp_h = drm_rect_height(dst);
-	if (dst->y1 + dsp_h > adjusted_mode->crtc_vdisplay) {
+	check_size = adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE ? adjusted_mode->vdisplay : adjusted_mode->crtc_vdisplay;
+	if (dst->y1 + dsp_h > check_size) {
 		DRM_ERROR("vp%d %s dest->y1[%d] + dsp_h[%d] exceed mode vdisplay[%d]\n",
 			  vp->id, win->name, dst->y1, dsp_h, adjusted_mode->crtc_vdisplay);
 		dsp_h = adjusted_mode->crtc_vdisplay - dst->y1;
@@ -5025,20 +5186,33 @@
 	if (vop2->version == VOP_VERSION_RK3568) {
 		/*
 		 * This is workaround solution for IC design:
-		 * esmart can't support scale down when actual_w % 16 == 1.
+		 * esmart can't support scale down when actual_w % 16 == 1;
+		 * esmart can't support scale down when dsp_w % 2 == 1;
+		 * esmart actual_w should align as 4 pixel when is linear 10 bit yuv format;
+		 *
+		 * cluster actual_w should align as 4 pixel when enable afbc;
 		 */
-		if (!(win->feature & WIN_FEATURE_AFBDC)) {
+		if (!vop2_cluster_window(win)) {
 			if (actual_w > dsp_w && (actual_w & 0xf) == 1) {
-				DRM_WARN("vp%d %s act_w[%d] MODE 16 == 1\n", vp->id, win->name, actual_w);
+				DRM_WARN("vp%d %s act_w[%d] MODE 16 == 1 at scale down mode\n", vp->id, win->name, actual_w);
 				actual_w -= 1;
+			}
+			if (actual_w > dsp_w && (dsp_w & 0x1) == 1) {
+				DRM_WARN("vp%d %s dsp_w[%d] MODE 2 == 1 at scale down mode\n", vp->id, win->name, dsp_w);
+				dsp_w -= 1;
 			}
 		}
 
-		if (vpstate->afbc_en && actual_w % 4) {
-			DRM_ERROR("vp%d %s actual_w[%d] should align as 4 pixel when enable afbc\n",
-				  vp->id, win->name, actual_w);
+		if (vop2_cluster_window(win) && actual_w % 4) {
+			DRM_WARN("vp%d %s actual_w[%d] should align as 4 pixel when enable afbc\n",
+				 vp->id, win->name, actual_w);
 			actual_w = ALIGN_DOWN(actual_w, 4);
 		}
+	}
+
+	if (is_linear_10bit_yuv(fb->format->format) && actual_w & 0x3) {
+		DRM_WARN("vp%d %s actual_w[%d] should align as 4 pixel when is linear 10 bit yuv format\n", vp->id, win->name, actual_w);
+		actual_w = ALIGN_DOWN(actual_w, 4);
 	}
 
 	act_info = (actual_h - 1) << 16 | ((actual_w - 1) & 0xffff);
@@ -5198,6 +5372,7 @@
 	VOP_WIN_SET(vop2, win, dither_up, dither_up);
 
 	VOP_WIN_SET(vop2, win, enable, 1);
+	vp->enabled_win_mask |= BIT(win->phys_id);
 	if (vop2_cluster_window(win)) {
 		lb_mode = vop2_get_cluster_lb_mode(win, vpstate);
 		VOP_CLUSTER_SET(vop2, win, lb_mode, lb_mode);
@@ -5885,7 +6060,58 @@
 	spin_unlock_irqrestore(&vop2->irq_lock, flags);
 }
 
-static int vop2_crtc_loader_protect(struct drm_crtc *crtc, bool on)
+static int vop2_crtc_get_inital_acm_info(struct drm_crtc *crtc)
+{
+	struct vop2_video_port *vp = to_vop2_video_port(crtc);
+	struct vop2 *vop2 = vp->vop2;
+	struct post_acm *acm = &vp->acm_info;
+	s16 *lut_y;
+	s16 *lut_h;
+	s16 *lut_s;
+	u32 value;
+	int i;
+
+	value = readl(vop2->acm_regs + RK3528_ACM_CTRL);
+	acm->acm_enable = value & 0x1;
+	value = readl(vop2->acm_regs + RK3528_ACM_DELTA_RANGE);
+	acm->y_gain = value & 0x3ff;
+	acm->h_gain = (value >> 10) & 0x3ff;
+	acm->s_gain = (value >> 20) & 0x3ff;
+
+	lut_y = &acm->gain_lut_hy[0];
+	lut_h = &acm->gain_lut_hy[ACM_GAIN_LUT_HY_LENGTH];
+	lut_s = &acm->gain_lut_hy[ACM_GAIN_LUT_HY_LENGTH * 2];
+	for (i = 0; i < ACM_GAIN_LUT_HY_LENGTH; i++) {
+		value = readl(vop2->acm_regs + RK3528_ACM_YHS_DEL_HY_SEG0 + (i << 2));
+		lut_y[i] = value & 0xff;
+		lut_h[i] = (value >> 8) & 0xff;
+		lut_s[i] = (value >> 16) & 0xff;
+	}
+
+	lut_y = &acm->gain_lut_hs[0];
+	lut_h = &acm->gain_lut_hs[ACM_GAIN_LUT_HS_LENGTH];
+	lut_s = &acm->gain_lut_hs[ACM_GAIN_LUT_HS_LENGTH * 2];
+	for (i = 0; i < ACM_GAIN_LUT_HS_LENGTH; i++) {
+		value = readl(vop2->acm_regs + RK3528_ACM_YHS_DEL_HS_SEG0 + (i << 2));
+		lut_y[i] = value & 0xff;
+		lut_h[i] = (value >> 8) & 0xff;
+		lut_s[i] = (value >> 16) & 0xff;
+	}
+
+	lut_y = &acm->delta_lut_h[0];
+	lut_h = &acm->delta_lut_h[ACM_DELTA_LUT_H_LENGTH];
+	lut_s = &acm->delta_lut_h[ACM_DELTA_LUT_H_LENGTH * 2];
+	for (i = 0; i < ACM_DELTA_LUT_H_LENGTH; i++) {
+		value = readl(vop2->acm_regs + RK3528_ACM_YHS_DEL_HGAIN_SEG0 + (i << 2));
+		lut_y[i] = value & 0x3ff;
+		lut_h[i] = (value >> 12) & 0xff;
+		lut_s[i] = (value >> 20) & 0x3ff;
+	}
+
+	return 0;
+}
+
+static int vop2_crtc_loader_protect(struct drm_crtc *crtc, bool on, void *data)
 {
 	struct vop2_video_port *vp = to_vop2_video_port(crtc);
 	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state);
@@ -5916,6 +6142,7 @@
 					win->pd->vp_mask |= BIT(vp->id);
 				}
 
+				vp->enabled_win_mask |= BIT(win->phys_id);
 				crtc_state = drm_atomic_get_crtc_state(crtc->state->state, crtc);
 				mode = &crtc_state->adjusted_mode;
 				if (mode->hdisplay > VOP2_MAX_VP_OUTPUT_WIDTH)	{
@@ -5928,6 +6155,7 @@
 					splice_vp->win_mask |=  BIT(splice_win->phys_id);
 					splice_win->vp_mask = BIT(splice_vp->id);
 					vop2->active_vp_mask |= BIT(splice_vp->id);
+					vp->enabled_win_mask |= BIT(splice_win->phys_id);
 
 					if (splice_win->pd &&
 					    VOP_WIN_GET(vop2, splice_win, enable)) {
@@ -5949,6 +6177,12 @@
 				ext_pll->vp_mask |= BIT(vp->id);
 		}
 		drm_crtc_vblank_on(crtc);
+		if (is_vop3(vop2)) {
+			if (vp_data->feature & (VOP_FEATURE_POST_ACM))
+				vop2_crtc_get_inital_acm_info(crtc);
+			if (data && (vp_data->feature & VOP_FEATURE_POST_CSC))
+				memcpy(&vp->csc_info, data, sizeof(struct post_csc));
+		}
 		if (private->cubic_lut[vp->id].enable) {
 			dma_addr_t cubic_lut_mst;
 			struct loader_cubic_lut *cubic_lut = &private->cubic_lut[vp->id];
@@ -6359,9 +6593,9 @@
 
 	bandwidth = bandwidth * src_width / dst_width;
 	bandwidth = bandwidth * src_height / dst_height;
-	if (vskiplines == 2)
+	if (vskiplines == 2 && vpstate->afbc_en == 0)
 		bandwidth /= 2;
-	else if (vskiplines == 4)
+	else if (vskiplines == 4 && vpstate->afbc_en == 0)
 		bandwidth /= 4;
 
 	return bandwidth;
@@ -6455,9 +6689,12 @@
 
 		act_w = drm_rect_width(&pstate->src) >> 16;
 		act_h = drm_rect_height(&pstate->src) >> 16;
+		if (pstate->fb->format->is_yuv && (act_w >= 3840 || act_h >= 3840))
+			vop_bw_info->plane_num_4k++;
+
 		bpp = rockchip_drm_get_bpp(pstate->fb->format);
 
-		vop_bw_info->frame_bw_mbyte += act_w * act_h / 1000 * bpp / 8 * fps / 1000;
+		vop_bw_info->frame_bw_mbyte += act_w * act_h / 1000 * bpp / 8 * fps / 1000 / afbc_fac;
 	}
 
 	sort(pbandwidth, cnt, sizeof(pbandwidth[0]), vop2_bandwidth_cmp, NULL);
@@ -6595,12 +6832,9 @@
 	if (mode->flags & DRM_MODE_FLAG_DBLCLK || vcstate->output_if & VOP_OUTPUT_IF_BT656)
 		adj_mode->crtc_clock *= 2;
 
-	if (vp->mcu_timing.mcu_pix_total) {
-		if (vcstate->output_mode == ROCKCHIP_OUT_MODE_S888)
-			adj_mode->crtc_clock *= 3;
-		else if (vcstate->output_mode == ROCKCHIP_OUT_MODE_S888_DUMMY)
-			adj_mode->crtc_clock *= 4;
-	}
+	if (vp->mcu_timing.mcu_pix_total)
+		adj_mode->crtc_clock *= rockchip_drm_get_cycles_per_pixel(vcstate->bus_format) *
+					(vp->mcu_timing.mcu_pix_total + 1);
 
 	drm_connector_list_iter_begin(crtc->dev, &conn_iter);
 	drm_for_each_connector_iter(connector, &conn_iter) {
@@ -6619,37 +6853,37 @@
 	return true;
 }
 
-static void vop2_dither_setup(struct drm_crtc *crtc)
+static void vop2_dither_setup(struct rockchip_crtc_state *vcstate, struct drm_crtc *crtc)
 {
-	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state);
 	struct vop2_video_port *vp = to_vop2_video_port(crtc);
 	struct vop2 *vop2 = vp->vop2;
+	bool pre_dither_down_en = false;
 
 	switch (vcstate->bus_format) {
 	case MEDIA_BUS_FMT_RGB565_1X16:
 		VOP_MODULE_SET(vop2, vp, dither_down_en, 1);
 		VOP_MODULE_SET(vop2, vp, dither_down_mode, RGB888_TO_RGB565);
-		VOP_MODULE_SET(vop2, vp, pre_dither_down_en, 1);
+		pre_dither_down_en = true;
 		break;
 	case MEDIA_BUS_FMT_RGB666_1X18:
 	case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
 	case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:
 		VOP_MODULE_SET(vop2, vp, dither_down_en, 1);
 		VOP_MODULE_SET(vop2, vp, dither_down_mode, RGB888_TO_RGB666);
-		VOP_MODULE_SET(vop2, vp, pre_dither_down_en, 1);
+		pre_dither_down_en = true;
 		break;
 	case MEDIA_BUS_FMT_YUYV8_1X16:
 	case MEDIA_BUS_FMT_YUV8_1X24:
 	case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
 		VOP_MODULE_SET(vop2, vp, dither_down_en, 0);
-		VOP_MODULE_SET(vop2, vp, pre_dither_down_en, 1);
+		pre_dither_down_en = true;
 		break;
 	case MEDIA_BUS_FMT_YUYV10_1X20:
 	case MEDIA_BUS_FMT_YUV10_1X30:
 	case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
 	case MEDIA_BUS_FMT_RGB101010_1X30:
 		VOP_MODULE_SET(vop2, vp, dither_down_en, 0);
-		VOP_MODULE_SET(vop2, vp, pre_dither_down_en, 0);
+		pre_dither_down_en = false;
 		break;
 	case MEDIA_BUS_FMT_RGB888_3X8:
 	case MEDIA_BUS_FMT_RGB888_DUMMY_4X8:
@@ -6658,10 +6892,14 @@
 	case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA:
 	default:
 		VOP_MODULE_SET(vop2, vp, dither_down_en, 0);
-		VOP_MODULE_SET(vop2, vp, pre_dither_down_en, 1);
+		pre_dither_down_en = true;
 		break;
 	}
 
+	if (is_yuv_output(vcstate->bus_format))
+		pre_dither_down_en = false;
+
+	VOP_MODULE_SET(vop2, vp, pre_dither_down_en, pre_dither_down_en);
 	VOP_MODULE_SET(vop2, vp, dither_down_sel, DITHER_DOWN_ALLEGRO);
 }
 
@@ -7266,24 +7504,45 @@
 	dsc->enabled = true;
 }
 
+static inline bool vop2_mark_as_left_panel(struct rockchip_crtc_state *vcstate, u32 output_if)
+{
+	return vcstate->output_if_left_panel & output_if;
+}
+
 static void vop2_setup_dual_channel_if(struct drm_crtc *crtc)
 {
 	struct vop2_video_port *vp = to_vop2_video_port(crtc);
 	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state);
 	struct vop2 *vop2 = vp->vop2;
 
+	if (vcstate->output_flags & ROCKCHIP_OUTPUT_DUAL_CHANNEL_ODD_EVEN_MODE) {
+		VOP_CTRL_SET(vop2, lvds_dual_en, 1);
+		VOP_CTRL_SET(vop2, lvds_dual_mode, 0);
+		if (vcstate->output_flags & ROCKCHIP_OUTPUT_DATA_SWAP)
+			VOP_CTRL_SET(vop2, lvds_dual_channel_swap, 1);
+		return;
+	}
+
 	VOP_MODULE_SET(vop2, vp, dual_channel_en, 1);
 	if (vcstate->output_flags & ROCKCHIP_OUTPUT_DATA_SWAP)
 		VOP_MODULE_SET(vop2, vp, dual_channel_swap, 1);
 
-	if (vcstate->output_if & VOP_OUTPUT_IF_DP1)
+	if (vcstate->output_if & VOP_OUTPUT_IF_DP1 &&
+	    !vop2_mark_as_left_panel(vcstate, VOP_OUTPUT_IF_DP1))
 		VOP_CTRL_SET(vop2, dp_dual_en, 1);
-	else if (vcstate->output_if & VOP_OUTPUT_IF_eDP1)
+	else if (vcstate->output_if & VOP_OUTPUT_IF_eDP1 &&
+		 !vop2_mark_as_left_panel(vcstate, VOP_OUTPUT_IF_eDP1))
 		VOP_CTRL_SET(vop2, edp_dual_en, 1);
-	else if (vcstate->output_if & VOP_OUTPUT_IF_HDMI1)
+	else if (vcstate->output_if & VOP_OUTPUT_IF_HDMI1 &&
+		 !vop2_mark_as_left_panel(vcstate, VOP_OUTPUT_IF_HDMI1))
 		VOP_CTRL_SET(vop2, hdmi_dual_en, 1);
-	else if (vcstate->output_if & VOP_OUTPUT_IF_MIPI1)
+	else if (vcstate->output_if & VOP_OUTPUT_IF_MIPI1 &&
+		 !vop2_mark_as_left_panel(vcstate, VOP_OUTPUT_IF_MIPI1))
 		VOP_CTRL_SET(vop2, mipi_dual_en, 1);
+	else if (vcstate->output_if & VOP_OUTPUT_IF_LVDS1) {
+		VOP_CTRL_SET(vop2, lvds_dual_en, 1);
+		VOP_CTRL_SET(vop2, lvds_dual_mode, 1);
+	}
 }
 
 /*
@@ -7443,14 +7702,6 @@
 	}
 }
 
-static int vop2_get_vrefresh(struct vop2_video_port *vp, const struct drm_display_mode *mode)
-{
-	if (vp->mcu_timing.mcu_pix_total)
-		return drm_mode_vrefresh(mode) / vp->mcu_timing.mcu_pix_total;
-	else
-		return drm_mode_vrefresh(mode);
-}
-
 static void vop2_crtc_atomic_enable(struct drm_crtc *crtc, struct drm_crtc_state *old_state)
 {
 	struct vop2_video_port *vp = to_vop2_video_port(crtc);
@@ -7485,10 +7736,7 @@
 	int ret;
 
 	if (old_state && old_state->self_refresh_active) {
-		drm_crtc_vblank_on(crtc);
-		if (vop2->aclk_rate_reset)
-			clk_set_rate(vop2->aclk, vop2->aclk_rate);
-		vop2->aclk_rate_reset = false;
+		vop2_crtc_atomic_exit_psr(crtc, old_state);
 
 		return;
 	}
@@ -7497,9 +7745,10 @@
 	vop2_set_system_status(vop2);
 
 	vop2_lock(vop2);
-	DRM_DEV_INFO(vop2->dev, "Update mode to %dx%d%s%d, type: %d(if:%x) for vp%d dclk: %d\n",
+	DRM_DEV_INFO(vop2->dev, "Update mode to %dx%d%s%d, type: %d(if:%x, flag:0x%x) for vp%d dclk: %d\n",
 		     hdisplay, adjusted_mode->vdisplay, interlaced ? "i" : "p",
-		     vop2_get_vrefresh(vp, adjusted_mode), vcstate->output_type, vcstate->output_if,
+		     drm_mode_vrefresh(adjusted_mode),
+		     vcstate->output_type, vcstate->output_if, vcstate->output_flags,
 		     vp->id, adjusted_mode->crtc_clock * 1000);
 
 	if (adjusted_mode->hdisplay > VOP2_MAX_VP_OUTPUT_WIDTH) {
@@ -7510,6 +7759,9 @@
 		splice_en = 1;
 		vop2->active_vp_mask |= BIT(splice_vp->id);
 	}
+
+	if (vcstate->output_flags & ROCKCHIP_OUTPUT_DUAL_CONNECTOR_SPLIT_MODE)
+		vcstate->output_flags |= ROCKCHIP_OUTPUT_DUAL_CHANNEL_LEFT_RIGHT_MODE;
 
 	if (vcstate->dsc_enable) {
 		int k = 1;
@@ -7600,15 +7852,6 @@
 		VOP_CTRL_SET(vop2, lvds_dclk_pol, dclk_inv);
 	}
 
-	if (vcstate->output_flags & (ROCKCHIP_OUTPUT_DUAL_CHANNEL_ODD_EVEN_MODE |
-	    ROCKCHIP_OUTPUT_DUAL_CHANNEL_LEFT_RIGHT_MODE)) {
-		VOP_CTRL_SET(vop2, lvds_dual_en, 1);
-		if (vcstate->output_flags & ROCKCHIP_OUTPUT_DUAL_CHANNEL_LEFT_RIGHT_MODE)
-			VOP_CTRL_SET(vop2, lvds_dual_mode, 1);
-		if (vcstate->output_flags & ROCKCHIP_OUTPUT_DATA_SWAP)
-			VOP_CTRL_SET(vop2, lvds_dual_channel_swap, 1);
-	}
-
 	if (vcstate->output_if & VOP_OUTPUT_IF_MIPI0) {
 		ret = vop2_calc_cru_cfg(crtc, VOP_OUTPUT_IF_MIPI0, &if_pixclk, &if_dclk);
 		if (ret < 0)
@@ -7652,7 +7895,8 @@
 		}
 	}
 
-	if (vcstate->output_flags & ROCKCHIP_OUTPUT_DUAL_CHANNEL_LEFT_RIGHT_MODE)
+	if (vcstate->output_flags & ROCKCHIP_OUTPUT_DUAL_CHANNEL_LEFT_RIGHT_MODE ||
+	    vcstate->output_flags & ROCKCHIP_OUTPUT_DUAL_CHANNEL_ODD_EVEN_MODE)
 		vop2_setup_dual_channel_if(crtc);
 
 	if (vcstate->output_if & VOP_OUTPUT_IF_eDP0) {
@@ -7910,8 +8154,11 @@
 	/*
 	 * restore the lut table.
 	 */
-	if (vp->gamma_lut_active)
+	if (vp->gamma_lut_active) {
 		vop2_crtc_load_lut(crtc);
+		vop2_cfg_done(crtc);
+		vop2_wait_for_fs_by_done_bit_status(vp);
+	}
 out:
 	vop2_unlock(vop2);
 }
@@ -9515,18 +9762,25 @@
 	u32 value;
 	int i;
 
-	if (!acm) {
-		writel(0x2, vop2->acm_regs + RK3528_ACM_CTRL);
-		VOP_MODULE_SET(vop2, vp, acm_bypass_en, 1);
+	writel(0, vop2->acm_regs + RK3528_ACM_CTRL);
+	VOP_MODULE_SET(vop2, vp, acm_bypass_en, 0);
+
+	if (!acm || !acm->acm_enable)
 		return;
-	}
 
-	writel(1, vop2->acm_regs + RK3528_ACM_FETCH_START);
+	/*
+	 * If acm update parameters, it need disable acm in the first frame,
+	 * then update parameters and enable acm in second frame.
+	 */
+	vop2_cfg_done(crtc);
+	readx_poll_timeout(readl, vop2->acm_regs + RK3528_ACM_CTRL, value, !value, 200, 50000);
 
-	value = (acm->acm_enable & 0x1) + ((adjusted_mode->hdisplay & 0xfff) << 8) +
+	value = RK3528_ACM_ENABLE + ((adjusted_mode->hdisplay & 0xfff) << 8) +
 		((adjusted_mode->vdisplay & 0xfff) << 20);
 	writel(value, vop2->acm_regs + RK3528_ACM_CTRL);
-	VOP_MODULE_SET(vop2, vp, acm_bypass_en, acm->acm_enable ? 0 : 1);
+
+
+	writel(1, vop2->acm_regs + RK3528_ACM_FETCH_START);
 
 	value = (acm->y_gain & 0x3ff) + ((acm->h_gain << 10) & 0xffc00) +
 		((acm->s_gain << 20) & 0x3ff00000);
@@ -9565,14 +9819,23 @@
 static void vop3_post_config(struct drm_crtc *crtc)
 {
 	struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state);
+	struct vop2_video_port *vp = to_vop2_video_port(crtc);
 	struct post_acm *acm;
 	struct post_csc *csc;
 
-	acm = vcstate->acm_lut_data ? (struct post_acm *)vcstate->acm_lut_data->data : NULL;
-	vop3_post_acm_config(crtc, acm);
-
 	csc = vcstate->post_csc_data ? (struct post_csc *)vcstate->post_csc_data->data : NULL;
-	vop3_post_csc_config(crtc, acm, csc);
+	if (csc && memcmp(&vp->csc_info, csc, sizeof(struct post_csc)))
+		memcpy(&vp->csc_info, csc, sizeof(struct post_csc));
+	vop3_post_csc_config(crtc, &vp->acm_info, &vp->csc_info);
+
+	acm = vcstate->acm_lut_data ? (struct post_acm *)vcstate->acm_lut_data->data : NULL;
+
+	if (acm && memcmp(&vp->acm_info, acm, sizeof(struct post_acm))) {
+		memcpy(&vp->acm_info, acm, sizeof(struct post_acm));
+		vop3_post_acm_config(crtc, &vp->acm_info);
+	} else if (crtc->state->active_changed) {
+		vop3_post_acm_config(crtc, &vp->acm_info);
+	}
 }
 
 static void vop2_cfg_update(struct drm_crtc *crtc,
@@ -9600,7 +9863,9 @@
 
 	vop2_post_color_swap(crtc);
 
-	vop2_dither_setup(crtc);
+	vop2_dither_setup(vcstate, crtc);
+	if (vcstate->splice_mode)
+		vop2_dither_setup(vcstate, &splice_vp->rockchip_crtc.crtc);
 
 	VOP_MODULE_SET(vop2, vp, overlay_mode, vcstate->yuv_overlay);
 
@@ -9633,10 +9898,10 @@
 	if (vp_data->feature & VOP_FEATURE_OVERSCAN)
 		vop2_post_config(crtc);
 
+	spin_unlock(&vop2->reg_lock);
+
 	if (vp_data->feature & (VOP_FEATURE_POST_ACM | VOP_FEATURE_POST_CSC))
 		vop3_post_config(crtc);
-
-	spin_unlock(&vop2->reg_lock);
 }
 
 static void vop2_sleep_scan_line_time(struct vop2_video_port *vp, int scan_line)
@@ -9755,13 +10020,11 @@
 				vp->gamma_lut = crtc->state->gamma_lut->data;
 			vop2_crtc_atomic_gamma_set(crtc, crtc->state);
 		}
-#if defined(CONFIG_ROCKCHIP_DRM_CUBIC_LUT)
-		if (crtc->state->cubic_lut || vp->cubic_lut) {
-			if (crtc->state->cubic_lut)
-				vp->cubic_lut = crtc->state->cubic_lut->data;
+		if (vcstate->cubic_lut_data || vp->cubic_lut) {
+			if (vcstate->cubic_lut_data)
+				vp->cubic_lut = vcstate->cubic_lut_data->data;
 			vop2_crtc_atomic_cubic_lut_set(crtc, crtc->state);
 		}
-#endif
 	} else {
 		VOP_MODULE_SET(vop2, vp, cubic_lut_update_en, 0);
 	}
@@ -9874,6 +10137,8 @@
 		drm_property_blob_get(vcstate->acm_lut_data);
 	if (vcstate->post_csc_data)
 		drm_property_blob_get(vcstate->post_csc_data);
+	if (vcstate->cubic_lut_data)
+		drm_property_blob_get(vcstate->cubic_lut_data);
 
 	__drm_atomic_helper_crtc_duplicate_state(crtc, &vcstate->base);
 	return &vcstate->base;
@@ -9888,6 +10153,7 @@
 	drm_property_blob_put(vcstate->hdr_ext_data);
 	drm_property_blob_put(vcstate->acm_lut_data);
 	drm_property_blob_put(vcstate->post_csc_data);
+	drm_property_blob_put(vcstate->cubic_lut_data);
 	kfree(vcstate);
 }
 
@@ -10034,6 +10300,11 @@
 		return 0;
 	}
 
+	if (property == private->cubic_lut_prop) {
+		*val = (vcstate->cubic_lut_data) ? vcstate->cubic_lut_data->base.id : 0;
+		return 0;
+	}
+
 	DRM_ERROR("failed to get vop2 crtc property: %s\n", property->name);
 
 	return -EINVAL;
@@ -10156,6 +10427,16 @@
 								val,
 								sizeof(struct post_csc), -1,
 								&replaced);
+		return ret;
+	}
+
+	if (property == private->cubic_lut_prop) {
+		ret = vop2_atomic_replace_property_blob_from_id(drm_dev,
+								&vcstate->cubic_lut_data,
+								val,
+								-1, sizeof(struct drm_color_lut),
+								&replaced);
+		state->color_mgmt_changed |= replaced;
 		return ret;
 	}
 
@@ -10790,6 +11071,7 @@
 		{ ROCKCHIP_DRM_CRTC_FEATURE_ALPHA_SCALE, "ALPHA_SCALE" },
 		{ ROCKCHIP_DRM_CRTC_FEATURE_HDR10, "HDR10" },
 		{ ROCKCHIP_DRM_CRTC_FEATURE_NEXT_HDR, "NEXT_HDR" },
+		{ ROCKCHIP_DRM_CRTC_FEATURE_VIVID_HDR, "VIVID_HDR" },
 	};
 
 	if (vp_data->feature & VOP_FEATURE_ALPHA_SCALE)
@@ -10798,6 +11080,8 @@
 		feature |= BIT(ROCKCHIP_DRM_CRTC_FEATURE_HDR10);
 	if (vp_data->feature & VOP_FEATURE_NEXT_HDR)
 		feature |= BIT(ROCKCHIP_DRM_CRTC_FEATURE_NEXT_HDR);
+	if (vp_data->feature & VOP_FEATURE_VIVID_HDR)
+		feature |= BIT(ROCKCHIP_DRM_CRTC_FEATURE_VIVID_HDR);
 
 	prop = drm_property_create_bitmask(vop2->drm_dev,
 					   DRM_MODE_PROP_IMMUTABLE, "FEATURE",
diff --git a/kernel/drivers/gpu/drm/rockchip/rockchip_lvds.c b/kernel/drivers/gpu/drm/rockchip/rockchip_lvds.c
index d4bd2fb..c4d74e1 100644
--- a/kernel/drivers/gpu/drm/rockchip/rockchip_lvds.c
+++ b/kernel/drivers/gpu/drm/rockchip/rockchip_lvds.c
@@ -83,10 +83,24 @@
 enum lvds_format {
 	LVDS_8BIT_MODE_FORMAT_1,
 	LVDS_8BIT_MODE_FORMAT_2,
-	LVDS_8BIT_MODE_FORMAT_3,
-	LVDS_6BIT_MODE,
+	LVDS_6BIT_MODE_FORMAT_1,
+	LVDS_6BIT_MODE_FORMAT_2,
 	LVDS_10BIT_MODE_FORMAT_1,
 	LVDS_10BIT_MODE_FORMAT_2,
+};
+
+enum rockchip_lvds_dual_link_pixels {
+	ROCKCHIP_LVDS_DUAL_LINK_EVEN_ODD_PIXELS = 0,
+	ROCKCHIP_LVDS_DUAL_LINK_ODD_EVEN_PIXELS = 1,
+	ROCKCHIP_LVDS_DUAL_LINK_LEFT_RIGHT_PIXELS = 2,
+	ROCKCHIP_LVDS_DUAL_LINK_RIGHT_LEFT_PIXELS = 3,
+};
+
+enum rockchip_of_lvds_pixels {
+	ROCKCHIP_OF_LVDS_EVEN = BIT(0),
+	ROCKCHIP_OF_LVDS_ODD = BIT(1),
+	ROCKCHIP_OF_LVDS_LEFT = BIT(2),
+	ROCKCHIP_OF_LVDS_RIGHT = BIT(3),
 };
 
 struct rockchip_lvds;
@@ -107,7 +121,7 @@
 	bool data_swap;
 	bool dual_channel;
 	bool phy_enabled;
-	enum drm_lvds_dual_link_pixels pixel_order;
+	enum rockchip_lvds_dual_link_pixels pixel_order;
 
 	struct rockchip_lvds *primary;
 	struct rockchip_lvds *secondary;
@@ -119,6 +133,99 @@
 	struct drm_display_mode mode;
 	struct rockchip_drm_sub_dev sub_dev;
 };
+
+static int rockchip_of_lvds_get_port_pixels_type(struct device_node *port_node)
+{
+	bool even_pixels =
+		of_property_read_bool(port_node, "dual-lvds-even-pixels");
+	bool odd_pixels =
+		of_property_read_bool(port_node, "dual-lvds-odd-pixels");
+	bool left_pixels =
+		of_property_read_bool(port_node, "dual-lvds-left-pixels");
+	bool right_pixels =
+		of_property_read_bool(port_node, "dual-lvds-right-pixels");
+
+	return (even_pixels ? ROCKCHIP_OF_LVDS_EVEN : 0) |
+	       (odd_pixels ? ROCKCHIP_OF_LVDS_ODD : 0) |
+	       (left_pixels ? ROCKCHIP_OF_LVDS_LEFT : 0) |
+	       (right_pixels ? ROCKCHIP_OF_LVDS_RIGHT : 0);
+}
+
+static int rockchip_of_lvds_get_remote_pixels_type(
+			const struct device_node *port_node)
+{
+	struct device_node *endpoint = NULL;
+	int pixels_type = -EPIPE;
+
+	for_each_child_of_node(port_node, endpoint) {
+		struct device_node *remote_port;
+		int current_pt;
+
+		if (!of_node_name_eq(endpoint, "endpoint"))
+			continue;
+
+		remote_port = of_graph_get_remote_port(endpoint);
+		if (!remote_port) {
+			of_node_put(endpoint);
+			return -EPIPE;
+		}
+
+		current_pt = rockchip_of_lvds_get_port_pixels_type(remote_port);
+		of_node_put(remote_port);
+		if (pixels_type < 0)
+			pixels_type = current_pt;
+
+		/*
+		 * Sanity check, ensure that all remote endpoints have the same
+		 * pixel type. We may lift this restriction later if we need to
+		 * support multiple sinks with different dual-link
+		 * configurations by passing the endpoints explicitly to
+		 * rockchip_of_lvds_get_dual_link_pixel_order().
+		 */
+		if (!current_pt || pixels_type != current_pt) {
+			of_node_put(endpoint);
+			return -EINVAL;
+		}
+	}
+
+	return pixels_type;
+}
+
+static int rockchip_of_lvds_get_dual_link_pixel_order(const struct device_node *port1,
+						      const struct device_node *port2)
+{
+	int remote_p1_pt, remote_p2_pt;
+
+	if (!port1 || !port2)
+		return -EINVAL;
+
+	remote_p1_pt = rockchip_of_lvds_get_remote_pixels_type(port1);
+	if (remote_p1_pt < 0)
+		return remote_p1_pt;
+
+	remote_p2_pt = rockchip_of_lvds_get_remote_pixels_type(port2);
+	if (remote_p2_pt < 0)
+		return remote_p2_pt;
+
+	/*
+	 * A valid dual-lVDS bus is found when one remote port is marked with
+	 * "dual-lvds-even-pixels" or "dual-lvds-left-pixels", and the other
+	 * remote port is marked with "dual-lvds-odd-pixels"or
+	 * "dual-lvds-right-pixels", bail out if the markers are not right.
+	 */
+	if ((remote_p1_pt + remote_p2_pt != ROCKCHIP_OF_LVDS_EVEN + ROCKCHIP_OF_LVDS_ODD) &&
+	    (remote_p1_pt + remote_p2_pt != ROCKCHIP_OF_LVDS_LEFT + ROCKCHIP_OF_LVDS_RIGHT))
+		return -EINVAL;
+
+	if (remote_p1_pt == ROCKCHIP_OF_LVDS_EVEN)
+		return ROCKCHIP_LVDS_DUAL_LINK_EVEN_ODD_PIXELS;
+	else if (remote_p1_pt == ROCKCHIP_OF_LVDS_ODD)
+		return ROCKCHIP_LVDS_DUAL_LINK_ODD_EVEN_PIXELS;
+	else if (remote_p1_pt == ROCKCHIP_OF_LVDS_LEFT)
+		return ROCKCHIP_LVDS_DUAL_LINK_LEFT_RIGHT_PIXELS;
+	else
+		return ROCKCHIP_LVDS_DUAL_LINK_RIGHT_LEFT_PIXELS;
+}
 
 static inline struct rockchip_lvds *connector_to_lvds(struct drm_connector *c)
 {
@@ -190,8 +297,8 @@
 	case MEDIA_BUS_FMT_RGB101010_1X7X5_JEIDA: /* jeida-30 */
 		lvds->format = LVDS_10BIT_MODE_FORMAT_2;
 		break;
-	case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:	/* vesa-18 */
-		lvds->format = LVDS_8BIT_MODE_FORMAT_3;
+	case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:	/* jeida-18, compatible with the [JEIDA], [LDI] and [VESA] specifications */
+		lvds->format = LVDS_6BIT_MODE_FORMAT_1;
 		break;
 	case MEDIA_BUS_FMT_RGB101010_1X7X5_SPWG: /* vesa-30 */
 		lvds->format = LVDS_10BIT_MODE_FORMAT_1;
@@ -236,29 +343,24 @@
 	s->color_space = V4L2_COLORSPACE_DEFAULT;
 
 	switch (lvds->pixel_order) {
-	case DRM_LVDS_DUAL_LINK_ODD_EVEN_PIXELS:
+	case ROCKCHIP_LVDS_DUAL_LINK_ODD_EVEN_PIXELS:
 		s->output_flags |= ROCKCHIP_OUTPUT_DUAL_CHANNEL_ODD_EVEN_MODE;
 		s->output_if |= VOP_OUTPUT_IF_LVDS1 | VOP_OUTPUT_IF_LVDS0;
 		break;
-	case DRM_LVDS_DUAL_LINK_EVEN_ODD_PIXELS:
+	case ROCKCHIP_LVDS_DUAL_LINK_EVEN_ODD_PIXELS:
 		s->output_flags |= ROCKCHIP_OUTPUT_DUAL_CHANNEL_ODD_EVEN_MODE;
 		s->output_flags |= ROCKCHIP_OUTPUT_DATA_SWAP;
 		s->output_if |= VOP_OUTPUT_IF_LVDS1 | VOP_OUTPUT_IF_LVDS0;
 		break;
-/*
- * Fix me: To do it with a GKI compatible version.
- */
-#if 0
-	case DRM_LVDS_DUAL_LINK_LEFT_RIGHT_PIXELS:
+	case ROCKCHIP_LVDS_DUAL_LINK_LEFT_RIGHT_PIXELS:
 		s->output_flags |= ROCKCHIP_OUTPUT_DUAL_CHANNEL_LEFT_RIGHT_MODE;
 		s->output_if |= VOP_OUTPUT_IF_LVDS1 | VOP_OUTPUT_IF_LVDS0;
 		break;
-	case DRM_LVDS_DUAL_LINK_RIGHT_LEFT_PIXELS:
+	case ROCKCHIP_LVDS_DUAL_LINK_RIGHT_LEFT_PIXELS:
 		s->output_flags |= ROCKCHIP_OUTPUT_DUAL_CHANNEL_LEFT_RIGHT_MODE;
 		s->output_flags |= ROCKCHIP_OUTPUT_DATA_SWAP;
 		s->output_if |= VOP_OUTPUT_IF_LVDS1 | VOP_OUTPUT_IF_LVDS0;
 		break;
-#endif
 	default:
 		if (lvds->id)
 			s->output_if |= VOP_OUTPUT_IF_LVDS1;
@@ -646,7 +748,7 @@
 	.disable = rk3368_lvds_disable,
 };
 
-static int __maybe_unused rockchip_secondary_lvds_probe(struct rockchip_lvds *lvds)
+static int rk3568_lvds_probe(struct rockchip_lvds *lvds)
 {
 	if (lvds->dual_channel) {
 		struct rockchip_lvds *secondary = NULL;
@@ -659,7 +761,7 @@
 
 		port0 = of_graph_get_port_by_id(lvds->dev->of_node, 1);
 		port1 = of_graph_get_port_by_id(secondary->dev->of_node, 1);
-		pixel_order = drm_of_lvds_get_dual_link_pixel_order(port0, port1);
+		pixel_order = rockchip_of_lvds_get_dual_link_pixel_order(port0, port1);
 		of_node_put(port1);
 		of_node_put(port0);
 
@@ -692,19 +794,37 @@
 
 static void rk3568_lvds_enable(struct rockchip_lvds *lvds)
 {
-	regmap_write(lvds->grf, RK3568_GRF_VO_CON2,
-		     RK3568_LVDS0_MODE_EN(1) | RK3568_LVDS0_P2S_EN(1) |
-		     RK3568_LVDS0_DCLK_INV_SEL(1));
-	regmap_write(lvds->grf, RK3568_GRF_VO_CON0,
-		     RK3568_LVDS0_SELECT(lvds->format) | RK3568_LVDS0_MSBSEL(1));
+	if (lvds->id) {
+		regmap_write(lvds->grf, RK3568_GRF_VO_CON3,
+			     RK3568_LVDS1_MODE_EN(1) |
+			     RK3568_LVDS1_P2S_EN(1) |
+			     RK3568_LVDS1_DCLK_INV_SEL(1));
+		regmap_write(lvds->grf, RK3568_GRF_VO_CON0,
+			     RK3568_LVDS1_SELECT(lvds->format) |
+			     RK3568_LVDS1_MSBSEL(1));
+	} else {
+		regmap_write(lvds->grf, RK3568_GRF_VO_CON2,
+			     RK3568_LVDS0_MODE_EN(1) |
+			     RK3568_LVDS0_P2S_EN(1) |
+			     RK3568_LVDS0_DCLK_INV_SEL(1));
+		regmap_write(lvds->grf, RK3568_GRF_VO_CON0,
+			     RK3568_LVDS0_SELECT(lvds->format) |
+			     RK3568_LVDS0_MSBSEL(1));
+	}
 }
 
 static void rk3568_lvds_disable(struct rockchip_lvds *lvds)
 {
-	regmap_write(lvds->grf, RK3568_GRF_VO_CON2, RK3568_LVDS0_MODE_EN(0));
+	if (lvds->id)
+		regmap_write(lvds->grf, RK3568_GRF_VO_CON3,
+			     RK3568_LVDS1_MODE_EN(0));
+	else
+		regmap_write(lvds->grf, RK3568_GRF_VO_CON2,
+			     RK3568_LVDS0_MODE_EN(0));
 }
 
 static const struct rockchip_lvds_funcs rk3568_lvds_funcs = {
+	.probe = rk3568_lvds_probe,
 	.enable = rk3568_lvds_enable,
 	.disable = rk3568_lvds_disable,
 };
diff --git a/kernel/drivers/gpu/drm/rockchip/rockchip_post_csc.c b/kernel/drivers/gpu/drm/rockchip/rockchip_post_csc.c
index 7160be2..212a4b4 100644
--- a/kernel/drivers/gpu/drm/rockchip/rockchip_post_csc.c
+++ b/kernel/drivers/gpu/drm/rockchip/rockchip_post_csc.c
@@ -5,7 +5,6 @@
  */
 
 #include "rockchip_post_csc.h"
-#include "rockchip_drm_drv.h"
 
 #define PQ_CSC_HUE_TABLE_NUM			256
 #define PQ_CSC_MODE_COEF_COMMENT_LEN		32
diff --git a/kernel/drivers/gpu/drm/rockchip/rockchip_post_csc.h b/kernel/drivers/gpu/drm/rockchip/rockchip_post_csc.h
index 6c96211..1215a5c 100644
--- a/kernel/drivers/gpu/drm/rockchip/rockchip_post_csc.h
+++ b/kernel/drivers/gpu/drm/rockchip/rockchip_post_csc.h
@@ -9,6 +9,7 @@
 #define _ROCKCHIP_POST_CSC_H
 
 #include <drm/drm_crtc.h>
+#include "rockchip_drm_drv.h"
 #include "rockchip_drm_vop.h"
 
 int rockchip_calc_post_csc(struct post_csc *csc, struct post_csc_coef *csc_coef,
diff --git a/kernel/drivers/gpu/drm/rockchip/rockchip_rgb.c b/kernel/drivers/gpu/drm/rockchip/rockchip_rgb.c
index 1ba2a8d..a7f2057 100644
--- a/kernel/drivers/gpu/drm/rockchip/rockchip_rgb.c
+++ b/kernel/drivers/gpu/drm/rockchip/rockchip_rgb.c
@@ -68,7 +68,8 @@
 };
 
 struct rockchip_rgb_data {
-	u32 max_dclk_rate;
+	u32 rgb_max_dclk_rate;
+	u32 mcu_max_dclk_rate;
 	const struct rockchip_rgb_funcs *funcs;
 };
 
@@ -129,7 +130,9 @@
 struct rockchip_rgb {
 	u8 id;
 	u32 max_dclk_rate;
+	u32 mcu_pix_total;
 	struct device *dev;
+	struct device_node *np_mcu_panel;
 	struct drm_panel *panel;
 	struct drm_bridge *bridge;
 	struct drm_connector connector;
@@ -137,7 +140,6 @@
 	struct phy *phy;
 	struct regmap *grf;
 	bool data_sync_bypass;
-	bool is_mcu_panel;
 	bool phy_enabled;
 	const struct rockchip_rgb_funcs *funcs;
 	struct rockchip_drm_sub_dev sub_dev;
@@ -277,6 +279,15 @@
 		s->output_mode = ROCKCHIP_OUT_MODE_P565;
 		s->output_if = VOP_OUTPUT_IF_RGB;
 		break;
+	case MEDIA_BUS_FMT_RGB565_2X8_LE:
+	case MEDIA_BUS_FMT_BGR565_2X8_LE:
+		s->output_mode = ROCKCHIP_OUT_MODE_S565;
+		s->output_if = VOP_OUTPUT_IF_RGB;
+		break;
+	case MEDIA_BUS_FMT_RGB666_3X6:
+		s->output_mode = ROCKCHIP_OUT_MODE_S666;
+		s->output_if = VOP_OUTPUT_IF_RGB;
+		break;
 	case MEDIA_BUS_FMT_RGB888_3X8:
 	case MEDIA_BUS_FMT_BGR888_3X8:
 		s->output_mode = ROCKCHIP_OUT_MODE_S888;
@@ -323,7 +334,7 @@
 {
 	struct rockchip_rgb *rgb = encoder_to_rgb(encoder);
 
-	if (rgb->is_mcu_panel) {
+	if (rgb->np_mcu_panel) {
 		struct rockchip_mcu_panel *mcu_panel = to_rockchip_mcu_panel(rgb->panel);
 
 		mcu_panel->prepared = true;
@@ -358,11 +369,22 @@
 {
 	struct rockchip_rgb *rgb = encoder_to_rgb(encoder);
 	struct device *dev = rgb->dev;
+	struct drm_display_info *info = &rgb->connector.display_info;
 	u32 request_clock = mode->clock;
 	u32 max_clock = rgb->max_dclk_rate;
+	u32 bus_format;
+
+	if (info->num_bus_formats)
+		bus_format = info->bus_formats[0];
+	else
+		bus_format = MEDIA_BUS_FMT_RGB888_1X24;
 
 	if (mode->flags & DRM_MODE_FLAG_DBLCLK)
 		request_clock *= 2;
+
+	if (rgb->np_mcu_panel)
+		request_clock *= rockchip_drm_get_cycles_per_pixel(bus_format) *
+				 (rgb->mcu_pix_total + 1);
 
 	if (max_clock != 0 && request_clock > max_clock) {
 		DRM_DEV_ERROR(dev, "mode [%dx%d] clock %d is higher than max_clock %d\n",
@@ -444,16 +466,18 @@
 	return 0;
 }
 
-static int rockchip_mcu_panel_init(struct rockchip_rgb *rgb, struct device_node *np_mcu_panel)
+static int rockchip_mcu_panel_init(struct rockchip_rgb *rgb)
 {
 	struct device *dev = rgb->dev;
-	struct device_node *port, *endpoint, *np_crtc, *remote;
+	struct device_node *np_mcu_panel = rgb->np_mcu_panel;
+	struct device_node *port, *endpoint, *np_crtc, *remote, *np_mcu_timing;
 	struct rockchip_mcu_panel *mcu_panel = to_rockchip_mcu_panel(rgb->panel);
 	struct drm_display_mode *mode;
 	const void *data;
 	int len;
 	int ret;
 	u32 bus_flags;
+	u32 val;
 
 	mcu_panel->enable_gpio = devm_fwnode_gpiod_get_index(dev, &np_mcu_panel->fwnode,
 							     "enable", 0, GPIOD_ASIS,
@@ -544,6 +568,8 @@
 				if (remote) {
 					np_crtc = of_get_next_parent(remote);
 					mcu_panel->np_crtc = np_crtc;
+
+					of_node_put(np_crtc);
 					break;
 				}
 			}
@@ -553,6 +579,31 @@
 			DRM_DEV_ERROR(dev, "failed to find available crtc for mcu panel\n");
 			return -EINVAL;
 		}
+
+		np_mcu_timing = of_get_child_by_name(mcu_panel->np_crtc, "mcu-timing");
+		if (!np_mcu_timing) {
+			np_crtc = of_get_parent(mcu_panel->np_crtc);
+			if (np_crtc)
+				np_mcu_timing = of_get_child_by_name(np_crtc, "mcu-timing");
+
+			if (!np_mcu_timing) {
+				DRM_DEV_ERROR(dev, "failed to find timing config for mcu panel\n");
+				of_node_put(np_crtc);
+				return -EINVAL;
+			}
+
+			of_node_put(np_crtc);
+		}
+
+		ret = of_property_read_u32(np_mcu_timing, "mcu-pix-total", &val);
+		if (ret || val == 0) {
+			DRM_DEV_ERROR(dev, "failed to parse mcu_pix_total config\n");
+			of_node_put(np_mcu_timing);
+			return -EINVAL;
+		}
+		rgb->mcu_pix_total = val;
+
+		of_node_put(np_mcu_timing);
 	}
 
 	return 0;
@@ -741,9 +792,10 @@
 	.get_modes = rockchip_mcu_panel_get_modes,
 };
 
-static struct backlight_device *rockchip_mcu_panel_find_backlight(struct device_node *np_mcu_panel)
+static struct backlight_device *rockchip_mcu_panel_find_backlight(struct rockchip_rgb *rgb)
 {
 	struct backlight_device *bd = NULL;
+	struct device_node *np_mcu_panel = rgb->np_mcu_panel;
 	struct device_node *np = NULL;
 
 	np = of_parse_phandle(np_mcu_panel, "backlight", 0);
@@ -768,45 +820,35 @@
 	struct drm_device *drm_dev = data;
 	struct drm_encoder *encoder = &rgb->encoder;
 	struct drm_connector *connector;
-	struct fwnode_handle *fwnode_mcu_panel;
 	int ret;
 
-	fwnode_mcu_panel = device_get_named_child_node(dev, "mcu-panel");
-	if (fwnode_mcu_panel) {
+	if (rgb->np_mcu_panel) {
 		struct rockchip_mcu_panel *mcu_panel;
-		struct device_node *np_mcu_panel = to_of_node(fwnode_mcu_panel);
 
 		mcu_panel = devm_kzalloc(dev, sizeof(*mcu_panel), GFP_KERNEL);
 		if (!mcu_panel) {
-			of_node_put(np_mcu_panel);
 			return -ENOMEM;
 		}
 		mcu_panel->drm_dev = drm_dev;
 
 		rgb->panel = &mcu_panel->base;
 
-		ret = rockchip_mcu_panel_init(rgb, np_mcu_panel);
+		ret = rockchip_mcu_panel_init(rgb);
 		if (ret < 0) {
 			DRM_DEV_ERROR(dev, "failed to init mcu panel: %d\n", ret);
-			of_node_put(np_mcu_panel);
 			return ret;
 		}
 
-		rgb->panel->backlight = rockchip_mcu_panel_find_backlight(np_mcu_panel);
+		rgb->panel->backlight = rockchip_mcu_panel_find_backlight(rgb);
 		if (!rgb->panel->backlight) {
 			DRM_DEV_ERROR(dev, "failed to find backlight device");
-			of_node_put(np_mcu_panel);
 			return -EINVAL;
 		}
-
-		of_node_put(np_mcu_panel);
 
 		drm_panel_init(&mcu_panel->base, dev, &rockchip_mcu_panel_funcs,
 			       DRM_MODE_CONNECTOR_DPI);
 
 		drm_panel_add(&mcu_panel->base);
-
-		rgb->is_mcu_panel = true;
 	} else {
 		ret = drm_of_find_panel_or_bridge(dev->of_node, 1, -1,
 						  &rgb->panel, &rgb->bridge);
@@ -899,6 +941,7 @@
 	struct device *dev = &pdev->dev;
 	struct rockchip_rgb *rgb;
 	const struct rockchip_rgb_data *rgb_data;
+	struct fwnode_handle *fwnode_mcu_panel;
 	int ret, id;
 
 	rgb = devm_kzalloc(&pdev->dev, sizeof(*rgb), GFP_KERNEL);
@@ -909,17 +952,23 @@
 	if (id < 0)
 		id = 0;
 
+	rgb->data_sync_bypass = of_property_read_bool(dev->of_node, "rockchip,data-sync-bypass");
+
+	fwnode_mcu_panel = device_get_named_child_node(dev, "mcu-panel");
+	if (fwnode_mcu_panel)
+		rgb->np_mcu_panel = to_of_node(fwnode_mcu_panel);
+
 	rgb_data = of_device_get_match_data(dev);
 	if (rgb_data) {
-		rgb->max_dclk_rate = rgb_data->max_dclk_rate;
 		rgb->funcs = rgb_data->funcs;
+		if (rgb->np_mcu_panel)
+			rgb->max_dclk_rate = rgb_data->mcu_max_dclk_rate;
+		else
+			rgb->max_dclk_rate = rgb_data->rgb_max_dclk_rate;
 	}
 	rgb->id = id;
 	rgb->dev = dev;
 	platform_set_drvdata(pdev, rgb);
-
-	rgb->data_sync_bypass =
-	    of_property_read_bool(dev->of_node, "rockchip,data-sync-bypass");
 
 	if (dev->parent && dev->parent->of_node) {
 		rgb->grf = syscon_node_to_regmap(dev->parent->of_node);
@@ -1061,7 +1110,8 @@
 };
 
 static const struct rockchip_rgb_data rv1106_rgb = {
-	.max_dclk_rate = 74250,
+	.rgb_max_dclk_rate = 74250,
+	.mcu_max_dclk_rate = 150000,
 	.funcs = &rv1106_rgb_funcs,
 };
 
diff --git a/kernel/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c b/kernel/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c
index eb14334..f844376 100644
--- a/kernel/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c
+++ b/kernel/drivers/gpu/drm/rockchip/rockchip_vop2_reg.c
@@ -1256,7 +1256,7 @@
 			VOP_FEATURE_HDR10 | VOP_FEATURE_OVERSCAN,
 	 .gamma_lut_len = 1024,
 	 .cubic_lut_len = 729, /* 9x9x9 */
-	 .max_output = { 4096, 2304 },
+	 .max_output = { 4096, 4096 },
 	 .pre_scan_max_dly = { 69, 53, 53, 42 },
 	 .intr = &rk3568_vp0_intr,
 	 .hdr_table = &rk3568_vop_hdr_table,
@@ -1267,7 +1267,7 @@
 	 .soc_id = { 0x3568, 0x3566 },
 	 .feature = VOP_FEATURE_ALPHA_SCALE | VOP_FEATURE_OVERSCAN,
 	 .gamma_lut_len = 1024,
-	 .max_output = { 2048, 1536 },
+	 .max_output = { 2048, 2048 },
 	 .pre_scan_max_dly = { 40, 40, 40, 40 },
 	 .intr = &rk3568_vp1_intr,
 	 .regs = &rk3568_vop_vp1_regs,
@@ -1277,7 +1277,7 @@
 	 .feature = VOP_FEATURE_ALPHA_SCALE | VOP_FEATURE_OVERSCAN,
 	 .soc_id = { 0x3568, 0x3566 },
 	 .gamma_lut_len = 1024,
-	 .max_output = { 1920, 1080 },
+	 .max_output = { 1920, 1920 },
 	 .pre_scan_max_dly = { 40, 40, 40, 40 },
 	 .intr = &rk3568_vp2_intr,
 	 .regs = &rk3568_vop_vp2_regs,
@@ -1883,6 +1883,7 @@
 	.afbc_enable = VOP_REG(RK3568_CLUSTER0_CTRL, 0x1, 1),
 	.enable = VOP_REG(RK3568_CLUSTER0_CTRL, 1, 0),
 	.lb_mode = VOP_REG(RK3568_CLUSTER0_CTRL, 0xf, 4),
+	.frm_reset_en = VOP_REG(RK3568_CLUSTER0_CTRL, 1, 31),
 	.src_color_ctrl = VOP_REG(RK3568_CLUSTER0_MIX_SRC_COLOR_CTRL, 0xffffffff, 0),
 	.dst_color_ctrl = VOP_REG(RK3568_CLUSTER0_MIX_DST_COLOR_CTRL, 0xffffffff, 0),
 	.src_alpha_ctrl = VOP_REG(RK3568_CLUSTER0_MIX_SRC_ALPHA_CTRL, 0xffffffff, 0),
@@ -1893,6 +1894,7 @@
 	.afbc_enable = VOP_REG(RK3568_CLUSTER1_CTRL, 0x1, 1),
 	.enable = VOP_REG(RK3568_CLUSTER1_CTRL, 1, 0),
 	.lb_mode = VOP_REG(RK3568_CLUSTER1_CTRL, 0xf, 4),
+	.frm_reset_en = VOP_REG(RK3568_CLUSTER1_CTRL, 1, 31),
 	.src_color_ctrl = VOP_REG(RK3568_CLUSTER1_MIX_SRC_COLOR_CTRL, 0xffffffff, 0),
 	.dst_color_ctrl = VOP_REG(RK3568_CLUSTER1_MIX_DST_COLOR_CTRL, 0xffffffff, 0),
 	.src_alpha_ctrl = VOP_REG(RK3568_CLUSTER1_MIX_SRC_ALPHA_CTRL, 0xffffffff, 0),
@@ -1903,6 +1905,7 @@
 	.afbc_enable = VOP_REG(RK3588_CLUSTER2_CTRL, 0x1, 1),
 	.enable = VOP_REG(RK3588_CLUSTER2_CTRL, 1, 0),
 	.lb_mode = VOP_REG(RK3588_CLUSTER2_CTRL, 0xf, 4),
+	.frm_reset_en = VOP_REG(RK3588_CLUSTER2_CTRL, 1, 31),
 	.src_color_ctrl = VOP_REG(RK3588_CLUSTER2_MIX_SRC_COLOR_CTRL, 0xffffffff, 0),
 	.dst_color_ctrl = VOP_REG(RK3588_CLUSTER2_MIX_DST_COLOR_CTRL, 0xffffffff, 0),
 	.src_alpha_ctrl = VOP_REG(RK3588_CLUSTER2_MIX_SRC_ALPHA_CTRL, 0xffffffff, 0),
@@ -1913,6 +1916,7 @@
 	.afbc_enable = VOP_REG(RK3588_CLUSTER3_CTRL, 0x1, 1),
 	.enable = VOP_REG(RK3588_CLUSTER3_CTRL, 1, 0),
 	.lb_mode = VOP_REG(RK3588_CLUSTER3_CTRL, 0xf, 4),
+	.frm_reset_en = VOP_REG(RK3588_CLUSTER3_CTRL, 1, 31),
 	.src_color_ctrl = VOP_REG(RK3588_CLUSTER3_MIX_SRC_COLOR_CTRL, 0xffffffff, 0),
 	.dst_color_ctrl = VOP_REG(RK3588_CLUSTER3_MIX_DST_COLOR_CTRL, 0xffffffff, 0),
 	.src_alpha_ctrl = VOP_REG(RK3588_CLUSTER3_MIX_SRC_ALPHA_CTRL, 0xffffffff, 0),
diff --git a/kernel/drivers/gpu/drm/rockchip/rockchip_vop_reg.h b/kernel/drivers/gpu/drm/rockchip/rockchip_vop_reg.h
index 56a3497..a33f6fc 100644
--- a/kernel/drivers/gpu/drm/rockchip/rockchip_vop_reg.h
+++ b/kernel/drivers/gpu/drm/rockchip/rockchip_vop_reg.h
@@ -1759,6 +1759,8 @@
 
 /* RK3588 ACM register definition */
 #define RK3528_ACM_CTRL				0x0000
+#define RK3528_ACM_ENABLE			BIT(0)
+#define RK3528_ACM_BYPASS			BIT(1)
 #define RK3528_ACM_DELTA_RANGE			0x0004
 #define RK3528_ACM_FETCH_START			0x0008
 #define RK3528_ACM_DEBUG_POINT0			0x0010
diff --git a/kernel/drivers/gpu/drm/ttm/ttm_bo_util.c b/kernel/drivers/gpu/drm/ttm/ttm_bo_util.c
index 164b9a0..fb2a25f 100644
--- a/kernel/drivers/gpu/drm/ttm/ttm_bo_util.c
+++ b/kernel/drivers/gpu/drm/ttm/ttm_bo_util.c
@@ -181,15 +181,13 @@
 		return -ENOMEM;
 
 	src = (void *)((unsigned long)src + (page << PAGE_SHIFT));
-	/*
-	 * Ensure that a highmem page is mapped with the correct
-	 * pgprot. For non highmem the mapping is already there.
-	 */
-	dst = kmap_local_page_prot(d, prot);
+	dst = kmap_atomic_prot(d, prot);
+	if (!dst)
+		return -ENOMEM;
 
 	memcpy_fromio(dst, src, PAGE_SIZE);
 
-	kunmap_local(dst);
+	kunmap_atomic(dst);
 
 	return 0;
 }
@@ -205,15 +203,13 @@
 		return -ENOMEM;
 
 	dst = (void *)((unsigned long)dst + (page << PAGE_SHIFT));
-	/*
-	 * Ensure that a highmem page is mapped with the correct
-	 * pgprot. For non highmem the mapping is already there.
-	 */
-	src = kmap_local_page_prot(s, prot);
+	src = kmap_atomic_prot(s, prot);
+	if (!src)
+		return -ENOMEM;
 
 	memcpy_toio(dst, src, PAGE_SIZE);
 
-	kunmap_local(src);
+	kunmap_atomic(src);
 
 	return 0;
 }
diff --git a/kernel/drivers/gpu/drm/vmwgfx/vmwgfx_blit.c b/kernel/drivers/gpu/drm/vmwgfx/vmwgfx_blit.c
index 71dba22..e8d6618 100644
--- a/kernel/drivers/gpu/drm/vmwgfx/vmwgfx_blit.c
+++ b/kernel/drivers/gpu/drm/vmwgfx/vmwgfx_blit.c
@@ -375,12 +375,12 @@
 		copy_size = min_t(u32, copy_size, PAGE_SIZE - src_page_offset);
 
 		if (unmap_src) {
-			kunmap_local(d->src_addr);
+			kunmap_atomic(d->src_addr);
 			d->src_addr = NULL;
 		}
 
 		if (unmap_dst) {
-			kunmap_local(d->dst_addr);
+			kunmap_atomic(d->dst_addr);
 			d->dst_addr = NULL;
 		}
 
@@ -388,8 +388,12 @@
 			if (WARN_ON_ONCE(dst_page >= d->dst_num_pages))
 				return -EINVAL;
 
-			d->dst_addr = kmap_local_page_prot(d->dst_pages[dst_page],
-							   d->dst_prot);
+			d->dst_addr =
+				kmap_atomic_prot(d->dst_pages[dst_page],
+						 d->dst_prot);
+			if (!d->dst_addr)
+				return -ENOMEM;
+
 			d->mapped_dst = dst_page;
 		}
 
@@ -397,8 +401,12 @@
 			if (WARN_ON_ONCE(src_page >= d->src_num_pages))
 				return -EINVAL;
 
-			d->src_addr = kmap_local_page_prot(d->src_pages[src_page],
-							   d->src_prot);
+			d->src_addr =
+				kmap_atomic_prot(d->src_pages[src_page],
+						 d->src_prot);
+			if (!d->src_addr)
+				return -ENOMEM;
+
 			d->mapped_src = src_page;
 		}
 		diff->do_cpy(diff, d->dst_addr + dst_page_offset,
@@ -428,10 +436,8 @@
  *
  * Performs a CPU blit from one buffer object to another avoiding a full
  * bo vmap which may exhaust- or fragment vmalloc space.
- *
- * On supported architectures (x86), we're using kmap_local_prot() which
- * avoids cross-processor TLB- and cache flushes. kmap_local_prot() will
- * either map a highmem page with the proper pgprot on HIGHMEM=y systems or
+ * On supported architectures (x86), we're using kmap_atomic which avoids
+ * cross-processor TLB- and cache flushes and may, on non-HIGHMEM systems
  * reference already set-up mappings.
  *
  * Neither of the buffer objects may be placed in PCI memory
@@ -494,9 +500,9 @@
 	}
 out:
 	if (d.src_addr)
-		kunmap_local(d.src_addr);
+		kunmap_atomic(d.src_addr);
 	if (d.dst_addr)
-		kunmap_local(d.dst_addr);
+		kunmap_atomic(d.dst_addr);
 
 	return ret;
 }
diff --git a/kernel/drivers/hv/vmbus_drv.c b/kernel/drivers/hv/vmbus_drv.c
index db39c96..e99400f 100644
--- a/kernel/drivers/hv/vmbus_drv.c
+++ b/kernel/drivers/hv/vmbus_drv.c
@@ -1359,8 +1359,7 @@
  * buffer and call into Hyper-V to transfer the data.
  */
 static void hv_kmsg_dump(struct kmsg_dumper *dumper,
-			 enum kmsg_dump_reason reason,
-			 struct kmsg_dumper_iter *iter)
+			 enum kmsg_dump_reason reason)
 {
 	size_t bytes_written;
 	phys_addr_t panic_pa;
@@ -1375,7 +1374,7 @@
 	 * Write dump contents to the page. No need to synchronize; panic should
 	 * be single-threaded.
 	 */
-	kmsg_dump_get_buffer(iter, false, hv_panic_page, HV_HYP_PAGE_SIZE,
+	kmsg_dump_get_buffer(dumper, false, hv_panic_page, HV_HYP_PAGE_SIZE,
 			     &bytes_written);
 	if (bytes_written)
 		hyperv_report_panic_msg(panic_pa, bytes_written);
diff --git a/kernel/drivers/i2c/busses/i2c-rk3x.c b/kernel/drivers/i2c/busses/i2c-rk3x.c
index cd1f713..71592ed 100644
--- a/kernel/drivers/i2c/busses/i2c-rk3x.c
+++ b/kernel/drivers/i2c/busses/i2c-rk3x.c
@@ -1489,7 +1489,8 @@
 		device_property_read_u32(i2c->dev, "i2c,clk-rate", (u32 *)&clk_rate);
 
 	rk3x_i2c_adapt_div(i2c, clk_rate);
-
+	if (rk3x_i2c_get_version(i2c) >= RK_I2C_VERSION5)
+		i2c->autostop_supported = true;
 	enable_irq(i2c->irq);
 }
 
@@ -1648,10 +1649,10 @@
 			device_property_read_u32(&pdev->dev, "i2c,clk-rate", (u32 *)&clk_rate);
 
 		rk3x_i2c_adapt_div(i2c, clk_rate);
-	}
 
-	if (rk3x_i2c_get_version(i2c) >= RK_I2C_VERSION5)
-		i2c->autostop_supported = true;
+		if (rk3x_i2c_get_version(i2c) >= RK_I2C_VERSION5)
+			i2c->autostop_supported = true;
+	}
 
 	ret = i2c_add_numbered_adapter(&i2c->adap);
 	if (ret < 0)
diff --git a/kernel/drivers/input/sensors/accel/Kconfig b/kernel/drivers/input/sensors/accel/Kconfig
index 790127d..4576b8c 100644
--- a/kernel/drivers/input/sensors/accel/Kconfig
+++ b/kernel/drivers/input/sensors/accel/Kconfig
@@ -158,4 +158,11 @@
 	  To have support for your specific gsesnor you will have to
 	  select the proper drivers which depend on this option.
 
+config IAM20680_ACC
+	tristate "gsensor iam20680"
+	default n
+	help
+	  To have support for your specific gsesnor you will have to
+	  select the proper drivers which depend on this option.
+
 endif
diff --git a/kernel/drivers/input/sensors/accel/Makefile b/kernel/drivers/input/sensors/accel/Makefile
index 687d23d..96f6cc6 100644
--- a/kernel/drivers/input/sensors/accel/Makefile
+++ b/kernel/drivers/input/sensors/accel/Makefile
@@ -22,3 +22,4 @@
 obj-$(CONFIG_GS_DA228E)		+= da228e/
 obj-$(CONFIG_ICM2060X_ACC)	+= icm2060x_acc.o
 da223-y	:= da223_cust.o da223_core.o
+obj-$(CONFIG_IAM20680_ACC)	+= iam20680_acc.o
diff --git a/kernel/drivers/input/sensors/accel/iam20680_acc.c b/kernel/drivers/input/sensors/accel/iam20680_acc.c
new file mode 100644
index 0000000..25e3a78
--- /dev/null
+++ b/kernel/drivers/input/sensors/accel/iam20680_acc.c
@@ -0,0 +1,239 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * accel driver for iam20680
+ *
+ * Copyright (c) 2021 Rockchip Electronics Co., Ltd.
+ *
+ * Author: sxj <sxj@rock-chips.com>
+ *
+ */
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/irq.h>
+#include <linux/miscdevice.h>
+#include <linux/gpio.h>
+#include <linux/uaccess.h>
+#include <linux/atomic.h>
+#include <linux/delay.h>
+#include <linux/input.h>
+#include <linux/workqueue.h>
+#include <linux/freezer.h>
+#include <linux/of_gpio.h>
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#include <linux/earlysuspend.h>
+#endif
+#include <linux/sensor-dev.h>
+#include <linux/iam20680.h>
+
+static int iam20680_set_rate(struct i2c_client *client, int rate)
+{
+	/* always use poll mode, no need to set rate */
+	return 0;
+}
+
+static int sensor_active(struct i2c_client *client, int enable, int rate)
+{
+	struct sensor_private_data *sensor =
+	    (struct sensor_private_data *) i2c_get_clientdata(client);
+	int result = 0;
+	int status = 0;
+	u8 pwrm1 = 0;
+
+	sensor->ops->ctrl_data = sensor_read_reg(client, sensor->ops->ctrl_reg);
+	pwrm1 = sensor_read_reg(client, IAM20680_PWR_MGMT_1);
+
+	if (!enable) {
+		status = BIT_ACCEL_STBY;
+		sensor->ops->ctrl_data |= status;
+		if ((sensor->ops->ctrl_data &  BIT_GYRO_STBY) == BIT_GYRO_STBY)
+			pwrm1 |= IAM20680_PWRM1_SLEEP;
+	} else {
+		status = ~BIT_ACCEL_STBY;
+		sensor->ops->ctrl_data &= status;
+		pwrm1 &= ~IAM20680_PWRM1_SLEEP;
+
+		iam20680_set_rate(client, rate);
+	}
+	result = sensor_write_reg(client, sensor->ops->ctrl_reg, sensor->ops->ctrl_data);
+	if (result) {
+		dev_err(&client->dev, "%s:fail to set pwrm2\n", __func__);
+		return -1;
+	}
+	msleep(20);
+
+	result = sensor_write_reg(client, IAM20680_PWR_MGMT_1, pwrm1);
+	if (result) {
+		dev_err(&client->dev, "%s:fail to set pwrm1\n", __func__);
+		return -1;
+	}
+	msleep(50);
+
+	return result;
+}
+
+static int sensor_init(struct i2c_client *client)
+{
+	int res = 0;
+	struct sensor_private_data *sensor =
+	    (struct sensor_private_data *) i2c_get_clientdata(client);
+
+	res = sensor_write_reg(client, IAM20680_PWR_MGMT_1, 0x80);
+	if (res) {
+		dev_err(&client->dev, "set IAM20680_PWR_MGMT_1 error,res: %d!\n", res);
+		return res;
+	}
+	msleep(40);
+
+	res = sensor_write_reg(client, IAM20680_GYRO_CONFIG, 0x18);
+	if (res) {
+		dev_err(&client->dev, "set IAM20680_GYRO_CONFIG error,res: %d!\n", res);
+		return res;
+	}
+	msleep(10);
+
+	res = sensor_write_reg(client, IAM20680_ACCEL_CONFIG, 0x00);
+	if (res) {
+		dev_err(&client->dev, "set IAM20680_ACCEL_CONFIG error,res: %d!\n", res);
+		return res;
+	}
+	msleep(10);
+
+	res = sensor_write_reg(client, IAM20680_ACCEL_CONFIG2, 0x00);
+	if (res) {
+		dev_err(&client->dev, "set IAM20680_ACCEL_CONFIG2 error,res: %d!\n", res);
+		return res;
+	}
+	res = sensor_write_reg(client, IAM20680_PWR_MGMT_2, 0x3F);
+	if (res) {
+		dev_err(&client->dev, "set IAM20680_PWR_MGMT_2 error,res: %d!\n", res);
+		return res;
+	}
+	msleep(10);
+	res = sensor_write_reg(client, IAM20680_PWR_MGMT_1, 0x41);
+	if (res) {
+		dev_err(&client->dev, "set IAM20680_PWR_MGMT_1 error,res: %d!\n", res);
+		return res;
+	}
+	msleep(10);
+
+	res = sensor->ops->active(client, 0, sensor->pdata->poll_delay_ms);
+	if (res) {
+		dev_err(&client->dev, "%s:line=%d,error\n", __func__, __LINE__);
+		return res;
+	}
+	return res;
+}
+
+static int gsensor_report_value(struct i2c_client *client, struct sensor_axis *axis)
+{
+	struct sensor_private_data *sensor =
+	    (struct sensor_private_data *) i2c_get_clientdata(client);
+
+	if (sensor->status_cur == SENSOR_ON) {
+		/* Report acceleration sensor information */
+		input_report_abs(sensor->input_dev, ABS_X, axis->x);
+		input_report_abs(sensor->input_dev, ABS_Y, axis->y);
+		input_report_abs(sensor->input_dev, ABS_Z, axis->z);
+		input_sync(sensor->input_dev);
+	}
+
+	return 0;
+}
+
+static int sensor_report_value(struct i2c_client *client)
+{
+	struct sensor_private_data *sensor =
+		(struct sensor_private_data *) i2c_get_clientdata(client);
+	struct sensor_platform_data *pdata = sensor->pdata;
+	int ret = 0;
+	short x, y, z;
+	struct sensor_axis axis;
+	u8 buffer[6] = {0};
+
+	if (sensor->ops->read_len < 6) {
+		dev_err(&client->dev, "%s: length is error, len=%d\n",
+				__func__, sensor->ops->read_len);
+		return -1;
+	}
+
+	memset(buffer, 0, 6);
+
+	/* Data bytes from hardware xL, xH, yL, yH, zL, zH */
+	do {
+		*buffer = sensor->ops->read_reg;
+		ret = sensor_rx_data(client, buffer, sensor->ops->read_len);
+		if (ret < 0)
+			return ret;
+	} while (0);
+
+	x = ((buffer[0] << 8) & 0xff00) + (buffer[1] & 0xFF);
+	y = ((buffer[2] << 8) & 0xff00) + (buffer[3] & 0xFF);
+	z = ((buffer[4] << 8) & 0xff00) + (buffer[5] & 0xFF);
+
+	axis.x = (pdata->orientation[0]) * x + (pdata->orientation[1]) * y + (pdata->orientation[2]) * z;
+	axis.y = (pdata->orientation[3]) * x + (pdata->orientation[4]) * y + (pdata->orientation[5]) * z;
+	axis.z = (pdata->orientation[6]) * x + (pdata->orientation[7]) * y + (pdata->orientation[8]) * z;
+
+	gsensor_report_value(client, &axis);
+
+	mutex_lock(&(sensor->data_mutex));
+	sensor->axis = axis;
+	mutex_unlock(&(sensor->data_mutex));
+
+	return ret;
+}
+
+static struct sensor_operate gsensor_iam20680_ops = {
+	.name				= "iam20680_acc",
+	.type				= SENSOR_TYPE_ACCEL,
+	.id_i2c				= ACCEL_ID_IAM20680,
+	.read_reg			= IAM20680_ACCEL_XOUT_H,
+	.read_len			= 6,
+	.id_reg				= IAM20680_WHOAMI,
+	.id_data			= IAM20680_DEVICE_ID,
+	.precision			= IAM20680_PRECISION,
+	.ctrl_reg			= IAM20680_PWR_MGMT_2,
+	.int_status_reg		= IAM20680_INT_STATUS,
+	.range				= {-32768, 32768},
+	.trig				= IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+	.active				= sensor_active,
+	.init				= sensor_init,
+	.report				= sensor_report_value,
+};
+
+/****************operate according to sensor chip:end************/
+static int gsensor_iam20680_probe(struct i2c_client *client,
+				 const struct i2c_device_id *devid)
+{
+	return sensor_register_device(client, NULL, devid, &gsensor_iam20680_ops);
+}
+
+static int gsensor_iam20680_remove(struct i2c_client *client)
+{
+	return sensor_unregister_device(client, NULL, &gsensor_iam20680_ops);
+}
+
+static const struct i2c_device_id gsensor_iam20680_id[] = {
+	{"iam20680_acc", ACCEL_ID_IAM20680},
+	{}
+};
+
+static struct i2c_driver gsensor_iam20680_driver = {
+	.probe = gsensor_iam20680_probe,
+	.remove = gsensor_iam20680_remove,
+	.shutdown = sensor_shutdown,
+	.id_table = gsensor_iam20680_id,
+	.driver = {
+		.name = "gsensor_iam20680",
+#ifdef CONFIG_PM
+		.pm = &sensor_pm_ops,
+#endif
+	},
+};
+
+module_i2c_driver(gsensor_iam20680_driver);
+
+MODULE_AUTHOR("sxj <sxj@rock-chips.com>");
+MODULE_DESCRIPTION("iam20680_acc 3-Axis accelerometer driver");
+MODULE_LICENSE("GPL");
diff --git a/kernel/drivers/input/sensors/gyro/Kconfig b/kernel/drivers/input/sensors/gyro/Kconfig
index fb721d4..5b27389 100644
--- a/kernel/drivers/input/sensors/gyro/Kconfig
+++ b/kernel/drivers/input/sensors/gyro/Kconfig
@@ -39,4 +39,11 @@
 
 config GYRO_ICM2060X
 	tristate "gyroscope icm2060x_gyro"
+
+config GYRO_IAM20680
+	tristate "gyroscope iam20680_gyro"
+	default n
+	help
+	  To have support for your specific gyroscope you will have to
+	  select the proper drivers which depend on this option.
 endif
diff --git a/kernel/drivers/input/sensors/gyro/Makefile b/kernel/drivers/input/sensors/gyro/Makefile
index 0943257..8c0a427 100644
--- a/kernel/drivers/input/sensors/gyro/Makefile
+++ b/kernel/drivers/input/sensors/gyro/Makefile
@@ -9,3 +9,4 @@
 obj-$(CONFIG_GYRO_MPU6880)    += mpu6880_gyro.o
 obj-$(CONFIG_GYRO_LSM330)   += lsm330_gyro.o
 obj-$(CONFIG_GYRO_ICM2060X) += icm2060x_gyro.o
+obj-$(CONFIG_GYRO_IAM20680) += iam20680_gyro.o
diff --git a/kernel/drivers/input/sensors/gyro/iam20680_gyro.c b/kernel/drivers/input/sensors/gyro/iam20680_gyro.c
new file mode 100644
index 0000000..6e05ea8
--- /dev/null
+++ b/kernel/drivers/input/sensors/gyro/iam20680_gyro.c
@@ -0,0 +1,206 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * gyroscope driver for iam20680
+ *
+ * Copyright (c) 2021 Rockchip Electronics Co., Ltd.
+ *
+ * Author: sxj <sxj@rock-chips.com>
+ *
+ */
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/irq.h>
+#include <linux/miscdevice.h>
+#include <linux/gpio.h>
+#include <linux/uaccess.h>
+#include <linux/atomic.h>
+#include <linux/delay.h>
+#include <linux/input.h>
+#include <linux/workqueue.h>
+#include <linux/freezer.h>
+#include <linux/of_gpio.h>
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#include <linux/earlysuspend.h>
+#endif
+#include <linux/sensor-dev.h>
+#include <linux/iam20680.h>
+
+static int sensor_active(struct i2c_client *client, int enable, int rate)
+{
+	struct sensor_private_data *sensor =
+	    (struct sensor_private_data *) i2c_get_clientdata(client);
+	int result = 0;
+	int status = 0;
+	u8 pwrm1 = 0;
+
+	sensor->ops->ctrl_data = sensor_read_reg(client, sensor->ops->ctrl_reg);
+	pwrm1 = sensor_read_reg(client, IAM20680_PWR_MGMT_1);
+
+	if (!enable) {
+		status = BIT_GYRO_STBY;
+		sensor->ops->ctrl_data |= status;
+		if ((sensor->ops->ctrl_data & BIT_ACCEL_STBY) == BIT_ACCEL_STBY)
+			pwrm1 |= IAM20680_PWRM1_SLEEP;
+	} else {
+		status = ~BIT_GYRO_STBY;
+		sensor->ops->ctrl_data &= status;
+		pwrm1 &= ~IAM20680_PWRM1_SLEEP;
+	}
+
+	result = sensor_write_reg(client, sensor->ops->ctrl_reg, sensor->ops->ctrl_data);
+	if (result) {
+		dev_err(&client->dev, "%s:fail to active sensor\n", __func__);
+		return -1;
+	}
+	msleep(20);
+
+	result = sensor_write_reg(client, IAM20680_PWR_MGMT_1, pwrm1);
+	if (result) {
+		dev_err(&client->dev, "%s:fail to set pwrm1\n", __func__);
+		return -1;
+	}
+	msleep(50);
+
+	return result;
+}
+
+static int sensor_init(struct i2c_client *client)
+{
+	int ret;
+	struct sensor_private_data *sensor =
+	    (struct sensor_private_data *) i2c_get_clientdata(client);
+
+	/* init on iam20680_acc.c */
+	ret = sensor->ops->active(client, 0, sensor->pdata->poll_delay_ms);
+	if (ret) {
+		dev_err(&client->dev, "%s:line=%d,error\n", __func__, __LINE__);
+		return ret;
+	}
+
+	return ret;
+}
+
+static int gyro_report_value(struct i2c_client *client, struct sensor_axis *axis)
+{
+	struct sensor_private_data *sensor =
+	    (struct sensor_private_data *) i2c_get_clientdata(client);
+
+	if (sensor->status_cur == SENSOR_ON) {
+		/* Report gyro sensor information */
+		input_report_rel(sensor->input_dev, ABS_RX, axis->x);
+		input_report_rel(sensor->input_dev, ABS_RY, axis->y);
+		input_report_rel(sensor->input_dev, ABS_RZ, axis->z);
+		input_sync(sensor->input_dev);
+	}
+
+	return 0;
+}
+
+static int sensor_report_value(struct i2c_client *client)
+{
+	struct sensor_private_data *sensor =
+		(struct sensor_private_data *) i2c_get_clientdata(client);
+	struct sensor_platform_data *pdata = sensor->pdata;
+	int ret = 0;
+	short x, y, z;
+	struct sensor_axis axis;
+	u8 buffer[6] = {0};
+
+	if (sensor->ops->read_len < 6) {
+		dev_err(&client->dev, "%s: length is error, len=%d\n",
+				__func__, sensor->ops->read_len);
+		return -1;
+	}
+
+	memset(buffer, 0, 6);
+
+	do {
+		*buffer = sensor->ops->read_reg;
+		ret = sensor_rx_data(client, buffer, sensor->ops->read_len);
+		if (ret < 0)
+			return ret;
+	} while (0);
+
+	x = ((buffer[0] << 8) & 0xFF00) + (buffer[1] & 0xFF);
+	y = ((buffer[2] << 8) & 0xFF00) + (buffer[3] & 0xFF);
+	z = ((buffer[4] << 8) & 0xFF00) + (buffer[5] & 0xFF);
+
+	axis.x = (pdata->orientation[0]) * x + (pdata->orientation[1]) * y + (pdata->orientation[2]) * z;
+	axis.y = (pdata->orientation[3]) * x + (pdata->orientation[4]) * y + (pdata->orientation[5]) * z;
+	axis.z = (pdata->orientation[6]) * x + (pdata->orientation[7]) * y + (pdata->orientation[8]) * z;
+
+	gyro_report_value(client, &axis);
+
+	mutex_lock(&(sensor->data_mutex));
+	sensor->axis = axis;
+	mutex_unlock(&(sensor->data_mutex));
+
+	return ret;
+}
+
+static struct sensor_operate gyro_iam20680_ops = {
+	.name				= "iam20680_gyro",
+	.type				= SENSOR_TYPE_GYROSCOPE,
+	.id_i2c				= GYRO_ID_IAM20680,
+	.read_reg			= IAM20680_GYRO_XOUT_H,
+	.read_len			= 6,
+	.id_reg				= IAM20680_WHOAMI,
+	.id_data			= IAM20680_DEVICE_ID,
+	.precision			= IAM20680_PRECISION,
+	.ctrl_reg			= IAM20680_PWR_MGMT_2,
+	.int_status_reg		= IAM20680_INT_STATUS,
+	.range				= {-32768, 32768},
+	.trig				= IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+	.active				= sensor_active,
+	.init				= sensor_init,
+	.report				= sensor_report_value,
+};
+
+/****************operate according to sensor chip:end************/
+static int gyro_iam20680_probe(struct i2c_client *client,
+				 const struct i2c_device_id *devid)
+{
+	return sensor_register_device(client, NULL, devid, &gyro_iam20680_ops);
+}
+
+static int gyro_iam20680_remove(struct i2c_client *client)
+{
+	return sensor_unregister_device(client, NULL, &gyro_iam20680_ops);
+}
+
+static const struct i2c_device_id gyro_iam20680_id[] = {
+	{"iam20680_gyro", GYRO_ID_IAM20680},
+	{}
+};
+
+static struct i2c_driver gyro_iam20680_driver = {
+	.probe = gyro_iam20680_probe,
+	.remove = gyro_iam20680_remove,
+	.shutdown = sensor_shutdown,
+	.id_table = gyro_iam20680_id,
+	.driver = {
+		.name = "gyro_iam20680",
+	#ifdef CONFIG_PM
+		.pm = &sensor_pm_ops,
+	#endif
+	},
+};
+
+static int __init gyro_iam20680_init(void)
+{
+	return i2c_add_driver(&gyro_iam20680_driver);
+}
+
+static void __exit gyro_iam20680_exit(void)
+{
+	i2c_del_driver(&gyro_iam20680_driver);
+}
+
+/* must register after iam20680_acc */
+device_initcall_sync(gyro_iam20680_init);
+module_exit(gyro_iam20680_exit);
+
+MODULE_AUTHOR("sxj <sxj@rock-chips.com>");
+MODULE_DESCRIPTION("iam20680_gyro 3-Axis Gyroscope driver");
+MODULE_LICENSE("GPL");
diff --git a/kernel/drivers/input/touchscreen/gt1x/gt1x.c b/kernel/drivers/input/touchscreen/gt1x/gt1x.c
index 5871d5d..8c8e06d 100644
--- a/kernel/drivers/input/touchscreen/gt1x/gt1x.c
+++ b/kernel/drivers/input/touchscreen/gt1x/gt1x.c
@@ -656,6 +656,18 @@
 }
 
 #if defined(CONFIG_FB)
+#include <linux/async.h>
+
+static void gt1x_resume_async(void *data, async_cookie_t cookie)
+{
+	gt1x_resume();
+}
+
+static void gt1x_suspend_async(void *data, async_cookie_t cookie)
+{
+	gt1x_suspend();
+}
+
 /* frame buffer notifier block control the suspend/resume procedure */
 static struct notifier_block gt1x_fb_notifier;
 static int tp_status;
@@ -686,7 +698,7 @@
 		if (*blank == FB_BLANK_UNBLANK) {
 			tp_status = *blank;
 			GTP_DEBUG("Resume by fb notifier.");
-			gt1x_resume();
+			async_schedule(gt1x_resume_async, NULL);
 		}
 	}
 #endif
@@ -697,7 +709,7 @@
 		if (*blank == FB_BLANK_POWERDOWN) {
 			tp_status = *blank;
 			GTP_DEBUG("Suspend by fb notifier.");
-			gt1x_suspend();
+			async_schedule(gt1x_suspend_async, NULL);
 		}
 	}
 
diff --git a/kernel/drivers/irqchip/irq-gic-common.c b/kernel/drivers/irqchip/irq-gic-common.c
index f47b41d..7ce79b5 100644
--- a/kernel/drivers/irqchip/irq-gic-common.c
+++ b/kernel/drivers/irqchip/irq-gic-common.c
@@ -10,6 +10,10 @@
 
 #include "irq-gic-common.h"
 
+#ifdef CONFIG_ROCKCHIP_AMP
+#include <soc/rockchip/rockchip_amp.h>
+#endif
+
 static DEFINE_RAW_SPINLOCK(irq_controller_lock);
 
 static const struct gic_kvm_info *gic_kvm_info;
@@ -112,8 +116,25 @@
 	/*
 	 * Set priority on all global interrupts.
 	 */
+#ifdef CONFIG_ROCKCHIP_AMP
+	for (i = 32; i < gic_irqs; i += 4) {
+		u32 amp_pri, j;
+
+		amp_pri = 0;
+		for (j = 0; j < 4; j++) {
+			if (rockchip_amp_check_amp_irq(i + j)) {
+				amp_pri |= rockchip_amp_get_irq_prio(i + j) <<
+					   (j * 8);
+			} else {
+				amp_pri |= GICD_INT_DEF_PRI << (j * 8);
+			}
+		}
+		writel_relaxed(amp_pri, base + GIC_DIST_PRI + i);
+	}
+#else
 	for (i = 32; i < gic_irqs; i += 4)
 		writel_relaxed(GICD_INT_DEF_PRI_X4, base + GIC_DIST_PRI + i);
+#endif
 
 	/*
 	 * Deactivate and disable all SPIs. Leave the PPI and SGIs
diff --git a/kernel/drivers/irqchip/irq-gic-v3-its.c b/kernel/drivers/irqchip/irq-gic-v3-its.c
index 8ccff68..5b73539 100644
--- a/kernel/drivers/irqchip/irq-gic-v3-its.c
+++ b/kernel/drivers/irqchip/irq-gic-v3-its.c
@@ -2168,7 +2168,9 @@
 {
 	struct page *prop_page;
 
-	if (of_machine_is_compatible("rockchip,rk3568") || of_machine_is_compatible("rockchip,rk3566"))
+	if (of_machine_is_compatible("rockchip,rk3568") ||
+	    of_machine_is_compatible("rockchip,rk3567") ||
+	    of_machine_is_compatible("rockchip,rk3566"))
 		gfp_flags |= GFP_DMA32;
 	prop_page = alloc_pages(gfp_flags, get_order(LPI_PROPBASE_SZ));
 	if (!prop_page)
@@ -2306,7 +2308,9 @@
 	}
 
 	gfp_flags = GFP_KERNEL | __GFP_ZERO;
-	if (of_machine_is_compatible("rockchip,rk3568") || of_machine_is_compatible("rockchip,rk3566"))
+	if (of_machine_is_compatible("rockchip,rk3568") ||
+	    of_machine_is_compatible("rockchip,rk3567") ||
+	    of_machine_is_compatible("rockchip,rk3566"))
 		gfp_flags |= GFP_DMA32;
 	page = alloc_pages_node(its->numa_node, gfp_flags, order);
 	if (!page)
@@ -2357,6 +2361,7 @@
 
 	if (IS_ENABLED(CONFIG_NO_GKI) &&
 	    (of_machine_is_compatible("rockchip,rk3568") ||
+	     of_machine_is_compatible("rockchip,rk3567") ||
 	     of_machine_is_compatible("rockchip,rk3566") ||
 	     of_machine_is_compatible("rockchip,rk3588"))) {
 		if (tmp & GITS_BASER_SHAREABILITY_MASK)
@@ -2947,7 +2952,9 @@
 {
 	struct page *pend_page;
 
-	if (of_machine_is_compatible("rockchip,rk3568") || of_machine_is_compatible("rockchip,rk3566"))
+	if (of_machine_is_compatible("rockchip,rk3568") ||
+	    of_machine_is_compatible("rockchip,rk3567") ||
+	    of_machine_is_compatible("rockchip,rk3566"))
 		gfp_flags |= GFP_DMA32;
 	pend_page = alloc_pages(gfp_flags | __GFP_ZERO,
 				get_order(LPI_PENDBASE_SZ));
@@ -3108,6 +3115,7 @@
 
 	if (IS_ENABLED(CONFIG_NO_GKI) &&
 	    (of_machine_is_compatible("rockchip,rk3568") ||
+	     of_machine_is_compatible("rockchip,rk3567") ||
 	     of_machine_is_compatible("rockchip,rk3566") ||
 	     of_machine_is_compatible("rockchip,rk3588")))
 		tmp &= ~GICR_PROPBASER_SHAREABILITY_MASK;
@@ -3138,6 +3146,7 @@
 
 	if (IS_ENABLED(CONFIG_NO_GKI) &&
 	    (of_machine_is_compatible("rockchip,rk3568") ||
+	     of_machine_is_compatible("rockchip,rk3567") ||
 	     of_machine_is_compatible("rockchip,rk3566") ||
 	     of_machine_is_compatible("rockchip,rk3588")))
 		tmp &= ~GICR_PENDBASER_SHAREABILITY_MASK;
@@ -3306,7 +3315,9 @@
 	if (!table[idx]) {
 		gfp_t gfp_flags = GFP_KERNEL | __GFP_ZERO;
 
-		if (of_machine_is_compatible("rockchip,rk3568") || of_machine_is_compatible("rockchip,rk3566"))
+		if (of_machine_is_compatible("rockchip,rk3568") ||
+		    of_machine_is_compatible("rockchip,rk3567") ||
+		    of_machine_is_compatible("rockchip,rk3566"))
 			gfp_flags |= GFP_DMA32;
 		page = alloc_pages_node(its->numa_node, gfp_flags,
 					get_order(baser->psz));
@@ -3414,7 +3425,9 @@
 	sz = nr_ites * (FIELD_GET(GITS_TYPER_ITT_ENTRY_SIZE, its->typer) + 1);
 	sz = max(sz, ITS_ITT_ALIGN) + ITS_ITT_ALIGN - 1;
 	gfp_flags = GFP_KERNEL;
-	if (of_machine_is_compatible("rockchip,rk3568") || of_machine_is_compatible("rockchip,rk3566")) {
+	if (of_machine_is_compatible("rockchip,rk3568") ||
+	    of_machine_is_compatible("rockchip,rk3567") ||
+	    of_machine_is_compatible("rockchip,rk3566")) {
 		gfp_flags |= GFP_DMA32;
 		itt = (void *)__get_free_pages(gfp_flags, get_order(sz));
 	} else {
@@ -3436,6 +3449,7 @@
 		kfree(dev);
 
 		if (of_machine_is_compatible("rockchip,rk3568") ||
+		    of_machine_is_compatible("rockchip,rk3567") ||
 		    of_machine_is_compatible("rockchip,rk3566"))
 			free_pages((unsigned long)itt, get_order(sz));
 		else
@@ -3480,6 +3494,7 @@
 	kfree(its_dev->event_map.col_map);
 
 	if (of_machine_is_compatible("rockchip,rk3568") ||
+	    of_machine_is_compatible("rockchip,rk3567") ||
 	    of_machine_is_compatible("rockchip,rk3566"))
 		free_pages((unsigned long)its_dev->itt, get_order(its_dev->itt_sz));
 	else
@@ -5085,7 +5100,9 @@
 	its->numa_node = numa_node;
 
 	gfp_flags = GFP_KERNEL | __GFP_ZERO;
-	if (of_machine_is_compatible("rockchip,rk3568") || of_machine_is_compatible("rockchip,rk3566"))
+	if (of_machine_is_compatible("rockchip,rk3568") ||
+	    of_machine_is_compatible("rockchip,rk3567") ||
+	    of_machine_is_compatible("rockchip,rk3566"))
 		gfp_flags |= GFP_DMA32;
 	page = alloc_pages_node(its->numa_node, gfp_flags,
 				get_order(ITS_CMD_QUEUE_SZ));
@@ -5120,6 +5137,7 @@
 
 	if (IS_ENABLED(CONFIG_NO_GKI) &&
 	    (of_machine_is_compatible("rockchip,rk3568") ||
+	     of_machine_is_compatible("rockchip,rk3567") ||
 	     of_machine_is_compatible("rockchip,rk3566") ||
 	     of_machine_is_compatible("rockchip,rk3588")))
 		tmp &= ~GITS_CBASER_SHAREABILITY_MASK;
diff --git a/kernel/drivers/irqchip/irq-gic.c b/kernel/drivers/irqchip/irq-gic.c
index ac027de..cd3f72b 100644
--- a/kernel/drivers/irqchip/irq-gic.c
+++ b/kernel/drivers/irqchip/irq-gic.c
@@ -47,6 +47,10 @@
 
 #include "irq-gic-common.h"
 
+#ifdef CONFIG_ROCKCHIP_AMP
+#include <soc/rockchip/rockchip_amp.h>
+#endif
+
 #ifdef CONFIG_ARM64
 #include <asm/cpufeature.h>
 
@@ -194,11 +198,19 @@
 
 static void gic_mask_irq(struct irq_data *d)
 {
+#ifdef CONFIG_ROCKCHIP_AMP
+	if (rockchip_amp_check_amp_irq(gic_irq(d)))
+		return;
+#endif
 	gic_poke_irq(d, GIC_DIST_ENABLE_CLEAR);
 }
 
 static void gic_eoimode1_mask_irq(struct irq_data *d)
 {
+#ifdef CONFIG_ROCKCHIP_AMP
+	if (rockchip_amp_check_amp_irq(gic_irq(d)))
+		return;
+#endif
 	gic_mask_irq(d);
 	/*
 	 * When masking a forwarded interrupt, make sure it is
@@ -214,6 +226,10 @@
 
 static void gic_unmask_irq(struct irq_data *d)
 {
+#ifdef CONFIG_ROCKCHIP_AMP
+	if (rockchip_amp_check_amp_irq(gic_irq(d)))
+		return;
+#endif
 	gic_poke_irq(d, GIC_DIST_ENABLE_SET);
 }
 
@@ -221,6 +237,10 @@
 {
 	u32 hwirq = gic_irq(d);
 
+#ifdef CONFIG_ROCKCHIP_AMP
+	if (rockchip_amp_check_amp_irq(hwirq))
+		return;
+#endif
 	if (hwirq < 16)
 		hwirq = this_cpu_read(sgi_intid);
 
@@ -231,6 +251,10 @@
 {
 	u32 hwirq = gic_irq(d);
 
+#ifdef CONFIG_ROCKCHIP_AMP
+	if (rockchip_amp_check_amp_irq(gic_irq(d)))
+		return;
+#endif
 	/* Do not deactivate an IRQ forwarded to a vcpu. */
 	if (irqd_is_forwarded_to_vcpu(d))
 		return;
@@ -246,6 +270,10 @@
 {
 	u32 reg;
 
+#ifdef CONFIG_ROCKCHIP_AMP
+	if (rockchip_amp_check_amp_irq(gic_irq(d)))
+		return -EINVAL;
+#endif
 	switch (which) {
 	case IRQCHIP_STATE_PENDING:
 		reg = val ? GIC_DIST_PENDING_SET : GIC_DIST_PENDING_CLEAR;
@@ -295,6 +323,11 @@
 	void __iomem *base = gic_dist_base(d);
 	unsigned int gicirq = gic_irq(d);
 	int ret;
+
+#ifdef CONFIG_ROCKCHIP_AMP
+	if (rockchip_amp_check_amp_irq(gic_irq(d)))
+		return -EINVAL;
+#endif
 
 	/* Interrupt configuration for SGIs can't be changed */
 	if (gicirq < 16)
@@ -492,10 +525,29 @@
 	 * Set all global interrupts to this CPU only.
 	 */
 	cpumask = gic_get_cpumask(gic);
+
+#ifdef CONFIG_ROCKCHIP_AMP
+	for (i = 32; i < gic_irqs; i += 4) {
+		u32 maskval;
+		unsigned int j;
+
+		maskval = 0;
+		for (j = 0; j < 4; j++) {
+			if (rockchip_amp_check_amp_irq(i + j)) {
+				maskval |= rockchip_amp_get_irq_cpumask(i + j) <<
+					   (j * 8);
+			} else {
+				maskval |= cpumask << (j * 8);
+			}
+		}
+		writel_relaxed(maskval, base + GIC_DIST_TARGET + i * 4 / 4);
+	}
+#else
 	cpumask |= cpumask << 8;
 	cpumask |= cpumask << 16;
 	for (i = 32; i < gic_irqs; i += 4)
 		writel_relaxed(cpumask, base + GIC_DIST_TARGET + i * 4 / 4);
+#endif
 
 	gic_dist_config(base, gic_irqs, NULL);
 
@@ -846,6 +898,11 @@
 {
 	void __iomem *reg = gic_dist_base(d) + GIC_DIST_TARGET + gic_irq(d);
 	unsigned int cpu;
+
+#ifdef CONFIG_ROCKCHIP_AMP
+	if (rockchip_amp_check_amp_irq(gic_irq(d)))
+		return -EINVAL;
+#endif
 
 	if (!force)
 		cpu = cpumask_any_and(mask_val, cpu_online_mask);
@@ -1509,6 +1566,10 @@
 
 	gic_enable_of_quirks(node, gic_quirks, gic);
 
+#ifdef CONFIG_ROCKCHIP_AMP
+	rockchip_amp_get_gic_info();
+#endif
+
 	return 0;
 
 error:
diff --git a/kernel/drivers/leds/trigger/Kconfig b/kernel/drivers/leds/trigger/Kconfig
index 29ccbd6..ce9429c 100644
--- a/kernel/drivers/leds/trigger/Kconfig
+++ b/kernel/drivers/leds/trigger/Kconfig
@@ -64,7 +64,6 @@
 
 config LEDS_TRIGGER_CPU
 	bool "LED CPU Trigger"
-	depends on !PREEMPT_RT
 	help
 	  This allows LEDs to be controlled by active CPUs. This shows
 	  the active CPUs across an array of LEDs so you can see which
diff --git a/kernel/drivers/mailbox/Kconfig b/kernel/drivers/mailbox/Kconfig
index 4d043f7..ab3d6f6 100644
--- a/kernel/drivers/mailbox/Kconfig
+++ b/kernel/drivers/mailbox/Kconfig
@@ -79,6 +79,12 @@
 	  Please check it that the Soc you use have Mailbox hardware.
 	  Say Y here if you want to use the Rockchip Mailbox support.
 
+config ROCKCHIP_MBOX_DEMO
+	tristate "Rockchip MBOX Demo"
+	depends on ROCKCHIP_MBOX
+	help
+	  Say y here to enable Rockchip MBOX Demo.
+
 config PCC
 	bool "Platform Communication Channel Driver"
 	depends on ACPI
diff --git a/kernel/drivers/mailbox/Makefile b/kernel/drivers/mailbox/Makefile
index 2e06e02..2a2eecd 100644
--- a/kernel/drivers/mailbox/Makefile
+++ b/kernel/drivers/mailbox/Makefile
@@ -19,6 +19,8 @@
 
 obj-$(CONFIG_ROCKCHIP_MBOX)	+= rockchip-mailbox.o
 
+obj-$(CONFIG_ROCKCHIP_MBOX_DEMO)	+= rockchip-mbox-demo.o
+
 obj-$(CONFIG_PCC)		+= pcc.o
 
 obj-$(CONFIG_ALTERA_MBOX)	+= mailbox-altera.o
diff --git a/kernel/drivers/mailbox/rockchip-mbox-demo.c b/kernel/drivers/mailbox/rockchip-mbox-demo.c
new file mode 100644
index 0000000..17a313b
--- /dev/null
+++ b/kernel/drivers/mailbox/rockchip-mbox-demo.c
@@ -0,0 +1,138 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Rockchip MBOX Demo.
+ *
+ * Copyright (c) 2023 Rockchip Electronics Co. Ltd.
+ * Author: Jiahang Zheng <jiahang.zheng@rock-chips.com>
+ */
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/mailbox_client.h>
+#include <linux/mailbox_controller.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of_reserved_mem.h>
+#include <linux/platform_device.h>
+#include <soc/rockchip/rockchip-mailbox.h>
+#include <linux/delay.h>
+
+/*
+ * The Linux kernel uses the mailbox framework TXDONE_BY_POLL mechanism.
+ * The minimum unit of the txpoll period interface is ms.
+ * Configure rockchip,txpoll-period-ms = <1> in dts.
+ * If data that is longer than MBOX_TX_QUEUE_LEN may be lost,
+ * each send should be at least interval txpoll-period-ms
+ */
+#define MSG_LIMIT (100)
+#define LINUX_TEST_COMPENSATION	(1)
+
+struct rk_mbox_dev {
+	struct platform_device *pdev;
+	struct mbox_client mbox_cl;
+	struct mbox_chan *mbox_rx_chan;
+	struct mbox_chan *mbox_tx_chan;
+	struct rockchip_mbox_msg tx_msg;
+	int rx_count;
+};
+
+static void rk_mbox_rx_callback(struct mbox_client *client, void *message)
+{
+	struct rk_mbox_dev *test_dev = container_of(client, struct rk_mbox_dev, mbox_cl);
+	struct platform_device *pdev = test_dev->pdev;
+	struct device *dev = &pdev->dev;
+	struct rockchip_mbox_msg *tx_msg;
+	struct rockchip_mbox_msg *rx_msg;
+
+	rx_msg = message;
+	dev_info(dev, "mbox master: rx_count:%d cmd=0x%x data=0x%x\n",
+			++test_dev->rx_count, rx_msg->cmd, rx_msg->data);
+
+	/* test should not live forever */
+	if (test_dev->rx_count >= MSG_LIMIT) {
+		dev_info(dev, "Rockchip mbox test exit!\n");
+		return;
+	}
+
+	mdelay(LINUX_TEST_COMPENSATION);
+	tx_msg = &test_dev->tx_msg;
+	mbox_send_message(test_dev->mbox_tx_chan, tx_msg);
+}
+
+static int mbox_demo_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct rk_mbox_dev *test_dev = NULL;
+	struct mbox_client *cl;
+	struct rockchip_mbox_msg *tx_msg;
+	int ret = 0;
+
+	test_dev = devm_kzalloc(dev, sizeof(*test_dev), GFP_KERNEL);
+	if (!test_dev)
+		return -ENOMEM;
+
+	/* link_id: master core 0 and remote core 3 */
+	tx_msg = &test_dev->tx_msg;
+	tx_msg->cmd = 0x03U;
+	tx_msg->data = 0x524D5347U;
+
+	dev_info(dev, "rockchip mbox demo probe.\n");
+	test_dev->pdev = pdev;
+	test_dev->rx_count = 0;
+
+	cl = &test_dev->mbox_cl;
+	cl->dev = dev;
+	cl->rx_callback = rk_mbox_rx_callback;
+
+	platform_set_drvdata(pdev, test_dev);
+	test_dev->mbox_rx_chan = mbox_request_channel_byname(cl, "test-rx");
+	if (IS_ERR(test_dev->mbox_rx_chan)) {
+		ret = PTR_ERR(test_dev->mbox_rx_chan);
+		dev_err(dev, "failed to request mbox rx chan, ret %d\n", ret);
+		return ret;
+	}
+	test_dev->mbox_tx_chan = mbox_request_channel_byname(cl, "test-tx");
+	if (IS_ERR(test_dev->mbox_tx_chan)) {
+		ret = PTR_ERR(test_dev->mbox_tx_chan);
+		dev_err(dev, "failed to request mbox tx chan, ret %d\n", ret);
+		return ret;
+	}
+
+	dev_info(dev, "mbox master: send cmd=0x%x data=0x%x\n", tx_msg->cmd, tx_msg->data);
+	mbox_send_message(test_dev->mbox_tx_chan, tx_msg);
+
+	return ret;
+}
+
+static int mbox_demo_remove(struct platform_device *pdev)
+{
+	struct rk_mbox_dev *test_dev = platform_get_drvdata(pdev);
+
+	mbox_free_channel(test_dev->mbox_rx_chan);
+	mbox_free_channel(test_dev->mbox_tx_chan);
+
+	return 0;
+}
+
+static const struct of_device_id mbox_demo_match[] = {
+	{ .compatible = "rockchip,mbox-demo", },
+	{ /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, mbox_demo_match);
+
+static struct platform_driver mbox_demo_driver = {
+	.probe = mbox_demo_probe,
+	.remove = mbox_demo_remove,
+	.driver = {
+		.name = "mbox-demo",
+		.of_match_table = mbox_demo_match,
+	},
+};
+module_platform_driver(mbox_demo_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Rockchip MBOX Demo");
+MODULE_AUTHOR("Jiahang Zheng <jiahang.zheng@rock-chips.com>");
diff --git a/kernel/drivers/md/raid5.c b/kernel/drivers/md/raid5.c
index 7a31aab..0dea4aa 100644
--- a/kernel/drivers/md/raid5.c
+++ b/kernel/drivers/md/raid5.c
@@ -2217,9 +2217,8 @@
 	struct raid5_percpu *percpu;
 	unsigned long cpu;
 
-	cpu = get_cpu_light();
+	cpu = get_cpu();
 	percpu = per_cpu_ptr(conf->percpu, cpu);
-	spin_lock(&percpu->lock);
 	if (test_bit(STRIPE_OP_BIOFILL, &ops_request)) {
 		ops_run_biofill(sh);
 		overlap_clear++;
@@ -2278,8 +2277,7 @@
 			if (test_and_clear_bit(R5_Overlap, &dev->flags))
 				wake_up(&sh->raid_conf->wait_for_overlap);
 		}
-	spin_unlock(&percpu->lock);
-	put_cpu_light();
+	put_cpu();
 }
 
 static void free_stripe(struct kmem_cache *sc, struct stripe_head *sh)
@@ -7109,7 +7107,6 @@
 			__func__, cpu);
 		return -ENOMEM;
 	}
-	spin_lock_init(&per_cpu_ptr(conf->percpu, cpu)->lock);
 	return 0;
 }
 
diff --git a/kernel/drivers/md/raid5.h b/kernel/drivers/md/raid5.h
index 665fe13..5c05acf 100644
--- a/kernel/drivers/md/raid5.h
+++ b/kernel/drivers/md/raid5.h
@@ -635,7 +635,6 @@
 	int			recovery_disabled;
 	/* per cpu variables */
 	struct raid5_percpu {
-		spinlock_t	lock;		/* Protection for -RT */
 		struct page	*spare_page; /* Used when checking P/Q in raid6 */
 		void		*scribble;  /* space for constructing buffer
 					     * lists and performing address
diff --git a/kernel/drivers/media/i2c/Kconfig b/kernel/drivers/media/i2c/Kconfig
index d66d1b1..669daed 100644
--- a/kernel/drivers/media/i2c/Kconfig
+++ b/kernel/drivers/media/i2c/Kconfig
@@ -351,6 +351,7 @@
 	depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API
 	select HDMI
 	select V4L2_FWNODE
+	select VIDEO_ROCKCHIP_HDMIRX_CLASS
 	help
 	  Support for the ITE IT6616 series HDMI to MIPI CSI-2 bridge.
 
@@ -362,6 +363,7 @@
 	depends on VIDEO_V4L2 && I2C && VIDEO_V4L2_SUBDEV_API
 	select HDMI
 	select V4L2_FWNODE
+	select VIDEO_ROCKCHIP_HDMIRX_CLASS
 	help
 	  Support for the Lontium LT6911UXC series HDMI to MIPI CSI-2 bridge.
 
@@ -661,6 +663,8 @@
 	  To compile this driver as a module, choose M here: the
 	  module will be called max96722.
 
+source "drivers/media/i2c/maxim4c/Kconfig"
+
 comment "Video and audio decoders"
 
 config VIDEO_SAA717X
@@ -930,6 +934,18 @@
 
 	  To compile this driver as a module, choose M here: the
 	  module will be called ar0230.
+
+config VIDEO_AR0822
+	tristate "Onsemi AR0822 sensor support"
+	depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+	depends on MEDIA_CAMERA_SUPPORT
+	select V4L2_FWNODE
+	help
+	  This is a Video4Linux2 sensor driver for the Onsemi
+	  AR0822 camera.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ar0822.
 
 config VIDEO_GC02M2
 	tristate "GalaxyCore GC02M2 sensor support"
@@ -1376,6 +1392,15 @@
 	help
 	  This is a Video4Linux2 sensor driver for the OmniVision
 	  OS02G10 camera.
+
+config VIDEO_OS02K10
+	tristate "OmniVision OS02K10 sensor support"
+	depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+	depends on MEDIA_CAMERA_SUPPORT
+	select V4L2_FWNODE
+	help
+	  This is a Video4Linux2 sensor driver for the OmniVision
+	  OS02K10 camera.
 
 config VIDEO_OS03B10
 	tristate "OmniVision OS03B10 sensor support"
@@ -1920,6 +1945,16 @@
 	  To compile this driver as a module, choose M here: the
 	  module will be called sc132gs.
 
+config VIDEO_SC1346
+	tristate "SmartSens SC1346 sensor support"
+	depends on I2C && VIDEO_V4L2
+	select MEDIA_CONTROLLER
+	select VIDEO_V4L2_SUBDEV_API
+	select V4L2_FWNODE
+	help
+	  This is a Video4Linux2 sensor driver for the SmartSens
+	  SC1346 camera.
+
 config VIDEO_SC200AI
 	tristate "SmartSens SC200AI sensor support"
 	depends on I2C && VIDEO_V4L2
@@ -1959,6 +1994,16 @@
 	help
 	  This is a Video4Linux2 sensor driver for the SmartSens
 	  SC2239 camera.
+
+config VIDEO_SC223A
+	tristate "SmartSens SC223A sensor support"
+	depends on I2C && VIDEO_V4L2
+	select MEDIA_CONTROLLER
+	select VIDEO_V4L2_SUBDEV_API
+	select V4L2_FWNODE
+	help
+	  This is a Video4Linux2 sensor driver for the SmartSens
+	  SC223A camera.
 
 config VIDEO_SC230AI
 	tristate "SmartSens SC230AI sensor support"
@@ -2100,6 +2145,16 @@
 	  This is a Video4Linux2 sensor driver for the SmartSens
 	  SC530AI camera.
 
+config VIDEO_SC5336
+	tristate "SmartSens SC5336 sensor support"
+	depends on I2C && VIDEO_V4L2
+	select MEDIA_CONTROLLER
+	select VIDEO_V4L2_SUBDEV_API
+	select V4L2_FWNODE
+	help
+	  This is a Video4Linux2 sensor driver for the SmartSens
+	  SC5336 camera.
+
 config VIDEO_SC850SL
 	tristate "SmartSens SC850SL sensor support"
 	depends on I2C && VIDEO_V4L2
diff --git a/kernel/drivers/media/i2c/Makefile b/kernel/drivers/media/i2c/Makefile
index eb2cad7..11c344f 100644
--- a/kernel/drivers/media/i2c/Makefile
+++ b/kernel/drivers/media/i2c/Makefile
@@ -10,6 +10,7 @@
 obj-$(CONFIG_VIDEO_NVP6158)	+= nvp6158_drv/
 obj-$(CONFIG_VIDEO_NVP6188)	+= nvp6188.o
 obj-$(CONFIG_VIDEO_NVP6324)	+= jaguar1_drv/
+obj-$(CONFIG_VIDEO_DES_MAXIM4C)	+= maxim4c/
 
 obj-$(CONFIG_VIDEO_APTINA_PLL) += aptina-pll.o
 obj-$(CONFIG_VIDEO_TVAUDIO) += tvaudio.o
@@ -75,6 +76,7 @@
 obj-$(CONFIG_VIDEO_UPD64031A) += upd64031a.o
 obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o
 obj-$(CONFIG_VIDEO_OS02G10) += os02g10.o
+obj-$(CONFIG_VIDEO_OS02K10) += os02k10.o
 obj-$(CONFIG_VIDEO_OS03B10) += os03b10.o
 obj-$(CONFIG_VIDEO_OS04A10) += os04a10.o
 obj-$(CONFIG_VIDEO_OS05A20) += os05a20.o
@@ -125,10 +127,12 @@
 obj-$(CONFIG_VIDEO_SC031GS) += sc031gs.o
 obj-$(CONFIG_VIDEO_SC035GS) += sc035gs.o
 obj-$(CONFIG_VIDEO_SC132GS) += sc132gs.o
+obj-$(CONFIG_VIDEO_SC1346) += sc1346.o
 obj-$(CONFIG_VIDEO_SC200AI) += sc200ai.o
 obj-$(CONFIG_VIDEO_SC210IOT) += sc210iot.o
 obj-$(CONFIG_VIDEO_SC2232) += sc2232.o
 obj-$(CONFIG_VIDEO_SC2239) += sc2239.o
+obj-$(CONFIG_VIDEO_SC223A) += sc223a.o
 obj-$(CONFIG_VIDEO_SC230AI) += sc230ai.o
 obj-$(CONFIG_VIDEO_SC2310) += sc2310.o
 obj-$(CONFIG_VIDEO_SC2336) += sc2336.o
@@ -143,6 +147,7 @@
 obj-$(CONFIG_VIDEO_SC500AI) += sc500ai.o
 obj-$(CONFIG_VIDEO_SC501AI) += sc501ai.o
 obj-$(CONFIG_VIDEO_SC530AI) += sc530ai.o
+obj-$(CONFIG_VIDEO_SC5336) += sc5336.o
 obj-$(CONFIG_VIDEO_SC850SL) += sc850sl.o
 obj-$(CONFIG_VIDEO_SENSOR_ADAPTER)	+= sensor_adapter.o
 obj-$(CONFIG_VIDEO_SR030PC30)	+= sr030pc30.o
@@ -179,6 +184,7 @@
 obj-$(CONFIG_VIDEO_THCV244)	+= thcv244.o
 obj-$(CONFIG_VIDEO_RK628)	+= rk628/
 obj-$(CONFIG_VIDEO_AR0230)	+= ar0230.o
+obj-$(CONFIG_VIDEO_AR0822)	+= ar0822.o
 obj-$(CONFIG_VIDEO_GC02M2)	+= gc02m2.o
 obj-$(CONFIG_VIDEO_GC08A3)	+= gc08a3.o
 obj-$(CONFIG_VIDEO_GC1084)	+= gc1084.o
diff --git a/kernel/drivers/media/i2c/ar0822.c b/kernel/drivers/media/i2c/ar0822.c
new file mode 100644
index 0000000..a883f4f
--- /dev/null
+++ b/kernel/drivers/media/i2c/ar0822.c
@@ -0,0 +1,5484 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ar0822 driver
+ *
+ * Copyright (C) 2023 Rockchip Electronics Co., Ltd.
+ *
+ * V0.0X01.0X00 first version.
+ * V0.0X01.0X01 support conversion gain switch.
+ * V0.0X01.0X02 add debug interface for conversion gain switch.
+ * V0.0X01.0X03 support enum sensor fmt
+ * V0.0X01.0X04 add quick stream on/off
+ */
+
+#include <linux/clk.h>
+#include <linux/device.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 <linux/sysfs.h>
+#include <linux/slab.h>
+#include <linux/version.h>
+#include <linux/rk-camera-module.h>
+#include <media/media-entity.h>
+#include <media/v4l2-async.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-subdev.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/rk-preisp.h>
+#include "../platform/rockchip/isp/rkisp_tb_helper.h"
+
+#define DRIVER_VERSION			KERNEL_VERSION(0, 0x01, 0x04)
+#ifndef V4L2_CID_DIGITAL_GAIN
+#define V4L2_CID_DIGITAL_GAIN		V4L2_CID_GAIN
+#endif
+
+#define MIPI_FREQ_492M			492000000//500000000
+#define MIPI_FREQ_657M			657000000
+#define MIPI_FREQ_823M                  823000000
+#define MIPI_FREQ_986M                  986000000/*657M for 1.314Gbps,986M for 1.972Gbps */
+
+#define PIXEL_RATE_MAX                  (MIPI_FREQ_986M  / 12 *2 * 4)
+
+#define OF_CAMERA_HDR_MODE		"rockchip,camera-hdr-mode"
+
+#define AR0822_XVCLK_FREQ		27000000 /*MCLK* need to config if XCLK from SOC; open.k*/
+
+#define CHIP_ID				0x0F56
+#define AR0822_REG_CHIP_ID		0x3000
+
+#define AR0822_REG_CTRL_MODE		0x301A
+#define AR0822_MODE_SW_STANDBY		0x0018
+#define AR0822_MODE_STREAMING		0x001C
+
+#define	AR0822_EXPOSURE_MIN		2 /* 最小曝光时间 行 * need to config; open.k*/
+#define	AR0822_EXPOSURE_STEP		1
+#define AR0822_VTS_MAX			0xffff  /* Frame length line; open.k*/
+
+#define AR0822_REG_EXP		0x3012
+
+#define AR0822_REG_GAIN		0x5900
+#define AR0822_REG_GAIN2	0x5902
+#define AR0822_REG_GAIN3	0x5904
+#define AR0822_GAIN_MIN		0
+#define AR0822_GAIN_MAX		119
+#define AR0822_GAIN_STEP		1
+#define AR0822_GAIN_DEFAULT		0x20
+
+#define AR0822_GROUP_UPDATE_ADDRESS	0x301A
+#define AR0822_GROUP_UPDATE_START_DATA	0x801C
+#define AR0822_GROUP_UPDATE_END_DATA	0x001C  /* make sure exposure and gain take effect from N+2 frame; open.k*/
+
+#define AR0822_SOFTWARE_RESET_REG	0x301A
+
+#define AR0822_REG_VTS			0x300A
+
+#define REG_NULL			0xFFFF   /* Flag address for I2C array write,indicate this is the last row of I2C register table; open.k*/
+#define REG_DELAY			0xFFFE
+
+#define AR0822_REG_VALUE_08BIT		1
+#define AR0822_REG_VALUE_16BIT		2
+#define AR0822_REG_VALUE_24BIT		3
+
+#define AR0822_LANES			4
+#define AR0822_BPP12			12
+#define AR0822_BPP14			14
+
+#define OF_CAMERA_PINCTRL_STATE_DEFAULT	"rockchip,camera_default"
+#define OF_CAMERA_PINCTRL_STATE_SLEEP	"rockchip,camera_sleep"
+
+#define AR0822_NAME			"ar0822"
+
+#define USED_SYS_DEBUG
+
+
+/*  sensor power on config, need check power, MCLK, GPIO etc,,, need go to .dts file to change the config; open.k */
+static const char * const ar0822_supply_names[] = {
+	"avdd",		/* Analog power */
+	"dovdd",	/* Digital I/O power */
+	"dvdd",		/* Digital core power */
+};
+
+
+#define AR0822_NUM_SUPPLIES ARRAY_SIZE(ar0822_supply_names)
+
+#define AR0822_FLIP_REG			0x3040
+#define MIRROR_BIT_MASK			BIT(14)
+#define FLIP_BIT_MASK			BIT(15)
+
+struct regval {
+	u16 addr;
+	u16 val;
+};
+
+/* Config resolution ,LLPCLK, FLL, exposure time,fps, MIPI channel config, HDR mode , open.k */
+struct ar0822_mode {
+	u32 bus_fmt;
+	u32 width;
+	u32 height;
+	struct v4l2_fract max_fps;
+	u32 hts_def;
+	u32 vts_def;
+	u32 exp_def;
+	const struct regval *reg_list;
+	u32 hdr_mode;
+	u32 mipi_freq;
+	u32 mipi_rate;
+	u32 vc[PAD_MAX];
+};
+
+struct ar0822 {
+	struct i2c_client	*client;
+	struct clk		*xvclk;
+	struct gpio_desc	*reset_gpio;
+	struct gpio_desc	*pwdn_gpio;
+	struct regulator_bulk_data supplies[AR0822_NUM_SUPPLIES];
+
+	struct pinctrl		*pinctrl;
+	struct pinctrl_state	*pins_default;
+	struct pinctrl_state	*pins_sleep;
+
+	struct v4l2_subdev	subdev;
+	struct media_pad	pad;
+	struct v4l2_ctrl_handler ctrl_handler;
+	struct v4l2_ctrl	*exposure;
+	struct v4l2_ctrl	*anal_gain;
+	struct v4l2_ctrl	*digi_gain;
+	struct v4l2_ctrl	*hblank;
+	struct v4l2_ctrl	*vblank;
+	struct v4l2_ctrl	*test_pattern;
+	struct v4l2_ctrl	*pixel_rate;
+	struct v4l2_ctrl	*link_freq;
+	struct v4l2_ctrl	*h_flip;
+	struct v4l2_ctrl	*v_flip;
+	struct mutex		mutex;
+	bool			streaming;
+	bool			power_on;
+	const struct ar0822_mode *cur_mode;
+	u32			cfg_num;
+	u32			module_index;
+	const char		*module_facing;
+	const char		*module_name;
+	const char		*len_name;
+	bool			has_init_exp;
+	struct preisp_hdrae_exp_s init_hdrae_exp;
+	bool			long_hcg;
+	bool			middle_hcg;
+	bool			short_hcg;
+	bool			is_thunderboot;
+	bool			is_thunderboot_ng;
+	bool			is_first_streamoff;
+	u8			flip;
+};
+#define to_ar0822(sd) container_of(sd, struct ar0822, subdev)
+
+/*
+ * Xclk 27Mhz
+ */
+static const struct regval ar0822_linear_global_regs[] = {
+	{REG_DELAY, 2000},
+	{0x3030,0x0092},//PLL_MULTIPLIER
+	{0x302E,0x0002},//PRE_PLL_CLK_DIV
+	{0x302C,0x0002},//VT_SYS_CLK_DIV
+	{0x302A,0x0006},//VT_PIX_CLK_DIV
+	{0x3038,0x0004},//OP_SYS_CLK_DIV
+	{0x3036,0x0006},//OP_WORD_CLK_DIV
+	{0x31B0,0x0071},//FRAME_PREAMBLE
+	{0x31B2,0x004D},//LINE_PREAMBLE
+	{0x31B4,0x51C8},//MIPI_TIMING_0
+	{0x31B6,0x5288},//MIPI_TIMING_1
+	{0x31B8,0x70CA},//MIPI_TIMING_2
+	{0x31BA,0x030B},//MIPI_TIMING_3
+	{0x31BC,0x0C89},//MIPI_TIMING_4
+	{0x3342,0x122C},//MIPI_F1_PDT_EDT
+	{0x2512,0xA000},//SEQ_CTRL_PORT
+	{0x2510,0x0720},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0x2122},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0x26FF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x0F8C},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20E0},//SEQ_DATA_PORT
+	{0x2510,0x8055},//SEQ_DATA_PORT
+	{0x2510,0xA0E1},//SEQ_DATA_PORT
+	{0x2510,0x3041},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x3088},//SEQ_DATA_PORT
+	{0x2510,0x3282},//SEQ_DATA_PORT
+	{0x2510,0xA681},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FE},//SEQ_DATA_PORT
+	{0x2510,0x9070},//SEQ_DATA_PORT
+	{0x2510,0x891D},//SEQ_DATA_PORT
+	{0x2510,0x867F},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FC},//SEQ_DATA_PORT
+	{0x2510,0x893F},//SEQ_DATA_PORT
+	{0x2510,0x0F92},//SEQ_DATA_PORT
+	{0x2510,0x20E0},//SEQ_DATA_PORT
+	{0x2510,0x0F8F},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20E0},//SEQ_DATA_PORT
+	{0x2510,0x9770},//SEQ_DATA_PORT
+	{0x2510,0x20FC},//SEQ_DATA_PORT
+	{0x2510,0x8054},//SEQ_DATA_PORT
+	{0x2510,0x896C},//SEQ_DATA_PORT
+	{0x2510,0x200A},//SEQ_DATA_PORT
+	{0x2510,0x9030},//SEQ_DATA_PORT
+	{0x2510,0x200A},//SEQ_DATA_PORT
+	{0x2510,0x8040},//SEQ_DATA_PORT
+	{0x2510,0x8948},//SEQ_DATA_PORT
+	{0x2510,0x200A},//SEQ_DATA_PORT
+	{0x2510,0x1597},//SEQ_DATA_PORT
+	{0x2510,0x8808},//SEQ_DATA_PORT
+	{0x2510,0x200A},//SEQ_DATA_PORT
+	{0x2510,0x1F96},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20E0},//SEQ_DATA_PORT
+	{0x2510,0xA0C0},//SEQ_DATA_PORT
+	{0x2510,0x200A},//SEQ_DATA_PORT
+	{0x2510,0x3044},//SEQ_DATA_PORT
+	{0x2510,0x3088},//SEQ_DATA_PORT
+	{0x2510,0x3282},//SEQ_DATA_PORT
+	{0x2510,0x2004},//SEQ_DATA_PORT
+	{0x2510,0x1FAA},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20E0},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x3108},//SEQ_DATA_PORT
+	{0x2510,0x2400},//SEQ_DATA_PORT
+	{0x2510,0x3244},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x3108},//SEQ_DATA_PORT
+	{0x2510,0x2400},//SEQ_DATA_PORT
+	{0x2510,0x2702},//SEQ_DATA_PORT
+	{0x2510,0x3242},//SEQ_DATA_PORT
+	{0x2510,0x3108},//SEQ_DATA_PORT
+	{0x2510,0x2420},//SEQ_DATA_PORT
+	{0x2510,0x2703},//SEQ_DATA_PORT
+	{0x2510,0x3242},//SEQ_DATA_PORT
+	{0x2510,0x3108},//SEQ_DATA_PORT
+	{0x2510,0x2420},//SEQ_DATA_PORT
+	{0x2510,0x2704},//SEQ_DATA_PORT
+	{0x2510,0x3244},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x8801},//SEQ_DATA_PORT
+	{0x2510,0x000F},//SEQ_DATA_PORT
+	{0x2510,0x109C},//SEQ_DATA_PORT
+	{0x2510,0x8855},//SEQ_DATA_PORT
+	{0x2510,0x3101},//SEQ_DATA_PORT
+	{0x2510,0x3041},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x3102},//SEQ_DATA_PORT
+	{0x2510,0x3041},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x3181},//SEQ_DATA_PORT
+	{0x2510,0x3041},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x3188},//SEQ_DATA_PORT
+	{0x2510,0x3041},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x3282},//SEQ_DATA_PORT
+	{0x2510,0x3104},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0xB0E4},//SEQ_DATA_PORT
+	{0x2510,0xAD92},//SEQ_DATA_PORT
+	{0x2510,0xBC0C},//SEQ_DATA_PORT
+	{0x2510,0x1028},//SEQ_DATA_PORT
+	{0x2510,0x0022},//SEQ_DATA_PORT
+	{0x2510,0xC020},//SEQ_DATA_PORT
+	{0x2510,0x003E},//SEQ_DATA_PORT
+	{0x2510,0x0045},//SEQ_DATA_PORT
+	{0x2510,0x00B0},//SEQ_DATA_PORT
+	{0x2510,0x0028},//SEQ_DATA_PORT
+	{0x2510,0x30C1},//SEQ_DATA_PORT
+	{0x2510,0x8015},//SEQ_DATA_PORT
+	{0x2510,0xA038},//SEQ_DATA_PORT
+	{0x2510,0x100F},//SEQ_DATA_PORT
+	{0x2510,0x0507},//SEQ_DATA_PORT
+	{0x2510,0xA220},//SEQ_DATA_PORT
+	{0x2510,0x0010},//SEQ_DATA_PORT
+	{0x2510,0x10C2},//SEQ_DATA_PORT
+	{0x2510,0xB760},//SEQ_DATA_PORT
+	{0x2510,0x0033},//SEQ_DATA_PORT
+	{0x2510,0x1082},//SEQ_DATA_PORT
+	{0x2510,0x100B},//SEQ_DATA_PORT
+	{0x2510,0x1029},//SEQ_DATA_PORT
+	{0x2510,0xA85A},//SEQ_DATA_PORT
+	{0x2510,0x998D},//SEQ_DATA_PORT
+	{0x2510,0xC810},//SEQ_DATA_PORT
+	{0x2510,0x2004},//SEQ_DATA_PORT
+	{0x2510,0x0ECE},//SEQ_DATA_PORT
+	{0x2510,0x123B},//SEQ_DATA_PORT
+	{0x2510,0xC000},//SEQ_DATA_PORT
+	{0x2510,0x032F},//SEQ_DATA_PORT
+	{0x2510,0x11D5},//SEQ_DATA_PORT
+	{0x2510,0x162F},//SEQ_DATA_PORT
+	{0x2510,0x9000},//SEQ_DATA_PORT
+	{0x2510,0x2034},//SEQ_DATA_PORT
+	{0x2510,0x0015},//SEQ_DATA_PORT
+	{0x2510,0x04CB},//SEQ_DATA_PORT
+	{0x2510,0x1022},//SEQ_DATA_PORT
+	{0x2510,0x1031},//SEQ_DATA_PORT
+	{0x2510,0x002D},//SEQ_DATA_PORT
+	{0x2510,0x1015},//SEQ_DATA_PORT
+	{0x2510,0x80B9},//SEQ_DATA_PORT
+	{0x2510,0xA101},//SEQ_DATA_PORT
+	{0x2510,0x001C},//SEQ_DATA_PORT
+	{0x2510,0x008E},//SEQ_DATA_PORT
+	{0x2510,0x124B},//SEQ_DATA_PORT
+	{0x2510,0x01B5},//SEQ_DATA_PORT
+	{0x2510,0x0B92},//SEQ_DATA_PORT
+	{0x2510,0xA400},//SEQ_DATA_PORT
+	{0x2510,0x8091},//SEQ_DATA_PORT
+	{0x2510,0x0028},//SEQ_DATA_PORT
+	{0x2510,0x3002},//SEQ_DATA_PORT
+	{0x2510,0x2004},//SEQ_DATA_PORT
+	{0x2510,0x1012},//SEQ_DATA_PORT
+	{0x2510,0x100E},//SEQ_DATA_PORT
+	{0x2510,0x10A8},//SEQ_DATA_PORT
+	{0x2510,0x00A1},//SEQ_DATA_PORT
+	{0x2510,0x132D},//SEQ_DATA_PORT
+	{0x2510,0x09AF},//SEQ_DATA_PORT
+	{0x2510,0x0159},//SEQ_DATA_PORT
+	{0x2510,0x121D},//SEQ_DATA_PORT
+	{0x2510,0x1259},//SEQ_DATA_PORT
+	{0x2510,0x11AF},//SEQ_DATA_PORT
+	{0x2510,0x18B5},//SEQ_DATA_PORT
+	{0x2510,0x0395},//SEQ_DATA_PORT
+	{0x2510,0x054B},//SEQ_DATA_PORT
+	{0x2510,0x1021},//SEQ_DATA_PORT
+	{0x2510,0x0020},//SEQ_DATA_PORT
+	{0x2510,0x1015},//SEQ_DATA_PORT
+	{0x2510,0x1030},//SEQ_DATA_PORT
+	{0x2510,0x00CF},//SEQ_DATA_PORT
+	{0x2510,0xB146},//SEQ_DATA_PORT
+	{0x2510,0xC290},//SEQ_DATA_PORT
+	{0x2510,0x103C},//SEQ_DATA_PORT
+	{0x2510,0xA882},//SEQ_DATA_PORT
+	{0x2510,0x8055},//SEQ_DATA_PORT
+	{0x2510,0x00A9},//SEQ_DATA_PORT
+	{0x2510,0x8801},//SEQ_DATA_PORT
+	{0x2510,0xB700},//SEQ_DATA_PORT
+	{0x2510,0x0001},//SEQ_DATA_PORT
+	{0x2510,0x02A2},//SEQ_DATA_PORT
+	{0x2510,0x000A},//SEQ_DATA_PORT
+	{0x2510,0x98BB},//SEQ_DATA_PORT
+	{0x2510,0x203F},//SEQ_DATA_PORT
+	{0x2510,0x0036},//SEQ_DATA_PORT
+	{0x2510,0x1001},//SEQ_DATA_PORT
+	{0x2510,0x99BE},//SEQ_DATA_PORT
+	{0x2510,0x0139},//SEQ_DATA_PORT
+	{0x2510,0x100A},//SEQ_DATA_PORT
+	{0x2510,0x0040},//SEQ_DATA_PORT
+	{0x2510,0x1022},//SEQ_DATA_PORT
+	{0x2510,0x124C},//SEQ_DATA_PORT
+	{0x2510,0x109F},//SEQ_DATA_PORT
+	{0x2510,0x15A3},//SEQ_DATA_PORT
+	{0x2510,0x002A},//SEQ_DATA_PORT
+	{0x2510,0x3081},//SEQ_DATA_PORT
+	{0x2510,0x2001},//SEQ_DATA_PORT
+	{0x2510,0x3044},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x112A},//SEQ_DATA_PORT
+	{0x2510,0x101D},//SEQ_DATA_PORT
+	{0x2510,0x202B},//SEQ_DATA_PORT
+	{0x2510,0x02B8},//SEQ_DATA_PORT
+	{0x2510,0x10B8},//SEQ_DATA_PORT
+	{0x2510,0x1136},//SEQ_DATA_PORT
+	{0x2510,0x996B},//SEQ_DATA_PORT
+	{0x2510,0x004C},//SEQ_DATA_PORT
+	{0x2510,0x1039},//SEQ_DATA_PORT
+	{0x2510,0x1040},//SEQ_DATA_PORT
+	{0x2510,0x00B5},//SEQ_DATA_PORT
+	{0x2510,0x03C4},//SEQ_DATA_PORT
+	{0x2510,0x1144},//SEQ_DATA_PORT
+	{0x2510,0x1245},//SEQ_DATA_PORT
+	{0x2510,0x9A7B},//SEQ_DATA_PORT
+	{0x2510,0x002B},//SEQ_DATA_PORT
+	{0x2510,0x30D0},//SEQ_DATA_PORT
+	{0x2510,0x3141},//SEQ_DATA_PORT
+	{0x2510,0x3041},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x3142},//SEQ_DATA_PORT
+	{0x2510,0x3041},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x3110},//SEQ_DATA_PORT
+	{0x2510,0x3041},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x3120},//SEQ_DATA_PORT
+	{0x2510,0x3041},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x3144},//SEQ_DATA_PORT
+	{0x2510,0x3041},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x3148},//SEQ_DATA_PORT
+	{0x2510,0x3041},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x3182},//SEQ_DATA_PORT
+	{0x2510,0x3041},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x3184},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x3190},//SEQ_DATA_PORT
+	{0x2510,0x3041},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x31A0},//SEQ_DATA_PORT
+	{0x2510,0x3088},//SEQ_DATA_PORT
+	{0x2510,0x2201},//SEQ_DATA_PORT
+	{0x2510,0x807D},//SEQ_DATA_PORT
+	{0x2510,0x2206},//SEQ_DATA_PORT
+	{0x2510,0x8815},//SEQ_DATA_PORT
+	{0x2510,0x8877},//SEQ_DATA_PORT
+	{0x2510,0x0092},//SEQ_DATA_PORT
+	{0x2510,0x220E},//SEQ_DATA_PORT
+	{0x2510,0x2211},//SEQ_DATA_PORT
+	{0x2510,0x8055},//SEQ_DATA_PORT
+	{0x2510,0x3001},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x8A61},//SEQ_DATA_PORT
+	{0x2510,0x8801},//SEQ_DATA_PORT
+	{0x2510,0x1092},//SEQ_DATA_PORT
+	{0x2510,0x181F},//SEQ_DATA_PORT
+	{0x2510,0x0B1F},//SEQ_DATA_PORT
+	{0x2510,0x101F},//SEQ_DATA_PORT
+	{0x2510,0x00B6},//SEQ_DATA_PORT
+	{0x2510,0x0023},//SEQ_DATA_PORT
+	{0x2510,0x00B9},//SEQ_DATA_PORT
+	{0x2510,0x104C},//SEQ_DATA_PORT
+	{0x2510,0x996E},//SEQ_DATA_PORT
+	{0x2510,0x0140},//SEQ_DATA_PORT
+	{0x2510,0x0257},//SEQ_DATA_PORT
+	{0x2510,0x1035},//SEQ_DATA_PORT
+	{0x2510,0x9F26},//SEQ_DATA_PORT
+	{0x2510,0x1423},//SEQ_DATA_PORT
+	{0x2510,0x0048},//SEQ_DATA_PORT
+	{0x2510,0xC878},//SEQ_DATA_PORT
+	{0x2510,0x200A},//SEQ_DATA_PORT
+	{0x2510,0x1548},//SEQ_DATA_PORT
+	{0x2510,0x0C49},//SEQ_DATA_PORT
+	{0x2510,0x1149},//SEQ_DATA_PORT
+	{0x2510,0x002A},//SEQ_DATA_PORT
+	{0x2510,0x1057},//SEQ_DATA_PORT
+	{0x2510,0x3281},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x3044},//SEQ_DATA_PORT
+	{0x2510,0x2001},//SEQ_DATA_PORT
+	{0x2510,0xA020},//SEQ_DATA_PORT
+	{0x2510,0x000C},//SEQ_DATA_PORT
+	{0x2510,0x9825},//SEQ_DATA_PORT
+	{0x2510,0x1040},//SEQ_DATA_PORT
+	{0x2510,0x1054},//SEQ_DATA_PORT
+	{0x2510,0xB06D},//SEQ_DATA_PORT
+	{0x2510,0x0035},//SEQ_DATA_PORT
+	{0x2510,0x004D},//SEQ_DATA_PORT
+	{0x2510,0x9905},//SEQ_DATA_PORT
+	{0x2510,0xB064},//SEQ_DATA_PORT
+	{0x2510,0x99C5},//SEQ_DATA_PORT
+	{0x2510,0x0047},//SEQ_DATA_PORT
+	{0x2510,0xB920},//SEQ_DATA_PORT
+	{0x2510,0x1447},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x31F8,0x0008},//MIPI_CONFIG_2
+	{0x3C70,0x6828},//CALIB_ROWS
+	{0x3092,0x0826},//ROW_NOISE_CONTROL
+	{0x3428,0x0209},//SEQUENCER_CONTROL
+	{0x3516,0xFF04},//DAC_LD_22_23
+	{0x3526,0x6480},//DAC_LD_38_39
+	{0x3504,0x8AAA},//DAC_LD_4_5
+	{0x353C,0x220C},//DAC_LD_60_61
+	{0x3536,0x4C6E},//DAC_LD_54_55
+	{0x3D2A,0x0FFF},//T1_END_DEC_TH
+	{0x3364,0x00EC},//DCG_TRIM
+	{0x3512,0x8888},//DAC_LD_18_19
+	{0x3514,0x888F},//DAC_LD_20_21
+	{0x3520,0xFBF0},//DAC_LD_32_33
+	{0x3524,0xB2A1},//DAC_LD_36_37
+	{0x3528,0xCC84},//DAC_LD_40_41
+	{0x3532,0x4C8E},//DAC_LD_50_51
+	{0x3534,0x4E64},//DAC_LD_52_53
+	{0x351E,0x5856},//DAC_LD_30_31
+	{0x353E,0x98F2},//DAC_LD_62_63
+	{0x352E,0x6A8A},//DAC_LD_46_47
+	{0x3370,0x0211},//DBLC_CONTROL
+	{0x3372,0x700F},//DBLC_FS0_CONTROL
+	{0x3540,0x3597},//DAC_LD_64_65
+	{0x58E2,0x0BE3},//COL_COUNT_VALUES1
+	{0x58E4,0x18B4},//COL_COUNT_VALUES2
+	{0x3522,0x7C97},//DAC_LD_34_35
+	{0x30BA,0x0024},//DIGITAL_CTRL
+	{0x31D4,0x0042},//CLK_MEM_GATING_CTRL
+	{0x352A,0x6F8F},//DAC_LD_42_43
+	{0x3530,0x4A08},//DAC_LD_48_49
+	{0x351A,0x5FFF},//DAC_LD_26_27
+	{0x350E,0x39D9},//DAC_LD_14_15
+	{0x3510,0x9988},//DAC_LD_16_17
+	{0x3380,0x1FFF},//DBLC_OFFSET1
+	{0x337A,0x1000},//DBLC_SCALE1
+	{0x3092,0x0800},//ROW_NOISE_CONTROL
+	{0x350A,0x0654},//DAC_LD_10_11
+	{0x3364,0x00E0},//DCG_TRIM
+	{0x591E,0x61AE},//ANALOG_GAIN_WR_DATA
+	{0x591E,0x722C},//ANALOG_GAIN_WR_DATA
+	{0x591E,0x82B8},//ANALOG_GAIN_WR_DATA
+	{0x591E,0x92F6},//ANALOG_GAIN_WR_DATA
+	{0x591E,0xA447},//ANALOG_GAIN_WR_DATA
+	{0x591E,0xB66D},//ANALOG_GAIN_WR_DATA
+	{0x591E,0xC6EA},//ANALOG_GAIN_WR_DATA
+	{0x591E,0xDECD},//ANALOG_GAIN_WR_DATA
+	{0x3532,0x4C8A},//DAC_LD_50_51
+	{0x3534,0x4E60},//DAC_LD_52_53
+	{0x353E,0x90F2},//DAC_LD_62_63
+	{0x351A,0x4FFF},//DAC_LD_26_27
+	{0x591C,0x00D7},//DGR_AMP_GAIN
+	{0x3522,0x6097},//DAC_LD_34_35
+	{0x5002,0x37C3},//T1_PIX_DEF_ID2
+	{0x51CC,0x0149},//T1_NOISE_GAIN_THRESHOLD0
+	{0x51D8,0x044D},//T1_NOISE_GAIN_THRESHOLD1
+	{0x51CE,0x0700},//T1_NOISE_GAIN_THRESHOLD2
+	{0x51D0,0x0001},//T1_NOISE_FLOOR0
+	{0x51D2,0x0002},//T1_NOISE_FLOOR1
+	{0x51D4,0x0003},//T1_NOISE_FLOOR2
+	{0x51D6,0x0004},//T1_NOISE_FLOOR3
+	{0x5202,0x37C3},//T2_PIX_DEF_ID2
+	{0x51EA,0x0149},//T2_NOISE_GAIN_THRESHOLD0
+	{0x51FC,0x044D},//T2_NOISE_GAIN_THRESHOLD1
+	{0x51EC,0x0700},//T2_NOISE_GAIN_THRESHOLD2
+	{0x51EE,0x0001},//T2_NOISE_FLOOR0
+	{0x51F0,0x0002},//T2_NOISE_FLOOR1
+	{0x51F2,0x0003},//T2_NOISE_FLOOR2
+	{0x51F4,0x0004},//T2_NOISE_FLOOR3
+	{0x5402,0x37C3},//T4_PIX_DEF_ID2
+	{0x5560,0x0149},//T4_NOISE_GAIN_THRESHOLD0
+	{0x556C,0x044D},//T4_NOISE_GAIN_THRESHOLD1
+	{0x5562,0x0700},//T4_NOISE_GAIN_THRESHOLD2
+	{0x5564,0x0001},//T4_NOISE_FLOOR0
+	{0x5566,0x0002},//T4_NOISE_FLOOR1
+	{0x5568,0x0003},//T4_NOISE_FLOOR2
+	{0x556A,0x0004},//T4_NOISE_FLOOR3
+	{0x31E0,0x0001},//PIX_DEF_ID
+	{0x5000,0x0080},//T1_PIX_DEF_ID
+	{0x5000,0x0180},//T1_PIX_DEF_ID
+	{0x5000,0x0180},//T1_PIX_DEF_ID
+	{0x5200,0x0080},//T2_PIX_DEF_ID
+	{0x5200,0x0180},//T2_PIX_DEF_ID
+	{0x5200,0x0180},//T2_PIX_DEF_ID
+	{0x5400,0x0080},//T4_PIX_DEF_ID
+	{0x5400,0x0180},//T4_PIX_DEF_ID
+	{0x5400,0x0180},//T4_PIX_DEF_ID
+	{0x5000,0x1180},//T1_PIX_DEF_ID
+	{0x50A2,0x2553},//BMT0
+	{0x50A4,0xDFD4},//BMT1
+	{0x50A6,0x030F},//SINGLEK_FACTOR0
+	{0x50A6,0x0F0F},//SINGLEK_FACTOR0
+	{0x50A8,0x030F},//SINGLEK_FACTOR1
+	{0x50A8,0x0F0F},//SINGLEK_FACTOR1
+	{0x50AA,0x030F},//SINGLEK_FACTOR2
+	{0x50AA,0x050F},//SINGLEK_FACTOR2
+	{0x50AC,0x0301},//CROSS_FACTOR0
+	{0x50AC,0x0101},//CROSS_FACTOR0
+	{0x50AE,0x0301},//CROSS_FACTOR1
+	{0x50AE,0x0101},//CROSS_FACTOR1
+	{0x50B0,0x0301},//CROSS_FACTOR2
+	{0x50B0,0x0101},//CROSS_FACTOR2
+	{0x50B2,0x03FF},//SINGLE_MAX_FACTOR
+	{0x50B4,0x030F},//COUPLE_FACTOR0
+	{0x50B4,0x0F0F},//COUPLE_FACTOR0
+	{0x50B6,0x030F},//COUPLE_FACTOR1
+	{0x50B6,0x0F0F},//COUPLE_FACTOR1
+	{0x50B8,0x030F},//COUPLE_FACTOR2
+	{0x50B8,0x050F},//COUPLE_FACTOR2
+	{0x31AE,0x0204},//SERIAL_FORMAT
+	{0x31AC,0x0C0C},//DATA_FORMAT_BITS
+	{0x3082,0x0001},//OPERATION_MODE_CTRL
+	{0x30BA,0x0024},//DIGITAL_CTRL
+	{0x31AE,0x0204},//SERIAL_FORMAT
+	{0x31AC,0x0C0C},//DATA_FORMAT_BITS
+	{0x300C,0x0866},//LINE_LENGTH_PCK_
+	{0x300A,0x09F3},//FRAME_LENGTH_LINES_
+	{0x3012,0x08F4},//COARSE_INTEGRATION_TIME_
+	{0x5914,0x4012},//SENSOR_GAIN_TABLE_SEL
+	{REG_DELAY,100},
+	{0x5914,0x4002},//SENSOR_GAIN_TABLE_SEL
+	{0x5910,0x608A},//SENSOR_GAIN_REG1
+	{0x5910,0x7091},//SENSOR_GAIN_REG1
+	{0x5910,0x689C},//SENSOR_GAIN_REG1
+	{0x5910,0x8885},//SENSOR_GAIN_REG1
+	{0x5910,0x98AD},//SENSOR_GAIN_REG1
+	{0x5910,0xA8A9},//SENSOR_GAIN_REG1
+	{0x5910,0xC894},//SENSOR_GAIN_REG1
+	{0x5910,0xC8D1},//SENSOR_GAIN_REG1
+	{0x5910,0xD88A},//SENSOR_GAIN_REG1
+	{0x5910,0xD8C3},//SENSOR_GAIN_REG1
+	{0x5910,0xD915},//SENSOR_GAIN_REG1
+	{0x5910,0xD988},//SENSOR_GAIN_REG1
+	{0x5910,0xDA2A},//SENSOR_GAIN_REG1
+	{0x5910,0xDB0E},//SENSOR_GAIN_REG1
+	{0x5910,0xDC53},//SENSOR_GAIN_REG1
+	{0x5910,0x608A},//SENSOR_GAIN_REG1
+	{0x5910,0xC919},//SENSOR_GAIN_REG1
+	{0x5910,0xCA00},//SENSOR_GAIN_REG1
+	{0x5910,0x0000},//SENSOR_GAIN_REG1
+	{0x5910,0x0000},//SENSOR_GAIN_REG1
+	{0x5910,0x0000},//SENSOR_GAIN_REG1
+	{0x5910,0x0001},//SENSOR_GAIN_REG1
+	{0x5910,0x0001},//SENSOR_GAIN_REG1
+	{0x5910,0x0003},//SENSOR_GAIN_REG1
+	{0x5910,0x0003},//SENSOR_GAIN_REG1
+	{0x5910,0x0003},//SENSOR_GAIN_REG1
+	{0x5910,0x0004},//SENSOR_GAIN_REG1
+	{0x5910,0x0004},//SENSOR_GAIN_REG1
+	{0x5910,0x0004},//SENSOR_GAIN_REG1
+	{0x5910,0x0004},//SENSOR_GAIN_REG1
+	{0x5910,0x0004},//SENSOR_GAIN_REG1
+	{0x5910,0x0004},//SENSOR_GAIN_REG1
+	{0x5910,0x0004},//SENSOR_GAIN_REG1
+	{0x5910,0x0002},//SENSOR_GAIN_REG1
+	{0x5910,0x0003},//SENSOR_GAIN_REG1
+	{0x5910,0x0003},//SENSOR_GAIN_REG1
+	{0x5910,0x5A8B},//SENSOR_GAIN_REG1
+	{0x5910,0xFF04},//SENSOR_GAIN_REG1
+	{0x5910,0xF704},//SENSOR_GAIN_REG1
+	{0x5910,0xFF04},//SENSOR_GAIN_REG1
+	{0x5910,0xF704},//SENSOR_GAIN_REG1
+	{0x5910,0xF704},//SENSOR_GAIN_REG1
+	{0x5910,0x0005},//SENSOR_GAIN_REG1
+	{0x5910,0x0006},//SENSOR_GAIN_REG1
+	{0x5910,0x0007},//SENSOR_GAIN_REG1
+	{0x5910,0x9A8B},//SENSOR_GAIN_REG1
+	{0x5910,0xFF04},//SENSOR_GAIN_REG1
+	{0x5910,0xF704},//SENSOR_GAIN_REG1
+	{0x5910,0xF704},//SENSOR_GAIN_REG1
+	{0x5910,0xF704},//SENSOR_GAIN_REG1
+	{0x5910,0xF704},//SENSOR_GAIN_REG1
+	{0x5910,0x0015},//SENSOR_GAIN_REG1
+	{0x5910,0x0016},//SENSOR_GAIN_REG1
+	{0x5910,0x0017},//SENSOR_GAIN_REG1
+	{0x5910,0xDA8B},//SENSOR_GAIN_REG1
+	{0x5910,0xFF04},//SENSOR_GAIN_REG1
+	{0x5910,0xF704},//SENSOR_GAIN_REG1
+	{0x5910,0xF704},//SENSOR_GAIN_REG1
+	{0x5910,0xF704},//SENSOR_GAIN_REG1
+	{0x5910,0xF704},//SENSOR_GAIN_REG1
+	{0x5910,0x0025},//SENSOR_GAIN_REG1
+	{0x5910,0x0026},//SENSOR_GAIN_REG1
+	{0x5910,0x0027},//SENSOR_GAIN_REG1
+	{0x5910,0x59B9},//SENSOR_GAIN_REG1
+	{0x5910,0x700F},//SENSOR_GAIN_REG1
+	{0x5910,0x7F0F},//SENSOR_GAIN_REG1
+	{0x5910,0x700F},//SENSOR_GAIN_REG1
+	{0x5910,0x7F0F},//SENSOR_GAIN_REG1
+	{0x5910,0x7F0F},//SENSOR_GAIN_REG1
+	{0x5910,0x0035},//SENSOR_GAIN_REG1
+	{0x5910,0x0036},//SENSOR_GAIN_REG1
+	{0x5910,0x0037},//SENSOR_GAIN_REG1
+	{0x5910,0x99B9},//SENSOR_GAIN_REG1
+	{0x5910,0x700F},//SENSOR_GAIN_REG1
+	{0x5910,0x7F0F},//SENSOR_GAIN_REG1
+	{0x5910,0x700F},//SENSOR_GAIN_REG1
+	{0x5910,0x7F0F},//SENSOR_GAIN_REG1
+	{0x5910,0x7F0F},//SENSOR_GAIN_REG1
+	{0x5910,0x0045},//SENSOR_GAIN_REG1
+	{0x5910,0x0046},//SENSOR_GAIN_REG1
+	{0x5910,0x0047},//SENSOR_GAIN_REG1
+	{0x5910,0xD9B9},//SENSOR_GAIN_REG1
+	{0x5910,0x700F},//SENSOR_GAIN_REG1
+	{0x5910,0x7F0F},//SENSOR_GAIN_REG1
+	{0x5910,0x700F},//SENSOR_GAIN_REG1
+	{0x5910,0x7F0F},//SENSOR_GAIN_REG1
+	{0x5910,0x7F0F},//SENSOR_GAIN_REG1
+	{0x5910,0x0055},//SENSOR_GAIN_REG1
+	{0x5910,0x0056},//SENSOR_GAIN_REG1
+	{0x5910,0x0057},//SENSOR_GAIN_REG1
+	{0x5910,0x9A85},//SENSOR_GAIN_REG1
+	{0x5910,0x0654},//SENSOR_GAIN_REG1
+	{0x5910,0x0654},//SENSOR_GAIN_REG1
+	{0x5910,0x0684},//SENSOR_GAIN_REG1
+	{0x5910,0x0654},//SENSOR_GAIN_REG1
+	{0x5910,0x0654},//SENSOR_GAIN_REG1
+	{0x5910,0x0065},//SENSOR_GAIN_REG1
+	{0x5910,0x0066},//SENSOR_GAIN_REG1
+	{0x5910,0x0067},//SENSOR_GAIN_REG1
+	{0x5910,0x59BD},//SENSOR_GAIN_REG1
+	{0x5910,0x1000},//SENSOR_GAIN_REG1
+	{0x5910,0x0C00},//SENSOR_GAIN_REG1
+	{0x5910,0x0F00},//SENSOR_GAIN_REG1
+	{0x5910,0x1000},//SENSOR_GAIN_REG1
+	{0x5910,0x10F0},//SENSOR_GAIN_REG1
+	{0x5910,0x0075},//SENSOR_GAIN_REG1
+	{0x5910,0x0076},//SENSOR_GAIN_REG1
+	{0x5910,0x0077},//SENSOR_GAIN_REG1
+	{0x5912,0x608A},//SENSOR_GAIN_REG2
+	{0x5912,0x7091},//SENSOR_GAIN_REG2
+	{0x5912,0x689C},//SENSOR_GAIN_REG2
+	{0x5912,0x8885},//SENSOR_GAIN_REG2
+	{0x5912,0x98AD},//SENSOR_GAIN_REG2
+	{0x5912,0xA8A9},//SENSOR_GAIN_REG2
+	{0x5912,0xC894},//SENSOR_GAIN_REG2
+	{0x5912,0xC8D1},//SENSOR_GAIN_REG2
+	{0x5912,0xC927},//SENSOR_GAIN_REG2
+	{0x5912,0xC9A0},//SENSOR_GAIN_REG2
+	{0x5912,0xCA4C},//SENSOR_GAIN_REG2
+	{0x5912,0xCB3F},//SENSOR_GAIN_REG2
+	{0x5912,0xCC97},//SENSOR_GAIN_REG2
+	{0x5912,0xCE7C},//SENSOR_GAIN_REG2
+	{0x5912,0xCFFF},//SENSOR_GAIN_REG2
+	{0x5912,0x608A},//SENSOR_GAIN_REG2
+	{0x5912,0xC8F0},//SENSOR_GAIN_REG2
+	{0x5912,0xCA00},//SENSOR_GAIN_REG2
+	{0x5912,0x0000},//SENSOR_GAIN_REG2
+	{0x5912,0x0000},//SENSOR_GAIN_REG2
+	{0x5912,0x0000},//SENSOR_GAIN_REG2
+	{0x5912,0x0001},//SENSOR_GAIN_REG2
+	{0x5912,0x0001},//SENSOR_GAIN_REG2
+	{0x5912,0x0003},//SENSOR_GAIN_REG2
+	{0x5912,0x0003},//SENSOR_GAIN_REG2
+	{0x5912,0x0003},//SENSOR_GAIN_REG2
+	{0x5912,0x0004},//SENSOR_GAIN_REG2
+	{0x5912,0x0004},//SENSOR_GAIN_REG2
+	{0x5912,0x0004},//SENSOR_GAIN_REG2
+	{0x5912,0x0004},//SENSOR_GAIN_REG2
+	{0x5912,0x0004},//SENSOR_GAIN_REG2
+	{0x5912,0x0004},//SENSOR_GAIN_REG2
+	{0x5912,0x0004},//SENSOR_GAIN_REG2
+	{0x5912,0x0002},//SENSOR_GAIN_REG2
+	{0x5912,0x0003},//SENSOR_GAIN_REG2
+	{0x5912,0x0003},//SENSOR_GAIN_REG2
+	{0x5912,0x5A8B},//SENSOR_GAIN_REG2
+	{0x5912,0xFF04},//SENSOR_GAIN_REG2
+	{0x5912,0xF704},//SENSOR_GAIN_REG2
+	{0x5912,0xFF04},//SENSOR_GAIN_REG2
+	{0x5912,0xF704},//SENSOR_GAIN_REG2
+	{0x5912,0xF704},//SENSOR_GAIN_REG2
+	{0x5912,0x0005},//SENSOR_GAIN_REG2
+	{0x5912,0x0006},//SENSOR_GAIN_REG2
+	{0x5912,0x0007},//SENSOR_GAIN_REG2
+	{0x5912,0x9A8B},//SENSOR_GAIN_REG2
+	{0x5912,0xFF04},//SENSOR_GAIN_REG2
+	{0x5912,0xF704},//SENSOR_GAIN_REG2
+	{0x5912,0xF704},//SENSOR_GAIN_REG2
+	{0x5912,0xF704},//SENSOR_GAIN_REG2
+	{0x5912,0xF704},//SENSOR_GAIN_REG2
+	{0x5912,0x0015},//SENSOR_GAIN_REG2
+	{0x5912,0x0016},//SENSOR_GAIN_REG2
+	{0x5912,0x0017},//SENSOR_GAIN_REG2
+	{0x5912,0xDA8B},//SENSOR_GAIN_REG2
+	{0x5912,0xFF04},//SENSOR_GAIN_REG2
+	{0x5912,0xF704},//SENSOR_GAIN_REG2
+	{0x5912,0xF704},//SENSOR_GAIN_REG2
+	{0x5912,0xF704},//SENSOR_GAIN_REG2
+	{0x5912,0xF704},//SENSOR_GAIN_REG2
+	{0x5912,0x0025},//SENSOR_GAIN_REG2
+	{0x5912,0x0026},//SENSOR_GAIN_REG2
+	{0x5912,0x0027},//SENSOR_GAIN_REG2
+	{0x5912,0x59B9},//SENSOR_GAIN_REG2
+	{0x5912,0x700F},//SENSOR_GAIN_REG2
+	{0x5912,0x7F0F},//SENSOR_GAIN_REG2
+	{0x5912,0x700F},//SENSOR_GAIN_REG2
+	{0x5912,0x7F0F},//SENSOR_GAIN_REG2
+	{0x5912,0x7F0F},//SENSOR_GAIN_REG2
+	{0x5912,0x0035},//SENSOR_GAIN_REG2
+	{0x5912,0x0036},//SENSOR_GAIN_REG2
+	{0x5912,0x0037},//SENSOR_GAIN_REG2
+	{0x5912,0x99B9},//SENSOR_GAIN_REG2
+	{0x5912,0x700F},//SENSOR_GAIN_REG2
+	{0x5912,0x7F0F},//SENSOR_GAIN_REG2
+	{0x5912,0x700F},//SENSOR_GAIN_REG2
+	{0x5912,0x7F0F},//SENSOR_GAIN_REG2
+	{0x5912,0x7F0F},//SENSOR_GAIN_REG2
+	{0x5912,0x0045},//SENSOR_GAIN_REG2
+	{0x5912,0x0046},//SENSOR_GAIN_REG2
+	{0x5912,0x0047},//SENSOR_GAIN_REG2
+	{0x5912,0xD9B9},//SENSOR_GAIN_REG2
+	{0x5912,0x700F},//SENSOR_GAIN_REG2
+	{0x5912,0x7F0F},//SENSOR_GAIN_REG2
+	{0x5912,0x700F},//SENSOR_GAIN_REG2
+	{0x5912,0x7F0F},//SENSOR_GAIN_REG2
+	{0x5912,0x7F0F},//SENSOR_GAIN_REG2
+	{0x5912,0x0055},//SENSOR_GAIN_REG2
+	{0x5912,0x0056},//SENSOR_GAIN_REG2
+	{0x5912,0x0057},//SENSOR_GAIN_REG2
+	{0x5912,0x9A85},//SENSOR_GAIN_REG2
+	{0x5912,0x0654},//SENSOR_GAIN_REG2
+	{0x5912,0x0654},//SENSOR_GAIN_REG2
+	{0x5912,0x0684},//SENSOR_GAIN_REG2
+	{0x5912,0x0654},//SENSOR_GAIN_REG2
+	{0x5912,0x0654},//SENSOR_GAIN_REG2
+	{0x5912,0x0065},//SENSOR_GAIN_REG2
+	{0x5912,0x0066},//SENSOR_GAIN_REG2
+	{0x5912,0x0067},//SENSOR_GAIN_REG2
+	{0x5912,0x59BD},//SENSOR_GAIN_REG2
+	{0x5912,0x1000},//SENSOR_GAIN_REG2
+	{0x5912,0x0C00},//SENSOR_GAIN_REG2
+	{0x5912,0x0F00},//SENSOR_GAIN_REG2
+	{0x5912,0x1000},//SENSOR_GAIN_REG2
+	{0x5912,0x10F0},//SENSOR_GAIN_REG2
+	{0x5912,0x0075},//SENSOR_GAIN_REG2
+	{0x5912,0x0076},//SENSOR_GAIN_REG2
+	{0x5912,0x0077},//SENSOR_GAIN_REG2
+	{0x5914,0x4002},//SENSOR_GAIN_TABLE_SEL
+	{0x5900,0x0000},//SENSOR_GAIN
+	{REG_NULL, 0x00},
+};
+
+static const struct regval ar0822_hdr12bit_3840x2160_25fps_regs[] = {
+	{REG_DELAY, 2000},
+	{0x3030,0x007A},//PLL_MULTIPLIER
+	{0x302E,0x0002},//PRE_PLL_CLK_DIV
+	{0x302C,0x0002},//VT_SYS_CLK_DIV
+	{0x302A,0x0005},//VT_PIX_CLK_DIV
+	{0x3038,0x0002},//OP_SYS_CLK_DIV
+	{0x3036,0x0006},//OP_WORD_CLK_DIV
+	{0x31B0,0x00A3},//FRAME_PREAMBLE
+	{0x31B2,0x006C},//LINE_PREAMBLE
+	{0x31B4,0x72CC},//MIPI_TIMING_0
+	{0x31B6,0x73CE},//MIPI_TIMING_1
+	{0x31B8,0xB0CD},//MIPI_TIMING_2
+	{0x31BA,0x0411},//MIPI_TIMING_3
+	{0x31BC,0x550E},//MIPI_TIMING_4
+	{0x3342,0x122C},//MIPI_F1_PDT_EDT
+	{0x31BC,0x550E},//MIPI_TIMING_4
+	{0x31DE,0x0004},//MIPI_HISPI_TRIM
+	{0x31C6,0xC000},//HISPI_CONTROL
+	{0x31C8,0x0B28},//MIPI_DESKEW_PAT_WIDTH
+	{0x2512,0xA000},//SEQ_CTRL_PORT
+	{0x2510,0x0720},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0x2122},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0x26FF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x0F8C},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20E0},//SEQ_DATA_PORT
+	{0x2510,0x8055},//SEQ_DATA_PORT
+	{0x2510,0xA0E1},//SEQ_DATA_PORT
+	{0x2510,0x3041},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x3088},//SEQ_DATA_PORT
+	{0x2510,0x3282},//SEQ_DATA_PORT
+	{0x2510,0xA681},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FE},//SEQ_DATA_PORT
+	{0x2510,0x9070},//SEQ_DATA_PORT
+	{0x2510,0x891D},//SEQ_DATA_PORT
+	{0x2510,0x867F},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FC},//SEQ_DATA_PORT
+	{0x2510,0x893F},//SEQ_DATA_PORT
+	{0x2510,0x0F92},//SEQ_DATA_PORT
+	{0x2510,0x20E0},//SEQ_DATA_PORT
+	{0x2510,0x0F8F},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20E0},//SEQ_DATA_PORT
+	{0x2510,0x9770},//SEQ_DATA_PORT
+	{0x2510,0x20FC},//SEQ_DATA_PORT
+	{0x2510,0x8054},//SEQ_DATA_PORT
+	{0x2510,0x896C},//SEQ_DATA_PORT
+	{0x2510,0x200A},//SEQ_DATA_PORT
+	{0x2510,0x9030},//SEQ_DATA_PORT
+	{0x2510,0x200A},//SEQ_DATA_PORT
+	{0x2510,0x8040},//SEQ_DATA_PORT
+	{0x2510,0x8948},//SEQ_DATA_PORT
+	{0x2510,0x200A},//SEQ_DATA_PORT
+	{0x2510,0x1597},//SEQ_DATA_PORT
+	{0x2510,0x8808},//SEQ_DATA_PORT
+	{0x2510,0x200A},//SEQ_DATA_PORT
+	{0x2510,0x1F96},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20E0},//SEQ_DATA_PORT
+	{0x2510,0xA0C0},//SEQ_DATA_PORT
+	{0x2510,0x200A},//SEQ_DATA_PORT
+	{0x2510,0x3044},//SEQ_DATA_PORT
+	{0x2510,0x3088},//SEQ_DATA_PORT
+	{0x2510,0x3282},//SEQ_DATA_PORT
+	{0x2510,0x2004},//SEQ_DATA_PORT
+	{0x2510,0x1FAA},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20E0},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x3108},//SEQ_DATA_PORT
+	{0x2510,0x2400},//SEQ_DATA_PORT
+	{0x2510,0x3244},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x3108},//SEQ_DATA_PORT
+	{0x2510,0x2400},//SEQ_DATA_PORT
+	{0x2510,0x2702},//SEQ_DATA_PORT
+	{0x2510,0x3242},//SEQ_DATA_PORT
+	{0x2510,0x3108},//SEQ_DATA_PORT
+	{0x2510,0x2420},//SEQ_DATA_PORT
+	{0x2510,0x2703},//SEQ_DATA_PORT
+	{0x2510,0x3242},//SEQ_DATA_PORT
+	{0x2510,0x3108},//SEQ_DATA_PORT
+	{0x2510,0x2420},//SEQ_DATA_PORT
+	{0x2510,0x2704},//SEQ_DATA_PORT
+	{0x2510,0x3244},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x8801},//SEQ_DATA_PORT
+	{0x2510,0x000F},//SEQ_DATA_PORT
+	{0x2510,0x109C},//SEQ_DATA_PORT
+	{0x2510,0x8855},//SEQ_DATA_PORT
+	{0x2510,0x3101},//SEQ_DATA_PORT
+	{0x2510,0x3041},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x3102},//SEQ_DATA_PORT
+	{0x2510,0x3041},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x3181},//SEQ_DATA_PORT
+	{0x2510,0x3041},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x3188},//SEQ_DATA_PORT
+	{0x2510,0x3041},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x3282},//SEQ_DATA_PORT
+	{0x2510,0x3104},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0xB0E4},//SEQ_DATA_PORT
+	{0x2510,0xAD92},//SEQ_DATA_PORT
+	{0x2510,0xBC0C},//SEQ_DATA_PORT
+	{0x2510,0x1028},//SEQ_DATA_PORT
+	{0x2510,0x0022},//SEQ_DATA_PORT
+	{0x2510,0xC020},//SEQ_DATA_PORT
+	{0x2510,0x003E},//SEQ_DATA_PORT
+	{0x2510,0x0045},//SEQ_DATA_PORT
+	{0x2510,0x00B0},//SEQ_DATA_PORT
+	{0x2510,0x0028},//SEQ_DATA_PORT
+	{0x2510,0x30C1},//SEQ_DATA_PORT
+	{0x2510,0x8015},//SEQ_DATA_PORT
+	{0x2510,0xA038},//SEQ_DATA_PORT
+	{0x2510,0x100F},//SEQ_DATA_PORT
+	{0x2510,0x0507},//SEQ_DATA_PORT
+	{0x2510,0xA220},//SEQ_DATA_PORT
+	{0x2510,0x0010},//SEQ_DATA_PORT
+	{0x2510,0x10C2},//SEQ_DATA_PORT
+	{0x2510,0xB760},//SEQ_DATA_PORT
+	{0x2510,0x0033},//SEQ_DATA_PORT
+	{0x2510,0x1082},//SEQ_DATA_PORT
+	{0x2510,0x100B},//SEQ_DATA_PORT
+	{0x2510,0x1029},//SEQ_DATA_PORT
+	{0x2510,0xA85A},//SEQ_DATA_PORT
+	{0x2510,0x998D},//SEQ_DATA_PORT
+	{0x2510,0xC810},//SEQ_DATA_PORT
+	{0x2510,0x2004},//SEQ_DATA_PORT
+	{0x2510,0x0ECE},//SEQ_DATA_PORT
+	{0x2510,0x123B},//SEQ_DATA_PORT
+	{0x2510,0xC000},//SEQ_DATA_PORT
+	{0x2510,0x032F},//SEQ_DATA_PORT
+	{0x2510,0x11D5},//SEQ_DATA_PORT
+	{0x2510,0x162F},//SEQ_DATA_PORT
+	{0x2510,0x9000},//SEQ_DATA_PORT
+	{0x2510,0x2034},//SEQ_DATA_PORT
+	{0x2510,0x0015},//SEQ_DATA_PORT
+	{0x2510,0x04CB},//SEQ_DATA_PORT
+	{0x2510,0x1022},//SEQ_DATA_PORT
+	{0x2510,0x1031},//SEQ_DATA_PORT
+	{0x2510,0x002D},//SEQ_DATA_PORT
+	{0x2510,0x1015},//SEQ_DATA_PORT
+	{0x2510,0x80B9},//SEQ_DATA_PORT
+	{0x2510,0xA101},//SEQ_DATA_PORT
+	{0x2510,0x001C},//SEQ_DATA_PORT
+	{0x2510,0x008E},//SEQ_DATA_PORT
+	{0x2510,0x124B},//SEQ_DATA_PORT
+	{0x2510,0x01B5},//SEQ_DATA_PORT
+	{0x2510,0x0B92},//SEQ_DATA_PORT
+	{0x2510,0xA400},//SEQ_DATA_PORT
+	{0x2510,0x8091},//SEQ_DATA_PORT
+	{0x2510,0x0028},//SEQ_DATA_PORT
+	{0x2510,0x3002},//SEQ_DATA_PORT
+	{0x2510,0x2004},//SEQ_DATA_PORT
+	{0x2510,0x1012},//SEQ_DATA_PORT
+	{0x2510,0x100E},//SEQ_DATA_PORT
+	{0x2510,0x10A8},//SEQ_DATA_PORT
+	{0x2510,0x00A1},//SEQ_DATA_PORT
+	{0x2510,0x132D},//SEQ_DATA_PORT
+	{0x2510,0x09AF},//SEQ_DATA_PORT
+	{0x2510,0x0159},//SEQ_DATA_PORT
+	{0x2510,0x121D},//SEQ_DATA_PORT
+	{0x2510,0x1259},//SEQ_DATA_PORT
+	{0x2510,0x11AF},//SEQ_DATA_PORT
+	{0x2510,0x18B5},//SEQ_DATA_PORT
+	{0x2510,0x0395},//SEQ_DATA_PORT
+	{0x2510,0x054B},//SEQ_DATA_PORT
+	{0x2510,0x1021},//SEQ_DATA_PORT
+	{0x2510,0x0020},//SEQ_DATA_PORT
+	{0x2510,0x1015},//SEQ_DATA_PORT
+	{0x2510,0x1030},//SEQ_DATA_PORT
+	{0x2510,0x00CF},//SEQ_DATA_PORT
+	{0x2510,0xB146},//SEQ_DATA_PORT
+	{0x2510,0xC290},//SEQ_DATA_PORT
+	{0x2510,0x103C},//SEQ_DATA_PORT
+	{0x2510,0xA882},//SEQ_DATA_PORT
+	{0x2510,0x8055},//SEQ_DATA_PORT
+	{0x2510,0x00A9},//SEQ_DATA_PORT
+	{0x2510,0x8801},//SEQ_DATA_PORT
+	{0x2510,0xB700},//SEQ_DATA_PORT
+	{0x2510,0x0001},//SEQ_DATA_PORT
+	{0x2510,0x02A2},//SEQ_DATA_PORT
+	{0x2510,0x000A},//SEQ_DATA_PORT
+	{0x2510,0x98BB},//SEQ_DATA_PORT
+	{0x2510,0x203F},//SEQ_DATA_PORT
+	{0x2510,0x0036},//SEQ_DATA_PORT
+	{0x2510,0x1001},//SEQ_DATA_PORT
+	{0x2510,0x99BE},//SEQ_DATA_PORT
+	{0x2510,0x0139},//SEQ_DATA_PORT
+	{0x2510,0x100A},//SEQ_DATA_PORT
+	{0x2510,0x0040},//SEQ_DATA_PORT
+	{0x2510,0x1022},//SEQ_DATA_PORT
+	{0x2510,0x124C},//SEQ_DATA_PORT
+	{0x2510,0x109F},//SEQ_DATA_PORT
+	{0x2510,0x15A3},//SEQ_DATA_PORT
+	{0x2510,0x002A},//SEQ_DATA_PORT
+	{0x2510,0x3081},//SEQ_DATA_PORT
+	{0x2510,0x2001},//SEQ_DATA_PORT
+	{0x2510,0x3044},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x112A},//SEQ_DATA_PORT
+	{0x2510,0x101D},//SEQ_DATA_PORT
+	{0x2510,0x202B},//SEQ_DATA_PORT
+	{0x2510,0x02B8},//SEQ_DATA_PORT
+	{0x2510,0x10B8},//SEQ_DATA_PORT
+	{0x2510,0x1136},//SEQ_DATA_PORT
+	{0x2510,0x996B},//SEQ_DATA_PORT
+	{0x2510,0x004C},//SEQ_DATA_PORT
+	{0x2510,0x1039},//SEQ_DATA_PORT
+	{0x2510,0x1040},//SEQ_DATA_PORT
+	{0x2510,0x00B5},//SEQ_DATA_PORT
+	{0x2510,0x03C4},//SEQ_DATA_PORT
+	{0x2510,0x1144},//SEQ_DATA_PORT
+	{0x2510,0x1245},//SEQ_DATA_PORT
+	{0x2510,0x9A7B},//SEQ_DATA_PORT
+	{0x2510,0x002B},//SEQ_DATA_PORT
+	{0x2510,0x30D0},//SEQ_DATA_PORT
+	{0x2510,0x3141},//SEQ_DATA_PORT
+	{0x2510,0x3041},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x3142},//SEQ_DATA_PORT
+	{0x2510,0x3041},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x3110},//SEQ_DATA_PORT
+	{0x2510,0x3041},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x3120},//SEQ_DATA_PORT
+	{0x2510,0x3041},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x3144},//SEQ_DATA_PORT
+	{0x2510,0x3041},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x3148},//SEQ_DATA_PORT
+	{0x2510,0x3041},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x3182},//SEQ_DATA_PORT
+	{0x2510,0x3041},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x3184},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x3190},//SEQ_DATA_PORT
+	{0x2510,0x3041},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x31A0},//SEQ_DATA_PORT
+	{0x2510,0x3088},//SEQ_DATA_PORT
+	{0x2510,0x2201},//SEQ_DATA_PORT
+	{0x2510,0x807D},//SEQ_DATA_PORT
+	{0x2510,0x2206},//SEQ_DATA_PORT
+	{0x2510,0x8815},//SEQ_DATA_PORT
+	{0x2510,0x8877},//SEQ_DATA_PORT
+	{0x2510,0x0092},//SEQ_DATA_PORT
+	{0x2510,0x220E},//SEQ_DATA_PORT
+	{0x2510,0x2211},//SEQ_DATA_PORT
+	{0x2510,0x8055},//SEQ_DATA_PORT
+	{0x2510,0x3001},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x8A61},//SEQ_DATA_PORT
+	{0x2510,0x8801},//SEQ_DATA_PORT
+	{0x2510,0x1092},//SEQ_DATA_PORT
+	{0x2510,0x181F},//SEQ_DATA_PORT
+	{0x2510,0x0B1F},//SEQ_DATA_PORT
+	{0x2510,0x101F},//SEQ_DATA_PORT
+	{0x2510,0x00B6},//SEQ_DATA_PORT
+	{0x2510,0x0023},//SEQ_DATA_PORT
+	{0x2510,0x00B9},//SEQ_DATA_PORT
+	{0x2510,0x104C},//SEQ_DATA_PORT
+	{0x2510,0x996E},//SEQ_DATA_PORT
+	{0x2510,0x0140},//SEQ_DATA_PORT
+	{0x2510,0x0257},//SEQ_DATA_PORT
+	{0x2510,0x1035},//SEQ_DATA_PORT
+	{0x2510,0x9F26},//SEQ_DATA_PORT
+	{0x2510,0x1423},//SEQ_DATA_PORT
+	{0x2510,0x0048},//SEQ_DATA_PORT
+	{0x2510,0xC878},//SEQ_DATA_PORT
+	{0x2510,0x200A},//SEQ_DATA_PORT
+	{0x2510,0x1548},//SEQ_DATA_PORT
+	{0x2510,0x0C49},//SEQ_DATA_PORT
+	{0x2510,0x1149},//SEQ_DATA_PORT
+	{0x2510,0x002A},//SEQ_DATA_PORT
+	{0x2510,0x1057},//SEQ_DATA_PORT
+	{0x2510,0x3281},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x3044},//SEQ_DATA_PORT
+	{0x2510,0x2001},//SEQ_DATA_PORT
+	{0x2510,0xA020},//SEQ_DATA_PORT
+	{0x2510,0x000C},//SEQ_DATA_PORT
+	{0x2510,0x9825},//SEQ_DATA_PORT
+	{0x2510,0x1040},//SEQ_DATA_PORT
+	{0x2510,0x1054},//SEQ_DATA_PORT
+	{0x2510,0xB06D},//SEQ_DATA_PORT
+	{0x2510,0x0035},//SEQ_DATA_PORT
+	{0x2510,0x004D},//SEQ_DATA_PORT
+	{0x2510,0x9905},//SEQ_DATA_PORT
+	{0x2510,0xB064},//SEQ_DATA_PORT
+	{0x2510,0x99C5},//SEQ_DATA_PORT
+	{0x2510,0x0047},//SEQ_DATA_PORT
+	{0x2510,0xB920},//SEQ_DATA_PORT
+	{0x2510,0x1447},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x31F8,0x0008},//MIPI_CONFIG_2
+	{0x3C70,0x6828},//CALIB_ROWS
+	{0x3092,0x0826},//ROW_NOISE_CONTROL
+	{0x3428,0x0209},//SEQUENCER_CONTROL
+	{0x3516,0xFF04},//DAC_LD_22_23
+	{0x3526,0x6480},//DAC_LD_38_39
+	{0x3504,0x8AAA},//DAC_LD_4_5
+	{0x353C,0x220C},//DAC_LD_60_61
+	{0x3536,0x4C6E},//DAC_LD_54_55
+	{0x3D2A,0x0FFF},//T1_END_DEC_TH
+	{0x3364,0x00EC},//DCG_TRIM
+	{0x3512,0x8888},//DAC_LD_18_19
+	{0x3514,0x888F},//DAC_LD_20_21
+	{0x3520,0xFBF0},//DAC_LD_32_33
+	{0x3524,0xB2A1},//DAC_LD_36_37
+	{0x3528,0xCC84},//DAC_LD_40_41
+	{0x3532,0x4C8E},//DAC_LD_50_51
+	{0x3534,0x4E64},//DAC_LD_52_53
+	{0x351E,0x5856},//DAC_LD_30_31
+	{0x353E,0x98F2},//DAC_LD_62_63
+	{0x352E,0x6A8A},//DAC_LD_46_47
+	{0x3370,0x0211},//DBLC_CONTROL
+	{0x3372,0x700F},//DBLC_FS0_CONTROL
+	{0x3540,0x3597},//DAC_LD_64_65
+	{0x58E2,0x0BE3},//COL_COUNT_VALUES1
+	{0x58E4,0x18B4},//COL_COUNT_VALUES2
+	{0x3522,0x7C97},//DAC_LD_34_35
+	{0x30BA,0x0024},//DIGITAL_CTRL
+	{0x31D4,0x0042},//CLK_MEM_GATING_CTRL
+	{0x352A,0x6F8F},//DAC_LD_42_43
+	{0x3530,0x4A08},//DAC_LD_48_49
+	{0x351A,0x5FFF},//DAC_LD_26_27
+	{0x350E,0x39D9},//DAC_LD_14_15
+	{0x3510,0x9988},//DAC_LD_16_17
+	{0x3380,0x1FFF},//DBLC_OFFSET1
+	{0x337A,0x1000},//DBLC_SCALE1
+	{0x3092,0x0800},//ROW_NOISE_CONTROL
+	{0x350A,0x0654},//DAC_LD_10_11
+	{0x3364,0x00E0},//DCG_TRIM
+	{0x591E,0x61AE},//ANALOG_GAIN_WR_DATA
+	{0x591E,0x722C},//ANALOG_GAIN_WR_DATA
+	{0x591E,0x82B8},//ANALOG_GAIN_WR_DATA
+	{0x591E,0x92F6},//ANALOG_GAIN_WR_DATA
+	{0x591E,0xA447},//ANALOG_GAIN_WR_DATA
+	{0x591E,0xB66D},//ANALOG_GAIN_WR_DATA
+	{0x591E,0xC6EA},//ANALOG_GAIN_WR_DATA
+	{0x591E,0xDECD},//ANALOG_GAIN_WR_DATA
+	{0x3532,0x4C8A},//DAC_LD_50_51
+	{0x3534,0x4E60},//DAC_LD_52_53
+	{0x353E,0x90F2},//DAC_LD_62_63
+	{0x351A,0x4FFF},//DAC_LD_26_27
+	{0x591C,0x00D7},//DGR_AMP_GAIN
+	{0x3522,0x6097},//DAC_LD_34_35
+	{0x5002,0x37C3},//T1_PIX_DEF_ID2
+	{0x51CC,0x0149},//T1_NOISE_GAIN_THRESHOLD0
+	{0x51D8,0x044D},//T1_NOISE_GAIN_THRESHOLD1
+	{0x51CE,0x0700},//T1_NOISE_GAIN_THRESHOLD2
+	{0x51D0,0x0001},//T1_NOISE_FLOOR0
+	{0x51D2,0x0002},//T1_NOISE_FLOOR1
+	{0x51D4,0x0003},//T1_NOISE_FLOOR2
+	{0x51D6,0x0004},//T1_NOISE_FLOOR3
+	{0x5202,0x37C3},//T2_PIX_DEF_ID2
+	{0x51EA,0x0149},//T2_NOISE_GAIN_THRESHOLD0
+	{0x51FC,0x044D},//T2_NOISE_GAIN_THRESHOLD1
+	{0x51EC,0x0700},//T2_NOISE_GAIN_THRESHOLD2
+	{0x51EE,0x0001},//T2_NOISE_FLOOR0
+	{0x51F0,0x0002},//T2_NOISE_FLOOR1
+	{0x51F2,0x0003},//T2_NOISE_FLOOR2
+	{0x51F4,0x0004},//T2_NOISE_FLOOR3
+	{0x5402,0x37C3},//T4_PIX_DEF_ID2
+	{0x5560,0x0149},//T4_NOISE_GAIN_THRESHOLD0
+	{0x556C,0x044D},//T4_NOISE_GAIN_THRESHOLD1
+	{0x5562,0x0700},//T4_NOISE_GAIN_THRESHOLD2
+	{0x5564,0x0001},//T4_NOISE_FLOOR0
+	{0x5566,0x0002},//T4_NOISE_FLOOR1
+	{0x5568,0x0003},//T4_NOISE_FLOOR2
+	{0x556A,0x0004},//T4_NOISE_FLOOR3
+	{0x31E0,0x0001},//PIX_DEF_ID
+	{0x5000,0x0080},//T1_PIX_DEF_ID
+	{0x5000,0x0180},//T1_PIX_DEF_ID
+	{0x5000,0x0180},//T1_PIX_DEF_ID
+	{0x5200,0x0080},//T2_PIX_DEF_ID
+	{0x5200,0x0180},//T2_PIX_DEF_ID
+	{0x5200,0x0180},//T2_PIX_DEF_ID
+	{0x5400,0x0080},//T4_PIX_DEF_ID
+	{0x5400,0x0180},//T4_PIX_DEF_ID
+	{0x5400,0x0180},//T4_PIX_DEF_ID
+	{0x5000,0x0180},//T1_PIX_DEF_ID
+	{0x5200,0x0180},//T2_PIX_DEF_ID
+	{0x5400,0x0180},//T4_PIX_DEF_ID
+	{0x50A2,0x3F2A},//BMT0
+	{0x50A4,0x875A},//BMT1
+	{0x50A6,0x030F},//SINGLEK_FACTOR0
+	{0x50A6,0x0F0F},//SINGLEK_FACTOR0
+	{0x50A8,0x030F},//SINGLEK_FACTOR1
+	{0x50A8,0x0F0F},//SINGLEK_FACTOR1
+	{0x50AA,0x030F},//SINGLEK_FACTOR2
+	{0x50AA,0x050F},//SINGLEK_FACTOR2
+	{0x50AC,0x0301},//CROSS_FACTOR0
+	{0x50AC,0x0101},//CROSS_FACTOR0
+	{0x50AE,0x0301},//CROSS_FACTOR1
+	{0x50AE,0x0101},//CROSS_FACTOR1
+	{0x50B0,0x0301},//CROSS_FACTOR2
+	{0x50B0,0x0101},//CROSS_FACTOR2
+	{0x50B2,0x03FF},//SINGLE_MAX_FACTOR
+	{0x50B4,0x030F},//COUPLE_FACTOR0
+	{0x50B4,0x0F0F},//COUPLE_FACTOR0
+	{0x50B6,0x030F},//COUPLE_FACTOR1
+	{0x50B6,0x0F0F},//COUPLE_FACTOR1
+	{0x50B8,0x030F},//COUPLE_FACTOR2
+	{0x50B8,0x050F},//COUPLE_FACTOR2
+	{0x3D2A,0x0FFF},//T1_END_DEC_TH
+	{0x3D34,0x9C40},//T2_STR_DEC_TH
+	{0x3D36,0xFFFF},//T2_END_DEC_TH
+	{0x3D02,0x5033},//MEC_CTRL2
+	{0x3086,0x1A28},//PARK_ROW_ADDR
+	{0x33E4,0x0040},//VERT_SHADING_CONTROL
+	{0x3C70,0x6222},//CALIB_ROWS
+	{0x3110,0x0011},//HDR_CONTROL0
+	{0x30B0,0x0820},//DIGITAL_TEST
+	{0x3280,0x0ED8},//T1_BARRIER_C0
+	{0x3282,0x0ED8},//T1_BARRIER_C1
+	{0x3284,0x0ED8},//T1_BARRIER_C2
+	{0x3286,0x0ED8},//T1_BARRIER_C3
+	{0x3288,0x0ED8},//T2_BARRIER_C0
+	{0x328A,0x0ED8},//T2_BARRIER_C1
+	{0x328C,0x0ED8},//T2_BARRIER_C2
+	{0x328E,0x0ED8},//T2_BARRIER_C3
+	{0x3290,0x0ED8},//T3_BARRIER_C0
+	{0x3292,0x0ED8},//T3_BARRIER_C1
+	{0x3294,0x0ED8},//T3_BARRIER_C2
+	{0x3296,0x0ED8},//T3_BARRIER_C3
+	{0x3100,0xC001},//DLO_CONTROL0
+	{0x3102,0xBED8},//DLO_CONTROL1
+	{0x3104,0xBED8},//DLO_CONTROL2
+	{0x3106,0xBED8},//DLO_CONTROL3
+	{0x3108,0x07D0},//DLO_CONTROL4
+	{0x3116,0x2001},//HDR_CONTROL3
+	{0x3124,0x006D},//HDR_MD_CONTROL0
+	{0x3126,0x003C},//HDR_MD_CONTROL1
+	{0x31AE,0x0204},//SERIAL_FORMAT
+	{0x31AC,0x0C0C},//DATA_FORMAT_BITS
+	{0x3082,0x0014},//OPERATION_MODE_CTRL
+	{0x30BA,0x0135},//DIGITAL_CTRL
+	{0x3238,0x0044},//EXPOSURE_RATIO
+	{0x3012,0x07A0},//COARSE_INTEGRATION_TIME_
+	{0x3212,0x007A},//COARSE_INTEGRATION_TIME2
+	{0x300C,0x0A8C},//LINE_LENGTH_PCK_
+	{0x300A,0x0980},//FRAME_LENGTH_LINES_
+	{0x5914,0x4012},//SENSOR_GAIN_TABLE_SEL
+	{REG_DELAY,100},
+	{0x5914,0x4002},//SENSOR_GAIN_TABLE_SEL
+	{0x5910,0x608A},//SENSOR_GAIN_REG1
+	{0x5910,0x7091},//SENSOR_GAIN_REG1
+	{0x5910,0x689C},//SENSOR_GAIN_REG1
+	{0x5910,0x8885},//SENSOR_GAIN_REG1
+	{0x5910,0x98AD},//SENSOR_GAIN_REG1
+	{0x5910,0xA8A9},//SENSOR_GAIN_REG1
+	{0x5910,0xC894},//SENSOR_GAIN_REG1
+	{0x5910,0xC8D1},//SENSOR_GAIN_REG1
+	{0x5910,0xD88A},//SENSOR_GAIN_REG1
+	{0x5910,0xD8C3},//SENSOR_GAIN_REG1
+	{0x5910,0xD915},//SENSOR_GAIN_REG1
+	{0x5910,0xD988},//SENSOR_GAIN_REG1
+	{0x5910,0xDA2A},//SENSOR_GAIN_REG1
+	{0x5910,0xDB0E},//SENSOR_GAIN_REG1
+	{0x5910,0xDC53},//SENSOR_GAIN_REG1
+	{0x5910,0x608A},//SENSOR_GAIN_REG1
+	{0x5910,0xC919},//SENSOR_GAIN_REG1
+	{0x5910,0xCA00},//SENSOR_GAIN_REG1
+	{0x5910,0x0000},//SENSOR_GAIN_REG1
+	{0x5910,0x0000},//SENSOR_GAIN_REG1
+	{0x5910,0x0000},//SENSOR_GAIN_REG1
+	{0x5910,0x0001},//SENSOR_GAIN_REG1
+	{0x5910,0x0001},//SENSOR_GAIN_REG1
+	{0x5910,0x0003},//SENSOR_GAIN_REG1
+	{0x5910,0x0003},//SENSOR_GAIN_REG1
+	{0x5910,0x0003},//SENSOR_GAIN_REG1
+	{0x5910,0x0004},//SENSOR_GAIN_REG1
+	{0x5910,0x0004},//SENSOR_GAIN_REG1
+	{0x5910,0x0004},//SENSOR_GAIN_REG1
+	{0x5910,0x0004},//SENSOR_GAIN_REG1
+	{0x5910,0x0004},//SENSOR_GAIN_REG1
+	{0x5910,0x0004},//SENSOR_GAIN_REG1
+	{0x5910,0x0004},//SENSOR_GAIN_REG1
+	{0x5910,0x0002},//SENSOR_GAIN_REG1
+	{0x5910,0x0003},//SENSOR_GAIN_REG1
+	{0x5910,0x0003},//SENSOR_GAIN_REG1
+	{0x5910,0x5A8B},//SENSOR_GAIN_REG1
+	{0x5910,0xFF04},//SENSOR_GAIN_REG1
+	{0x5910,0xF704},//SENSOR_GAIN_REG1
+	{0x5910,0xFF04},//SENSOR_GAIN_REG1
+	{0x5910,0xF704},//SENSOR_GAIN_REG1
+	{0x5910,0xF704},//SENSOR_GAIN_REG1
+	{0x5910,0x0005},//SENSOR_GAIN_REG1
+	{0x5910,0x0006},//SENSOR_GAIN_REG1
+	{0x5910,0x0007},//SENSOR_GAIN_REG1
+	{0x5910,0x9A8B},//SENSOR_GAIN_REG1
+	{0x5910,0xFF04},//SENSOR_GAIN_REG1
+	{0x5910,0xF704},//SENSOR_GAIN_REG1
+	{0x5910,0xF704},//SENSOR_GAIN_REG1
+	{0x5910,0xF704},//SENSOR_GAIN_REG1
+	{0x5910,0xF704},//SENSOR_GAIN_REG1
+	{0x5910,0x0015},//SENSOR_GAIN_REG1
+	{0x5910,0x0016},//SENSOR_GAIN_REG1
+	{0x5910,0x0017},//SENSOR_GAIN_REG1
+	{0x5910,0xDA8B},//SENSOR_GAIN_REG1
+	{0x5910,0xFF04},//SENSOR_GAIN_REG1
+	{0x5910,0xF704},//SENSOR_GAIN_REG1
+	{0x5910,0xF704},//SENSOR_GAIN_REG1
+	{0x5910,0xF704},//SENSOR_GAIN_REG1
+	{0x5910,0xF704},//SENSOR_GAIN_REG1
+	{0x5910,0x0025},//SENSOR_GAIN_REG1
+	{0x5910,0x0026},//SENSOR_GAIN_REG1
+	{0x5910,0x0027},//SENSOR_GAIN_REG1
+	{0x5910,0x59B9},//SENSOR_GAIN_REG1
+	{0x5910,0x700F},//SENSOR_GAIN_REG1
+	{0x5910,0x7F0F},//SENSOR_GAIN_REG1
+	{0x5910,0x700F},//SENSOR_GAIN_REG1
+	{0x5910,0x7F0F},//SENSOR_GAIN_REG1
+	{0x5910,0x7F0F},//SENSOR_GAIN_REG1
+	{0x5910,0x0035},//SENSOR_GAIN_REG1
+	{0x5910,0x0036},//SENSOR_GAIN_REG1
+	{0x5910,0x0037},//SENSOR_GAIN_REG1
+	{0x5910,0x99B9},//SENSOR_GAIN_REG1
+	{0x5910,0x700F},//SENSOR_GAIN_REG1
+	{0x5910,0x7F0F},//SENSOR_GAIN_REG1
+	{0x5910,0x700F},//SENSOR_GAIN_REG1
+	{0x5910,0x7F0F},//SENSOR_GAIN_REG1
+	{0x5910,0x7F0F},//SENSOR_GAIN_REG1
+	{0x5910,0x0045},//SENSOR_GAIN_REG1
+	{0x5910,0x0046},//SENSOR_GAIN_REG1
+	{0x5910,0x0047},//SENSOR_GAIN_REG1
+	{0x5910,0xD9B9},//SENSOR_GAIN_REG1
+	{0x5910,0x700F},//SENSOR_GAIN_REG1
+	{0x5910,0x7F0F},//SENSOR_GAIN_REG1
+	{0x5910,0x700F},//SENSOR_GAIN_REG1
+	{0x5910,0x7F0F},//SENSOR_GAIN_REG1
+	{0x5910,0x7F0F},//SENSOR_GAIN_REG1
+	{0x5910,0x0055},//SENSOR_GAIN_REG1
+	{0x5910,0x0056},//SENSOR_GAIN_REG1
+	{0x5910,0x0057},//SENSOR_GAIN_REG1
+	{0x5910,0x9A85},//SENSOR_GAIN_REG1
+	{0x5910,0x0654},//SENSOR_GAIN_REG1
+	{0x5910,0x0654},//SENSOR_GAIN_REG1
+	{0x5910,0x0684},//SENSOR_GAIN_REG1
+	{0x5910,0x0654},//SENSOR_GAIN_REG1
+	{0x5910,0x0654},//SENSOR_GAIN_REG1
+	{0x5910,0x0065},//SENSOR_GAIN_REG1
+	{0x5910,0x0066},//SENSOR_GAIN_REG1
+	{0x5910,0x0067},//SENSOR_GAIN_REG1
+	{0x5910,0x59BD},//SENSOR_GAIN_REG1
+	{0x5910,0x1000},//SENSOR_GAIN_REG1
+	{0x5910,0x0C00},//SENSOR_GAIN_REG1
+	{0x5910,0x0F00},//SENSOR_GAIN_REG1
+	{0x5910,0x1000},//SENSOR_GAIN_REG1
+	{0x5910,0x10F0},//SENSOR_GAIN_REG1
+	{0x5910,0x0075},//SENSOR_GAIN_REG1
+	{0x5910,0x0076},//SENSOR_GAIN_REG1
+	{0x5910,0x0077},//SENSOR_GAIN_REG1
+	{0x5912,0x608A},//SENSOR_GAIN_REG2
+	{0x5912,0x7091},//SENSOR_GAIN_REG2
+	{0x5912,0x689C},//SENSOR_GAIN_REG2
+	{0x5912,0x8885},//SENSOR_GAIN_REG2
+	{0x5912,0x98AD},//SENSOR_GAIN_REG2
+	{0x5912,0xA8A9},//SENSOR_GAIN_REG2
+	{0x5912,0xC894},//SENSOR_GAIN_REG2
+	{0x5912,0xC8D1},//SENSOR_GAIN_REG2
+	{0x5912,0xC927},//SENSOR_GAIN_REG2
+	{0x5912,0xC9A0},//SENSOR_GAIN_REG2
+	{0x5912,0xCA4C},//SENSOR_GAIN_REG2
+	{0x5912,0xCB3F},//SENSOR_GAIN_REG2
+	{0x5912,0xCC97},//SENSOR_GAIN_REG2
+	{0x5912,0xCE7C},//SENSOR_GAIN_REG2
+	{0x5912,0xCFFF},//SENSOR_GAIN_REG2
+	{0x5912,0x608A},//SENSOR_GAIN_REG2
+	{0x5912,0xC8F0},//SENSOR_GAIN_REG2
+	{0x5912,0xCA00},//SENSOR_GAIN_REG2
+	{0x5912,0x0000},//SENSOR_GAIN_REG2
+	{0x5912,0x0000},//SENSOR_GAIN_REG2
+	{0x5912,0x0000},//SENSOR_GAIN_REG2
+	{0x5912,0x0001},//SENSOR_GAIN_REG2
+	{0x5912,0x0001},//SENSOR_GAIN_REG2
+	{0x5912,0x0003},//SENSOR_GAIN_REG2
+	{0x5912,0x0003},//SENSOR_GAIN_REG2
+	{0x5912,0x0003},//SENSOR_GAIN_REG2
+	{0x5912,0x0004},//SENSOR_GAIN_REG2
+	{0x5912,0x0004},//SENSOR_GAIN_REG2
+	{0x5912,0x0004},//SENSOR_GAIN_REG2
+	{0x5912,0x0004},//SENSOR_GAIN_REG2
+	{0x5912,0x0004},//SENSOR_GAIN_REG2
+	{0x5912,0x0004},//SENSOR_GAIN_REG2
+	{0x5912,0x0004},//SENSOR_GAIN_REG2
+	{0x5912,0x0002},//SENSOR_GAIN_REG2
+	{0x5912,0x0003},//SENSOR_GAIN_REG2
+	{0x5912,0x0003},//SENSOR_GAIN_REG2
+	{0x5912,0x5A8B},//SENSOR_GAIN_REG2
+	{0x5912,0xFF04},//SENSOR_GAIN_REG2
+	{0x5912,0xF704},//SENSOR_GAIN_REG2
+	{0x5912,0xFF04},//SENSOR_GAIN_REG2
+	{0x5912,0xF704},//SENSOR_GAIN_REG2
+	{0x5912,0xF704},//SENSOR_GAIN_REG2
+	{0x5912,0x0005},//SENSOR_GAIN_REG2
+	{0x5912,0x0006},//SENSOR_GAIN_REG2
+	{0x5912,0x0007},//SENSOR_GAIN_REG2
+	{0x5912,0x9A8B},//SENSOR_GAIN_REG2
+	{0x5912,0xFF04},//SENSOR_GAIN_REG2
+	{0x5912,0xF704},//SENSOR_GAIN_REG2
+	{0x5912,0xF704},//SENSOR_GAIN_REG2
+	{0x5912,0xF704},//SENSOR_GAIN_REG2
+	{0x5912,0xF704},//SENSOR_GAIN_REG2
+	{0x5912,0x0015},//SENSOR_GAIN_REG2
+	{0x5912,0x0016},//SENSOR_GAIN_REG2
+	{0x5912,0x0017},//SENSOR_GAIN_REG2
+	{0x5912,0xDA8B},//SENSOR_GAIN_REG2
+	{0x5912,0xFF04},//SENSOR_GAIN_REG2
+	{0x5912,0xF704},//SENSOR_GAIN_REG2
+	{0x5912,0xF704},//SENSOR_GAIN_REG2
+	{0x5912,0xF704},//SENSOR_GAIN_REG2
+	{0x5912,0xF704},//SENSOR_GAIN_REG2
+	{0x5912,0x0025},//SENSOR_GAIN_REG2
+	{0x5912,0x0026},//SENSOR_GAIN_REG2
+	{0x5912,0x0027},//SENSOR_GAIN_REG2
+	{0x5912,0x59B9},//SENSOR_GAIN_REG2
+	{0x5912,0x700F},//SENSOR_GAIN_REG2
+	{0x5912,0x7F0F},//SENSOR_GAIN_REG2
+	{0x5912,0x700F},//SENSOR_GAIN_REG2
+	{0x5912,0x7F0F},//SENSOR_GAIN_REG2
+	{0x5912,0x7F0F},//SENSOR_GAIN_REG2
+	{0x5912,0x0035},//SENSOR_GAIN_REG2
+	{0x5912,0x0036},//SENSOR_GAIN_REG2
+	{0x5912,0x0037},//SENSOR_GAIN_REG2
+	{0x5912,0x99B9},//SENSOR_GAIN_REG2
+	{0x5912,0x700F},//SENSOR_GAIN_REG2
+	{0x5912,0x7F0F},//SENSOR_GAIN_REG2
+	{0x5912,0x700F},//SENSOR_GAIN_REG2
+	{0x5912,0x7F0F},//SENSOR_GAIN_REG2
+	{0x5912,0x7F0F},//SENSOR_GAIN_REG2
+	{0x5912,0x0045},//SENSOR_GAIN_REG2
+	{0x5912,0x0046},//SENSOR_GAIN_REG2
+	{0x5912,0x0047},//SENSOR_GAIN_REG2
+	{0x5912,0xD9B9},//SENSOR_GAIN_REG2
+	{0x5912,0x700F},//SENSOR_GAIN_REG2
+	{0x5912,0x7F0F},//SENSOR_GAIN_REG2
+	{0x5912,0x700F},//SENSOR_GAIN_REG2
+	{0x5912,0x7F0F},//SENSOR_GAIN_REG2
+	{0x5912,0x7F0F},//SENSOR_GAIN_REG2
+	{0x5912,0x0055},//SENSOR_GAIN_REG2
+	{0x5912,0x0056},//SENSOR_GAIN_REG2
+	{0x5912,0x0057},//SENSOR_GAIN_REG2
+	{0x5912,0x9A85},//SENSOR_GAIN_REG2
+	{0x5912,0x0654},//SENSOR_GAIN_REG2
+	{0x5912,0x0654},//SENSOR_GAIN_REG2
+	{0x5912,0x0684},//SENSOR_GAIN_REG2
+	{0x5912,0x0654},//SENSOR_GAIN_REG2
+	{0x5912,0x0654},//SENSOR_GAIN_REG2
+	{0x5912,0x0065},//SENSOR_GAIN_REG2
+	{0x5912,0x0066},//SENSOR_GAIN_REG2
+	{0x5912,0x0067},//SENSOR_GAIN_REG2
+	{0x5912,0x59BD},//SENSOR_GAIN_REG2
+	{0x5912,0x1000},//SENSOR_GAIN_REG2
+	{0x5912,0x0C00},//SENSOR_GAIN_REG2
+	{0x5912,0x0F00},//SENSOR_GAIN_REG2
+	{0x5912,0x1000},//SENSOR_GAIN_REG2
+	{0x5912,0x10F0},//SENSOR_GAIN_REG2
+	{0x5912,0x0075},//SENSOR_GAIN_REG2
+	{0x5912,0x0076},//SENSOR_GAIN_REG2
+	{0x5912,0x0077},//SENSOR_GAIN_REG2
+	{0x5914,0x4006},//SENSOR_GAIN_TABLE_SEL
+	{0x5900,0x0020},//SENSOR_GAIN
+	{0x5902,0x0000},//SENSOR_GAIN_T2
+	{0x3110,0x0001},//HDR_CONTROL0
+
+	{REG_NULL, 0x00},
+};
+static const struct regval ar0822_hdr12bit_3840x2160_20fps_regs[] = {
+	{REG_DELAY, 2000},
+	{0x3030,0x0124},//PLL_MULTIPLIER
+	{0x302E,0x0006},//PRE_PLL_CLK_DIV
+	{0x302C,0x0002},//VT_SYS_CLK_DIV
+	{0x302A,0x0004},//VT_PIX_CLK_DIV
+	{0x3038,0x0002},//OP_SYS_CLK_DIV
+	{0x3036,0x0006},//OP_WORD_CLK_DIV
+	{0x31B0,0x0089},//FRAME_PREAMBLE
+	{0x31B2,0x005C},//LINE_PREAMBLE
+	{0x31B4,0x624A},//MIPI_TIMING_0
+	{0x31B6,0x630B},//MIPI_TIMING_1
+	{0x31B8,0x90CB},//MIPI_TIMING_2
+	{0x31BA,0x038E},//MIPI_TIMING_3
+	{0x31BC,0x508B},//MIPI_TIMING_4
+	{0x3342,0x122C},//MIPI_F1_PDT_EDT
+	{0x2512,0xA000},//SEQ_CTRL_PORT
+	{0x2510,0x0720},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0x2122},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0x26FF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x0F8C},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20E0},//SEQ_DATA_PORT
+	{0x2510,0x8055},//SEQ_DATA_PORT
+	{0x2510,0xA0E1},//SEQ_DATA_PORT
+	{0x2510,0x3041},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x3088},//SEQ_DATA_PORT
+	{0x2510,0x3282},//SEQ_DATA_PORT
+	{0x2510,0xA681},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FE},//SEQ_DATA_PORT
+	{0x2510,0x9070},//SEQ_DATA_PORT
+	{0x2510,0x891D},//SEQ_DATA_PORT
+	{0x2510,0x867F},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FC},//SEQ_DATA_PORT
+	{0x2510,0x893F},//SEQ_DATA_PORT
+	{0x2510,0x0F92},//SEQ_DATA_PORT
+	{0x2510,0x20E0},//SEQ_DATA_PORT
+	{0x2510,0x0F8F},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20E0},//SEQ_DATA_PORT
+	{0x2510,0x9770},//SEQ_DATA_PORT
+	{0x2510,0x20FC},//SEQ_DATA_PORT
+	{0x2510,0x8054},//SEQ_DATA_PORT
+	{0x2510,0x896C},//SEQ_DATA_PORT
+	{0x2510,0x200A},//SEQ_DATA_PORT
+	{0x2510,0x9030},//SEQ_DATA_PORT
+	{0x2510,0x200A},//SEQ_DATA_PORT
+	{0x2510,0x8040},//SEQ_DATA_PORT
+	{0x2510,0x8948},//SEQ_DATA_PORT
+	{0x2510,0x200A},//SEQ_DATA_PORT
+	{0x2510,0x1597},//SEQ_DATA_PORT
+	{0x2510,0x8808},//SEQ_DATA_PORT
+	{0x2510,0x200A},//SEQ_DATA_PORT
+	{0x2510,0x1F96},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20E0},//SEQ_DATA_PORT
+	{0x2510,0xA0C0},//SEQ_DATA_PORT
+	{0x2510,0x200A},//SEQ_DATA_PORT
+	{0x2510,0x3044},//SEQ_DATA_PORT
+	{0x2510,0x3088},//SEQ_DATA_PORT
+	{0x2510,0x3282},//SEQ_DATA_PORT
+	{0x2510,0x2004},//SEQ_DATA_PORT
+	{0x2510,0x1FAA},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20E0},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x3108},//SEQ_DATA_PORT
+	{0x2510,0x2400},//SEQ_DATA_PORT
+	{0x2510,0x3244},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x3108},//SEQ_DATA_PORT
+	{0x2510,0x2400},//SEQ_DATA_PORT
+	{0x2510,0x2702},//SEQ_DATA_PORT
+	{0x2510,0x3242},//SEQ_DATA_PORT
+	{0x2510,0x3108},//SEQ_DATA_PORT
+	{0x2510,0x2420},//SEQ_DATA_PORT
+	{0x2510,0x2703},//SEQ_DATA_PORT
+	{0x2510,0x3242},//SEQ_DATA_PORT
+	{0x2510,0x3108},//SEQ_DATA_PORT
+	{0x2510,0x2420},//SEQ_DATA_PORT
+	{0x2510,0x2704},//SEQ_DATA_PORT
+	{0x2510,0x3244},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x8801},//SEQ_DATA_PORT
+	{0x2510,0x000F},//SEQ_DATA_PORT
+	{0x2510,0x109C},//SEQ_DATA_PORT
+	{0x2510,0x8855},//SEQ_DATA_PORT
+	{0x2510,0x3101},//SEQ_DATA_PORT
+	{0x2510,0x3041},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x3102},//SEQ_DATA_PORT
+	{0x2510,0x3041},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x3181},//SEQ_DATA_PORT
+	{0x2510,0x3041},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x3188},//SEQ_DATA_PORT
+	{0x2510,0x3041},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x3282},//SEQ_DATA_PORT
+	{0x2510,0x3104},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0xB0E4},//SEQ_DATA_PORT
+	{0x2510,0xAD92},//SEQ_DATA_PORT
+	{0x2510,0xBC0C},//SEQ_DATA_PORT
+	{0x2510,0x1028},//SEQ_DATA_PORT
+	{0x2510,0x0022},//SEQ_DATA_PORT
+	{0x2510,0xC020},//SEQ_DATA_PORT
+	{0x2510,0x003E},//SEQ_DATA_PORT
+	{0x2510,0x0045},//SEQ_DATA_PORT
+	{0x2510,0x00B0},//SEQ_DATA_PORT
+	{0x2510,0x0028},//SEQ_DATA_PORT
+	{0x2510,0x30C1},//SEQ_DATA_PORT
+	{0x2510,0x8015},//SEQ_DATA_PORT
+	{0x2510,0xA038},//SEQ_DATA_PORT
+	{0x2510,0x100F},//SEQ_DATA_PORT
+	{0x2510,0x0507},//SEQ_DATA_PORT
+	{0x2510,0xA220},//SEQ_DATA_PORT
+	{0x2510,0x0010},//SEQ_DATA_PORT
+	{0x2510,0x10C2},//SEQ_DATA_PORT
+	{0x2510,0xB760},//SEQ_DATA_PORT
+	{0x2510,0x0033},//SEQ_DATA_PORT
+	{0x2510,0x1082},//SEQ_DATA_PORT
+	{0x2510,0x100B},//SEQ_DATA_PORT
+	{0x2510,0x1029},//SEQ_DATA_PORT
+	{0x2510,0xA85A},//SEQ_DATA_PORT
+	{0x2510,0x998D},//SEQ_DATA_PORT
+	{0x2510,0xC810},//SEQ_DATA_PORT
+	{0x2510,0x2004},//SEQ_DATA_PORT
+	{0x2510,0x0ECE},//SEQ_DATA_PORT
+	{0x2510,0x123B},//SEQ_DATA_PORT
+	{0x2510,0xC000},//SEQ_DATA_PORT
+	{0x2510,0x032F},//SEQ_DATA_PORT
+	{0x2510,0x11D5},//SEQ_DATA_PORT
+	{0x2510,0x162F},//SEQ_DATA_PORT
+	{0x2510,0x9000},//SEQ_DATA_PORT
+	{0x2510,0x2034},//SEQ_DATA_PORT
+	{0x2510,0x0015},//SEQ_DATA_PORT
+	{0x2510,0x04CB},//SEQ_DATA_PORT
+	{0x2510,0x1022},//SEQ_DATA_PORT
+	{0x2510,0x1031},//SEQ_DATA_PORT
+	{0x2510,0x002D},//SEQ_DATA_PORT
+	{0x2510,0x1015},//SEQ_DATA_PORT
+	{0x2510,0x80B9},//SEQ_DATA_PORT
+	{0x2510,0xA101},//SEQ_DATA_PORT
+	{0x2510,0x001C},//SEQ_DATA_PORT
+	{0x2510,0x008E},//SEQ_DATA_PORT
+	{0x2510,0x124B},//SEQ_DATA_PORT
+	{0x2510,0x01B5},//SEQ_DATA_PORT
+	{0x2510,0x0B92},//SEQ_DATA_PORT
+	{0x2510,0xA400},//SEQ_DATA_PORT
+	{0x2510,0x8091},//SEQ_DATA_PORT
+	{0x2510,0x0028},//SEQ_DATA_PORT
+	{0x2510,0x3002},//SEQ_DATA_PORT
+	{0x2510,0x2004},//SEQ_DATA_PORT
+	{0x2510,0x1012},//SEQ_DATA_PORT
+	{0x2510,0x100E},//SEQ_DATA_PORT
+	{0x2510,0x10A8},//SEQ_DATA_PORT
+	{0x2510,0x00A1},//SEQ_DATA_PORT
+	{0x2510,0x132D},//SEQ_DATA_PORT
+	{0x2510,0x09AF},//SEQ_DATA_PORT
+	{0x2510,0x0159},//SEQ_DATA_PORT
+	{0x2510,0x121D},//SEQ_DATA_PORT
+	{0x2510,0x1259},//SEQ_DATA_PORT
+	{0x2510,0x11AF},//SEQ_DATA_PORT
+	{0x2510,0x18B5},//SEQ_DATA_PORT
+	{0x2510,0x0395},//SEQ_DATA_PORT
+	{0x2510,0x054B},//SEQ_DATA_PORT
+	{0x2510,0x1021},//SEQ_DATA_PORT
+	{0x2510,0x0020},//SEQ_DATA_PORT
+	{0x2510,0x1015},//SEQ_DATA_PORT
+	{0x2510,0x1030},//SEQ_DATA_PORT
+	{0x2510,0x00CF},//SEQ_DATA_PORT
+	{0x2510,0xB146},//SEQ_DATA_PORT
+	{0x2510,0xC290},//SEQ_DATA_PORT
+	{0x2510,0x103C},//SEQ_DATA_PORT
+	{0x2510,0xA882},//SEQ_DATA_PORT
+	{0x2510,0x8055},//SEQ_DATA_PORT
+	{0x2510,0x00A9},//SEQ_DATA_PORT
+	{0x2510,0x8801},//SEQ_DATA_PORT
+	{0x2510,0xB700},//SEQ_DATA_PORT
+	{0x2510,0x0001},//SEQ_DATA_PORT
+	{0x2510,0x02A2},//SEQ_DATA_PORT
+	{0x2510,0x000A},//SEQ_DATA_PORT
+	{0x2510,0x98BB},//SEQ_DATA_PORT
+	{0x2510,0x203F},//SEQ_DATA_PORT
+	{0x2510,0x0036},//SEQ_DATA_PORT
+	{0x2510,0x1001},//SEQ_DATA_PORT
+	{0x2510,0x99BE},//SEQ_DATA_PORT
+	{0x2510,0x0139},//SEQ_DATA_PORT
+	{0x2510,0x100A},//SEQ_DATA_PORT
+	{0x2510,0x0040},//SEQ_DATA_PORT
+	{0x2510,0x1022},//SEQ_DATA_PORT
+	{0x2510,0x124C},//SEQ_DATA_PORT
+	{0x2510,0x109F},//SEQ_DATA_PORT
+	{0x2510,0x15A3},//SEQ_DATA_PORT
+	{0x2510,0x002A},//SEQ_DATA_PORT
+	{0x2510,0x3081},//SEQ_DATA_PORT
+	{0x2510,0x2001},//SEQ_DATA_PORT
+	{0x2510,0x3044},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x112A},//SEQ_DATA_PORT
+	{0x2510,0x101D},//SEQ_DATA_PORT
+	{0x2510,0x202B},//SEQ_DATA_PORT
+	{0x2510,0x02B8},//SEQ_DATA_PORT
+	{0x2510,0x10B8},//SEQ_DATA_PORT
+	{0x2510,0x1136},//SEQ_DATA_PORT
+	{0x2510,0x996B},//SEQ_DATA_PORT
+	{0x2510,0x004C},//SEQ_DATA_PORT
+	{0x2510,0x1039},//SEQ_DATA_PORT
+	{0x2510,0x1040},//SEQ_DATA_PORT
+	{0x2510,0x00B5},//SEQ_DATA_PORT
+	{0x2510,0x03C4},//SEQ_DATA_PORT
+	{0x2510,0x1144},//SEQ_DATA_PORT
+	{0x2510,0x1245},//SEQ_DATA_PORT
+	{0x2510,0x9A7B},//SEQ_DATA_PORT
+	{0x2510,0x002B},//SEQ_DATA_PORT
+	{0x2510,0x30D0},//SEQ_DATA_PORT
+	{0x2510,0x3141},//SEQ_DATA_PORT
+	{0x2510,0x3041},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x3142},//SEQ_DATA_PORT
+	{0x2510,0x3041},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x3110},//SEQ_DATA_PORT
+	{0x2510,0x3041},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x3120},//SEQ_DATA_PORT
+	{0x2510,0x3041},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x3144},//SEQ_DATA_PORT
+	{0x2510,0x3041},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x3148},//SEQ_DATA_PORT
+	{0x2510,0x3041},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x3182},//SEQ_DATA_PORT
+	{0x2510,0x3041},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x3184},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x3190},//SEQ_DATA_PORT
+	{0x2510,0x3041},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x31A0},//SEQ_DATA_PORT
+	{0x2510,0x3088},//SEQ_DATA_PORT
+	{0x2510,0x2201},//SEQ_DATA_PORT
+	{0x2510,0x807D},//SEQ_DATA_PORT
+	{0x2510,0x2206},//SEQ_DATA_PORT
+	{0x2510,0x8815},//SEQ_DATA_PORT
+	{0x2510,0x8877},//SEQ_DATA_PORT
+	{0x2510,0x0092},//SEQ_DATA_PORT
+	{0x2510,0x220E},//SEQ_DATA_PORT
+	{0x2510,0x2211},//SEQ_DATA_PORT
+	{0x2510,0x8055},//SEQ_DATA_PORT
+	{0x2510,0x3001},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x8A61},//SEQ_DATA_PORT
+	{0x2510,0x8801},//SEQ_DATA_PORT
+	{0x2510,0x1092},//SEQ_DATA_PORT
+	{0x2510,0x181F},//SEQ_DATA_PORT
+	{0x2510,0x0B1F},//SEQ_DATA_PORT
+	{0x2510,0x101F},//SEQ_DATA_PORT
+	{0x2510,0x00B6},//SEQ_DATA_PORT
+	{0x2510,0x0023},//SEQ_DATA_PORT
+	{0x2510,0x00B9},//SEQ_DATA_PORT
+	{0x2510,0x104C},//SEQ_DATA_PORT
+	{0x2510,0x996E},//SEQ_DATA_PORT
+	{0x2510,0x0140},//SEQ_DATA_PORT
+	{0x2510,0x0257},//SEQ_DATA_PORT
+	{0x2510,0x1035},//SEQ_DATA_PORT
+	{0x2510,0x9F26},//SEQ_DATA_PORT
+	{0x2510,0x1423},//SEQ_DATA_PORT
+	{0x2510,0x0048},//SEQ_DATA_PORT
+	{0x2510,0xC878},//SEQ_DATA_PORT
+	{0x2510,0x200A},//SEQ_DATA_PORT
+	{0x2510,0x1548},//SEQ_DATA_PORT
+	{0x2510,0x0C49},//SEQ_DATA_PORT
+	{0x2510,0x1149},//SEQ_DATA_PORT
+	{0x2510,0x002A},//SEQ_DATA_PORT
+	{0x2510,0x1057},//SEQ_DATA_PORT
+	{0x2510,0x3281},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x3044},//SEQ_DATA_PORT
+	{0x2510,0x2001},//SEQ_DATA_PORT
+	{0x2510,0xA020},//SEQ_DATA_PORT
+	{0x2510,0x000C},//SEQ_DATA_PORT
+	{0x2510,0x9825},//SEQ_DATA_PORT
+	{0x2510,0x1040},//SEQ_DATA_PORT
+	{0x2510,0x1054},//SEQ_DATA_PORT
+	{0x2510,0xB06D},//SEQ_DATA_PORT
+	{0x2510,0x0035},//SEQ_DATA_PORT
+	{0x2510,0x004D},//SEQ_DATA_PORT
+	{0x2510,0x9905},//SEQ_DATA_PORT
+	{0x2510,0xB064},//SEQ_DATA_PORT
+	{0x2510,0x99C5},//SEQ_DATA_PORT
+	{0x2510,0x0047},//SEQ_DATA_PORT
+	{0x2510,0xB920},//SEQ_DATA_PORT
+	{0x2510,0x1447},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x31F8,0x0008},//MIPI_CONFIG_2
+	{0x3C70,0x6828},//CALIB_ROWS
+	{0x3092,0x0826},//ROW_NOISE_CONTROL
+	{0x3428,0x0209},//SEQUENCER_CONTROL
+	{0x3516,0xFF04},//DAC_LD_22_23
+	{0x3526,0x6480},//DAC_LD_38_39
+	{0x3504,0x8AAA},//DAC_LD_4_5
+	{0x353C,0x220C},//DAC_LD_60_61
+	{0x3536,0x4C6E},//DAC_LD_54_55
+	{0x3D2A,0x0FFF},//T1_END_DEC_TH
+	{0x3364,0x00EC},//DCG_TRIM
+	{0x3512,0x8888},//DAC_LD_18_19
+	{0x3514,0x888F},//DAC_LD_20_21
+	{0x3520,0xFBF0},//DAC_LD_32_33
+	{0x3524,0xB2A1},//DAC_LD_36_37
+	{0x3528,0xCC84},//DAC_LD_40_41
+	{0x3532,0x4C8E},//DAC_LD_50_51
+	{0x3534,0x4E64},//DAC_LD_52_53
+	{0x351E,0x5856},//DAC_LD_30_31
+	{0x353E,0x98F2},//DAC_LD_62_63
+	{0x352E,0x6A8A},//DAC_LD_46_47
+	{0x3370,0x0211},//DBLC_CONTROL
+	{0x3372,0x700F},//DBLC_FS0_CONTROL
+	{0x3540,0x3597},//DAC_LD_64_65
+	{0x58E2,0x0BE3},//COL_COUNT_VALUES1
+	{0x58E4,0x18B4},//COL_COUNT_VALUES2
+	{0x3522,0x7C97},//DAC_LD_34_35
+	{0x30BA,0x0024},//DIGITAL_CTRL
+	{0x31D4,0x0042},//CLK_MEM_GATING_CTRL
+	{0x352A,0x6F8F},//DAC_LD_42_43
+	{0x3530,0x4A08},//DAC_LD_48_49
+	{0x351A,0x5FFF},//DAC_LD_26_27
+	{0x350E,0x39D9},//DAC_LD_14_15
+	{0x3510,0x9988},//DAC_LD_16_17
+	{0x3380,0x1FFF},//DBLC_OFFSET1
+	{0x337A,0x1000},//DBLC_SCALE1
+	{0x3092,0x0800},//ROW_NOISE_CONTROL
+	{0x350A,0x0654},//DAC_LD_10_11
+	{0x3364,0x00E0},//DCG_TRIM
+	{0x591E,0x61AE},//ANALOG_GAIN_WR_DATA
+	{0x591E,0x722C},//ANALOG_GAIN_WR_DATA
+	{0x591E,0x82B8},//ANALOG_GAIN_WR_DATA
+	{0x591E,0x92F6},//ANALOG_GAIN_WR_DATA
+	{0x591E,0xA447},//ANALOG_GAIN_WR_DATA
+	{0x591E,0xB66D},//ANALOG_GAIN_WR_DATA
+	{0x591E,0xC6EA},//ANALOG_GAIN_WR_DATA
+	{0x591E,0xDECD},//ANALOG_GAIN_WR_DATA
+	{0x3532,0x4C8A},//DAC_LD_50_51
+	{0x3534,0x4E60},//DAC_LD_52_53
+	{0x353E,0x90F2},//DAC_LD_62_63
+	{0x351A,0x4FFF},//DAC_LD_26_27
+	{0x591C,0x00D7},//DGR_AMP_GAIN
+	{0x3522,0x6097},//DAC_LD_34_35
+	{0x5002,0x37C3},//T1_PIX_DEF_ID2
+	{0x51CC,0x0149},//T1_NOISE_GAIN_THRESHOLD0
+	{0x51D8,0x044D},//T1_NOISE_GAIN_THRESHOLD1
+	{0x51CE,0x0700},//T1_NOISE_GAIN_THRESHOLD2
+	{0x51D0,0x0001},//T1_NOISE_FLOOR0
+	{0x51D2,0x0002},//T1_NOISE_FLOOR1
+	{0x51D4,0x0003},//T1_NOISE_FLOOR2
+	{0x51D6,0x0004},//T1_NOISE_FLOOR3
+	{0x5202,0x37C3},//T2_PIX_DEF_ID2
+	{0x51EA,0x0149},//T2_NOISE_GAIN_THRESHOLD0
+	{0x51FC,0x044D},//T2_NOISE_GAIN_THRESHOLD1
+	{0x51EC,0x0700},//T2_NOISE_GAIN_THRESHOLD2
+	{0x51EE,0x0001},//T2_NOISE_FLOOR0
+	{0x51F0,0x0002},//T2_NOISE_FLOOR1
+	{0x51F2,0x0003},//T2_NOISE_FLOOR2
+	{0x51F4,0x0004},//T2_NOISE_FLOOR3
+	{0x5402,0x37C3},//T4_PIX_DEF_ID2
+	{0x5560,0x0149},//T4_NOISE_GAIN_THRESHOLD0
+	{0x556C,0x044D},//T4_NOISE_GAIN_THRESHOLD1
+	{0x5562,0x0700},//T4_NOISE_GAIN_THRESHOLD2
+	{0x5564,0x0001},//T4_NOISE_FLOOR0
+	{0x5566,0x0002},//T4_NOISE_FLOOR1
+	{0x5568,0x0003},//T4_NOISE_FLOOR2
+	{0x556A,0x0004},//T4_NOISE_FLOOR3
+	{0x31E0,0x0001},//PIX_DEF_ID
+	{0x5000,0x0080},//T1_PIX_DEF_ID
+	{0x5000,0x0180},//T1_PIX_DEF_ID
+	{0x5000,0x0180},//T1_PIX_DEF_ID
+	{0x5200,0x0080},//T2_PIX_DEF_ID
+	{0x5200,0x0180},//T2_PIX_DEF_ID
+	{0x5200,0x0180},//T2_PIX_DEF_ID
+	{0x5400,0x0080},//T4_PIX_DEF_ID
+	{0x5400,0x0180},//T4_PIX_DEF_ID
+	{0x5400,0x0180},//T4_PIX_DEF_ID
+	{0x5000,0x0180},//T1_PIX_DEF_ID
+	{0x5200,0x0180},//T2_PIX_DEF_ID
+	{0x5400,0x0180},//T4_PIX_DEF_ID
+	{0x50A2,0x3F2A},//BMT0
+	{0x50A4,0x875A},//BMT1
+	{0x50A6,0x030F},//SINGLEK_FACTOR0
+	{0x50A6,0x0F0F},//SINGLEK_FACTOR0
+	{0x50A8,0x030F},//SINGLEK_FACTOR1
+	{0x50A8,0x0F0F},//SINGLEK_FACTOR1
+	{0x50AA,0x030F},//SINGLEK_FACTOR2
+	{0x50AA,0x050F},//SINGLEK_FACTOR2
+	{0x50AC,0x0301},//CROSS_FACTOR0
+	{0x50AC,0x0101},//CROSS_FACTOR0
+	{0x50AE,0x0301},//CROSS_FACTOR1
+	{0x50AE,0x0101},//CROSS_FACTOR1
+	{0x50B0,0x0301},//CROSS_FACTOR2
+	{0x50B0,0x0101},//CROSS_FACTOR2
+	{0x50B2,0x03FF},//SINGLE_MAX_FACTOR
+	{0x50B4,0x030F},//COUPLE_FACTOR0
+	{0x50B4,0x0F0F},//COUPLE_FACTOR0
+	{0x50B6,0x030F},//COUPLE_FACTOR1
+	{0x50B6,0x0F0F},//COUPLE_FACTOR1
+	{0x50B8,0x030F},//COUPLE_FACTOR2
+	{0x50B8,0x050F},//COUPLE_FACTOR2
+	{0x3D2A,0x0FFF},//T1_END_DEC_TH
+	{0x3D34,0x9C40},//T2_STR_DEC_TH
+	{0x3D36,0xFFFF},//T2_END_DEC_TH
+	{0x3D02,0x5033},//MEC_CTRL2
+	{0x3086,0x1A28},//PARK_ROW_ADDR
+	{0x33E4,0x0040},//VERT_SHADING_CONTROL
+	{0x3C70,0x6222},//CALIB_ROWS
+	{0x3110,0x0011},//HDR_CONTROL0
+	{0x30B0,0x0820},//DIGITAL_TEST
+	{0x3280,0x0ED8},//T1_BARRIER_C0
+	{0x3282,0x0ED8},//T1_BARRIER_C1
+	{0x3284,0x0ED8},//T1_BARRIER_C2
+	{0x3286,0x0ED8},//T1_BARRIER_C3
+	{0x3288,0x0ED8},//T2_BARRIER_C0
+	{0x328A,0x0ED8},//T2_BARRIER_C1
+	{0x328C,0x0ED8},//T2_BARRIER_C2
+	{0x328E,0x0ED8},//T2_BARRIER_C3
+	{0x3290,0x0ED8},//T3_BARRIER_C0
+	{0x3292,0x0ED8},//T3_BARRIER_C1
+	{0x3294,0x0ED8},//T3_BARRIER_C2
+	{0x3296,0x0ED8},//T3_BARRIER_C3
+	{0x3100,0xC001},//DLO_CONTROL0
+	{0x3102,0xBED8},//DLO_CONTROL1
+	{0x3104,0xBED8},//DLO_CONTROL2
+	{0x3106,0xBED8},//DLO_CONTROL3
+	{0x3108,0x07D0},//DLO_CONTROL4
+	{0x3116,0x2001},//HDR_CONTROL3
+	{0x3124,0x006D},//HDR_MD_CONTROL0
+	{0x3126,0x003C},//HDR_MD_CONTROL1
+	{0x31AE,0x0204},//SERIAL_FORMAT
+	{0x31AC,0x0C0C},//DATA_FORMAT_BITS
+	{0x3082,0x0014},//OPERATION_MODE_CTRL
+	{0x30BA,0x0135},//DIGITAL_CTRL
+	{0x3238,0x0044},//EXPOSURE_RATIO
+	{0x3012,0x0900},//COARSE_INTEGRATION_TIME_
+	{0x3212,0x0090},//COARSE_INTEGRATION_TIME2
+	{0x300C,0x0CE2},//LINE_LENGTH_PCK_
+	{0x300A,0x09B8},//FRAME_LENGTH_LINES_
+	{0x5914,0x4012},//SENSOR_GAIN_TABLE_SEL
+	{REG_DELAY,100},
+	{0x5914,0x4002},//SENSOR_GAIN_TABLE_SEL
+	{0x5910,0x608A},//SENSOR_GAIN_REG1
+	{0x5910,0x7091},//SENSOR_GAIN_REG1
+	{0x5910,0x689C},//SENSOR_GAIN_REG1
+	{0x5910,0x8885},//SENSOR_GAIN_REG1
+	{0x5910,0x98AD},//SENSOR_GAIN_REG1
+	{0x5910,0xA8A9},//SENSOR_GAIN_REG1
+	{0x5910,0xC894},//SENSOR_GAIN_REG1
+	{0x5910,0xC8D1},//SENSOR_GAIN_REG1
+	{0x5910,0xD88A},//SENSOR_GAIN_REG1
+	{0x5910,0xD8C3},//SENSOR_GAIN_REG1
+	{0x5910,0xD915},//SENSOR_GAIN_REG1
+	{0x5910,0xD988},//SENSOR_GAIN_REG1
+	{0x5910,0xDA2A},//SENSOR_GAIN_REG1
+	{0x5910,0xDB0E},//SENSOR_GAIN_REG1
+	{0x5910,0xDC53},//SENSOR_GAIN_REG1
+	{0x5910,0x608A},//SENSOR_GAIN_REG1
+	{0x5910,0xC919},//SENSOR_GAIN_REG1
+	{0x5910,0xCA00},//SENSOR_GAIN_REG1
+	{0x5910,0x0000},//SENSOR_GAIN_REG1
+	{0x5910,0x0000},//SENSOR_GAIN_REG1
+	{0x5910,0x0000},//SENSOR_GAIN_REG1
+	{0x5910,0x0001},//SENSOR_GAIN_REG1
+	{0x5910,0x0001},//SENSOR_GAIN_REG1
+	{0x5910,0x0003},//SENSOR_GAIN_REG1
+	{0x5910,0x0003},//SENSOR_GAIN_REG1
+	{0x5910,0x0003},//SENSOR_GAIN_REG1
+	{0x5910,0x0004},//SENSOR_GAIN_REG1
+	{0x5910,0x0004},//SENSOR_GAIN_REG1
+	{0x5910,0x0004},//SENSOR_GAIN_REG1
+	{0x5910,0x0004},//SENSOR_GAIN_REG1
+	{0x5910,0x0004},//SENSOR_GAIN_REG1
+	{0x5910,0x0004},//SENSOR_GAIN_REG1
+	{0x5910,0x0004},//SENSOR_GAIN_REG1
+	{0x5910,0x0002},//SENSOR_GAIN_REG1
+	{0x5910,0x0003},//SENSOR_GAIN_REG1
+	{0x5910,0x0003},//SENSOR_GAIN_REG1
+	{0x5910,0x5A8B},//SENSOR_GAIN_REG1
+	{0x5910,0xFF04},//SENSOR_GAIN_REG1
+	{0x5910,0xF704},//SENSOR_GAIN_REG1
+	{0x5910,0xFF04},//SENSOR_GAIN_REG1
+	{0x5910,0xF704},//SENSOR_GAIN_REG1
+	{0x5910,0xF704},//SENSOR_GAIN_REG1
+	{0x5910,0x0005},//SENSOR_GAIN_REG1
+	{0x5910,0x0006},//SENSOR_GAIN_REG1
+	{0x5910,0x0007},//SENSOR_GAIN_REG1
+	{0x5910,0x9A8B},//SENSOR_GAIN_REG1
+	{0x5910,0xFF04},//SENSOR_GAIN_REG1
+	{0x5910,0xF704},//SENSOR_GAIN_REG1
+	{0x5910,0xF704},//SENSOR_GAIN_REG1
+	{0x5910,0xF704},//SENSOR_GAIN_REG1
+	{0x5910,0xF704},//SENSOR_GAIN_REG1
+	{0x5910,0x0015},//SENSOR_GAIN_REG1
+	{0x5910,0x0016},//SENSOR_GAIN_REG1
+	{0x5910,0x0017},//SENSOR_GAIN_REG1
+	{0x5910,0xDA8B},//SENSOR_GAIN_REG1
+	{0x5910,0xFF04},//SENSOR_GAIN_REG1
+	{0x5910,0xF704},//SENSOR_GAIN_REG1
+	{0x5910,0xF704},//SENSOR_GAIN_REG1
+	{0x5910,0xF704},//SENSOR_GAIN_REG1
+	{0x5910,0xF704},//SENSOR_GAIN_REG1
+	{0x5910,0x0025},//SENSOR_GAIN_REG1
+	{0x5910,0x0026},//SENSOR_GAIN_REG1
+	{0x5910,0x0027},//SENSOR_GAIN_REG1
+	{0x5910,0x59B9},//SENSOR_GAIN_REG1
+	{0x5910,0x700F},//SENSOR_GAIN_REG1
+	{0x5910,0x7F0F},//SENSOR_GAIN_REG1
+	{0x5910,0x700F},//SENSOR_GAIN_REG1
+	{0x5910,0x7F0F},//SENSOR_GAIN_REG1
+	{0x5910,0x7F0F},//SENSOR_GAIN_REG1
+	{0x5910,0x0035},//SENSOR_GAIN_REG1
+	{0x5910,0x0036},//SENSOR_GAIN_REG1
+	{0x5910,0x0037},//SENSOR_GAIN_REG1
+	{0x5910,0x99B9},//SENSOR_GAIN_REG1
+	{0x5910,0x700F},//SENSOR_GAIN_REG1
+	{0x5910,0x7F0F},//SENSOR_GAIN_REG1
+	{0x5910,0x700F},//SENSOR_GAIN_REG1
+	{0x5910,0x7F0F},//SENSOR_GAIN_REG1
+	{0x5910,0x7F0F},//SENSOR_GAIN_REG1
+	{0x5910,0x0045},//SENSOR_GAIN_REG1
+	{0x5910,0x0046},//SENSOR_GAIN_REG1
+	{0x5910,0x0047},//SENSOR_GAIN_REG1
+	{0x5910,0xD9B9},//SENSOR_GAIN_REG1
+	{0x5910,0x700F},//SENSOR_GAIN_REG1
+	{0x5910,0x7F0F},//SENSOR_GAIN_REG1
+	{0x5910,0x700F},//SENSOR_GAIN_REG1
+	{0x5910,0x7F0F},//SENSOR_GAIN_REG1
+	{0x5910,0x7F0F},//SENSOR_GAIN_REG1
+	{0x5910,0x0055},//SENSOR_GAIN_REG1
+	{0x5910,0x0056},//SENSOR_GAIN_REG1
+	{0x5910,0x0057},//SENSOR_GAIN_REG1
+	{0x5910,0x9A85},//SENSOR_GAIN_REG1
+	{0x5910,0x0654},//SENSOR_GAIN_REG1
+	{0x5910,0x0654},//SENSOR_GAIN_REG1
+	{0x5910,0x0684},//SENSOR_GAIN_REG1
+	{0x5910,0x0654},//SENSOR_GAIN_REG1
+	{0x5910,0x0654},//SENSOR_GAIN_REG1
+	{0x5910,0x0065},//SENSOR_GAIN_REG1
+	{0x5910,0x0066},//SENSOR_GAIN_REG1
+	{0x5910,0x0067},//SENSOR_GAIN_REG1
+	{0x5910,0x59BD},//SENSOR_GAIN_REG1
+	{0x5910,0x1000},//SENSOR_GAIN_REG1
+	{0x5910,0x0C00},//SENSOR_GAIN_REG1
+	{0x5910,0x0F00},//SENSOR_GAIN_REG1
+	{0x5910,0x1000},//SENSOR_GAIN_REG1
+	{0x5910,0x10F0},//SENSOR_GAIN_REG1
+	{0x5910,0x0075},//SENSOR_GAIN_REG1
+	{0x5910,0x0076},//SENSOR_GAIN_REG1
+	{0x5910,0x0077},//SENSOR_GAIN_REG1
+	{0x5912,0x608A},//SENSOR_GAIN_REG2
+	{0x5912,0x7091},//SENSOR_GAIN_REG2
+	{0x5912,0x689C},//SENSOR_GAIN_REG2
+	{0x5912,0x8885},//SENSOR_GAIN_REG2
+	{0x5912,0x98AD},//SENSOR_GAIN_REG2
+	{0x5912,0xA8A9},//SENSOR_GAIN_REG2
+	{0x5912,0xC894},//SENSOR_GAIN_REG2
+	{0x5912,0xC8D1},//SENSOR_GAIN_REG2
+	{0x5912,0xC927},//SENSOR_GAIN_REG2
+	{0x5912,0xC9A0},//SENSOR_GAIN_REG2
+	{0x5912,0xCA4C},//SENSOR_GAIN_REG2
+	{0x5912,0xCB3F},//SENSOR_GAIN_REG2
+	{0x5912,0xCC97},//SENSOR_GAIN_REG2
+	{0x5912,0xCE7C},//SENSOR_GAIN_REG2
+	{0x5912,0xCFFF},//SENSOR_GAIN_REG2
+	{0x5912,0x608A},//SENSOR_GAIN_REG2
+	{0x5912,0xC8F0},//SENSOR_GAIN_REG2
+	{0x5912,0xCA00},//SENSOR_GAIN_REG2
+	{0x5912,0x0000},//SENSOR_GAIN_REG2
+	{0x5912,0x0000},//SENSOR_GAIN_REG2
+	{0x5912,0x0000},//SENSOR_GAIN_REG2
+	{0x5912,0x0001},//SENSOR_GAIN_REG2
+	{0x5912,0x0001},//SENSOR_GAIN_REG2
+	{0x5912,0x0003},//SENSOR_GAIN_REG2
+	{0x5912,0x0003},//SENSOR_GAIN_REG2
+	{0x5912,0x0003},//SENSOR_GAIN_REG2
+	{0x5912,0x0004},//SENSOR_GAIN_REG2
+	{0x5912,0x0004},//SENSOR_GAIN_REG2
+	{0x5912,0x0004},//SENSOR_GAIN_REG2
+	{0x5912,0x0004},//SENSOR_GAIN_REG2
+	{0x5912,0x0004},//SENSOR_GAIN_REG2
+	{0x5912,0x0004},//SENSOR_GAIN_REG2
+	{0x5912,0x0004},//SENSOR_GAIN_REG2
+	{0x5912,0x0002},//SENSOR_GAIN_REG2
+	{0x5912,0x0003},//SENSOR_GAIN_REG2
+	{0x5912,0x0003},//SENSOR_GAIN_REG2
+	{0x5912,0x5A8B},//SENSOR_GAIN_REG2
+	{0x5912,0xFF04},//SENSOR_GAIN_REG2
+	{0x5912,0xF704},//SENSOR_GAIN_REG2
+	{0x5912,0xFF04},//SENSOR_GAIN_REG2
+	{0x5912,0xF704},//SENSOR_GAIN_REG2
+	{0x5912,0xF704},//SENSOR_GAIN_REG2
+	{0x5912,0x0005},//SENSOR_GAIN_REG2
+	{0x5912,0x0006},//SENSOR_GAIN_REG2
+	{0x5912,0x0007},//SENSOR_GAIN_REG2
+	{0x5912,0x9A8B},//SENSOR_GAIN_REG2
+	{0x5912,0xFF04},//SENSOR_GAIN_REG2
+	{0x5912,0xF704},//SENSOR_GAIN_REG2
+	{0x5912,0xF704},//SENSOR_GAIN_REG2
+	{0x5912,0xF704},//SENSOR_GAIN_REG2
+	{0x5912,0xF704},//SENSOR_GAIN_REG2
+	{0x5912,0x0015},//SENSOR_GAIN_REG2
+	{0x5912,0x0016},//SENSOR_GAIN_REG2
+	{0x5912,0x0017},//SENSOR_GAIN_REG2
+	{0x5912,0xDA8B},//SENSOR_GAIN_REG2
+	{0x5912,0xFF04},//SENSOR_GAIN_REG2
+	{0x5912,0xF704},//SENSOR_GAIN_REG2
+	{0x5912,0xF704},//SENSOR_GAIN_REG2
+	{0x5912,0xF704},//SENSOR_GAIN_REG2
+	{0x5912,0xF704},//SENSOR_GAIN_REG2
+	{0x5912,0x0025},//SENSOR_GAIN_REG2
+	{0x5912,0x0026},//SENSOR_GAIN_REG2
+	{0x5912,0x0027},//SENSOR_GAIN_REG2
+	{0x5912,0x59B9},//SENSOR_GAIN_REG2
+	{0x5912,0x700F},//SENSOR_GAIN_REG2
+	{0x5912,0x7F0F},//SENSOR_GAIN_REG2
+	{0x5912,0x700F},//SENSOR_GAIN_REG2
+	{0x5912,0x7F0F},//SENSOR_GAIN_REG2
+	{0x5912,0x7F0F},//SENSOR_GAIN_REG2
+	{0x5912,0x0035},//SENSOR_GAIN_REG2
+	{0x5912,0x0036},//SENSOR_GAIN_REG2
+	{0x5912,0x0037},//SENSOR_GAIN_REG2
+	{0x5912,0x99B9},//SENSOR_GAIN_REG2
+	{0x5912,0x700F},//SENSOR_GAIN_REG2
+	{0x5912,0x7F0F},//SENSOR_GAIN_REG2
+	{0x5912,0x700F},//SENSOR_GAIN_REG2
+	{0x5912,0x7F0F},//SENSOR_GAIN_REG2
+	{0x5912,0x7F0F},//SENSOR_GAIN_REG2
+	{0x5912,0x0045},//SENSOR_GAIN_REG2
+	{0x5912,0x0046},//SENSOR_GAIN_REG2
+	{0x5912,0x0047},//SENSOR_GAIN_REG2
+	{0x5912,0xD9B9},//SENSOR_GAIN_REG2
+	{0x5912,0x700F},//SENSOR_GAIN_REG2
+	{0x5912,0x7F0F},//SENSOR_GAIN_REG2
+	{0x5912,0x700F},//SENSOR_GAIN_REG2
+	{0x5912,0x7F0F},//SENSOR_GAIN_REG2
+	{0x5912,0x7F0F},//SENSOR_GAIN_REG2
+	{0x5912,0x0055},//SENSOR_GAIN_REG2
+	{0x5912,0x0056},//SENSOR_GAIN_REG2
+	{0x5912,0x0057},//SENSOR_GAIN_REG2
+	{0x5912,0x9A85},//SENSOR_GAIN_REG2
+	{0x5912,0x0654},//SENSOR_GAIN_REG2
+	{0x5912,0x0654},//SENSOR_GAIN_REG2
+	{0x5912,0x0684},//SENSOR_GAIN_REG2
+	{0x5912,0x0654},//SENSOR_GAIN_REG2
+	{0x5912,0x0654},//SENSOR_GAIN_REG2
+	{0x5912,0x0065},//SENSOR_GAIN_REG2
+	{0x5912,0x0066},//SENSOR_GAIN_REG2
+	{0x5912,0x0067},//SENSOR_GAIN_REG2
+	{0x5912,0x59BD},//SENSOR_GAIN_REG2
+	{0x5912,0x1000},//SENSOR_GAIN_REG2
+	{0x5912,0x0C00},//SENSOR_GAIN_REG2
+	{0x5912,0x0F00},//SENSOR_GAIN_REG2
+	{0x5912,0x1000},//SENSOR_GAIN_REG2
+	{0x5912,0x10F0},//SENSOR_GAIN_REG2
+	{0x5912,0x0075},//SENSOR_GAIN_REG2
+	{0x5912,0x0076},//SENSOR_GAIN_REG2
+	{0x5912,0x0077},//SENSOR_GAIN_REG2
+	{0x5914,0x4006},//SENSOR_GAIN_TABLE_SEL
+	{0x5900,0x0020},//SENSOR_GAIN
+	{0x5902,0x0000},//SENSOR_GAIN_T2
+	{0x3110,0x0001},//HDR_CONTROL0
+
+	{REG_NULL, 0x00},
+};
+static const struct regval ar0822_hdr12bit_3840x2160_30fps_regs[] = {
+	{REG_DELAY, 2000},
+	{0x3030,0x0092},//PLL_MULTIPLIER
+	{0x302E,0x0002},//PRE_PLL_CLK_DIV
+	{0x302C,0x0002},//VT_SYS_CLK_DIV
+	{0x302A,0x0006},//VT_PIX_CLK_DIV
+	{0x3038,0x0002},//OP_SYS_CLK_DIV
+	{0x3036,0x0006},//OP_WORD_CLK_DIV
+	{0x31B0,0x00BF},//FRAME_PREAMBLE
+	{0x31B2,0x007D},//LINE_PREAMBLE
+	{0x31B4,0x834E},//MIPI_TIMING_0
+	{0x31B6,0x8491},//MIPI_TIMING_1
+	{0x31B8,0xD0CF},//MIPI_TIMING_2
+	{0x31BA,0x0515},//MIPI_TIMING_3
+	{0x31BC,0x1911},//MIPI_TIMING_4
+	{0x3342,0x122C},//MIPI_F1_PDT_EDT
+	{0x2510,0x0720},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0x2122},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0x26FF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x0F8C},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20E0},//SEQ_DATA_PORT
+	{0x2510,0x8055},//SEQ_DATA_PORT
+	{0x2510,0xA0E1},//SEQ_DATA_PORT
+	{0x2510,0x3041},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x3088},//SEQ_DATA_PORT
+	{0x2510,0x3282},//SEQ_DATA_PORT
+	{0x2510,0xA681},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FE},//SEQ_DATA_PORT
+	{0x2510,0x9070},//SEQ_DATA_PORT
+	{0x2510,0x891D},//SEQ_DATA_PORT
+	{0x2510,0x867F},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FC},//SEQ_DATA_PORT
+	{0x2510,0x893F},//SEQ_DATA_PORT
+	{0x2510,0x0F92},//SEQ_DATA_PORT
+	{0x2510,0x20E0},//SEQ_DATA_PORT
+	{0x2510,0x0F8F},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20E0},//SEQ_DATA_PORT
+	{0x2510,0x9770},//SEQ_DATA_PORT
+	{0x2510,0x20FC},//SEQ_DATA_PORT
+	{0x2510,0x8054},//SEQ_DATA_PORT
+	{0x2510,0x896C},//SEQ_DATA_PORT
+	{0x2510,0x200A},//SEQ_DATA_PORT
+	{0x2510,0x9030},//SEQ_DATA_PORT
+	{0x2510,0x200A},//SEQ_DATA_PORT
+	{0x2510,0x8040},//SEQ_DATA_PORT
+	{0x2510,0x8948},//SEQ_DATA_PORT
+	{0x2510,0x200A},//SEQ_DATA_PORT
+	{0x2510,0x1597},//SEQ_DATA_PORT
+	{0x2510,0x8808},//SEQ_DATA_PORT
+	{0x2510,0x200A},//SEQ_DATA_PORT
+	{0x2510,0x1F96},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20E0},//SEQ_DATA_PORT
+	{0x2510,0xA0C0},//SEQ_DATA_PORT
+	{0x2510,0x200A},//SEQ_DATA_PORT
+	{0x2510,0x3044},//SEQ_DATA_PORT
+	{0x2510,0x3088},//SEQ_DATA_PORT
+	{0x2510,0x3282},//SEQ_DATA_PORT
+	{0x2510,0x2004},//SEQ_DATA_PORT
+	{0x2510,0x1FAA},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20E0},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x3108},//SEQ_DATA_PORT
+	{0x2510,0x2400},//SEQ_DATA_PORT
+	{0x2510,0x3244},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x3108},//SEQ_DATA_PORT
+	{0x2510,0x2400},//SEQ_DATA_PORT
+	{0x2510,0x2702},//SEQ_DATA_PORT
+	{0x2510,0x3242},//SEQ_DATA_PORT
+	{0x2510,0x3108},//SEQ_DATA_PORT
+	{0x2510,0x2420},//SEQ_DATA_PORT
+	{0x2510,0x2703},//SEQ_DATA_PORT
+	{0x2510,0x3242},//SEQ_DATA_PORT
+	{0x2510,0x3108},//SEQ_DATA_PORT
+	{0x2510,0x2420},//SEQ_DATA_PORT
+	{0x2510,0x2704},//SEQ_DATA_PORT
+	{0x2510,0x3244},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x8801},//SEQ_DATA_PORT
+	{0x2510,0x010F},//SEQ_DATA_PORT
+	{0x2510,0x8855},//SEQ_DATA_PORT
+	{0x2510,0x3101},//SEQ_DATA_PORT
+	{0x2510,0x3041},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x3102},//SEQ_DATA_PORT
+	{0x2510,0x3041},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x3181},//SEQ_DATA_PORT
+	{0x2510,0x3041},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x3188},//SEQ_DATA_PORT
+	{0x2510,0x3041},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x3282},//SEQ_DATA_PORT
+	{0x2510,0x3104},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0xB0E4},//SEQ_DATA_PORT
+	{0x2510,0xAD92},//SEQ_DATA_PORT
+	{0x2510,0xBC0C},//SEQ_DATA_PORT
+	{0x2510,0x1028},//SEQ_DATA_PORT
+	{0x2510,0x0022},//SEQ_DATA_PORT
+	{0x2510,0xC020},//SEQ_DATA_PORT
+	{0x2510,0x003E},//SEQ_DATA_PORT
+	{0x2510,0x0045},//SEQ_DATA_PORT
+	{0x2510,0x00B0},//SEQ_DATA_PORT
+	{0x2510,0x0028},//SEQ_DATA_PORT
+	{0x2510,0x30C1},//SEQ_DATA_PORT
+	{0x2510,0x8015},//SEQ_DATA_PORT
+	{0x2510,0xA038},//SEQ_DATA_PORT
+	{0x2510,0x100F},//SEQ_DATA_PORT
+	{0x2510,0x0507},//SEQ_DATA_PORT
+	{0x2510,0xA220},//SEQ_DATA_PORT
+	{0x2510,0x0010},//SEQ_DATA_PORT
+	{0x2510,0x10C2},//SEQ_DATA_PORT
+	{0x2510,0xB760},//SEQ_DATA_PORT
+	{0x2510,0x0033},//SEQ_DATA_PORT
+	{0x2510,0x1082},//SEQ_DATA_PORT
+	{0x2510,0x100B},//SEQ_DATA_PORT
+	{0x2510,0x1029},//SEQ_DATA_PORT
+	{0x2510,0xA85A},//SEQ_DATA_PORT
+	{0x2510,0x998D},//SEQ_DATA_PORT
+	{0x2510,0xC810},//SEQ_DATA_PORT
+	{0x2510,0x2004},//SEQ_DATA_PORT
+	{0x2510,0x0CCE},//SEQ_DATA_PORT
+	{0x2510,0x113B},//SEQ_DATA_PORT
+	{0x2510,0x1055},//SEQ_DATA_PORT
+	{0x2510,0x101D},//SEQ_DATA_PORT
+	{0x2510,0xC000},//SEQ_DATA_PORT
+	{0x2510,0x052F},//SEQ_DATA_PORT
+	{0x2510,0x162F},//SEQ_DATA_PORT
+	{0x2510,0x9000},//SEQ_DATA_PORT
+	{0x2510,0x2034},//SEQ_DATA_PORT
+	{0x2510,0x0015},//SEQ_DATA_PORT
+	{0x2510,0x04CB},//SEQ_DATA_PORT
+	{0x2510,0x1022},//SEQ_DATA_PORT
+	{0x2510,0x1031},//SEQ_DATA_PORT
+	{0x2510,0x002D},//SEQ_DATA_PORT
+	{0x2510,0x1015},//SEQ_DATA_PORT
+	{0x2510,0x80B9},//SEQ_DATA_PORT
+	{0x2510,0xA301},//SEQ_DATA_PORT
+	{0x2510,0x008E},//SEQ_DATA_PORT
+	{0x2510,0x124B},//SEQ_DATA_PORT
+	{0x2510,0x01B5},//SEQ_DATA_PORT
+	{0x2510,0x0B92},//SEQ_DATA_PORT
+	{0x2510,0xA400},//SEQ_DATA_PORT
+	{0x2510,0x8091},//SEQ_DATA_PORT
+	{0x2510,0x0028},//SEQ_DATA_PORT
+	{0x2510,0x3002},//SEQ_DATA_PORT
+	{0x2510,0x2004},//SEQ_DATA_PORT
+	{0x2510,0x1012},//SEQ_DATA_PORT
+	{0x2510,0x100E},//SEQ_DATA_PORT
+	{0x2510,0x10A8},//SEQ_DATA_PORT
+	{0x2510,0x00A1},//SEQ_DATA_PORT
+	{0x2510,0x132D},//SEQ_DATA_PORT
+	{0x2510,0x09AF},//SEQ_DATA_PORT
+	{0x2510,0x03D9},//SEQ_DATA_PORT
+	{0x2510,0x1259},//SEQ_DATA_PORT
+	{0x2510,0x11AF},//SEQ_DATA_PORT
+	{0x2510,0x18B5},//SEQ_DATA_PORT
+	{0x2510,0x0395},//SEQ_DATA_PORT
+	{0x2510,0x05CB},//SEQ_DATA_PORT
+	{0x2510,0x1021},//SEQ_DATA_PORT
+	{0x2510,0x1015},//SEQ_DATA_PORT
+	{0x2510,0x1030},//SEQ_DATA_PORT
+	{0x2510,0x004F},//SEQ_DATA_PORT
+	{0x2510,0x001C},//SEQ_DATA_PORT
+	{0x2510,0xB146},//SEQ_DATA_PORT
+	{0x2510,0xC090},//SEQ_DATA_PORT
+	{0x2510,0x0020},//SEQ_DATA_PORT
+	{0x2510,0x103C},//SEQ_DATA_PORT
+	{0x2510,0xA882},//SEQ_DATA_PORT
+	{0x2510,0x8055},//SEQ_DATA_PORT
+	{0x2510,0x00A9},//SEQ_DATA_PORT
+	{0x2510,0x8801},//SEQ_DATA_PORT
+	{0x2510,0xB700},//SEQ_DATA_PORT
+	{0x2510,0x0001},//SEQ_DATA_PORT
+	{0x2510,0x00A2},//SEQ_DATA_PORT
+	{0x2510,0x11AE},//SEQ_DATA_PORT
+	{0x2510,0x000A},//SEQ_DATA_PORT
+	{0x2510,0x98BB},//SEQ_DATA_PORT
+	{0x2510,0x2047},//SEQ_DATA_PORT
+	{0x2510,0x0036},//SEQ_DATA_PORT
+	{0x2510,0x1001},//SEQ_DATA_PORT
+	{0x2510,0x9FBE},//SEQ_DATA_PORT
+	{0x2510,0x108A},//SEQ_DATA_PORT
+	{0x2510,0x1022},//SEQ_DATA_PORT
+	{0x2510,0x0039},//SEQ_DATA_PORT
+	{0x2510,0x01C0},//SEQ_DATA_PORT
+	{0x2510,0x109F},//SEQ_DATA_PORT
+	{0x2510,0x1023},//SEQ_DATA_PORT
+	{0x2510,0x052E},//SEQ_DATA_PORT
+	{0x2510,0x002A},//SEQ_DATA_PORT
+	{0x2510,0x3081},//SEQ_DATA_PORT
+	{0x2510,0x2001},//SEQ_DATA_PORT
+	{0x2510,0x3044},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x112A},//SEQ_DATA_PORT
+	{0x2510,0x101D},//SEQ_DATA_PORT
+	{0x2510,0x2020},//SEQ_DATA_PORT
+	{0x2510,0x02B8},//SEQ_DATA_PORT
+	{0x2510,0x10B8},//SEQ_DATA_PORT
+	{0x2510,0x1136},//SEQ_DATA_PORT
+	{0x2510,0x9B6B},//SEQ_DATA_PORT
+	{0x2510,0x1039},//SEQ_DATA_PORT
+	{0x2510,0x1040},//SEQ_DATA_PORT
+	{0x2510,0xAB80},//SEQ_DATA_PORT
+	{0x2510,0x03C4},//SEQ_DATA_PORT
+	{0x2510,0x10C4},//SEQ_DATA_PORT
+	{0x2510,0x1023},//SEQ_DATA_PORT
+	{0x2510,0x1245},//SEQ_DATA_PORT
+	{0x2510,0x009F},//SEQ_DATA_PORT
+	{0x2510,0x002B},//SEQ_DATA_PORT
+	{0x2510,0x30D0},//SEQ_DATA_PORT
+	{0x2510,0x3141},//SEQ_DATA_PORT
+	{0x2510,0x3041},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x3142},//SEQ_DATA_PORT
+	{0x2510,0x3041},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x3110},//SEQ_DATA_PORT
+	{0x2510,0x3041},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x3120},//SEQ_DATA_PORT
+	{0x2510,0x3041},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x3144},//SEQ_DATA_PORT
+	{0x2510,0x3041},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x3148},//SEQ_DATA_PORT
+	{0x2510,0x3041},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x3182},//SEQ_DATA_PORT
+	{0x2510,0x3041},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x3184},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x3190},//SEQ_DATA_PORT
+	{0x2510,0x3041},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x31A0},//SEQ_DATA_PORT
+	{0x2510,0x3088},//SEQ_DATA_PORT
+	{0x2510,0x2201},//SEQ_DATA_PORT
+	{0x2510,0x807D},//SEQ_DATA_PORT
+	{0x2510,0x2206},//SEQ_DATA_PORT
+	{0x2510,0x8815},//SEQ_DATA_PORT
+	{0x2510,0x8877},//SEQ_DATA_PORT
+	{0x2510,0x0092},//SEQ_DATA_PORT
+	{0x2510,0x220E},//SEQ_DATA_PORT
+	{0x2510,0x2211},//SEQ_DATA_PORT
+	{0x2510,0x8055},//SEQ_DATA_PORT
+	{0x2510,0x3001},//SEQ_DATA_PORT
+	{0x2510,0x2004},//SEQ_DATA_PORT
+	{0x2510,0x8C61},//SEQ_DATA_PORT
+	{0x2510,0x8801},//SEQ_DATA_PORT
+	{0x2510,0x1012},//SEQ_DATA_PORT
+	{0x2510,0x1D1F},//SEQ_DATA_PORT
+	{0x2510,0x0D9F},//SEQ_DATA_PORT
+	{0x2510,0x101F},//SEQ_DATA_PORT
+	{0x2510,0x0036},//SEQ_DATA_PORT
+	{0x2510,0x0040},//SEQ_DATA_PORT
+	{0x2510,0x0023},//SEQ_DATA_PORT
+	{0x2510,0x996E},//SEQ_DATA_PORT
+	{0x2510,0x0257},//SEQ_DATA_PORT
+	{0x2510,0x1035},//SEQ_DATA_PORT
+	{0x2510,0x9926},//SEQ_DATA_PORT
+	{0x2510,0x0039},//SEQ_DATA_PORT
+	{0x2510,0x00AE},//SEQ_DATA_PORT
+	{0x2510,0x11A3},//SEQ_DATA_PORT
+	{0x2510,0x0048},//SEQ_DATA_PORT
+	{0x2510,0xC878},//SEQ_DATA_PORT
+	{0x2510,0x200A},//SEQ_DATA_PORT
+	{0x2510,0x1548},//SEQ_DATA_PORT
+	{0x2510,0x0C49},//SEQ_DATA_PORT
+	{0x2510,0x1149},//SEQ_DATA_PORT
+	{0x2510,0x002A},//SEQ_DATA_PORT
+	{0x2510,0x1057},//SEQ_DATA_PORT
+	{0x2510,0x3281},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x3044},//SEQ_DATA_PORT
+	{0x2510,0x2001},//SEQ_DATA_PORT
+	{0x2510,0xA020},//SEQ_DATA_PORT
+	{0x2510,0x000C},//SEQ_DATA_PORT
+	{0x2510,0x9825},//SEQ_DATA_PORT
+	{0x2510,0x1040},//SEQ_DATA_PORT
+	{0x2510,0x1054},//SEQ_DATA_PORT
+	{0x2510,0xB06D},//SEQ_DATA_PORT
+	{0x2510,0x0035},//SEQ_DATA_PORT
+	{0x2510,0x004D},//SEQ_DATA_PORT
+	{0x2510,0x1020},//SEQ_DATA_PORT
+	{0x2510,0xB064},//SEQ_DATA_PORT
+	{0x2510,0x99C5},//SEQ_DATA_PORT
+	{0x2510,0x0047},//SEQ_DATA_PORT
+	{0x2510,0xB920},//SEQ_DATA_PORT
+	{0x2510,0x1447},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x31F8,0x0008},//MIPI_CONFIG_2
+	{0x3C70,0x6828},//CALIB_ROWS
+	{0x3092,0x0826},//ROW_NOISE_CONTROL
+	{0x3428,0x0209},//SEQUENCER_CONTROL
+	{0x3516,0xFF04},//DAC_LD_22_23
+	{0x3526,0x6480},//DAC_LD_38_39
+	{0x3504,0x8AAA},//DAC_LD_4_5
+	{0x353C,0x220C},//DAC_LD_60_61
+	{0x3536,0x4C6E},//DAC_LD_54_55
+	{0x3D2A,0x0FFF},//T1_END_DEC_TH
+	{0x3364,0x00EC},//DCG_TRIM
+	{0x3512,0x8888},//DAC_LD_18_19
+	{0x3514,0x888F},//DAC_LD_20_21
+	{0x3520,0xFBF0},//DAC_LD_32_33
+	{0x3524,0xB2A1},//DAC_LD_36_37
+	{0x3528,0xCC84},//DAC_LD_40_41
+	{0x3532,0x4C8E},//DAC_LD_50_51
+	{0x3534,0x4E64},//DAC_LD_52_53
+	{0x351E,0x5856},//DAC_LD_30_31
+	{0x353E,0x98F2},//DAC_LD_62_63
+	{0x352E,0x6A8A},//DAC_LD_46_47
+	{0x3370,0x0211},//DBLC_CONTROL
+	{0x3372,0x700F},//DBLC_FS0_CONTROL
+	{0x3540,0x3597},//DAC_LD_64_65
+	{0x58E2,0x0BE3},//COL_COUNT_VALUES1
+	{0x58E4,0x18B4},//COL_COUNT_VALUES2
+	{0x3522,0x7C97},//DAC_LD_34_35
+	{0x30BA,0x0024},//DIGITAL_CTRL
+	{0x31D4,0x0042},//CLK_MEM_GATING_CTRL
+	{0x352A,0x6F8F},//DAC_LD_42_43
+	{0x3530,0x4A08},//DAC_LD_48_49
+	{0x351A,0x5FFF},//DAC_LD_26_27
+	{0x350E,0x39D9},//DAC_LD_14_15
+	{0x3510,0x9988},//DAC_LD_16_17
+	{0x3380,0x1FFF},//DBLC_OFFSET1
+	{0x337A,0x1000},//DBLC_SCALE1
+	{0x3092,0x0800},//ROW_NOISE_CONTROL
+	{0x350A,0x0654},//DAC_LD_10_11
+	{0x3364,0x00E0},//DCG_TRIM
+	{0x591E,0x61AE},//ANALOG_GAIN_WR_DATA
+	{0x591E,0x722C},//ANALOG_GAIN_WR_DATA
+	{0x591E,0x82B8},//ANALOG_GAIN_WR_DATA
+	{0x591E,0x92F6},//ANALOG_GAIN_WR_DATA
+	{0x591E,0xA447},//ANALOG_GAIN_WR_DATA
+	{0x591E,0xB66D},//ANALOG_GAIN_WR_DATA
+	{0x591E,0xC6EA},//ANALOG_GAIN_WR_DATA
+	{0x591E,0xDECD},//ANALOG_GAIN_WR_DATA
+	{0x3532,0x4C8A},//DAC_LD_50_51
+	{0x3534,0x4E60},//DAC_LD_52_53
+	{0x353E,0x90F2},//DAC_LD_62_63
+	{0x351A,0x4FFF},//DAC_LD_26_27
+	{0x591C,0x00D7},//DGR_AMP_GAIN
+	{0x5002,0x37C3},//T1_PIX_DEF_ID2
+	{0x51CC,0x0149},//T1_NOISE_GAIN_THRESHOLD0
+	{0x51D8,0x044D},//T1_NOISE_GAIN_THRESHOLD1
+	{0x51CE,0x0700},//T1_NOISE_GAIN_THRESHOLD2
+	{0x51D0,0x0001},//T1_NOISE_FLOOR0
+	{0x51D2,0x0002},//T1_NOISE_FLOOR1
+	{0x51D4,0x0003},//T1_NOISE_FLOOR2
+	{0x51D6,0x0004},//T1_NOISE_FLOOR3
+	{0x5202,0x37C3},//T2_PIX_DEF_ID2
+	{0x51EA,0x0149},//T2_NOISE_GAIN_THRESHOLD0
+	{0x51FC,0x044D},//T2_NOISE_GAIN_THRESHOLD1
+	{0x51EC,0x0700},//T2_NOISE_GAIN_THRESHOLD2
+	{0x51EE,0x0001},//T2_NOISE_FLOOR0
+	{0x51F0,0x0002},//T2_NOISE_FLOOR1
+	{0x51F2,0x0003},//T2_NOISE_FLOOR2
+	{0x51F4,0x0004},//T2_NOISE_FLOOR3
+	{0x5402,0x37C3},//T4_PIX_DEF_ID2
+	{0x5560,0x0149},//T4_NOISE_GAIN_THRESHOLD0
+	{0x556C,0x044D},//T4_NOISE_GAIN_THRESHOLD1
+	{0x5562,0x0700},//T4_NOISE_GAIN_THRESHOLD2
+	{0x5564,0x0001},//T4_NOISE_FLOOR0
+	{0x5566,0x0002},//T4_NOISE_FLOOR1
+	{0x5568,0x0003},//T4_NOISE_FLOOR2
+	{0x556A,0x0004},//T4_NOISE_FLOOR3
+	{0x31E0,0x0001},//PIX_DEF_ID
+	{0x5000,0x0080},//T1_PIX_DEF_ID
+	{0x5000,0x0180},//T1_PIX_DEF_ID
+	{0x5000,0x0180},//T1_PIX_DEF_ID
+	{0x5200,0x0080},//T2_PIX_DEF_ID
+	{0x5200,0x0180},//T2_PIX_DEF_ID
+	{0x5200,0x0180},//T2_PIX_DEF_ID
+	{0x5400,0x0080},//T4_PIX_DEF_ID
+	{0x5400,0x0180},//T4_PIX_DEF_ID
+	{0x5400,0x0180},//T4_PIX_DEF_ID
+	{0x5000,0x0180},//T1_PIX_DEF_ID
+	{0x5200,0x0180},//T2_PIX_DEF_ID
+	{0x5400,0x0180},//T4_PIX_DEF_ID
+	{0x50A2,0x3F2A},//BMT0
+	{0x50A4,0x875A},//BMT1
+	{0x50A6,0x030F},//SINGLEK_FACTOR0
+	{0x50A6,0x0F0F},//SINGLEK_FACTOR0
+	{0x50A8,0x030F},//SINGLEK_FACTOR1
+	{0x50A8,0x0F0F},//SINGLEK_FACTOR1
+	{0x50AA,0x030F},//SINGLEK_FACTOR2
+	{0x50AA,0x050F},//SINGLEK_FACTOR2
+	{0x50AC,0x0301},//CROSS_FACTOR0
+	{0x50AC,0x0101},//CROSS_FACTOR0
+	{0x50AE,0x0301},//CROSS_FACTOR1
+	{0x50AE,0x0101},//CROSS_FACTOR1
+	{0x50B0,0x0301},//CROSS_FACTOR2
+	{0x50B0,0x0101},//CROSS_FACTOR2
+	{0x50B2,0x03FF},//SINGLE_MAX_FACTOR
+	{0x50B4,0x030F},//COUPLE_FACTOR0
+	{0x50B4,0x0F0F},//COUPLE_FACTOR0
+	{0x50B6,0x030F},//COUPLE_FACTOR1
+	{0x50B6,0x0F0F},//COUPLE_FACTOR1
+	{0x50B8,0x030F},//COUPLE_FACTOR2
+	{0x50B8,0x050F},//COUPLE_FACTOR2
+	{0x3D2A,0x0FFF},//T1_END_DEC_TH
+	{0x3D34,0x9C40},//T2_STR_DEC_TH
+	{0x3D36,0xFFFF},//T2_END_DEC_TH
+	{0x3D02,0x5033},//MEC_CTRL2
+	{0x3D00,0x600F},//MEC_CTRL1
+	{0x3086,0x1A28},//PARK_ROW_ADDR
+	{0x33E4,0x0040},//VERT_SHADING_CONTROL
+	{0x3C70,0x6222},//CALIB_ROWS
+	{0x3110,0x0011},//HDR_CONTROL0
+	{0x30B0,0x0820},//DIGITAL_TEST
+	{0x3280,0x0ED8},//T1_BARRIER_C0
+	{0x3282,0x0ED8},//T1_BARRIER_C1
+	{0x3284,0x0ED8},//T1_BARRIER_C2
+	{0x3286,0x0ED8},//T1_BARRIER_C3
+	{0x3288,0x0ED8},//T2_BARRIER_C0
+	{0x328A,0x0ED8},//T2_BARRIER_C1
+	{0x328C,0x0ED8},//T2_BARRIER_C2
+	{0x328E,0x0ED8},//T2_BARRIER_C3
+	{0x3290,0x0ED8},//T3_BARRIER_C0
+	{0x3292,0x0ED8},//T3_BARRIER_C1
+	{0x3294,0x0ED8},//T3_BARRIER_C2
+	{0x3296,0x0ED8},//T3_BARRIER_C3
+	{0x3100,0xC001},//DLO_CONTROL0
+	{0x3102,0xBED8},//DLO_CONTROL1
+	{0x3104,0xBED8},//DLO_CONTROL2
+	{0x3106,0xBED8},//DLO_CONTROL3
+	{0x3108,0x07D0},//DLO_CONTROL4
+	{0x3116,0x4001},//HDR_CONTROL3
+	{0x3124,0x006D},//HDR_MD_CONTROL0
+	{0x3126,0x003C},//HDR_MD_CONTROL1
+	{0x31AE,0x0204},//SERIAL_FORMAT
+	{0x31AC,0x0C0C},//DATA_FORMAT_BITS
+	{0x3082,0x0014},//OPERATION_MODE_CTRL
+	{0x30BA,0x0135},//DIGITAL_CTRL
+	{0x3238,0x0044},//EXPOSURE_RATIO
+	{0x3012,0x0700},//COARSE_INTEGRATION_TIME_
+	{0x3212,0x0070},//COARSE_INTEGRATION_TIME2
+	{0x300C,0x10CC},//LINE_LENGTH_PCK_
+	{0x300A,0x09F3},//FRAME_LENGTH_LINES_
+	{0x5914,0x4002},//SENSOR_GAIN_TABLE_SEL
+	{0x5910,0x608A},//SENSOR_GAIN_REG1
+	{0x5910,0x7091},//SENSOR_GAIN_REG1
+	{0x5910,0x689C},//SENSOR_GAIN_REG1
+	{0x5910,0x8885},//SENSOR_GAIN_REG1
+	{0x5910,0x98AD},//SENSOR_GAIN_REG1
+	{0x5910,0xA8A9},//SENSOR_GAIN_REG1
+	{0x5910,0xC894},//SENSOR_GAIN_REG1
+	{0x5910,0xC8D1},//SENSOR_GAIN_REG1
+	{0x5910,0xD88A},//SENSOR_GAIN_REG1
+	{0x5910,0xD8C3},//SENSOR_GAIN_REG1
+	{0x5910,0xD915},//SENSOR_GAIN_REG1
+	{0x5910,0xD988},//SENSOR_GAIN_REG1
+	{0x5910,0xDA2A},//SENSOR_GAIN_REG1
+	{0x5910,0xDB0E},//SENSOR_GAIN_REG1
+	{0x5910,0xDC53},//SENSOR_GAIN_REG1
+	{0x5910,0x608A},//SENSOR_GAIN_REG1
+	{0x5910,0xC919},//SENSOR_GAIN_REG1
+	{0x5910,0xCA00},//SENSOR_GAIN_REG1
+	{0x5910,0x0000},//SENSOR_GAIN_REG1
+	{0x5910,0x0000},//SENSOR_GAIN_REG1
+	{0x5910,0x0000},//SENSOR_GAIN_REG1
+	{0x5910,0x0001},//SENSOR_GAIN_REG1
+	{0x5910,0x0001},//SENSOR_GAIN_REG1
+	{0x5910,0x0003},//SENSOR_GAIN_REG1
+	{0x5910,0x0003},//SENSOR_GAIN_REG1
+	{0x5910,0x0003},//SENSOR_GAIN_REG1
+	{0x5910,0x0004},//SENSOR_GAIN_REG1
+	{0x5910,0x0004},//SENSOR_GAIN_REG1
+	{0x5910,0x0004},//SENSOR_GAIN_REG1
+	{0x5910,0x0004},//SENSOR_GAIN_REG1
+	{0x5910,0x0004},//SENSOR_GAIN_REG1
+	{0x5910,0x0004},//SENSOR_GAIN_REG1
+	{0x5910,0x0004},//SENSOR_GAIN_REG1
+	{0x5910,0x0002},//SENSOR_GAIN_REG1
+	{0x5910,0x0003},//SENSOR_GAIN_REG1
+	{0x5910,0x0003},//SENSOR_GAIN_REG1
+	{0x5910,0x5A8B},//SENSOR_GAIN_REG1
+	{0x5910,0xFF04},//SENSOR_GAIN_REG1
+	{0x5910,0xF704},//SENSOR_GAIN_REG1
+	{0x5910,0xFF04},//SENSOR_GAIN_REG1
+	{0x5910,0xF704},//SENSOR_GAIN_REG1
+	{0x5910,0xF704},//SENSOR_GAIN_REG1
+	{0x5910,0x0005},//SENSOR_GAIN_REG1
+	{0x5910,0x0006},//SENSOR_GAIN_REG1
+	{0x5910,0x0007},//SENSOR_GAIN_REG1
+	{0x5910,0x9A8B},//SENSOR_GAIN_REG1
+	{0x5910,0xFF04},//SENSOR_GAIN_REG1
+	{0x5910,0xF704},//SENSOR_GAIN_REG1
+	{0x5910,0xF704},//SENSOR_GAIN_REG1
+	{0x5910,0xF704},//SENSOR_GAIN_REG1
+	{0x5910,0xF704},//SENSOR_GAIN_REG1
+	{0x5910,0x0015},//SENSOR_GAIN_REG1
+	{0x5910,0x0016},//SENSOR_GAIN_REG1
+	{0x5910,0x0017},//SENSOR_GAIN_REG1
+	{0x5910,0xDA8B},//SENSOR_GAIN_REG1
+	{0x5910,0xFF04},//SENSOR_GAIN_REG1
+	{0x5910,0xF704},//SENSOR_GAIN_REG1
+	{0x5910,0xF704},//SENSOR_GAIN_REG1
+	{0x5910,0xF704},//SENSOR_GAIN_REG1
+	{0x5910,0xF704},//SENSOR_GAIN_REG1
+	{0x5910,0x0025},//SENSOR_GAIN_REG1
+	{0x5910,0x0026},//SENSOR_GAIN_REG1
+	{0x5910,0x0027},//SENSOR_GAIN_REG1
+	{0x5910,0x59B9},//SENSOR_GAIN_REG1
+	{0x5910,0x700F},//SENSOR_GAIN_REG1
+	{0x5910,0x7F0F},//SENSOR_GAIN_REG1
+	{0x5910,0x700F},//SENSOR_GAIN_REG1
+	{0x5910,0x7F0F},//SENSOR_GAIN_REG1
+	{0x5910,0x7F0F},//SENSOR_GAIN_REG1
+	{0x5910,0x0035},//SENSOR_GAIN_REG1
+	{0x5910,0x0036},//SENSOR_GAIN_REG1
+	{0x5910,0x0037},//SENSOR_GAIN_REG1
+	{0x5910,0x99B9},//SENSOR_GAIN_REG1
+	{0x5910,0x700F},//SENSOR_GAIN_REG1
+	{0x5910,0x7F0F},//SENSOR_GAIN_REG1
+	{0x5910,0x700F},//SENSOR_GAIN_REG1
+	{0x5910,0x7F0F},//SENSOR_GAIN_REG1
+	{0x5910,0x7F0F},//SENSOR_GAIN_REG1
+	{0x5910,0x0045},//SENSOR_GAIN_REG1
+	{0x5910,0x0046},//SENSOR_GAIN_REG1
+	{0x5910,0x0047},//SENSOR_GAIN_REG1
+	{0x5910,0xD9B9},//SENSOR_GAIN_REG1
+	{0x5910,0x700F},//SENSOR_GAIN_REG1
+	{0x5910,0x7F0F},//SENSOR_GAIN_REG1
+	{0x5910,0x700F},//SENSOR_GAIN_REG1
+	{0x5910,0x7F0F},//SENSOR_GAIN_REG1
+	{0x5910,0x7F0F},//SENSOR_GAIN_REG1
+	{0x5910,0x0055},//SENSOR_GAIN_REG1
+	{0x5910,0x0056},//SENSOR_GAIN_REG1
+	{0x5910,0x0057},//SENSOR_GAIN_REG1
+	{0x5910,0x9A85},//SENSOR_GAIN_REG1
+	{0x5910,0x0654},//SENSOR_GAIN_REG1
+	{0x5910,0x0654},//SENSOR_GAIN_REG1
+	{0x5910,0x0684},//SENSOR_GAIN_REG1
+	{0x5910,0x0654},//SENSOR_GAIN_REG1
+	{0x5910,0x0654},//SENSOR_GAIN_REG1
+	{0x5910,0x0065},//SENSOR_GAIN_REG1
+	{0x5910,0x0066},//SENSOR_GAIN_REG1
+	{0x5910,0x0067},//SENSOR_GAIN_REG1
+	{0x5910,0x59BD},//SENSOR_GAIN_REG1
+	{0x5910,0x1000},//SENSOR_GAIN_REG1
+	{0x5910,0x0C00},//SENSOR_GAIN_REG1
+	{0x5910,0x0F00},//SENSOR_GAIN_REG1
+	{0x5910,0x1000},//SENSOR_GAIN_REG1
+	{0x5910,0x10F0},//SENSOR_GAIN_REG1
+	{0x5910,0x0075},//SENSOR_GAIN_REG1
+	{0x5910,0x0076},//SENSOR_GAIN_REG1
+	{0x5910,0x0077},//SENSOR_GAIN_REG1
+	{0x5912,0x608A},//SENSOR_GAIN_REG2
+	{0x5912,0x7091},//SENSOR_GAIN_REG2
+	{0x5912,0x689C},//SENSOR_GAIN_REG2
+	{0x5912,0x8885},//SENSOR_GAIN_REG2
+	{0x5912,0x98AD},//SENSOR_GAIN_REG2
+	{0x5912,0xA8A9},//SENSOR_GAIN_REG2
+	{0x5912,0xC894},//SENSOR_GAIN_REG2
+	{0x5912,0xC8D1},//SENSOR_GAIN_REG2
+	{0x5912,0xC927},//SENSOR_GAIN_REG2
+	{0x5912,0xC9A0},//SENSOR_GAIN_REG2
+	{0x5912,0xCA4C},//SENSOR_GAIN_REG2
+	{0x5912,0xCB3F},//SENSOR_GAIN_REG2
+	{0x5912,0xCC97},//SENSOR_GAIN_REG2
+	{0x5912,0xCE7C},//SENSOR_GAIN_REG2
+	{0x5912,0xCFFF},//SENSOR_GAIN_REG2
+	{0x5912,0x608A},//SENSOR_GAIN_REG2
+	{0x5912,0xC919},//SENSOR_GAIN_REG2
+	{0x5912,0xCA00},//SENSOR_GAIN_REG2
+	{0x5912,0x0000},//SENSOR_GAIN_REG2
+	{0x5912,0x0000},//SENSOR_GAIN_REG2
+	{0x5912,0x0000},//SENSOR_GAIN_REG2
+	{0x5912,0x0001},//SENSOR_GAIN_REG2
+	{0x5912,0x0001},//SENSOR_GAIN_REG2
+	{0x5912,0x0003},//SENSOR_GAIN_REG2
+	{0x5912,0x0003},//SENSOR_GAIN_REG2
+	{0x5912,0x0003},//SENSOR_GAIN_REG2
+	{0x5912,0x0004},//SENSOR_GAIN_REG2
+	{0x5912,0x0004},//SENSOR_GAIN_REG2
+	{0x5912,0x0004},//SENSOR_GAIN_REG2
+	{0x5912,0x0004},//SENSOR_GAIN_REG2
+	{0x5912,0x0004},//SENSOR_GAIN_REG2
+	{0x5912,0x0004},//SENSOR_GAIN_REG2
+	{0x5912,0x0004},//SENSOR_GAIN_REG2
+	{0x5912,0x0002},//SENSOR_GAIN_REG2
+	{0x5912,0x0003},//SENSOR_GAIN_REG2
+	{0x5912,0x0003},//SENSOR_GAIN_REG2
+	{0x5912,0x5A8B},//SENSOR_GAIN_REG2
+	{0x5912,0xFF04},//SENSOR_GAIN_REG2
+	{0x5912,0xF704},//SENSOR_GAIN_REG2
+	{0x5912,0xFF04},//SENSOR_GAIN_REG2
+	{0x5912,0xF704},//SENSOR_GAIN_REG2
+	{0x5912,0xF704},//SENSOR_GAIN_REG2
+	{0x5912,0x0005},//SENSOR_GAIN_REG2
+	{0x5912,0x0006},//SENSOR_GAIN_REG2
+	{0x5912,0x0007},//SENSOR_GAIN_REG2
+	{0x5912,0x9A8B},//SENSOR_GAIN_REG2
+	{0x5912,0xFF04},//SENSOR_GAIN_REG2
+	{0x5912,0xF704},//SENSOR_GAIN_REG2
+	{0x5912,0xF704},//SENSOR_GAIN_REG2
+	{0x5912,0xF704},//SENSOR_GAIN_REG2
+	{0x5912,0xF704},//SENSOR_GAIN_REG2
+	{0x5912,0x0015},//SENSOR_GAIN_REG2
+	{0x5912,0x0016},//SENSOR_GAIN_REG2
+	{0x5912,0x0017},//SENSOR_GAIN_REG2
+	{0x5912,0xDA8B},//SENSOR_GAIN_REG2
+	{0x5912,0xFF04},//SENSOR_GAIN_REG2
+	{0x5912,0xF704},//SENSOR_GAIN_REG2
+	{0x5912,0xF704},//SENSOR_GAIN_REG2
+	{0x5912,0xF704},//SENSOR_GAIN_REG2
+	{0x5912,0xF704},//SENSOR_GAIN_REG2
+	{0x5912,0x0025},//SENSOR_GAIN_REG2
+	{0x5912,0x0026},//SENSOR_GAIN_REG2
+	{0x5912,0x0027},//SENSOR_GAIN_REG2
+	{0x5912,0x59B9},//SENSOR_GAIN_REG2
+	{0x5912,0x700F},//SENSOR_GAIN_REG2
+	{0x5912,0x7F0F},//SENSOR_GAIN_REG2
+	{0x5912,0x700F},//SENSOR_GAIN_REG2
+	{0x5912,0x7F0F},//SENSOR_GAIN_REG2
+	{0x5912,0x7F0F},//SENSOR_GAIN_REG2
+	{0x5912,0x0035},//SENSOR_GAIN_REG2
+	{0x5912,0x0036},//SENSOR_GAIN_REG2
+	{0x5912,0x0037},//SENSOR_GAIN_REG2
+	{0x5912,0x99B9},//SENSOR_GAIN_REG2
+	{0x5912,0x700F},//SENSOR_GAIN_REG2
+	{0x5912,0x7F0F},//SENSOR_GAIN_REG2
+	{0x5912,0x700F},//SENSOR_GAIN_REG2
+	{0x5912,0x7F0F},//SENSOR_GAIN_REG2
+	{0x5912,0x7F0F},//SENSOR_GAIN_REG2
+	{0x5912,0x0045},//SENSOR_GAIN_REG2
+	{0x5912,0x0046},//SENSOR_GAIN_REG2
+	{0x5912,0x0047},//SENSOR_GAIN_REG2
+	{0x5912,0xD9B9},//SENSOR_GAIN_REG2
+	{0x5912,0x700F},//SENSOR_GAIN_REG2
+	{0x5912,0x7F0F},//SENSOR_GAIN_REG2
+	{0x5912,0x700F},//SENSOR_GAIN_REG2
+	{0x5912,0x7F0F},//SENSOR_GAIN_REG2
+	{0x5912,0x7F0F},//SENSOR_GAIN_REG2
+	{0x5912,0x0055},//SENSOR_GAIN_REG2
+	{0x5912,0x0056},//SENSOR_GAIN_REG2
+	{0x5912,0x0057},//SENSOR_GAIN_REG2
+	{0x5912,0x9A85},//SENSOR_GAIN_REG2
+	{0x5912,0x0654},//SENSOR_GAIN_REG2
+	{0x5912,0x0654},//SENSOR_GAIN_REG2
+	{0x5912,0x0684},//SENSOR_GAIN_REG2
+	{0x5912,0x0654},//SENSOR_GAIN_REG2
+	{0x5912,0x0654},//SENSOR_GAIN_REG2
+	{0x5912,0x0065},//SENSOR_GAIN_REG2
+	{0x5912,0x0066},//SENSOR_GAIN_REG2
+	{0x5912,0x0067},//SENSOR_GAIN_REG2
+	{0x5912,0x59BD},//SENSOR_GAIN_REG2
+	{0x5912,0x1000},//SENSOR_GAIN_REG2
+	{0x5912,0x0C00},//SENSOR_GAIN_REG2
+	{0x5912,0x0F00},//SENSOR_GAIN_REG2
+	{0x5912,0x1000},//SENSOR_GAIN_REG2
+	{0x5912,0x10F0},//SENSOR_GAIN_REG2
+	{0x5912,0x0075},//SENSOR_GAIN_REG2
+	{0x5912,0x0076},//SENSOR_GAIN_REG2
+	{0x5912,0x0077},//SENSOR_GAIN_REG2
+	{0x5914,0x4006},//SENSOR_GAIN_TABLE_SEL
+	{0x5900,0x0020},//SENSOR_GAIN
+	{0x5902,0x0000},//SENSOR_GAIN_T2
+	{0x3110,0x0001},//HDR_CONTROL0
+
+	{REG_NULL, 0x00},
+};
+
+static const struct regval ar0822_linear_60fps_regs[] = {
+	{REG_DELAY, 2000},
+	{0x3030,0x0092},//PLL_MULTIPLIER
+	{0x302E,0x0002},//PRE_PLL_CLK_DIV
+	{0x302C,0x0002},//VT_SYS_CLK_DIV
+	{0x302A,0x0006},//VT_PIX_CLK_DIV
+	{0x3038,0x0002},//OP_SYS_CLK_DIV
+	{0x3036,0x0006},//OP_WORD_CLK_DIV
+	{0x31B0,0x00BF},//FRAME_PREAMBLE
+	{0x31B2,0x007D},//LINE_PREAMBLE
+	{0x31B4,0x834E},//MIPI_TIMING_0
+	{0x31B6,0x8491},//MIPI_TIMING_1
+	{0x31B8,0xD0CF},//MIPI_TIMING_2
+	{0x31BA,0x0515},//MIPI_TIMING_3
+	{0x31BC,0x1911},//MIPI_TIMING_4
+	{0x3342,0x122C},//MIPI_F1_PDT_EDT
+	{0x31BC,0x5911},//MIPI_TIMING_4
+	{0x31DE,0x0004},//MIPI_HISPI_TRIM
+	{0x31C6,0xC000},//HISPI_CONTROL
+	{0x31C8,0x0B28},//MIPI_DESKEW_PAT_WIDTH
+	{0x2512,0xA000},//SEQ_CTRL_PORT
+	{0x2510,0x0720},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0x2122},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0x26FF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0xFFFF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x0F8C},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20E0},//SEQ_DATA_PORT
+	{0x2510,0x8055},//SEQ_DATA_PORT
+	{0x2510,0xA0E1},//SEQ_DATA_PORT
+	{0x2510,0x3041},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x3088},//SEQ_DATA_PORT
+	{0x2510,0x3282},//SEQ_DATA_PORT
+	{0x2510,0xA681},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FE},//SEQ_DATA_PORT
+	{0x2510,0x9070},//SEQ_DATA_PORT
+	{0x2510,0x891D},//SEQ_DATA_PORT
+	{0x2510,0x867F},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FC},//SEQ_DATA_PORT
+	{0x2510,0x893F},//SEQ_DATA_PORT
+	{0x2510,0x0F92},//SEQ_DATA_PORT
+	{0x2510,0x20E0},//SEQ_DATA_PORT
+	{0x2510,0x0F8F},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20E0},//SEQ_DATA_PORT
+	{0x2510,0x9770},//SEQ_DATA_PORT
+	{0x2510,0x20FC},//SEQ_DATA_PORT
+	{0x2510,0x8054},//SEQ_DATA_PORT
+	{0x2510,0x896C},//SEQ_DATA_PORT
+	{0x2510,0x200A},//SEQ_DATA_PORT
+	{0x2510,0x9030},//SEQ_DATA_PORT
+	{0x2510,0x200A},//SEQ_DATA_PORT
+	{0x2510,0x8040},//SEQ_DATA_PORT
+	{0x2510,0x8948},//SEQ_DATA_PORT
+	{0x2510,0x200A},//SEQ_DATA_PORT
+	{0x2510,0x1597},//SEQ_DATA_PORT
+	{0x2510,0x8808},//SEQ_DATA_PORT
+	{0x2510,0x200A},//SEQ_DATA_PORT
+	{0x2510,0x1F96},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20E0},//SEQ_DATA_PORT
+	{0x2510,0xA0C0},//SEQ_DATA_PORT
+	{0x2510,0x200A},//SEQ_DATA_PORT
+	{0x2510,0x3044},//SEQ_DATA_PORT
+	{0x2510,0x3088},//SEQ_DATA_PORT
+	{0x2510,0x3282},//SEQ_DATA_PORT
+	{0x2510,0x2004},//SEQ_DATA_PORT
+	{0x2510,0x1FAA},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x20E0},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x20FF},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x3108},//SEQ_DATA_PORT
+	{0x2510,0x2400},//SEQ_DATA_PORT
+	{0x2510,0x3244},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x3108},//SEQ_DATA_PORT
+	{0x2510,0x2400},//SEQ_DATA_PORT
+	{0x2510,0x2702},//SEQ_DATA_PORT
+	{0x2510,0x3242},//SEQ_DATA_PORT
+	{0x2510,0x3108},//SEQ_DATA_PORT
+	{0x2510,0x2420},//SEQ_DATA_PORT
+	{0x2510,0x2703},//SEQ_DATA_PORT
+	{0x2510,0x3242},//SEQ_DATA_PORT
+	{0x2510,0x3108},//SEQ_DATA_PORT
+	{0x2510,0x2420},//SEQ_DATA_PORT
+	{0x2510,0x2704},//SEQ_DATA_PORT
+	{0x2510,0x3244},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x8801},//SEQ_DATA_PORT
+	{0x2510,0x000F},//SEQ_DATA_PORT
+	{0x2510,0x109C},//SEQ_DATA_PORT
+	{0x2510,0x8855},//SEQ_DATA_PORT
+	{0x2510,0x3101},//SEQ_DATA_PORT
+	{0x2510,0x3041},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x3102},//SEQ_DATA_PORT
+	{0x2510,0x3041},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x3181},//SEQ_DATA_PORT
+	{0x2510,0x3041},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x3188},//SEQ_DATA_PORT
+	{0x2510,0x3041},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x3282},//SEQ_DATA_PORT
+	{0x2510,0x3104},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0xB0E4},//SEQ_DATA_PORT
+	{0x2510,0xAD92},//SEQ_DATA_PORT
+	{0x2510,0xBC0C},//SEQ_DATA_PORT
+	{0x2510,0x1028},//SEQ_DATA_PORT
+	{0x2510,0x0022},//SEQ_DATA_PORT
+	{0x2510,0xC020},//SEQ_DATA_PORT
+	{0x2510,0x003E},//SEQ_DATA_PORT
+	{0x2510,0x0045},//SEQ_DATA_PORT
+	{0x2510,0x00B0},//SEQ_DATA_PORT
+	{0x2510,0x0028},//SEQ_DATA_PORT
+	{0x2510,0x30C1},//SEQ_DATA_PORT
+	{0x2510,0x8015},//SEQ_DATA_PORT
+	{0x2510,0xA038},//SEQ_DATA_PORT
+	{0x2510,0x100F},//SEQ_DATA_PORT
+	{0x2510,0x0507},//SEQ_DATA_PORT
+	{0x2510,0xA220},//SEQ_DATA_PORT
+	{0x2510,0x0010},//SEQ_DATA_PORT
+	{0x2510,0x10C2},//SEQ_DATA_PORT
+	{0x2510,0xB760},//SEQ_DATA_PORT
+	{0x2510,0x0033},//SEQ_DATA_PORT
+	{0x2510,0x1082},//SEQ_DATA_PORT
+	{0x2510,0x100B},//SEQ_DATA_PORT
+	{0x2510,0x1029},//SEQ_DATA_PORT
+	{0x2510,0xA85A},//SEQ_DATA_PORT
+	{0x2510,0x998D},//SEQ_DATA_PORT
+	{0x2510,0xC810},//SEQ_DATA_PORT
+	{0x2510,0x2004},//SEQ_DATA_PORT
+	{0x2510,0x0ECE},//SEQ_DATA_PORT
+	{0x2510,0x123B},//SEQ_DATA_PORT
+	{0x2510,0xC000},//SEQ_DATA_PORT
+	{0x2510,0x032F},//SEQ_DATA_PORT
+	{0x2510,0x11D5},//SEQ_DATA_PORT
+	{0x2510,0x162F},//SEQ_DATA_PORT
+	{0x2510,0x9000},//SEQ_DATA_PORT
+	{0x2510,0x2034},//SEQ_DATA_PORT
+	{0x2510,0x0015},//SEQ_DATA_PORT
+	{0x2510,0x04CB},//SEQ_DATA_PORT
+	{0x2510,0x1022},//SEQ_DATA_PORT
+	{0x2510,0x1031},//SEQ_DATA_PORT
+	{0x2510,0x002D},//SEQ_DATA_PORT
+	{0x2510,0x1015},//SEQ_DATA_PORT
+	{0x2510,0x80B9},//SEQ_DATA_PORT
+	{0x2510,0xA101},//SEQ_DATA_PORT
+	{0x2510,0x001C},//SEQ_DATA_PORT
+	{0x2510,0x008E},//SEQ_DATA_PORT
+	{0x2510,0x124B},//SEQ_DATA_PORT
+	{0x2510,0x01B5},//SEQ_DATA_PORT
+	{0x2510,0x0B92},//SEQ_DATA_PORT
+	{0x2510,0xA400},//SEQ_DATA_PORT
+	{0x2510,0x8091},//SEQ_DATA_PORT
+	{0x2510,0x0028},//SEQ_DATA_PORT
+	{0x2510,0x3002},//SEQ_DATA_PORT
+	{0x2510,0x2004},//SEQ_DATA_PORT
+	{0x2510,0x1012},//SEQ_DATA_PORT
+	{0x2510,0x100E},//SEQ_DATA_PORT
+	{0x2510,0x10A8},//SEQ_DATA_PORT
+	{0x2510,0x00A1},//SEQ_DATA_PORT
+	{0x2510,0x132D},//SEQ_DATA_PORT
+	{0x2510,0x09AF},//SEQ_DATA_PORT
+	{0x2510,0x0159},//SEQ_DATA_PORT
+	{0x2510,0x121D},//SEQ_DATA_PORT
+	{0x2510,0x1259},//SEQ_DATA_PORT
+	{0x2510,0x11AF},//SEQ_DATA_PORT
+	{0x2510,0x18B5},//SEQ_DATA_PORT
+	{0x2510,0x0395},//SEQ_DATA_PORT
+	{0x2510,0x054B},//SEQ_DATA_PORT
+	{0x2510,0x1021},//SEQ_DATA_PORT
+	{0x2510,0x0020},//SEQ_DATA_PORT
+	{0x2510,0x1015},//SEQ_DATA_PORT
+	{0x2510,0x1030},//SEQ_DATA_PORT
+	{0x2510,0x00CF},//SEQ_DATA_PORT
+	{0x2510,0xB146},//SEQ_DATA_PORT
+	{0x2510,0xC290},//SEQ_DATA_PORT
+	{0x2510,0x103C},//SEQ_DATA_PORT
+	{0x2510,0xA882},//SEQ_DATA_PORT
+	{0x2510,0x8055},//SEQ_DATA_PORT
+	{0x2510,0x00A9},//SEQ_DATA_PORT
+	{0x2510,0x8801},//SEQ_DATA_PORT
+	{0x2510,0xB700},//SEQ_DATA_PORT
+	{0x2510,0x0001},//SEQ_DATA_PORT
+	{0x2510,0x02A2},//SEQ_DATA_PORT
+	{0x2510,0x000A},//SEQ_DATA_PORT
+	{0x2510,0x98BB},//SEQ_DATA_PORT
+	{0x2510,0x203F},//SEQ_DATA_PORT
+	{0x2510,0x0036},//SEQ_DATA_PORT
+	{0x2510,0x1001},//SEQ_DATA_PORT
+	{0x2510,0x99BE},//SEQ_DATA_PORT
+	{0x2510,0x0139},//SEQ_DATA_PORT
+	{0x2510,0x100A},//SEQ_DATA_PORT
+	{0x2510,0x0040},//SEQ_DATA_PORT
+	{0x2510,0x1022},//SEQ_DATA_PORT
+	{0x2510,0x124C},//SEQ_DATA_PORT
+	{0x2510,0x109F},//SEQ_DATA_PORT
+	{0x2510,0x15A3},//SEQ_DATA_PORT
+	{0x2510,0x002A},//SEQ_DATA_PORT
+	{0x2510,0x3081},//SEQ_DATA_PORT
+	{0x2510,0x2001},//SEQ_DATA_PORT
+	{0x2510,0x3044},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x112A},//SEQ_DATA_PORT
+	{0x2510,0x101D},//SEQ_DATA_PORT
+	{0x2510,0x202B},//SEQ_DATA_PORT
+	{0x2510,0x02B8},//SEQ_DATA_PORT
+	{0x2510,0x10B8},//SEQ_DATA_PORT
+	{0x2510,0x1136},//SEQ_DATA_PORT
+	{0x2510,0x996B},//SEQ_DATA_PORT
+	{0x2510,0x004C},//SEQ_DATA_PORT
+	{0x2510,0x1039},//SEQ_DATA_PORT
+	{0x2510,0x1040},//SEQ_DATA_PORT
+	{0x2510,0x00B5},//SEQ_DATA_PORT
+	{0x2510,0x03C4},//SEQ_DATA_PORT
+	{0x2510,0x1144},//SEQ_DATA_PORT
+	{0x2510,0x1245},//SEQ_DATA_PORT
+	{0x2510,0x9A7B},//SEQ_DATA_PORT
+	{0x2510,0x002B},//SEQ_DATA_PORT
+	{0x2510,0x30D0},//SEQ_DATA_PORT
+	{0x2510,0x3141},//SEQ_DATA_PORT
+	{0x2510,0x3041},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x3142},//SEQ_DATA_PORT
+	{0x2510,0x3041},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x3110},//SEQ_DATA_PORT
+	{0x2510,0x3041},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x3120},//SEQ_DATA_PORT
+	{0x2510,0x3041},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x3144},//SEQ_DATA_PORT
+	{0x2510,0x3041},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x3148},//SEQ_DATA_PORT
+	{0x2510,0x3041},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x3182},//SEQ_DATA_PORT
+	{0x2510,0x3041},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x3184},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x3190},//SEQ_DATA_PORT
+	{0x2510,0x3041},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x31A0},//SEQ_DATA_PORT
+	{0x2510,0x3088},//SEQ_DATA_PORT
+	{0x2510,0x2201},//SEQ_DATA_PORT
+	{0x2510,0x807D},//SEQ_DATA_PORT
+	{0x2510,0x2206},//SEQ_DATA_PORT
+	{0x2510,0x8815},//SEQ_DATA_PORT
+	{0x2510,0x8877},//SEQ_DATA_PORT
+	{0x2510,0x0092},//SEQ_DATA_PORT
+	{0x2510,0x220E},//SEQ_DATA_PORT
+	{0x2510,0x2211},//SEQ_DATA_PORT
+	{0x2510,0x8055},//SEQ_DATA_PORT
+	{0x2510,0x3001},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x8A61},//SEQ_DATA_PORT
+	{0x2510,0x8801},//SEQ_DATA_PORT
+	{0x2510,0x1092},//SEQ_DATA_PORT
+	{0x2510,0x181F},//SEQ_DATA_PORT
+	{0x2510,0x0B1F},//SEQ_DATA_PORT
+	{0x2510,0x101F},//SEQ_DATA_PORT
+	{0x2510,0x00B6},//SEQ_DATA_PORT
+	{0x2510,0x0023},//SEQ_DATA_PORT
+	{0x2510,0x00B9},//SEQ_DATA_PORT
+	{0x2510,0x104C},//SEQ_DATA_PORT
+	{0x2510,0x996E},//SEQ_DATA_PORT
+	{0x2510,0x0140},//SEQ_DATA_PORT
+	{0x2510,0x0257},//SEQ_DATA_PORT
+	{0x2510,0x1035},//SEQ_DATA_PORT
+	{0x2510,0x9F26},//SEQ_DATA_PORT
+	{0x2510,0x1423},//SEQ_DATA_PORT
+	{0x2510,0x0048},//SEQ_DATA_PORT
+	{0x2510,0xC878},//SEQ_DATA_PORT
+	{0x2510,0x200A},//SEQ_DATA_PORT
+	{0x2510,0x1548},//SEQ_DATA_PORT
+	{0x2510,0x0C49},//SEQ_DATA_PORT
+	{0x2510,0x1149},//SEQ_DATA_PORT
+	{0x2510,0x002A},//SEQ_DATA_PORT
+	{0x2510,0x1057},//SEQ_DATA_PORT
+	{0x2510,0x3281},//SEQ_DATA_PORT
+	{0x2510,0x2000},//SEQ_DATA_PORT
+	{0x2510,0x3044},//SEQ_DATA_PORT
+	{0x2510,0x2001},//SEQ_DATA_PORT
+	{0x2510,0xA020},//SEQ_DATA_PORT
+	{0x2510,0x000C},//SEQ_DATA_PORT
+	{0x2510,0x9825},//SEQ_DATA_PORT
+	{0x2510,0x1040},//SEQ_DATA_PORT
+	{0x2510,0x1054},//SEQ_DATA_PORT
+	{0x2510,0xB06D},//SEQ_DATA_PORT
+	{0x2510,0x0035},//SEQ_DATA_PORT
+	{0x2510,0x004D},//SEQ_DATA_PORT
+	{0x2510,0x9905},//SEQ_DATA_PORT
+	{0x2510,0xB064},//SEQ_DATA_PORT
+	{0x2510,0x99C5},//SEQ_DATA_PORT
+	{0x2510,0x0047},//SEQ_DATA_PORT
+	{0x2510,0xB920},//SEQ_DATA_PORT
+	{0x2510,0x1447},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x2510,0x7FFF},//SEQ_DATA_PORT
+	{0x31F8,0x0008},//MIPI_CONFIG_2
+	{0x3C70,0x6828},//CALIB_ROWS
+	{0x3092,0x0826},//ROW_NOISE_CONTROL
+	{0x3428,0x0209},//SEQUENCER_CONTROL
+	{0x3516,0xFF04},//DAC_LD_22_23
+	{0x3526,0x6480},//DAC_LD_38_39
+	{0x3504,0x8AAA},//DAC_LD_4_5
+	{0x353C,0x220C},//DAC_LD_60_61
+	{0x3536,0x4C6E},//DAC_LD_54_55
+	{0x3D2A,0x0FFF},//T1_END_DEC_TH
+	{0x3364,0x00EC},//DCG_TRIM
+	{0x3512,0x8888},//DAC_LD_18_19
+	{0x3514,0x888F},//DAC_LD_20_21
+	{0x3520,0xFBF0},//DAC_LD_32_33
+	{0x3524,0xB2A1},//DAC_LD_36_37
+	{0x3528,0xCC84},//DAC_LD_40_41
+	{0x3532,0x4C8E},//DAC_LD_50_51
+	{0x3534,0x4E64},//DAC_LD_52_53
+	{0x351E,0x5856},//DAC_LD_30_31
+	{0x353E,0x98F2},//DAC_LD_62_63
+	{0x352E,0x6A8A},//DAC_LD_46_47
+	{0x3370,0x0211},//DBLC_CONTROL
+	{0x3372,0x700F},//DBLC_FS0_CONTROL
+	{0x3540,0x3597},//DAC_LD_64_65
+	{0x58E2,0x0BE3},//COL_COUNT_VALUES1
+	{0x58E4,0x18B4},//COL_COUNT_VALUES2
+	{0x3522,0x7C97},//DAC_LD_34_35
+	{0x30BA,0x0024},//DIGITAL_CTRL
+	{0x31D4,0x0042},//CLK_MEM_GATING_CTRL
+	{0x352A,0x6F8F},//DAC_LD_42_43
+	{0x3530,0x4A08},//DAC_LD_48_49
+	{0x351A,0x5FFF},//DAC_LD_26_27
+	{0x350E,0x39D9},//DAC_LD_14_15
+	{0x3510,0x9988},//DAC_LD_16_17
+	{0x3380,0x1FFF},//DBLC_OFFSET1
+	{0x337A,0x1000},//DBLC_SCALE1
+	{0x3092,0x0800},//ROW_NOISE_CONTROL
+	{0x350A,0x0654},//DAC_LD_10_11
+	{0x3364,0x00E0},//DCG_TRIM
+	{0x591E,0x61AE},//ANALOG_GAIN_WR_DATA
+	{0x591E,0x722C},//ANALOG_GAIN_WR_DATA
+	{0x591E,0x82B8},//ANALOG_GAIN_WR_DATA
+	{0x591E,0x92F6},//ANALOG_GAIN_WR_DATA
+	{0x591E,0xA447},//ANALOG_GAIN_WR_DATA
+	{0x591E,0xB66D},//ANALOG_GAIN_WR_DATA
+	{0x591E,0xC6EA},//ANALOG_GAIN_WR_DATA
+	{0x591E,0xDECD},//ANALOG_GAIN_WR_DATA
+	{0x3532,0x4C8A},//DAC_LD_50_51
+	{0x3534,0x4E60},//DAC_LD_52_53
+	{0x353E,0x90F2},//DAC_LD_62_63
+	{0x351A,0x4FFF},//DAC_LD_26_27
+	{0x591C,0x00D7},//DGR_AMP_GAIN
+	{0x3522,0x6097},//DAC_LD_34_35
+	{0x5002,0x37C3},//T1_PIX_DEF_ID2
+	{0x51CC,0x0149},//T1_NOISE_GAIN_THRESHOLD0
+	{0x51D8,0x044D},//T1_NOISE_GAIN_THRESHOLD1
+	{0x51CE,0x0700},//T1_NOISE_GAIN_THRESHOLD2
+	{0x51D0,0x0001},//T1_NOISE_FLOOR0
+	{0x51D2,0x0002},//T1_NOISE_FLOOR1
+	{0x51D4,0x0003},//T1_NOISE_FLOOR2
+	{0x51D6,0x0004},//T1_NOISE_FLOOR3
+	{0x5202,0x37C3},//T2_PIX_DEF_ID2
+	{0x51EA,0x0149},//T2_NOISE_GAIN_THRESHOLD0
+	{0x51FC,0x044D},//T2_NOISE_GAIN_THRESHOLD1
+	{0x51EC,0x0700},//T2_NOISE_GAIN_THRESHOLD2
+	{0x51EE,0x0001},//T2_NOISE_FLOOR0
+	{0x51F0,0x0002},//T2_NOISE_FLOOR1
+	{0x51F2,0x0003},//T2_NOISE_FLOOR2
+	{0x51F4,0x0004},//T2_NOISE_FLOOR3
+	{0x5402,0x37C3},//T4_PIX_DEF_ID2
+	{0x5560,0x0149},//T4_NOISE_GAIN_THRESHOLD0
+	{0x556C,0x044D},//T4_NOISE_GAIN_THRESHOLD1
+	{0x5562,0x0700},//T4_NOISE_GAIN_THRESHOLD2
+	{0x5564,0x0001},//T4_NOISE_FLOOR0
+	{0x5566,0x0002},//T4_NOISE_FLOOR1
+	{0x5568,0x0003},//T4_NOISE_FLOOR2
+	{0x556A,0x0004},//T4_NOISE_FLOOR3
+	{0x31E0,0x0001},//PIX_DEF_ID
+	{0x5000,0x0080},//T1_PIX_DEF_ID
+	{0x5000,0x0180},//T1_PIX_DEF_ID
+	{0x5000,0x0180},//T1_PIX_DEF_ID
+	{0x5200,0x0080},//T2_PIX_DEF_ID
+	{0x5200,0x0180},//T2_PIX_DEF_ID
+	{0x5200,0x0180},//T2_PIX_DEF_ID
+	{0x5400,0x0080},//T4_PIX_DEF_ID
+	{0x5400,0x0180},//T4_PIX_DEF_ID
+	{0x5400,0x0180},//T4_PIX_DEF_ID
+	{0x5000,0x1180},//T1_PIX_DEF_ID
+	{0x50A2,0x2553},//BMT0
+	{0x50A4,0xDFD4},//BMT1
+	{0x50A6,0x030F},//SINGLEK_FACTOR0
+	{0x50A6,0x0F0F},//SINGLEK_FACTOR0
+	{0x50A8,0x030F},//SINGLEK_FACTOR1
+	{0x50A8,0x0F0F},//SINGLEK_FACTOR1
+	{0x50AA,0x030F},//SINGLEK_FACTOR2
+	{0x50AA,0x050F},//SINGLEK_FACTOR2
+	{0x50AC,0x0301},//CROSS_FACTOR0
+	{0x50AC,0x0101},//CROSS_FACTOR0
+	{0x50AE,0x0301},//CROSS_FACTOR1
+	{0x50AE,0x0101},//CROSS_FACTOR1
+	{0x50B0,0x0301},//CROSS_FACTOR2
+	{0x50B0,0x0101},//CROSS_FACTOR2
+	{0x50B2,0x03FF},//SINGLE_MAX_FACTOR
+	{0x50B4,0x030F},//COUPLE_FACTOR0
+	{0x50B4,0x0F0F},//COUPLE_FACTOR0
+	{0x50B6,0x030F},//COUPLE_FACTOR1
+	{0x50B6,0x0F0F},//COUPLE_FACTOR1
+	{0x50B8,0x030F},//COUPLE_FACTOR2
+	{0x50B8,0x050F},//COUPLE_FACTOR2
+	{0x31AE,0x0204},//SERIAL_FORMAT
+	{0x31AC,0x0C0C},//DATA_FORMAT_BITS
+	{0x3082,0x0001},//OPERATION_MODE_CTRL
+	{0x30BA,0x0024},//DIGITAL_CTRL
+	{0x31AE,0x0204},//SERIAL_FORMAT
+	{0x31AC,0x0C0C},//DATA_FORMAT_BITS
+	{0x300C,0x0482},//LINE_LENGTH_PCK_
+	{0x300A,0x0944},//FRAME_LENGTH_LINES_
+	{0x3012,0x093E},//COARSE_INTEGRATION_TIME_
+	{0x5914,0x4012},//SENSOR_GAIN_TABLE_SEL
+	{REG_DELAY,100},
+	{0x5914,0x4002},//SENSOR_GAIN_TABLE_SEL
+	{0x5910,0x608A},//SENSOR_GAIN_REG1
+	{0x5910,0x7091},//SENSOR_GAIN_REG1
+	{0x5910,0x689C},//SENSOR_GAIN_REG1
+	{0x5910,0x8885},//SENSOR_GAIN_REG1
+	{0x5910,0x98AD},//SENSOR_GAIN_REG1
+	{0x5910,0xA8A9},//SENSOR_GAIN_REG1
+	{0x5910,0xC894},//SENSOR_GAIN_REG1
+	{0x5910,0xC8D1},//SENSOR_GAIN_REG1
+	{0x5910,0xD88A},//SENSOR_GAIN_REG1
+	{0x5910,0xD8C3},//SENSOR_GAIN_REG1
+	{0x5910,0xD915},//SENSOR_GAIN_REG1
+	{0x5910,0xD988},//SENSOR_GAIN_REG1
+	{0x5910,0xDA2A},//SENSOR_GAIN_REG1
+	{0x5910,0xDB0E},//SENSOR_GAIN_REG1
+	{0x5910,0xDC53},//SENSOR_GAIN_REG1
+	{0x5910,0x608A},//SENSOR_GAIN_REG1
+	{0x5910,0xC919},//SENSOR_GAIN_REG1
+	{0x5910,0xCA00},//SENSOR_GAIN_REG1
+	{0x5910,0x0000},//SENSOR_GAIN_REG1
+	{0x5910,0x0000},//SENSOR_GAIN_REG1
+	{0x5910,0x0000},//SENSOR_GAIN_REG1
+	{0x5910,0x0001},//SENSOR_GAIN_REG1
+	{0x5910,0x0001},//SENSOR_GAIN_REG1
+	{0x5910,0x0003},//SENSOR_GAIN_REG1
+	{0x5910,0x0003},//SENSOR_GAIN_REG1
+	{0x5910,0x0003},//SENSOR_GAIN_REG1
+	{0x5910,0x0004},//SENSOR_GAIN_REG1
+	{0x5910,0x0004},//SENSOR_GAIN_REG1
+	{0x5910,0x0004},//SENSOR_GAIN_REG1
+	{0x5910,0x0004},//SENSOR_GAIN_REG1
+	{0x5910,0x0004},//SENSOR_GAIN_REG1
+	{0x5910,0x0004},//SENSOR_GAIN_REG1
+	{0x5910,0x0004},//SENSOR_GAIN_REG1
+	{0x5910,0x0002},//SENSOR_GAIN_REG1
+	{0x5910,0x0003},//SENSOR_GAIN_REG1
+	{0x5910,0x0003},//SENSOR_GAIN_REG1
+	{0x5910,0x5A8B},//SENSOR_GAIN_REG1
+	{0x5910,0xFF04},//SENSOR_GAIN_REG1
+	{0x5910,0xF704},//SENSOR_GAIN_REG1
+	{0x5910,0xFF04},//SENSOR_GAIN_REG1
+	{0x5910,0xF704},//SENSOR_GAIN_REG1
+	{0x5910,0xF704},//SENSOR_GAIN_REG1
+	{0x5910,0x0005},//SENSOR_GAIN_REG1
+	{0x5910,0x0006},//SENSOR_GAIN_REG1
+	{0x5910,0x0007},//SENSOR_GAIN_REG1
+	{0x5910,0x9A8B},//SENSOR_GAIN_REG1
+	{0x5910,0xFF04},//SENSOR_GAIN_REG1
+	{0x5910,0xF704},//SENSOR_GAIN_REG1
+	{0x5910,0xF704},//SENSOR_GAIN_REG1
+	{0x5910,0xF704},//SENSOR_GAIN_REG1
+	{0x5910,0xF704},//SENSOR_GAIN_REG1
+	{0x5910,0x0015},//SENSOR_GAIN_REG1
+	{0x5910,0x0016},//SENSOR_GAIN_REG1
+	{0x5910,0x0017},//SENSOR_GAIN_REG1
+	{0x5910,0xDA8B},//SENSOR_GAIN_REG1
+	{0x5910,0xFF04},//SENSOR_GAIN_REG1
+	{0x5910,0xF704},//SENSOR_GAIN_REG1
+	{0x5910,0xF704},//SENSOR_GAIN_REG1
+	{0x5910,0xF704},//SENSOR_GAIN_REG1
+	{0x5910,0xF704},//SENSOR_GAIN_REG1
+	{0x5910,0x0025},//SENSOR_GAIN_REG1
+	{0x5910,0x0026},//SENSOR_GAIN_REG1
+	{0x5910,0x0027},//SENSOR_GAIN_REG1
+	{0x5910,0x59B9},//SENSOR_GAIN_REG1
+	{0x5910,0x700F},//SENSOR_GAIN_REG1
+	{0x5910,0x7F0F},//SENSOR_GAIN_REG1
+	{0x5910,0x700F},//SENSOR_GAIN_REG1
+	{0x5910,0x7F0F},//SENSOR_GAIN_REG1
+	{0x5910,0x7F0F},//SENSOR_GAIN_REG1
+	{0x5910,0x0035},//SENSOR_GAIN_REG1
+	{0x5910,0x0036},//SENSOR_GAIN_REG1
+	{0x5910,0x0037},//SENSOR_GAIN_REG1
+	{0x5910,0x99B9},//SENSOR_GAIN_REG1
+	{0x5910,0x700F},//SENSOR_GAIN_REG1
+	{0x5910,0x7F0F},//SENSOR_GAIN_REG1
+	{0x5910,0x700F},//SENSOR_GAIN_REG1
+	{0x5910,0x7F0F},//SENSOR_GAIN_REG1
+	{0x5910,0x7F0F},//SENSOR_GAIN_REG1
+	{0x5910,0x0045},//SENSOR_GAIN_REG1
+	{0x5910,0x0046},//SENSOR_GAIN_REG1
+	{0x5910,0x0047},//SENSOR_GAIN_REG1
+	{0x5910,0xD9B9},//SENSOR_GAIN_REG1
+	{0x5910,0x700F},//SENSOR_GAIN_REG1
+	{0x5910,0x7F0F},//SENSOR_GAIN_REG1
+	{0x5910,0x700F},//SENSOR_GAIN_REG1
+	{0x5910,0x7F0F},//SENSOR_GAIN_REG1
+	{0x5910,0x7F0F},//SENSOR_GAIN_REG1
+	{0x5910,0x0055},//SENSOR_GAIN_REG1
+	{0x5910,0x0056},//SENSOR_GAIN_REG1
+	{0x5910,0x0057},//SENSOR_GAIN_REG1
+	{0x5910,0x9A85},//SENSOR_GAIN_REG1
+	{0x5910,0x0654},//SENSOR_GAIN_REG1
+	{0x5910,0x0654},//SENSOR_GAIN_REG1
+	{0x5910,0x0684},//SENSOR_GAIN_REG1
+	{0x5910,0x0654},//SENSOR_GAIN_REG1
+	{0x5910,0x0654},//SENSOR_GAIN_REG1
+	{0x5910,0x0065},//SENSOR_GAIN_REG1
+	{0x5910,0x0066},//SENSOR_GAIN_REG1
+	{0x5910,0x0067},//SENSOR_GAIN_REG1
+	{0x5910,0x59BD},//SENSOR_GAIN_REG1
+	{0x5910,0x1000},//SENSOR_GAIN_REG1
+	{0x5910,0x0C00},//SENSOR_GAIN_REG1
+	{0x5910,0x0F00},//SENSOR_GAIN_REG1
+	{0x5910,0x1000},//SENSOR_GAIN_REG1
+	{0x5910,0x10F0},//SENSOR_GAIN_REG1
+	{0x5910,0x0075},//SENSOR_GAIN_REG1
+	{0x5910,0x0076},//SENSOR_GAIN_REG1
+	{0x5910,0x0077},//SENSOR_GAIN_REG1
+	{0x5912,0x608A},//SENSOR_GAIN_REG2
+	{0x5912,0x7091},//SENSOR_GAIN_REG2
+	{0x5912,0x689C},//SENSOR_GAIN_REG2
+	{0x5912,0x8885},//SENSOR_GAIN_REG2
+	{0x5912,0x98AD},//SENSOR_GAIN_REG2
+	{0x5912,0xA8A9},//SENSOR_GAIN_REG2
+	{0x5912,0xC894},//SENSOR_GAIN_REG2
+	{0x5912,0xC8D1},//SENSOR_GAIN_REG2
+	{0x5912,0xC927},//SENSOR_GAIN_REG2
+	{0x5912,0xC9A0},//SENSOR_GAIN_REG2
+	{0x5912,0xCA4C},//SENSOR_GAIN_REG2
+	{0x5912,0xCB3F},//SENSOR_GAIN_REG2
+	{0x5912,0xCC97},//SENSOR_GAIN_REG2
+	{0x5912,0xCE7C},//SENSOR_GAIN_REG2
+	{0x5912,0xCFFF},//SENSOR_GAIN_REG2
+	{0x5912,0x608A},//SENSOR_GAIN_REG2
+	{0x5912,0xC8F0},//SENSOR_GAIN_REG2
+	{0x5912,0xCA00},//SENSOR_GAIN_REG2
+	{0x5912,0x0000},//SENSOR_GAIN_REG2
+	{0x5912,0x0000},//SENSOR_GAIN_REG2
+	{0x5912,0x0000},//SENSOR_GAIN_REG2
+	{0x5912,0x0001},//SENSOR_GAIN_REG2
+	{0x5912,0x0001},//SENSOR_GAIN_REG2
+	{0x5912,0x0003},//SENSOR_GAIN_REG2
+	{0x5912,0x0003},//SENSOR_GAIN_REG2
+	{0x5912,0x0003},//SENSOR_GAIN_REG2
+	{0x5912,0x0004},//SENSOR_GAIN_REG2
+	{0x5912,0x0004},//SENSOR_GAIN_REG2
+	{0x5912,0x0004},//SENSOR_GAIN_REG2
+	{0x5912,0x0004},//SENSOR_GAIN_REG2
+	{0x5912,0x0004},//SENSOR_GAIN_REG2
+	{0x5912,0x0004},//SENSOR_GAIN_REG2
+	{0x5912,0x0004},//SENSOR_GAIN_REG2
+	{0x5912,0x0002},//SENSOR_GAIN_REG2
+	{0x5912,0x0003},//SENSOR_GAIN_REG2
+	{0x5912,0x0003},//SENSOR_GAIN_REG2
+	{0x5912,0x5A8B},//SENSOR_GAIN_REG2
+	{0x5912,0xFF04},//SENSOR_GAIN_REG2
+	{0x5912,0xF704},//SENSOR_GAIN_REG2
+	{0x5912,0xFF04},//SENSOR_GAIN_REG2
+	{0x5912,0xF704},//SENSOR_GAIN_REG2
+	{0x5912,0xF704},//SENSOR_GAIN_REG2
+	{0x5912,0x0005},//SENSOR_GAIN_REG2
+	{0x5912,0x0006},//SENSOR_GAIN_REG2
+	{0x5912,0x0007},//SENSOR_GAIN_REG2
+	{0x5912,0x9A8B},//SENSOR_GAIN_REG2
+	{0x5912,0xFF04},//SENSOR_GAIN_REG2
+	{0x5912,0xF704},//SENSOR_GAIN_REG2
+	{0x5912,0xF704},//SENSOR_GAIN_REG2
+	{0x5912,0xF704},//SENSOR_GAIN_REG2
+	{0x5912,0xF704},//SENSOR_GAIN_REG2
+	{0x5912,0x0015},//SENSOR_GAIN_REG2
+	{0x5912,0x0016},//SENSOR_GAIN_REG2
+	{0x5912,0x0017},//SENSOR_GAIN_REG2
+	{0x5912,0xDA8B},//SENSOR_GAIN_REG2
+	{0x5912,0xFF04},//SENSOR_GAIN_REG2
+	{0x5912,0xF704},//SENSOR_GAIN_REG2
+	{0x5912,0xF704},//SENSOR_GAIN_REG2
+	{0x5912,0xF704},//SENSOR_GAIN_REG2
+	{0x5912,0xF704},//SENSOR_GAIN_REG2
+	{0x5912,0x0025},//SENSOR_GAIN_REG2
+	{0x5912,0x0026},//SENSOR_GAIN_REG2
+	{0x5912,0x0027},//SENSOR_GAIN_REG2
+	{0x5912,0x59B9},//SENSOR_GAIN_REG2
+	{0x5912,0x700F},//SENSOR_GAIN_REG2
+	{0x5912,0x7F0F},//SENSOR_GAIN_REG2
+	{0x5912,0x700F},//SENSOR_GAIN_REG2
+	{0x5912,0x7F0F},//SENSOR_GAIN_REG2
+	{0x5912,0x7F0F},//SENSOR_GAIN_REG2
+	{0x5912,0x0035},//SENSOR_GAIN_REG2
+	{0x5912,0x0036},//SENSOR_GAIN_REG2
+	{0x5912,0x0037},//SENSOR_GAIN_REG2
+	{0x5912,0x99B9},//SENSOR_GAIN_REG2
+	{0x5912,0x700F},//SENSOR_GAIN_REG2
+	{0x5912,0x7F0F},//SENSOR_GAIN_REG2
+	{0x5912,0x700F},//SENSOR_GAIN_REG2
+	{0x5912,0x7F0F},//SENSOR_GAIN_REG2
+	{0x5912,0x7F0F},//SENSOR_GAIN_REG2
+	{0x5912,0x0045},//SENSOR_GAIN_REG2
+	{0x5912,0x0046},//SENSOR_GAIN_REG2
+	{0x5912,0x0047},//SENSOR_GAIN_REG2
+	{0x5912,0xD9B9},//SENSOR_GAIN_REG2
+	{0x5912,0x700F},//SENSOR_GAIN_REG2
+	{0x5912,0x7F0F},//SENSOR_GAIN_REG2
+	{0x5912,0x700F},//SENSOR_GAIN_REG2
+	{0x5912,0x7F0F},//SENSOR_GAIN_REG2
+	{0x5912,0x7F0F},//SENSOR_GAIN_REG2
+	{0x5912,0x0055},//SENSOR_GAIN_REG2
+	{0x5912,0x0056},//SENSOR_GAIN_REG2
+	{0x5912,0x0057},//SENSOR_GAIN_REG2
+	{0x5912,0x9A85},//SENSOR_GAIN_REG2
+	{0x5912,0x0654},//SENSOR_GAIN_REG2
+	{0x5912,0x0654},//SENSOR_GAIN_REG2
+	{0x5912,0x0684},//SENSOR_GAIN_REG2
+	{0x5912,0x0654},//SENSOR_GAIN_REG2
+	{0x5912,0x0654},//SENSOR_GAIN_REG2
+	{0x5912,0x0065},//SENSOR_GAIN_REG2
+	{0x5912,0x0066},//SENSOR_GAIN_REG2
+	{0x5912,0x0067},//SENSOR_GAIN_REG2
+	{0x5912,0x59BD},//SENSOR_GAIN_REG2
+	{0x5912,0x1000},//SENSOR_GAIN_REG2
+	{0x5912,0x0C00},//SENSOR_GAIN_REG2
+	{0x5912,0x0F00},//SENSOR_GAIN_REG2
+	{0x5912,0x1000},//SENSOR_GAIN_REG2
+	{0x5912,0x10F0},//SENSOR_GAIN_REG2
+	{0x5912,0x0075},//SENSOR_GAIN_REG2
+	{0x5912,0x0076},//SENSOR_GAIN_REG2
+	{0x5912,0x0077},//SENSOR_GAIN_REG2
+	{0x5914,0x4002},//SENSOR_GAIN_TABLE_SEL
+	{0x5900,0x0000},//SENSOR_GAIN
+
+	{REG_NULL, 0x00},
+};
+static const s64 link_freq_menu_items[] = {
+	MIPI_FREQ_492M,
+	MIPI_FREQ_657M,
+	MIPI_FREQ_823M,
+	MIPI_FREQ_986M,
+};
+#define MIPI_FREQ_492M_INDEX 0
+#define MIPI_FREQ_657M_INDEX 1
+#define MIPI_FREQ_823M_INDEX 2
+#define MIPI_FREQ_986M_INDEX 3
+#define MIPI_FREQ_MAX_INDEX  4
+/*
+ * The width and height must be configured to be
+ * the same as the current output resolution of the sensor.
+ * The input width of the isp needs to be 16 aligned.
+ * The input height of the isp needs to be 8 aligned.
+ * If the width or height does not meet the alignment rules,
+ * you can configure the cropping parameters with the following function to
+ * crop out the appropriate resolution.
+ * struct v4l2_subdev_pad_ops {
+ *	.get_selection
+ * }
+ */
+
+/* Config resolution ,LLPCLK, FLL, exposure time,fps, MIPI channel config, HDR mode , open.k */
+static const struct ar0822_mode supported_modes[] = {
+/*	{
+		.bus_fmt = MEDIA_BUS_FMT_SGRBG12_1X12,
+		.width = 3840,
+		.height = 2160,
+		.max_fps = {
+			.numerator = 10000,
+			.denominator = 600000,
+		},
+		.exp_def = 0x0240,
+		.hts_def = 0x4330,//for linear mode, hblank is 4*LINE_LENGTH_PCK_-WIDTH,so hts is 4*LINE_LENGTH_PCK_. not used param by RK.
+		.vts_def = 0x0944,//used by AEC, should set correctly.
+		.reg_list = ar0822_linear_60fps_regs,
+		.hdr_mode = NO_HDR,
+		.mipi_freq = MIPI_FREQ_986M_INDEX,
+		.mipi_rate = MIPI_FREQ_986M/AR0822_BPP12*2*AR0822_LANES,
+		.vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_0,
+	},*/
+	{
+		.bus_fmt = MEDIA_BUS_FMT_SGRBG12_1X12,
+		.width = 3840,
+		.height = 2160,
+		.max_fps = {
+			.numerator = 10000,
+			.denominator = 300000,
+		},
+		.exp_def = 0x0240,
+		.hts_def = 0x4330,//for linear mode, hblank is 4*LINE_LENGTH_PCK_-WIDTH,so hts is 4*LINE_LENGTH_PCK_. not used param by RK.
+		.vts_def = 0x09F3,//used by AEC, should set correctly.
+		.reg_list = ar0822_linear_global_regs,
+		.hdr_mode = NO_HDR,
+		.mipi_freq = MIPI_FREQ_492M_INDEX,
+		.mipi_rate = MIPI_FREQ_492M / AR0822_BPP12 * 2 * AR0822_LANES,
+		.vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_0,
+	},
+
+	{
+		.bus_fmt = MEDIA_BUS_FMT_SGRBG12_1X12,
+		.width = 3840,
+		.height = 2160,
+		.max_fps = {
+			.numerator = 10000,
+			.denominator = 200000,
+		},
+		.exp_def = 0x0240,
+		.hts_def = 0x0E7C*2,//
+		.vts_def = 0x9b8,//0x0888,//
+		.reg_list = ar0822_hdr12bit_3840x2160_20fps_regs,
+		.hdr_mode = HDR_X2,
+		.mipi_freq = MIPI_FREQ_657M_INDEX,
+		.mipi_rate = MIPI_FREQ_657M / AR0822_BPP12 * 2 *AR0822_LANES,
+		.vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_1,
+		.vc[PAD1] = V4L2_MBUS_CSI2_CHANNEL_0,//L->csi wr0
+		.vc[PAD2] = V4L2_MBUS_CSI2_CHANNEL_1,
+		.vc[PAD3] = V4L2_MBUS_CSI2_CHANNEL_1,//M->csi wr2
+	},
+
+	{
+		.bus_fmt = MEDIA_BUS_FMT_SGRBG12_1X12,
+		.width = 3840,
+		.height = 2160,
+		.max_fps = {
+			.numerator = 10000,
+			.denominator = 250000,
+		},
+		.exp_def = 0x0080,
+		.hts_def = 0x0B98*4-3840,//
+		.vts_def = 0x0980,//0x0888,//
+		.reg_list = ar0822_hdr12bit_3840x2160_25fps_regs,
+		.hdr_mode = HDR_X2,
+		.mipi_freq = MIPI_FREQ_823M_INDEX,
+		.mipi_rate = MIPI_FREQ_823M / AR0822_BPP12 * 2 * AR0822_LANES,
+		.vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_1,
+		.vc[PAD1] = V4L2_MBUS_CSI2_CHANNEL_0,
+		.vc[PAD2] = V4L2_MBUS_CSI2_CHANNEL_1,//M->csi wr0
+		.vc[PAD3] = V4L2_MBUS_CSI2_CHANNEL_1,//M->csi wr2
+	},
+
+	{
+		.bus_fmt = MEDIA_BUS_FMT_SGRBG12_1X12,
+		.width = 3840,
+		.height = 2160,
+		.max_fps = {
+			.numerator = 10000,
+			.denominator = 300000,
+		},
+		.exp_def = 0x0240,
+		.hts_def = 0x3430,//for HDR, hblank is 4*LINE_LENGTH_PCK_-WIDTH*2, so hts is 4*LINE_LENGTH_PCK_-WIDTH. param not used by RK.
+		.vts_def = 0x9F3,//should be set correctly,
+		.reg_list = ar0822_hdr12bit_3840x2160_30fps_regs,
+		.hdr_mode = HDR_X2,
+		.mipi_freq = MIPI_FREQ_986M_INDEX,
+		.mipi_rate = MIPI_FREQ_986M / AR0822_BPP12 * 2 * AR0822_LANES,
+		.vc[PAD1] = V4L2_MBUS_CSI2_CHANNEL_0,
+		.vc[PAD3] = V4L2_MBUS_CSI2_CHANNEL_1,//M->csi wr0
+	},
+
+
+};
+
+
+/* use ar0822_enable_test_pattern to config test pattern mode here, open.k */
+static const char * const ar0822_test_pattern_menu[] = {
+	"Disabled",
+	"Vertical Color Bar Type 1",
+	"Vertical Color Bar Type 2",
+	"Vertical Color Bar Type 3",
+	"Vertical Color Bar Type 4"
+};
+
+static int __ar0822_power_on(struct ar0822 *ar0822);
+
+/* Write registers up to 4 at a time */
+static int ar0822_write_reg(struct i2c_client *client, u16 reg,
+			    u32 len, u32 val)
+{
+	u32 buf_i, val_i;
+	u8 buf[6];
+	u8 *val_p;
+	__be32 val_be;
+
+	if (len > 4)
+		return -EINVAL;
+
+	buf[0] = reg >> 8;
+	buf[1] = reg & 0xff;
+
+	val_be = cpu_to_be32(val);
+	val_p = (u8 *)&val_be;
+	buf_i = 2;
+	val_i = 4 - len;
+
+	while (val_i < 4)
+		buf[buf_i++] = val_p[val_i++];
+
+	if (i2c_master_send(client, buf, len + 2) != len + 2)
+		return -EIO;
+
+	return 0;
+}
+
+static int ar0822_write_array(struct i2c_client *client,
+			       const struct regval *regs)
+{
+	u32 i;
+	int ret = 0;
+
+	for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++) {
+		if (unlikely(regs[i].addr == REG_DELAY))
+			usleep_range(regs[i].val, regs[i].val * 2);
+		else
+			ret |= ar0822_write_reg(client, regs[i].addr,
+				AR0822_REG_VALUE_16BIT, regs[i].val);
+	}
+	return ret;
+}
+
+/* Read registers up to 4 at a time */
+static int ar0822_read_reg(struct i2c_client *client,
+			    u16 reg,
+			    unsigned int len,
+			    u32 *val)
+{
+	struct i2c_msg msgs[2];
+	u8 *data_be_p;
+	__be32 data_be = 0;
+	__be16 reg_addr_be = cpu_to_be16(reg);
+	int ret;
+
+	if (len > 4 || !len)
+		return -EINVAL;
+
+	data_be_p = (u8 *)&data_be;
+	/* Write register address */
+	msgs[0].addr = client->addr;
+	msgs[0].flags = 0;
+	msgs[0].len = 2;
+	msgs[0].buf = (u8 *)&reg_addr_be;
+
+	/* Read data from register */
+	msgs[1].addr = client->addr;
+	msgs[1].flags = I2C_M_RD;
+	msgs[1].len = len;
+	msgs[1].buf = &data_be_p[4 - len];
+
+	ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+	if (ret != ARRAY_SIZE(msgs))
+		return -EIO;
+
+	*val = be32_to_cpu(data_be);
+
+	return 0;
+}
+
+static int ar0822_get_reso_dist(const struct ar0822_mode *mode,
+				struct v4l2_mbus_framefmt *framefmt)
+{
+	return abs(mode->width - framefmt->width) +
+	       abs(mode->height - framefmt->height);
+}
+
+static const struct ar0822_mode *
+ar0822_find_best_fit(struct ar0822 *ar0822, struct v4l2_subdev_format *fmt)
+{
+	struct v4l2_mbus_framefmt *framefmt = &fmt->format;
+	int dist;
+	int cur_best_fit = 0;
+	int cur_best_fit_dist = -1;
+	unsigned int i;
+	
+	for (i = 0; i < ar0822->cfg_num; i++) {
+		dist = ar0822_get_reso_dist(&supported_modes[i], framefmt);
+		if ((cur_best_fit_dist == -1 || dist < cur_best_fit_dist) &&
+			(supported_modes[i].bus_fmt == framefmt->code)) {
+			cur_best_fit_dist = dist;
+			cur_best_fit = i;
+		}
+	}
+
+	return &supported_modes[cur_best_fit];
+}
+static int ar0822_set_rates(struct ar0822 *ar0822)
+{
+	const struct ar0822_mode *mode = ar0822->cur_mode;
+	s64 h_blank, vblank_def;
+	int ret = 0;
+
+	h_blank = mode->hts_def - mode->width;
+	dev_err(&ar0822->client->dev,
+		"set format hblank is (%lld), mipi freq: %d, rate: %d\n",
+		 h_blank, mode->mipi_freq,mode->mipi_rate);
+	__v4l2_ctrl_modify_range(ar0822->hblank, h_blank,
+				h_blank, 1, h_blank);
+	vblank_def = mode->vts_def - mode->height;
+	__v4l2_ctrl_modify_range(ar0822->vblank, vblank_def,
+				AR0822_VTS_MAX - mode->height,
+				1, vblank_def);
+
+	__v4l2_ctrl_s_ctrl_int64(ar0822->pixel_rate,
+				 mode->mipi_rate);
+	__v4l2_ctrl_s_ctrl(ar0822->link_freq,
+			   mode->mipi_freq);
+
+	return ret;
+}
+/* setup sensor work format to determine the MIPI speed, open.k */
+static int ar0822_set_fmt(struct v4l2_subdev *sd,
+			  struct v4l2_subdev_pad_config *cfg,
+			  struct v4l2_subdev_format *fmt)
+{
+	struct ar0822 *ar0822 = to_ar0822(sd);
+	const struct ar0822_mode *mode;
+
+	mutex_lock(&ar0822->mutex);
+
+	mode = ar0822_find_best_fit(ar0822, fmt);
+	fmt->format.code = mode->bus_fmt;
+	fmt->format.width = mode->width;
+	fmt->format.height = mode->height;
+	fmt->format.field = V4L2_FIELD_NONE;
+	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+		*v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = fmt->format;
+#else
+		mutex_unlock(&ar0822->mutex);
+		return -ENOTTY;
+#endif
+	} else {
+		ar0822->cur_mode = mode;
+		ar0822_set_rates(ar0822);
+	}
+
+	mutex_unlock(&ar0822->mutex);
+
+	return 0;
+}
+
+static int ar0822_get_fmt(struct v4l2_subdev *sd,
+			  struct v4l2_subdev_pad_config *cfg,
+			  struct v4l2_subdev_format *fmt)
+{
+	struct ar0822 *ar0822 = to_ar0822(sd);
+	const struct ar0822_mode *mode = ar0822->cur_mode;
+
+	mutex_lock(&ar0822->mutex);
+	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+		fmt->format = *v4l2_subdev_get_try_format(sd, cfg, fmt->pad);
+#else
+		mutex_unlock(&ar0822->mutex);
+		return -ENOTTY;
+#endif
+	} else {
+		fmt->format.width = mode->width;
+		fmt->format.height = mode->height;
+		fmt->format.code = mode->bus_fmt;
+		fmt->format.field = V4L2_FIELD_NONE;
+		if (fmt->pad < PAD_MAX && mode->hdr_mode != NO_HDR)
+			fmt->reserved[0] = mode->vc[fmt->pad];
+		else
+			fmt->reserved[0] = mode->vc[PAD0];
+	}
+	mutex_unlock(&ar0822->mutex);
+
+	return 0;
+}
+
+static int ar0822_enum_mbus_code(struct v4l2_subdev *sd,
+				 struct v4l2_subdev_pad_config *cfg,
+				 struct v4l2_subdev_mbus_code_enum *code)
+{
+	struct ar0822 *ar0822 = to_ar0822(sd);
+
+	if (code->index >= ar0822->cfg_num)
+		return -EINVAL;
+	code->code = supported_modes[code->index].bus_fmt;
+
+	return 0;
+}
+
+static int ar0822_enum_frame_sizes(struct v4l2_subdev *sd,
+				   struct v4l2_subdev_pad_config *cfg,
+				   struct v4l2_subdev_frame_size_enum *fse)
+{
+	struct ar0822 *ar0822 = to_ar0822(sd);
+
+	if (fse->index >= ar0822->cfg_num)
+		return -EINVAL;
+
+	if (fse->code != supported_modes[fse->index].bus_fmt)
+		return -EINVAL;
+
+	fse->min_width  = supported_modes[fse->index].width;
+	fse->max_width  = supported_modes[fse->index].width;
+	fse->max_height = supported_modes[fse->index].height;
+	fse->min_height = supported_modes[fse->index].height;
+
+	return 0;
+}
+/* use ar0822_enable_test_pattern to config test pattern mode here, open.k */
+static int ar0822_enable_test_pattern(struct ar0822 *ar0822, u32 pattern)
+{
+	int ret = 0;
+
+	return ret;
+}
+
+static int ar0822_g_frame_interval(struct v4l2_subdev *sd,
+				   struct v4l2_subdev_frame_interval *fi)
+{
+	struct ar0822 *ar0822 = to_ar0822(sd);
+	const struct ar0822_mode *mode = ar0822->cur_mode;
+
+	mutex_lock(&ar0822->mutex);
+	fi->interval = mode->max_fps;
+	mutex_unlock(&ar0822->mutex);
+
+	return 0;
+}
+
+static int ar0822_g_mbus_config(struct v4l2_subdev *sd, unsigned int pad_id,
+				struct v4l2_mbus_config *config)
+{
+	struct ar0822 *ar0822 = to_ar0822(sd);
+	const struct ar0822_mode *mode = ar0822->cur_mode;
+	u32 val = 0;
+
+	val = 1 << (AR0822_LANES - 1) |
+		  V4L2_MBUS_CSI2_CHANNEL_0 |
+		  V4L2_MBUS_CSI2_CONTINUOUS_CLOCK;
+	if (mode->hdr_mode != NO_HDR)
+		val |= V4L2_MBUS_CSI2_CHANNEL_1;
+	if (mode->hdr_mode == HDR_X3)
+		val |= V4L2_MBUS_CSI2_CHANNEL_2;
+
+	config->type = V4L2_MBUS_CSI2_DPHY;
+	config->flags = val;
+
+	return 0;
+}
+
+
+static void ar0822_get_module_inf(struct ar0822 *ar0822,
+				  struct rkmodule_inf *inf)
+{
+	memset(inf, 0, sizeof(*inf));
+	strlcpy(inf->base.sensor, AR0822_NAME, sizeof(inf->base.sensor));
+	strlcpy(inf->base.module, ar0822->module_name,
+		sizeof(inf->base.module));
+	strlcpy(inf->base.lens, ar0822->len_name, sizeof(inf->base.lens));
+}
+
+static int ar0822_set_hdrae(struct ar0822 *ar0822,
+			     struct preisp_hdrae_exp_s *ae)
+{
+	u32 l_exp_time, m_exp_time, s_exp_time;
+	u32 l_a_gain, m_a_gain, s_a_gain;
+	int ret = 0;
+	u8 l_cg_mode = 0;
+	u8 m_cg_mode = 0;
+	u8 s_cg_mode = 0;
+	u32 gain_val = 0;
+
+	if (!ar0822->has_init_exp && !ar0822->streaming) {
+		ar0822->init_hdrae_exp = *ae;
+		ar0822->has_init_exp = true;
+		dev_err(&ar0822->client->dev, "ar0822 don't stream, record exp for hdr!\n");
+		return ret;
+	}
+	l_exp_time = ae->long_exp_reg;
+	m_exp_time = ae->middle_exp_reg;
+	s_exp_time = ae->short_exp_reg;
+	l_a_gain = ae->long_gain_reg;
+	m_a_gain = ae->middle_gain_reg;
+	s_a_gain = ae->short_gain_reg;
+	l_cg_mode = ae->long_cg_mode;
+	m_cg_mode = ae->middle_cg_mode;
+	s_cg_mode = ae->short_cg_mode;
+	dev_dbg(&ar0822->client->dev,
+		"Li-HDR irev exp req: L_exp: 0x%x, 0x%x, M_exp: 0x%x, 0x%x S_exp: 0x%x, 0x%x\n",
+		l_exp_time, l_a_gain,
+		m_exp_time, m_a_gain,
+		s_exp_time, s_a_gain);
+
+	if (ar0822->cur_mode->hdr_mode == HDR_X2) {
+		//2 stagger
+		l_a_gain = m_a_gain;
+		l_exp_time = m_exp_time;
+		l_cg_mode = m_cg_mode;
+		m_a_gain = s_a_gain;
+		m_exp_time = s_exp_time;
+		m_cg_mode = s_cg_mode;
+	}
+
+	l_a_gain = (l_a_gain > AR0822_GAIN_MAX) ? AR0822_GAIN_MAX:l_a_gain;
+	m_a_gain = (m_a_gain > AR0822_GAIN_MAX) ? AR0822_GAIN_MAX:m_a_gain;
+	s_a_gain = (s_a_gain > AR0822_GAIN_MAX) ? AR0822_GAIN_MAX:s_a_gain;
+	l_a_gain = (l_a_gain < AR0822_GAIN_MIN) ? AR0822_GAIN_MIN:l_a_gain;
+	m_a_gain = (m_a_gain < AR0822_GAIN_MIN) ? AR0822_GAIN_MIN:m_a_gain;
+	s_a_gain = (s_a_gain < AR0822_GAIN_MIN) ? AR0822_GAIN_MIN:s_a_gain;
+
+	gain_val = l_a_gain;
+	ret |= ar0822_write_reg(ar0822->client,
+		AR0822_GROUP_UPDATE_ADDRESS,
+		AR0822_REG_VALUE_16BIT,
+		AR0822_GROUP_UPDATE_START_DATA);
+
+	ret |= ar0822_write_reg(ar0822->client,
+		AR0822_REG_GAIN,
+		AR0822_REG_VALUE_16BIT, gain_val);
+
+	gain_val = m_a_gain;
+	ret |= ar0822_write_reg(ar0822->client,
+		AR0822_REG_GAIN2,
+		AR0822_REG_VALUE_16BIT, gain_val);
+
+	if (ar0822->cur_mode->hdr_mode == HDR_X3) {
+		gain_val = s_a_gain;
+		ret |= ar0822_write_reg(ar0822->client,
+			AR0822_REG_GAIN3,
+			AR0822_REG_VALUE_16BIT, gain_val);
+    	}
+	ret |= ar0822_write_reg(ar0822->client,
+		AR0822_REG_EXP,
+		AR0822_REG_VALUE_16BIT,
+		l_exp_time);//fixed ratio 1/16 is used here, T2 and T3 is from ratio* T1 or ratio^2* T1.
+
+	ret |= ar0822_write_reg(ar0822->client,
+		AR0822_GROUP_UPDATE_ADDRESS,
+		AR0822_REG_VALUE_16BIT,
+		AR0822_GROUP_UPDATE_END_DATA);
+
+	dev_dbg(&ar0822->client->dev, "ar0822_set_hdrae exp 0x%x\n",l_exp_time);
+
+	return ret;
+}
+
+static int ar0822_set_conversion_gain(struct ar0822 *ar0822, u32 *cg)
+{
+	int ret = 0;
+	return ret;
+}
+
+#ifdef USED_SYS_DEBUG
+//ag: echo 0 >  /sys/devices/platform/ff510000.i2c/i2c-1/1-0036-1/cam_s_cg
+static ssize_t set_conversion_gain_status(struct device *dev,
+	struct device_attribute *attr,
+	const char *buf,
+	size_t count)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct ar0822 *ar0822 = to_ar0822(sd);
+	int status = 0;
+	int ret = 0;
+
+	ret = kstrtoint(buf, 0, &status);
+	if (!ret && status >= 0 && status < 2)
+		ar0822_set_conversion_gain(ar0822, &status);
+	else
+		dev_err(dev, "input 0 for LCG, 1 for HCG, cur %d\n", status);
+	return count;
+}
+
+static struct device_attribute attributes[] = {
+	__ATTR(cam_s_cg, S_IWUSR, NULL, set_conversion_gain_status),
+};
+
+static int add_sysfs_interfaces(struct device *dev)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(attributes); i++)
+		if (device_create_file(dev, attributes + i))
+			goto undo;
+	return 0;
+undo:
+	for (i--; i >= 0 ; i--)
+		device_remove_file(dev, attributes + i);
+	dev_err(dev, "%s: failed to create sysfs interface\n", __func__);
+	return -ENODEV;
+}
+#endif
+
+static int ar0822_get_channel_info(struct ar0822 *ar0822, struct rkmodule_channel_info *ch_info)
+{
+	if (ch_info->index < PAD0 || ch_info->index >= PAD_MAX)
+		return -EINVAL;
+	ch_info->vc = ar0822->cur_mode->vc[ch_info->index];
+	ch_info->width = ar0822->cur_mode->width;
+	ch_info->height = ar0822->cur_mode->height;
+	ch_info->bus_fmt = ar0822->cur_mode->bus_fmt;
+	return 0;
+}
+
+static long ar0822_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
+{
+	struct ar0822 *ar0822 = to_ar0822(sd);
+	struct rkmodule_hdr_cfg *hdr_cfg;
+	struct rkmodule_channel_info *ch_info;
+	long ret = 0;
+	u32 i, h, w;
+	u32 stream = 0;
+
+	switch (cmd) {
+	case PREISP_CMD_SET_HDRAE_EXP:
+		ar0822_set_hdrae(ar0822, arg);
+		break;
+	case RKMODULE_SET_HDR_CFG:
+		hdr_cfg = (struct rkmodule_hdr_cfg *)arg;
+		w = ar0822->cur_mode->width;
+		h = ar0822->cur_mode->height;
+		for (i = 0; i < ar0822->cfg_num; i++) {
+			if (w == supported_modes[i].width &&
+			h == supported_modes[i].height &&
+			supported_modes[i].hdr_mode == hdr_cfg->hdr_mode) {
+				ar0822->cur_mode = &supported_modes[i];
+				break;
+			}
+		}
+		if (i == ar0822->cfg_num) {
+			dev_err(&ar0822->client->dev,
+				"not find hdr mode:%d %dx%d config\n",
+				hdr_cfg->hdr_mode, w, h);
+			ret = -EINVAL;
+		} else {
+			w = ar0822->cur_mode->hts_def - ar0822->cur_mode->width;
+			h = ar0822->cur_mode->vts_def - ar0822->cur_mode->height;
+			dev_info(&ar0822->client->dev,
+			"set hdr cfg, hblank is (%d)\n", w);
+			__v4l2_ctrl_modify_range(ar0822->hblank, w, w, 1, w);
+			__v4l2_ctrl_modify_range(ar0822->vblank, h,
+				AR0822_VTS_MAX - ar0822->cur_mode->height,
+				1, h);
+			dev_info(&ar0822->client->dev,
+				"sensor mode: %d\n",
+				ar0822->cur_mode->hdr_mode);
+		}
+		ar0822_set_rates(ar0822);
+		break;
+	case RKMODULE_GET_MODULE_INFO:
+		ar0822_get_module_inf(ar0822, (struct rkmodule_inf *)arg);
+		break;
+	case RKMODULE_GET_HDR_CFG:
+		hdr_cfg = (struct rkmodule_hdr_cfg *)arg;
+		hdr_cfg->esp.mode = HDR_NORMAL_VC;
+		hdr_cfg->hdr_mode = ar0822->cur_mode->hdr_mode;
+		break;
+	case RKMODULE_SET_CONVERSION_GAIN:
+		ret = 0;//ar0822_set_conversion_gain(ar0822, (u32 *)arg);
+		break;
+	case RKMODULE_SET_QUICK_STREAM:
+
+		stream = *((u32 *)arg);
+
+		if (stream)
+			ret = ar0822_write_reg(ar0822->client, AR0822_REG_CTRL_MODE,
+				AR0822_REG_VALUE_16BIT, AR0822_MODE_STREAMING);
+		else
+			ret = ar0822_write_reg(ar0822->client, AR0822_REG_CTRL_MODE,
+				AR0822_REG_VALUE_16BIT, AR0822_MODE_SW_STANDBY);
+		break;
+	case RKMODULE_GET_CHANNEL_INFO:
+		ch_info = (struct rkmodule_channel_info *)arg;
+		ret = ar0822_get_channel_info(ar0822, ch_info);
+		break;
+	default:
+		ret = -ENOIOCTLCMD;
+		break;
+	}
+
+	return ret;
+}
+
+#ifdef CONFIG_COMPAT
+static long ar0822_compat_ioctl32(struct v4l2_subdev *sd,
+				  unsigned int cmd, unsigned long arg)
+{
+	void __user *up = compat_ptr(arg);
+	struct rkmodule_inf *inf;
+	struct rkmodule_awb_cfg *cfg;
+	struct rkmodule_hdr_cfg *hdr;
+	struct preisp_hdrae_exp_s *hdrae;
+	struct rkmodule_channel_info *ch_info;
+	long ret;
+	u32 cg = 0;
+	u32 stream = 0;
+
+	switch (cmd) {
+	case RKMODULE_GET_MODULE_INFO:
+		inf = kzalloc(sizeof(*inf), GFP_KERNEL);
+		if (!inf) {
+			ret = -ENOMEM;
+			return ret;
+		}
+
+		ret = ar0822_ioctl(sd, cmd, inf);
+		if (!ret)
+			ret = copy_to_user(up, inf, sizeof(*inf));
+		kfree(inf);
+		break;
+	case RKMODULE_AWB_CFG:
+		cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
+		if (!cfg) {
+			ret = -ENOMEM;
+			return ret;
+		}
+
+		ret = copy_from_user(cfg, up, sizeof(*cfg));
+		if (!ret)
+			ret = ar0822_ioctl(sd, cmd, cfg);
+		kfree(cfg);
+		break;
+	case RKMODULE_GET_HDR_CFG:
+		hdr = kzalloc(sizeof(*hdr), GFP_KERNEL);
+		if (!hdr) {
+			ret = -ENOMEM;
+			return ret;
+		}
+
+		ret = ar0822_ioctl(sd, cmd, hdr);
+		if (!ret)
+			ret = copy_to_user(up, hdr, sizeof(*hdr));
+		kfree(hdr);
+		break;
+	case RKMODULE_SET_HDR_CFG:
+		hdr = kzalloc(sizeof(*hdr), GFP_KERNEL);
+		if (!hdr) {
+			ret = -ENOMEM;
+			return ret;
+		}
+
+		ret = copy_from_user(hdr, up, sizeof(*hdr));
+		if (!ret)
+			ret = ar0822_ioctl(sd, cmd, hdr);
+		kfree(hdr);
+		break;
+	case PREISP_CMD_SET_HDRAE_EXP:
+		hdrae = kzalloc(sizeof(*hdrae), GFP_KERNEL);
+		if (!hdrae) {
+			ret = -ENOMEM;
+			return ret;
+		}
+
+		ret = copy_from_user(hdrae, up, sizeof(*hdrae));
+		if (!ret)
+			ret = ar0822_ioctl(sd, cmd, hdrae);
+		kfree(hdrae);
+		break;
+	case RKMODULE_SET_CONVERSION_GAIN:
+		ret = copy_from_user(&cg, up, sizeof(cg));
+		if (!ret)
+			ret = ar0822_ioctl(sd, cmd, &cg);
+		break;
+	case RKMODULE_SET_QUICK_STREAM:
+		ret = copy_from_user(&stream, up, sizeof(u32));
+		if (!ret)
+			ret = ar0822_ioctl(sd, cmd, &stream);
+		break;
+	case RKMODULE_GET_CHANNEL_INFO:
+		ch_info = kzalloc(sizeof(*ch_info), GFP_KERNEL);
+		if (!ch_info) {
+			ret = -ENOMEM;
+			return ret;
+		}
+
+		ret = ar0822_ioctl(sd, cmd, ch_info);
+		if (!ret) {
+			ret = copy_to_user(up, ch_info, sizeof(*ch_info));
+			if (ret)
+				ret = -EFAULT;
+		}
+		kfree(ch_info);
+		break;
+	default:
+		ret = -ENOIOCTLCMD;
+		break;
+	}
+
+	return ret;
+}
+#endif
+
+static int __ar0822_start_stream(struct ar0822 *ar0822)
+{
+	int ret;
+
+	if (!ar0822->is_thunderboot) {
+		ret = ar0822_write_reg(ar0822->client,
+					 AR0822_SOFTWARE_RESET_REG,
+					 AR0822_REG_VALUE_16BIT,
+					 0x0001);
+		usleep_range(100000, 200000);
+		ret = ar0822_write_array(ar0822->client, ar0822->cur_mode->reg_list);
+		if (ret)
+			return ret;
+	}
+
+	/* In case these controls are set before streaming */
+	ret = __v4l2_ctrl_handler_setup(&ar0822->ctrl_handler);
+	if (ret)
+		return ret;
+	if (ar0822->has_init_exp && ar0822->cur_mode->hdr_mode != NO_HDR) {
+		ret = ar0822_ioctl(&ar0822->subdev, PREISP_CMD_SET_HDRAE_EXP, &ar0822->init_hdrae_exp);
+		if (ret) {
+			dev_err(&ar0822->client->dev,
+				"init exp fail in hdr mode\n");
+			return ret;
+		}
+		dev_err(&ar0822->client->dev,
+				"init exp success in hdr mode\n");
+	}
+	return ar0822_write_reg(ar0822->client, AR0822_REG_CTRL_MODE,
+		AR0822_REG_VALUE_16BIT, AR0822_MODE_STREAMING);
+}
+
+static int __ar0822_stop_stream(struct ar0822 *ar0822)
+{
+	ar0822->has_init_exp = false;
+	if (ar0822->is_thunderboot)
+		ar0822->is_first_streamoff = true;
+	return ar0822_write_reg(ar0822->client, AR0822_REG_CTRL_MODE,
+		AR0822_REG_VALUE_16BIT, AR0822_MODE_SW_STANDBY);
+}
+
+static int ar0822_s_stream(struct v4l2_subdev *sd, int on)
+{
+	struct ar0822 *ar0822 = to_ar0822(sd);
+	struct i2c_client *client = ar0822->client;
+	int ret = 0;
+
+	mutex_lock(&ar0822->mutex);
+	on = !!on;
+	if (on == ar0822->streaming)
+		goto unlock_and_return;
+	if (on) {
+		if (ar0822->is_thunderboot && rkisp_tb_get_state() == RKISP_TB_NG) {
+			ar0822->is_thunderboot = false;
+			__ar0822_power_on(ar0822);
+		}
+		ret = pm_runtime_get_sync(&client->dev);
+		if (ret < 0) {
+			pm_runtime_put_noidle(&client->dev);
+			goto unlock_and_return;
+		}
+
+		ret = __ar0822_start_stream(ar0822);
+		if (ret) {
+			v4l2_err(sd, "start stream failed while write regs\n");
+			pm_runtime_put(&client->dev);
+			goto unlock_and_return;
+		}
+	} else {
+		__ar0822_stop_stream(ar0822);
+		pm_runtime_put(&client->dev);
+	}
+	ar0822->streaming = on;
+
+
+unlock_and_return:
+	mutex_unlock(&ar0822->mutex);
+
+	return ret;
+}
+
+static int ar0822_s_power(struct v4l2_subdev *sd, int on)
+{
+	struct ar0822 *ar0822 = to_ar0822(sd);
+	struct i2c_client *client = ar0822->client;
+	int ret = 0;
+
+	mutex_lock(&ar0822->mutex);
+	/* If the power state is not modified - no work to do. */
+	if (ar0822->power_on == !!on)
+		goto unlock_and_return;
+
+	if (on) {
+		ret = pm_runtime_get_sync(&client->dev);
+		if (ret < 0) {
+			pm_runtime_put_noidle(&client->dev);
+			goto unlock_and_return;
+		}
+
+		if (!ar0822->is_thunderboot) {
+			ret |= ar0822_write_reg(ar0822->client,
+						 AR0822_SOFTWARE_RESET_REG,
+						 AR0822_REG_VALUE_16BIT,
+						 0x0001);
+			usleep_range(100, 200);
+		}
+
+		ar0822->power_on = true;
+	} else {
+		pm_runtime_put(&client->dev);
+		ar0822->power_on = false;
+	}
+
+unlock_and_return:
+	mutex_unlock(&ar0822->mutex);
+
+	return ret;
+}
+
+/* Calculate the delay in us by clock rate and clock cycles */
+static inline u32 ar0822_cal_delay(u32 cycles)
+{
+	return DIV_ROUND_UP(cycles, AR0822_XVCLK_FREQ / 1000 / 1000);
+}
+
+static int __ar0822_power_on(struct ar0822 *ar0822)  /*  sensor power on config, need check power, MCLK, GPIO etc,,, need go to .dts file to change the config; open.k */
+{
+	int ret;
+	u32 delay_us;
+	struct device *dev = &ar0822->client->dev;
+
+	if (ar0822->is_thunderboot)
+		return 0;
+
+	if (!IS_ERR_OR_NULL(ar0822->pins_default)) {
+		ret = pinctrl_select_state(ar0822->pinctrl,
+					   ar0822->pins_default);
+		if (ret < 0)
+			dev_err(dev, "could not set pins\n");
+	}
+	ret = clk_set_rate(ar0822->xvclk, AR0822_XVCLK_FREQ);
+	if (ret < 0)
+		dev_warn(dev, "Failed to set xvclk rate (24MHz)\n");
+	if (clk_get_rate(ar0822->xvclk) != AR0822_XVCLK_FREQ)
+		dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n");
+	ret = clk_prepare_enable(ar0822->xvclk);
+	if (ret < 0) {
+		dev_err(dev, "Failed to enable xvclk\n");
+		return ret;
+	}
+	if (!IS_ERR(ar0822->reset_gpio))
+		gpiod_direction_output(ar0822->reset_gpio, 1);
+
+	ret = regulator_bulk_enable(AR0822_NUM_SUPPLIES, ar0822->supplies);
+	if (ret < 0) {
+		dev_err(dev, "Failed to enable regulators\n");
+		goto disable_clk;
+	}
+
+	if (!IS_ERR(ar0822->reset_gpio))
+		gpiod_direction_output(ar0822->reset_gpio, 0);
+
+	usleep_range(500, 1000);
+	if (!IS_ERR(ar0822->pwdn_gpio))
+		gpiod_direction_output(ar0822->pwdn_gpio, 1);
+	/*
+	 * There is no need to wait for the delay of RC circuit
+	 * if the reset signal is directly controlled by GPIO.
+	 */
+	if (!IS_ERR(ar0822->reset_gpio))
+		usleep_range(6000, 8000);
+	else
+		usleep_range(12000, 16000);
+
+	/* 8192 cycles prior to first SCCB transaction */
+	delay_us = ar0822_cal_delay(8192);
+	usleep_range(delay_us, delay_us * 2);
+
+	return 0;
+
+disable_clk:
+	clk_disable_unprepare(ar0822->xvclk);
+
+	return ret;
+}
+
+static void __ar0822_power_off(struct ar0822 *ar0822)
+{
+	int ret;
+	struct device *dev = &ar0822->client->dev;
+
+	if (ar0822->is_thunderboot) {
+		if (ar0822->is_first_streamoff) {
+			ar0822->is_thunderboot = false;
+			ar0822->is_first_streamoff = false;
+		} else {
+			return;
+		}
+	}
+
+	if (!IS_ERR(ar0822->pwdn_gpio))
+		gpiod_direction_output(ar0822->pwdn_gpio, 0);
+
+	clk_disable_unprepare(ar0822->xvclk);
+
+	if (!IS_ERR(ar0822->reset_gpio))
+		gpiod_direction_output(ar0822->reset_gpio, 0);
+	if (!IS_ERR_OR_NULL(ar0822->pins_sleep)) {
+		ret = pinctrl_select_state(ar0822->pinctrl,
+					   ar0822->pins_sleep);
+		if (ret < 0)
+			dev_dbg(dev, "could not set pins\n");
+	}
+
+	if (ar0822->is_thunderboot_ng) {
+		ar0822->is_thunderboot_ng = false;
+		regulator_bulk_disable(AR0822_NUM_SUPPLIES, ar0822->supplies);
+	}
+}
+
+static int ar0822_runtime_resume(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct ar0822 *ar0822 = to_ar0822(sd);
+
+	return __ar0822_power_on(ar0822);
+}
+
+static int ar0822_runtime_suspend(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct ar0822 *ar0822 = to_ar0822(sd);
+
+	__ar0822_power_off(ar0822);
+
+	return 0;
+}
+
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+static int ar0822_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+	struct ar0822 *ar0822 = to_ar0822(sd);
+	struct v4l2_mbus_framefmt *try_fmt =
+				v4l2_subdev_get_try_format(sd, fh->pad, 0);
+	const struct ar0822_mode *def_mode = &supported_modes[0];
+	mutex_lock(&ar0822->mutex);
+	/* Initialize try_fmt */
+	try_fmt->width = def_mode->width;
+	try_fmt->height = def_mode->height;
+	try_fmt->code = def_mode->bus_fmt;
+	try_fmt->field = V4L2_FIELD_NONE;
+
+	mutex_unlock(&ar0822->mutex);
+	/* No crop or compose */
+
+	return 0;
+}
+#endif
+
+static int ar0822_enum_frame_interval(struct v4l2_subdev *sd,
+				       struct v4l2_subdev_pad_config *cfg,
+				       struct v4l2_subdev_frame_interval_enum *fie)
+{
+	struct ar0822 *ar0822 = to_ar0822(sd);
+
+	if (fie->index >= ar0822->cfg_num)
+		return -EINVAL;
+	fie->code = supported_modes[fie->index].bus_fmt;
+	fie->width = supported_modes[fie->index].width;
+	fie->height = supported_modes[fie->index].height;
+	fie->interval = supported_modes[fie->index].max_fps;
+	fie->reserved[0] = supported_modes[fie->index].hdr_mode;
+	return 0;
+}
+
+static const struct dev_pm_ops ar0822_pm_ops = {
+	SET_RUNTIME_PM_OPS(ar0822_runtime_suspend,
+			   ar0822_runtime_resume, NULL)
+};
+
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+static const struct v4l2_subdev_internal_ops ar0822_internal_ops = {
+	.open = ar0822_open,
+};
+#endif
+
+static const struct v4l2_subdev_core_ops ar0822_core_ops = {
+	.s_power = ar0822_s_power,
+	.ioctl = ar0822_ioctl,
+#ifdef CONFIG_COMPAT
+	.compat_ioctl32 = ar0822_compat_ioctl32,
+#endif
+};
+
+static const struct v4l2_subdev_video_ops ar0822_video_ops = {
+	.s_stream = ar0822_s_stream,
+	.g_frame_interval = ar0822_g_frame_interval,
+};
+
+static const struct v4l2_subdev_pad_ops ar0822_pad_ops = {
+	.enum_mbus_code = ar0822_enum_mbus_code,
+	.enum_frame_size = ar0822_enum_frame_sizes,
+	.enum_frame_interval = ar0822_enum_frame_interval,
+	.get_fmt = ar0822_get_fmt,
+	.set_fmt = ar0822_set_fmt,
+	.get_mbus_config = ar0822_g_mbus_config,
+};
+
+static const struct v4l2_subdev_ops ar0822_subdev_ops = {
+	.core	= &ar0822_core_ops,
+	.video	= &ar0822_video_ops,
+	.pad	= &ar0822_pad_ops,
+};
+
+
+static int ar0822_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct ar0822 *ar0822 = container_of(ctrl->handler,
+					     struct ar0822, ctrl_handler);
+	struct i2c_client *client = ar0822->client;
+	s64 max;
+	int ret = 0;
+	u32 again = 0;
+	u32 val = 0;
+
+	/* Propagate change of current control to all related controls */
+	switch (ctrl->id) {
+	case V4L2_CID_VBLANK:
+		/* Update max exposure while meeting expected vblanking */
+		max = ar0822->cur_mode->height + ctrl->val - 4;
+		__v4l2_ctrl_modify_range(ar0822->exposure,
+					 ar0822->exposure->minimum, max,
+					 ar0822->exposure->step,
+					 ar0822->exposure->default_value);
+		break;
+	}
+
+	if (!pm_runtime_get_if_in_use(&client->dev))
+		return 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_EXPOSURE:
+		if (ar0822->cur_mode->hdr_mode != NO_HDR)
+			goto ctrl_end;
+		ret = ar0822_write_reg(ar0822->client,
+					AR0822_REG_EXP,
+					AR0822_REG_VALUE_16BIT,
+					ctrl->val);
+
+		dev_dbg(&client->dev, "set exposure 0x%x\n",
+			ctrl->val);
+		break;
+	case V4L2_CID_ANALOGUE_GAIN:
+		if (ar0822->cur_mode->hdr_mode != NO_HDR)
+			goto ctrl_end;
+		if (ctrl->val > AR0822_GAIN_MAX) {
+			again = AR0822_GAIN_MAX;
+		} else {
+			again = ctrl->val;
+		}
+		if (ctrl->val < AR0822_GAIN_MIN) {
+			again = AR0822_GAIN_MIN;
+		} else {
+			again = ctrl->val;
+		}
+
+		val = again;
+		ret = ar0822_write_reg(ar0822->client,
+					AR0822_REG_GAIN,
+					AR0822_REG_VALUE_16BIT,
+					val);
+
+		dev_dbg(&client->dev, "Corn set analog gain 0x%x\n",
+			ctrl->val);
+		break;
+	case V4L2_CID_VBLANK:
+		//ret = ar0822_write_reg(ar0822->client, AR0822_REG_VTS,
+		//			AR0822_REG_VALUE_16BIT,
+		//			ctrl->val + ar0822->cur_mode->height);
+		dev_dbg(&client->dev, "set vblank 0x%x\n",
+			ctrl->val);
+		break;
+	case V4L2_CID_TEST_PATTERN:
+		ret = ar0822_enable_test_pattern(ar0822, ctrl->val);
+		break;
+	case V4L2_CID_HFLIP:
+		ret = ar0822_read_reg(ar0822->client, AR0822_FLIP_REG,
+				       AR0822_REG_VALUE_16BIT,
+				       &val);
+		if (ctrl->val)
+			val |= MIRROR_BIT_MASK;
+		else
+			val &= ~MIRROR_BIT_MASK;
+		ret = ar0822_write_reg(ar0822->client, AR0822_FLIP_REG,
+					AR0822_REG_VALUE_16BIT,
+					val);
+		if (ret == 0)
+			ar0822->flip = val;
+		break;
+	case V4L2_CID_VFLIP:
+		ret = ar0822_read_reg(ar0822->client, AR0822_FLIP_REG,
+				       AR0822_REG_VALUE_16BIT,
+				       &val);
+		if (ctrl->val)
+			val |= FLIP_BIT_MASK;
+		else
+			val &= ~FLIP_BIT_MASK;
+		ret = ar0822_write_reg(ar0822->client, AR0822_FLIP_REG,
+					AR0822_REG_VALUE_16BIT,
+					val);
+		if (ret == 0)
+			ar0822->flip = val;
+		break;
+	default:
+		dev_warn(&client->dev, "%s Unhandled id:0x%x, val:0x%x\n",
+			 __func__, ctrl->id, ctrl->val);
+		break;
+	}
+
+
+ctrl_end:
+	pm_runtime_put(&client->dev);
+
+	return ret;
+}
+
+static const struct v4l2_ctrl_ops ar0822_ctrl_ops = {
+	.s_ctrl = ar0822_set_ctrl,
+};
+
+static int ar0822_initialize_controls(struct ar0822 *ar0822)
+{
+	const struct ar0822_mode *mode;
+	struct v4l2_ctrl_handler *handler;
+	s64 exposure_max, vblank_def;
+	u32 h_blank;
+	int ret;
+	u64 dst_link_freq = 0;
+	u64 dst_pixel_rate = 0;
+
+	handler = &ar0822->ctrl_handler;
+	mode = ar0822->cur_mode;
+	ret = v4l2_ctrl_handler_init(handler, 9);
+	if (ret)
+		return ret;
+	handler->lock = &ar0822->mutex;
+	ar0822->link_freq = v4l2_ctrl_new_int_menu(handler, NULL,
+			V4L2_CID_LINK_FREQ,
+			MIPI_FREQ_MAX_INDEX, 0, link_freq_menu_items);
+
+	dst_link_freq = mode->mipi_freq;
+	dst_pixel_rate = mode->mipi_rate;
+	/* pixel rate = link frequency * 2 * lanes / BITS_PER_SAMPLE */
+	ar0822->pixel_rate = v4l2_ctrl_new_std(handler, NULL,
+			V4L2_CID_PIXEL_RATE,
+			0, PIXEL_RATE_MAX,
+			1, dst_pixel_rate);
+	__v4l2_ctrl_s_ctrl(ar0822->link_freq,
+			   dst_link_freq);
+
+	h_blank = mode->hts_def - mode->width;
+	ar0822->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK,
+				h_blank, h_blank, 1, h_blank);
+	if (ar0822->hblank)
+		ar0822->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+	vblank_def = mode->vts_def - mode->height;
+	ar0822->vblank = v4l2_ctrl_new_std(handler, &ar0822_ctrl_ops,
+				V4L2_CID_VBLANK, vblank_def,
+				AR0822_VTS_MAX - mode->height,
+				1, vblank_def);
+
+	exposure_max = mode->vts_def - 4;
+	ar0822->exposure = v4l2_ctrl_new_std(handler, &ar0822_ctrl_ops,
+				V4L2_CID_EXPOSURE, AR0822_EXPOSURE_MIN,
+				exposure_max, AR0822_EXPOSURE_STEP,
+				mode->exp_def);
+
+	ar0822->anal_gain = v4l2_ctrl_new_std(handler, &ar0822_ctrl_ops,
+				V4L2_CID_ANALOGUE_GAIN, AR0822_GAIN_MIN,
+				AR0822_GAIN_MAX, AR0822_GAIN_STEP,
+				AR0822_GAIN_DEFAULT);
+
+	ar0822->test_pattern = v4l2_ctrl_new_std_menu_items(handler,
+				&ar0822_ctrl_ops, V4L2_CID_TEST_PATTERN,
+				ARRAY_SIZE(ar0822_test_pattern_menu) - 1,
+				0, 0, ar0822_test_pattern_menu);
+
+	ar0822->h_flip = v4l2_ctrl_new_std(handler, &ar0822_ctrl_ops,
+				V4L2_CID_HFLIP, 0, 1, 1, 0);
+
+	ar0822->v_flip = v4l2_ctrl_new_std(handler, &ar0822_ctrl_ops,
+				V4L2_CID_VFLIP, 0, 1, 1, 0);
+	ar0822->flip = 0;
+	if (handler->error) {
+		ret = handler->error;
+		dev_err(&ar0822->client->dev,
+			"Failed to init controls(%d)\n", ret);
+		goto err_free_handler;
+	}
+
+	ar0822->subdev.ctrl_handler = handler;
+	ar0822->has_init_exp = false;
+	ar0822->long_hcg = false;
+	ar0822->middle_hcg = false;
+	ar0822->short_hcg = false;
+
+	return 0;
+
+err_free_handler:
+	v4l2_ctrl_handler_free(handler);
+
+	return ret;
+}
+
+static int ar0822_check_sensor_id(struct ar0822 *ar0822,
+				  struct i2c_client *client)
+{
+	struct device *dev = &ar0822->client->dev;
+	u32 id = 0;
+	int ret;
+
+	if (ar0822->is_thunderboot) {
+		dev_info(dev, "Enable thunderboot mode, skip sensor id check\n");
+		return 0;
+	}
+
+	ret = ar0822_read_reg(client, AR0822_REG_CHIP_ID,
+			       AR0822_REG_VALUE_16BIT, &id);
+	if (id != CHIP_ID) {
+		dev_err(dev, "Unexpected sensor id(%06x), ret(%d)\n", id, ret);
+		return -ENODEV;
+	}
+
+	dev_info(dev, "Detected ar0822%04x sensor\n", CHIP_ID);
+
+	return 0;
+}
+
+static int ar0822_configure_regulators(struct ar0822 *ar0822)
+{
+	unsigned int i;
+
+	for (i = 0; i < AR0822_NUM_SUPPLIES; i++)
+		ar0822->supplies[i].supply = ar0822_supply_names[i];
+
+	return devm_regulator_bulk_get(&ar0822->client->dev,
+				       AR0822_NUM_SUPPLIES,
+				       ar0822->supplies);
+}
+
+static int ar0822_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
+{
+	struct device *dev = &client->dev;
+	struct device_node *node = dev->of_node;
+	struct ar0822 *ar0822;
+	struct v4l2_subdev *sd;
+	char facing[2];
+	int ret;
+	u32 i, hdr_mode = 0;
+
+	dev_info(dev, "driver version: %02x.%02x.%02x",
+		DRIVER_VERSION >> 16,
+		(DRIVER_VERSION & 0xff00) >> 8,
+		DRIVER_VERSION & 0x00ff);
+
+	ar0822 = devm_kzalloc(dev, sizeof(*ar0822), GFP_KERNEL);
+	if (!ar0822)
+		return -ENOMEM;
+
+	ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX,
+				   &ar0822->module_index);
+	ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING,
+				       &ar0822->module_facing);
+	ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME,
+				       &ar0822->module_name);
+	ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME,
+				       &ar0822->len_name);
+	if (ret) {
+		dev_err(dev, "could not get module information!\n");
+		return -EINVAL;
+	}
+
+	ar0822->is_thunderboot = IS_ENABLED(CONFIG_VIDEO_ROCKCHIP_THUNDER_BOOT_ISP);
+	ret = of_property_read_u32(node, OF_CAMERA_HDR_MODE,
+			&hdr_mode);
+	if (ret) {
+		hdr_mode = NO_HDR;
+		dev_warn(dev, " Get hdr mode failed! no hdr default\n");
+	}
+	ar0822->cfg_num = ARRAY_SIZE(supported_modes);
+	if(ar0822->cfg_num == 0){
+		dev_err(dev, "no any supported mode providec, force exit probe!\n");
+		return -EINVAL;
+	}
+	ar0822->cur_mode = &supported_modes[0];//initialize.
+	for (i = 0; i < ar0822->cfg_num; i++) {
+		if (hdr_mode == supported_modes[i].hdr_mode) {
+			ar0822->cur_mode = &supported_modes[i];
+			break;
+		}
+	}
+	ar0822->client = client;
+
+	ar0822->xvclk = devm_clk_get(dev, "xvclk");
+	if (IS_ERR(ar0822->xvclk)) {
+		dev_err(dev, "Failed to get xvclk\n");
+		return -EINVAL;
+	}
+
+	ar0822->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_ASIS);
+	if (IS_ERR(ar0822->reset_gpio))
+		dev_warn(dev, "Failed to get reset-gpios\n");
+
+	ar0822->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_ASIS);
+	if (IS_ERR(ar0822->pwdn_gpio))
+		dev_warn(dev, "Failed to get pwdn-gpios\n");
+
+	ar0822->pinctrl = devm_pinctrl_get(dev);
+	if (!IS_ERR(ar0822->pinctrl)) {
+		ar0822->pins_default =
+			pinctrl_lookup_state(ar0822->pinctrl,
+					     OF_CAMERA_PINCTRL_STATE_DEFAULT);
+		if (IS_ERR(ar0822->pins_default))
+			dev_err(dev, "could not get default pinstate\n");
+
+		ar0822->pins_sleep =
+			pinctrl_lookup_state(ar0822->pinctrl,
+					     OF_CAMERA_PINCTRL_STATE_SLEEP);
+		if (IS_ERR(ar0822->pins_sleep))
+			dev_err(dev, "could not get sleep pinstate\n");
+	} else {
+		dev_err(dev, "no pinctrl\n");
+	}
+
+	ret = ar0822_configure_regulators(ar0822);
+	if (ret) {
+		dev_err(dev, "Failed to get power regulators\n");
+		return ret;
+	}
+
+	mutex_init(&ar0822->mutex);
+
+	sd = &ar0822->subdev;
+	v4l2_i2c_subdev_init(sd, client, &ar0822_subdev_ops);
+	ret = ar0822_initialize_controls(ar0822);
+	if (ret)
+		goto err_destroy_mutex;
+
+	ret = __ar0822_power_on(ar0822);
+	if (ret)
+		goto err_free_handler;
+
+	ret = ar0822_check_sensor_id(ar0822, client);
+	if (ret)
+		goto err_power_off;
+
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+	sd->internal_ops = &ar0822_internal_ops;
+	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+#endif
+#if defined(CONFIG_MEDIA_CONTROLLER)
+	ar0822->pad.flags = MEDIA_PAD_FL_SOURCE;
+	sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
+	ret = media_entity_pads_init(&sd->entity, 1, &ar0822->pad);
+	if (ret < 0)
+		goto err_power_off;
+#endif
+
+	memset(facing, 0, sizeof(facing));
+	if (strcmp(ar0822->module_facing, "back") == 0)
+		facing[0] = 'b';
+	else
+		facing[0] = 'f';
+
+	snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s",
+		 ar0822->module_index, facing,
+		 AR0822_NAME, dev_name(sd->dev));
+	ret = v4l2_async_register_subdev_sensor_common(sd);
+	if (ret) {
+		dev_err(dev, "v4l2 async register subdev failed\n");
+		goto err_clean_entity;
+	}
+
+	pm_runtime_set_active(dev);
+	pm_runtime_enable(dev);
+	pm_runtime_idle(dev);
+#ifdef USED_SYS_DEBUG
+	add_sysfs_interfaces(dev);
+#endif
+	return 0;
+
+err_clean_entity:
+#if defined(CONFIG_MEDIA_CONTROLLER)
+	media_entity_cleanup(&sd->entity);
+#endif
+err_power_off:
+	__ar0822_power_off(ar0822);
+err_free_handler:
+	v4l2_ctrl_handler_free(&ar0822->ctrl_handler);
+err_destroy_mutex:
+	mutex_destroy(&ar0822->mutex);
+
+	return ret;
+}
+
+static int ar0822_remove(struct i2c_client *client)
+{
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct ar0822 *ar0822 = to_ar0822(sd);
+
+	v4l2_async_unregister_subdev(sd);
+#if defined(CONFIG_MEDIA_CONTROLLER)
+	media_entity_cleanup(&sd->entity);
+#endif
+	v4l2_ctrl_handler_free(&ar0822->ctrl_handler);
+	mutex_destroy(&ar0822->mutex);
+
+	pm_runtime_disable(&client->dev);
+	if (!pm_runtime_status_suspended(&client->dev))
+		__ar0822_power_off(ar0822);
+	pm_runtime_set_suspended(&client->dev);
+
+	return 0;
+}
+
+#if IS_ENABLED(CONFIG_OF)
+static const struct of_device_id ar0822_of_match[] = {
+	{ .compatible = "onsemi,ar0822" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, ar0822_of_match);
+#endif
+
+static const struct i2c_device_id ar0822_match_id[] = {
+	{ "onsemi,ar0822", 0 },
+	{ },
+};
+
+static struct i2c_driver ar0822_i2c_driver = {
+	.driver = {
+		.name = AR0822_NAME,
+		.pm = &ar0822_pm_ops,
+		.of_match_table = of_match_ptr(ar0822_of_match),
+	},
+	.probe		= &ar0822_probe,
+	.remove		= &ar0822_remove,
+	.id_table	= ar0822_match_id,
+};
+
+#ifdef CONFIG_ROCKCHIP_THUNDER_BOOT
+module_i2c_driver(ar0822_i2c_driver);
+#else
+static int __init sensor_mod_init(void)
+{
+	return i2c_add_driver(&ar0822_i2c_driver);
+}
+
+static void __exit sensor_mod_exit(void)
+{
+	i2c_del_driver(&ar0822_i2c_driver);
+}
+
+device_initcall_sync(sensor_mod_init);
+module_exit(sensor_mod_exit);
+#endif
+
+MODULE_DESCRIPTION("Onsemi ar0822 sensor driver");
+MODULE_LICENSE("GPL");
diff --git a/kernel/drivers/media/i2c/dw9763.c b/kernel/drivers/media/i2c/dw9763.c
index 907b543..337f97f 100644
--- a/kernel/drivers/media/i2c/dw9763.c
+++ b/kernel/drivers/media/i2c/dw9763.c
@@ -15,12 +15,14 @@
 #include <media/v4l2-device.h>
 #include <linux/rk_vcm_head.h>
 #include <linux/compat.h>
+#include <linux/regulator/consumer.h>
 
-#define DRIVER_VERSION			KERNEL_VERSION(0, 0x01, 0x0)
+#define DRIVER_VERSION			KERNEL_VERSION(0, 0x01, 0x1)
 #define DW9763_NAME			"dw9763"
 
 #define DW9763_MAX_CURRENT		120U
 #define DW9763_MAX_REG			1023U
+#define DW9763_GRADUAL_MOVELENS_STEPS	32
 
 #define DW9763_DEFAULT_START_CURRENT	20
 #define DW9763_DEFAULT_RATED_CURRENT	90
@@ -40,6 +42,10 @@
 	SAC4_MODE = 5,
 	DIRECT_MODE,
 };
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "debug level (0-2)");
 
 /* dw9763 device structure */
 struct dw9763_device {
@@ -69,6 +75,8 @@
 	struct rk_cam_vcm_cfg vcm_cfg;
 	int max_ma;
 	struct mutex lock;
+	struct regulator *supply;
+	bool power_on;
 };
 
 static inline struct dw9763_device *to_dw9763_vcm(struct v4l2_ctrl *ctrl)
@@ -88,6 +96,7 @@
 	u8 buf[5];
 	u8 *val_p;
 	__be32 val_be;
+	struct dw9763_device *dev_vcm = i2c_get_clientdata(client);
 
 	if (len > 4)
 		return -EINVAL;
@@ -106,7 +115,7 @@
 		dev_err(&client->dev, "Failed to write 0x%04x,0x%x\n", reg, val);
 		return -EIO;
 	}
-	dev_dbg(&client->dev, "succeed to write 0x%04x,0x%x\n", reg, val);
+	v4l2_dbg(1, debug, &dev_vcm->sd, "succeed to write 0x%04x,0x%x\n", reg, val);
 
 	return 0;
 }
@@ -120,6 +129,7 @@
 	u8 *data_be_p;
 	__be32 data_be = 0;
 	int ret;
+	struct dw9763_device *dev_vcm = i2c_get_clientdata(client);
 
 	if (len > 4 || !len)
 		return -EINVAL;
@@ -142,6 +152,8 @@
 		return -EIO;
 
 	*val = be32_to_cpu(data_be);
+
+	v4l2_dbg(1, debug, &dev_vcm->sd, "succeed to read 0x%04x,0x%x\n", reg, *val);
 
 	return 0;
 }
@@ -204,11 +216,11 @@
 		break;
 	}
 
-	dev_dbg(&client->dev,
+	v4l2_dbg(1, debug, &dev_vcm->sd,
 		"%s: vcm_movefull_t is: %d us\n",
 		__func__, move_time_us);
 
-	return move_time_us;
+	return ((move_time_us + 500) / 1000);
 }
 
 static int dw9763_set_dac(struct dw9763_device *dev_vcm,
@@ -228,7 +240,7 @@
 	ret = dw9763_write_reg(client, 0x03, 2, dest_dac);
 	if (ret != 0)
 		goto err;
-	dev_dbg(&client->dev,
+	v4l2_dbg(1, debug, &dev_vcm->sd,
 		"%s: set reg val %d\n", __func__, dest_dac);
 
 	return ret;
@@ -249,7 +261,7 @@
 		goto err;
 
 	*cur_dac = abs_step;
-	dev_dbg(&client->dev, "%s: get dac %d\n", __func__, *cur_dac);
+	v4l2_dbg(1, debug, &dev_vcm->sd, "%s: get dac %d\n", __func__, *cur_dac);
 
 	return 0;
 
@@ -279,7 +291,7 @@
 		abs_step = 0;
 
 	*cur_pos = abs_step;
-	dev_dbg(&client->dev, "%s: get position %d\n", __func__, *cur_pos);
+	v4l2_dbg(1, debug, &dev_vcm->sd, "%s: get position %d\n", __func__, *cur_pos);
 	return 0;
 
 err:
@@ -293,7 +305,6 @@
 {
 	int ret;
 	unsigned int position = 0;
-	struct i2c_client *client = dev_vcm->client;
 
 	if (dest_pos >= VCMDRV_MAX_LOG)
 		position = dev_vcm->start_current;
@@ -308,7 +319,8 @@
 	dev_vcm->current_related_pos = dest_pos;
 
 	ret = dw9763_set_dac(dev_vcm, position);
-	dev_dbg(&client->dev, "%s: set position %d, dac %d\n", __func__, dest_pos, position);
+	v4l2_dbg(1, debug, &dev_vcm->sd, "%s: set position %d, dac %d\n",
+		 __func__, dest_pos, position);
 
 	return ret;
 }
@@ -331,7 +343,7 @@
 	long mv_us;
 	int ret = 0;
 
-	dev_dbg(&client->dev, "ctrl->id: 0x%x, ctrl->val: 0x%x\n",
+	v4l2_dbg(1, debug, &dev_vcm->sd, "ctrl->id: 0x%x, ctrl->val: 0x%x\n",
 		ctrl->id, ctrl->val);
 
 	if (ctrl->id == V4L2_CID_FOCUS_ABSOLUTE) {
@@ -345,9 +357,9 @@
 
 		ret = dw9763_set_pos(dev_vcm, dest_pos);
 
-		dev_vcm->move_us = dev_vcm->vcm_movefull_t;
+		dev_vcm->move_us = dev_vcm->vcm_movefull_t * 1000;
 
-		dev_dbg(&client->dev,
+		v4l2_dbg(1, debug, &dev_vcm->sd,
 			"dest_pos %d, move_us %ld\n",
 			dest_pos, dev_vcm->move_us);
 
@@ -373,9 +385,19 @@
 	.s_ctrl = dw9763_set_ctrl,
 };
 
+static int dw9763_init(struct i2c_client *client);
 static int dw9763_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
 {
 	int rval;
+	struct dw9763_device *dev_vcm = sd_to_dw9763_vcm(sd);
+	unsigned int move_time;
+	int dac = dev_vcm->start_current;
+	struct i2c_client *client = dev_vcm->client;
+
+#ifdef CONFIG_PM
+	v4l2_info(sd, "%s: enter, power.usage_count(%d)!\n", __func__,
+		  atomic_read(&sd->dev->power.usage_count));
+#endif
 
 	rval = pm_runtime_get_sync(sd->dev);
 	if (rval < 0) {
@@ -383,12 +405,72 @@
 		return rval;
 	}
 
+	dw9763_init(client);
+
+	v4l2_dbg(1, debug, sd, "%s: current_lens_pos %d, current_related_pos %d\n",
+		 __func__, dev_vcm->current_lens_pos, dev_vcm->current_related_pos);
+
+	move_time = 1000 * dw9763_move_time(dev_vcm, DW9763_GRADUAL_MOVELENS_STEPS);
+	while (dac <= dev_vcm->current_lens_pos) {
+		dw9763_set_dac(dev_vcm, dac);
+		usleep_range(move_time, move_time + 100);
+		dac += DW9763_GRADUAL_MOVELENS_STEPS;
+		if (dac > dev_vcm->current_lens_pos)
+			break;
+	}
+
+	if (dac > dev_vcm->current_lens_pos) {
+		dac = dev_vcm->current_lens_pos;
+		dw9763_set_dac(dev_vcm, dac);
+	}
+
+#ifdef CONFIG_PM
+	v4l2_info(sd, "%s: exit, power.usage_count(%d)!\n", __func__,
+		  atomic_read(&sd->dev->power.usage_count));
+#endif
 	return 0;
 }
 
 static int dw9763_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
 {
+	struct dw9763_device *dev_vcm = sd_to_dw9763_vcm(sd);
+	int dac = dev_vcm->current_lens_pos;
+	unsigned int move_time;
+	int ret;
+	struct i2c_client *client = dev_vcm->client;
+
+#ifdef CONFIG_PM
+	v4l2_info(sd, "%s: enter, power.usage_count(%d)!\n", __func__,
+		  atomic_read(&sd->dev->power.usage_count));
+#endif
+
+	v4l2_dbg(1, debug, sd, "%s: current_lens_pos %d, current_related_pos %d\n",
+		 __func__, dev_vcm->current_lens_pos, dev_vcm->current_related_pos);
+
+	dac -= DW9763_GRADUAL_MOVELENS_STEPS;
+	move_time = 1000 * dw9763_move_time(dev_vcm, DW9763_GRADUAL_MOVELENS_STEPS);
+	while (dac >= DW9763_GRADUAL_MOVELENS_STEPS) {
+		dw9763_set_dac(dev_vcm, dac);
+		usleep_range(move_time, move_time + 1000);
+		dac -= DW9763_GRADUAL_MOVELENS_STEPS;
+		if (dac <= 0)
+			break;
+	}
+
+	if (dac < DW9763_GRADUAL_MOVELENS_STEPS) {
+		dac = DW9763_GRADUAL_MOVELENS_STEPS / 2;
+		dw9763_set_dac(dev_vcm, dac);
+	}
+	/* set to power down mode */
+	ret = dw9763_write_reg(client, 0x02, 1, 0x01);
+	if (ret)
+		dev_err(&client->dev, "failed to set power down mode!\n");
+
 	pm_runtime_put(sd->dev);
+#ifdef CONFIG_PM
+	v4l2_info(sd, "%s: exit, power.usage_count(%d)!\n", __func__,
+		  atomic_read(&sd->dev->power.usage_count));
+#endif
 
 	return 0;
 }
@@ -442,7 +524,7 @@
 		vcm_tim->vcm_end_t.tv_sec = dev_vcm->end_move_tv.tv_sec;
 		vcm_tim->vcm_end_t.tv_usec = dev_vcm->end_move_tv.tv_usec;
 
-		dev_dbg(&client->dev, "dw9763_get_move_res 0x%lx, 0x%lx, 0x%lx, 0x%lx\n",
+		v4l2_dbg(1, debug, &dev_vcm->sd, "dw9763_get_move_res 0x%lx, 0x%lx, 0x%lx, 0x%lx\n",
 			vcm_tim->vcm_start_t.tv_sec,
 			vcm_tim->vcm_start_t.tv_usec,
 			vcm_tim->vcm_end_t.tv_sec,
@@ -634,13 +716,50 @@
 }
 #endif
 
-static int __dw9763_set_power(struct dw9763_device *dw9763_dev, bool on)
+static int __dw9763_set_power(struct dw9763_device *dw9763, bool on)
 {
-	if (dw9763_dev->power_gpio)
-		gpiod_direction_output(dw9763_dev->power_gpio, on);
-	usleep_range(10000, 11000);
+	struct i2c_client *client = dw9763->client;
+	int ret = 0;
 
-	return 0;
+	dev_info(&client->dev, "%s(%d) on(%d)\n", __func__, __LINE__, on);
+
+	if (dw9763->power_on == !!on)
+		goto unlock_and_return;
+
+	if (on) {
+		ret = regulator_enable(dw9763->supply);
+		if (ret < 0) {
+			dev_err(&client->dev, "Failed to enable regulator\n");
+			goto unlock_and_return;
+		}
+		dw9763->power_on = true;
+	} else {
+		ret = regulator_disable(dw9763->supply);
+		if (ret < 0) {
+			dev_err(&client->dev, "Failed to disable regulator\n");
+			goto unlock_and_return;
+		}
+		dw9763->power_on = false;
+	}
+
+unlock_and_return:
+	return ret;
+}
+
+static int dw9763_configure_regulator(struct dw9763_device *dw9763)
+{
+	struct i2c_client *client = dw9763->client;
+	int ret = 0;
+
+	dw9763->supply = devm_regulator_get(&client->dev, "avdd");
+	if (IS_ERR(dw9763->supply)) {
+		ret = PTR_ERR(dw9763->supply);
+		if (ret != -EPROBE_DEFER)
+			dev_err(&client->dev, "could not get regulator avdd\n");
+		return ret;
+	}
+	dw9763->power_on = false;
+	return ret;
 }
 
 static int __maybe_unused dw9763_check_id(struct dw9763_device *dw9763_dev)
@@ -661,20 +780,6 @@
 	dev_info(&dw9763_dev->client->dev,
 		 "Detected dw9763 vcm id:0x%x\n", DW9763_CHIP_ID);
 	return 0;
-}
-static int dw9763_probe_init(struct i2c_client *client)
-{
-	int ret = 0;
-
-	/* Default goto power down mode when finished probe */
-	ret = dw9763_write_reg(client, 0x02, 1, 0x01);
-	if (ret)
-		goto err;
-
-	return 0;
-err:
-	dev_err(&client->dev, "probe init failed with error %d\n", ret);
-	return -1;
 }
 
 static int dw9763_probe(struct i2c_client *client,
@@ -765,9 +870,11 @@
 		dev_warn(&client->dev,
 			"Failed to get power-gpios, maybe no use\n");
 	}
-
-	/* enter power down mode */
-	dw9763_probe_init(client);
+	ret = dw9763_configure_regulator(dw9763_dev);
+	if (ret) {
+		dev_err(&client->dev, "Failed to get power regulator!\n");
+		return ret;
+	}
 
 	v4l2_i2c_subdev_init(&dw9763_dev->sd, client, &dw9763_ops);
 	dw9763_dev->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
@@ -780,6 +887,10 @@
 	ret = media_entity_pads_init(&dw9763_dev->sd.entity, 0, NULL);
 	if (ret < 0)
 		goto err_cleanup;
+
+	ret = dw9763_check_id(dw9763_dev);
+	if (ret)
+		goto err_power_off;
 
 	sd = &dw9763_dev->sd;
 	sd->entity.function = MEDIA_ENT_F_LENS;
@@ -823,6 +934,9 @@
 	dev_info(&client->dev, "probing successful\n");
 
 	return 0;
+err_power_off:
+	__dw9763_set_power(dw9763_dev, false);
+
 err_cleanup:
 	dw9763_subdev_cleanup(dw9763_dev);
 
@@ -847,21 +961,18 @@
 {
 	struct dw9763_device *dev_vcm = i2c_get_clientdata(client);
 	int ret = 0;
-	u32 ring = 0;
 	u32 mode_val = 0;
 	u32 algo_time = 0;
 
+	if (dev_vcm->step_mode == DIRECT_MODE)
+		return 0;
 
-	/* Delay 200us~300us */
-	usleep_range(200, 300);
 	ret = dw9763_write_reg(client, 0x02, 1, 0x00);
 	if (ret)
 		goto err;
-	usleep_range(100, 200);
 
-	if (dev_vcm->step_mode != DIRECT_MODE)
-		ring = 0x02;
-	ret = dw9763_write_reg(client, 0x02, 1, ring);
+	usleep_range(200, 300);
+	ret = dw9763_write_reg(client, 0x02, 1, 0x02);
 	if (ret)
 		goto err;
 	switch (dev_vcm->step_mode) {
@@ -895,13 +1006,15 @@
 static int __maybe_unused dw9763_vcm_suspend(struct device *dev)
 {
 	struct i2c_client *client = to_i2c_client(dev);
-	int ret = 0;
+	struct dw9763_device *dev_vcm = i2c_get_clientdata(client);
+	struct v4l2_subdev *sd = &(dev_vcm->sd);
 
-	/* set to power down mode */
-	ret = dw9763_write_reg(client, 0x02, 1, 0x01);
-	if (ret)
-		dev_err(&client->dev, "failed to set power down mode!\n");
+#ifdef CONFIG_PM
+	v4l2_dbg(1, debug, sd, "%s: enter, power.usage_count(%d)!\n", __func__,
+		 atomic_read(&sd->dev->power.usage_count));
+#endif
 
+	__dw9763_set_power(dev_vcm, false);
 	return 0;
 }
 
@@ -909,9 +1022,14 @@
 {
 	struct i2c_client *client = to_i2c_client(dev);
 	struct dw9763_device *dev_vcm = i2c_get_clientdata(client);
+	struct v4l2_subdev *sd = &(dev_vcm->sd);
 
-	dw9763_init(client);
-	dw9763_set_pos(dev_vcm, dev_vcm->current_related_pos);
+#ifdef CONFIG_PM
+	v4l2_dbg(1, debug, sd, "%s: enter, power.usage_count(%d)!\n", __func__,
+		 atomic_read(&sd->dev->power.usage_count));
+#endif
+	__dw9763_set_power(dev_vcm, true);
+
 	return 0;
 }
 
diff --git a/kernel/drivers/media/i2c/gc1084.c b/kernel/drivers/media/i2c/gc1084.c
index db95d15..170d1d3 100644
--- a/kernel/drivers/media/i2c/gc1084.c
+++ b/kernel/drivers/media/i2c/gc1084.c
@@ -136,6 +136,7 @@
 	const char      *module_facing;
 	const char      *module_name;
 	const char      *len_name;
+	enum rkmodule_sync_mode	sync_mode;
 	u32		cur_vts;
 
 	bool			  has_init_exp;
@@ -150,6 +151,27 @@
 
 static const s64 link_freq_menu_items[] = {
 	MIPI_FREQ_400M,
+};
+
+static const struct reg_sequence gc1084_master_mode_regs[] = {
+	{0x0068, 0x85},
+	{0x0d6a, 0x80},
+	{0x0069, 0x00},
+	{0x006a, 0x02},
+	{0x0d69, 0x04},
+};
+
+static const struct reg_sequence gc1084_slave_mode_regs[] = {
+	{0x0d67, 0x00},
+	{0x0d69, 0x03},
+	{0x0d6a, 0x08},
+	{0x0d6b, 0x50},
+	{0x0d6c, 0x00},
+	{0x0d6d, 0x53},
+	{0x0d6e, 0x00},
+	{0x0d6f, 0x10},
+	{0x0d70, 0x00},
+	{0x0d71, 0x12},
 };
 
 /*
@@ -629,6 +651,7 @@
 	u32 stream = 0;
 	u64 delay_us = 0;
 	u32 fps = 0;
+	u32 *sync_mode = NULL;
 
 	switch (cmd) {
 	case RKMODULE_GET_HDR_CFG:
@@ -654,6 +677,14 @@
 			delay_us = 1000000 / (gc1084->cur_mode->vts_def * fps / gc1084->cur_vts);
 			usleep_range(delay_us, delay_us + 2000);
 		}
+		break;
+	case RKMODULE_GET_SYNC_MODE:
+		sync_mode = (u32 *)arg;
+		*sync_mode = gc1084->sync_mode;
+		break;
+	case RKMODULE_SET_SYNC_MODE:
+		sync_mode = (u32 *)arg;
+		gc1084->sync_mode = *sync_mode;
 		break;
 	default:
 		ret = -ENOIOCTLCMD;
@@ -686,6 +717,25 @@
 		}
 	}
 
+	if (gc1084->sync_mode == INTERNAL_MASTER_MODE) {
+		ret = regmap_multi_reg_write(gc1084->regmap, gc1084_master_mode_regs,
+					     ARRAY_SIZE(gc1084_master_mode_regs));
+		if (ret)
+			dev_err(gc1084->dev,
+				"write internal master mode reg failed %d\n", ret);
+	} else if (gc1084->sync_mode == EXTERNAL_MASTER_MODE) {
+		ret = regmap_multi_reg_write(gc1084->regmap, gc1084_slave_mode_regs,
+					     ARRAY_SIZE(gc1084_slave_mode_regs));
+		if (ret)
+			dev_err(gc1084->dev,
+				"write external master mode reg failed %d\n", ret);
+	} else if (gc1084->sync_mode == SLAVE_MODE) {
+		ret = regmap_multi_reg_write(gc1084->regmap, gc1084_slave_mode_regs,
+					     ARRAY_SIZE(gc1084_slave_mode_regs));
+		if (ret)
+			dev_err(gc1084->dev, "write slave mode reg failed %d\n", ret);
+	}
+
 	return gc1084_write_reg(gc1084, GC1084_REG_CTRL_MODE,
 				GC1084_MODE_STREAMING);
 }
@@ -707,6 +757,7 @@
 	struct preisp_hdrae_exp_s *hdrae;
 	long ret = 0;
 	u32 stream = 0;
+	u32 sync_mode;
 
 	switch (cmd) {
 	case RKMODULE_GET_MODULE_INFO:
@@ -771,6 +822,21 @@
 		ret = copy_from_user(&stream, up, sizeof(u32));
 		if (!ret)
 			ret = gc1084_ioctl(sd, cmd, &stream);
+		else
+			ret = -EFAULT;
+		break;
+	case RKMODULE_GET_SYNC_MODE:
+		ret = gc1084_ioctl(sd, cmd, &sync_mode);
+		if (!ret) {
+			ret = copy_to_user(up, &sync_mode, sizeof(u32));
+			if (ret)
+				ret = -EFAULT;
+		}
+		break;
+	case RKMODULE_SET_SYNC_MODE:
+		ret = copy_from_user(&sync_mode, up, sizeof(u32));
+		if (!ret)
+			ret = gc1084_ioctl(sd, cmd, &sync_mode);
 		else
 			ret = -EFAULT;
 		break;
@@ -1096,6 +1162,7 @@
 	struct v4l2_subdev *sd;
 	char facing[2];
 	int ret;
+	const char *sync_mode_name = NULL;
 
 	dev_info(dev, "driver version: %02x.%02x.%02x",
 		 DRIVER_VERSION >> 16,
@@ -1126,6 +1193,20 @@
 		return -EINVAL;
 	}
 
+	ret = of_property_read_string(node, RKMODULE_CAMERA_SYNC_MODE,
+				      &sync_mode_name);
+	if (ret) {
+		gc1084->sync_mode = NO_SYNC_MODE;
+		dev_err(dev, "could not get sync mode!\n");
+	} else {
+		if (strcmp(sync_mode_name, RKMODULE_EXTERNAL_MASTER_MODE) == 0)
+			gc1084->sync_mode = EXTERNAL_MASTER_MODE;
+		else if (strcmp(sync_mode_name, RKMODULE_INTERNAL_MASTER_MODE) == 0)
+			gc1084->sync_mode = INTERNAL_MASTER_MODE;
+		else if (strcmp(sync_mode_name, RKMODULE_SLAVE_MODE) == 0)
+			gc1084->sync_mode = SLAVE_MODE;
+	}
+
 	gc1084->xvclk = devm_clk_get(gc1084->dev, "xvclk");
 	if (IS_ERR(gc1084->xvclk)) {
 		dev_err(gc1084->dev, "Failed to get xvclk\n");
diff --git a/kernel/drivers/media/i2c/gc2053.c b/kernel/drivers/media/i2c/gc2053.c
index 7a93d86..8475b67 100644
--- a/kernel/drivers/media/i2c/gc2053.c
+++ b/kernel/drivers/media/i2c/gc2053.c
@@ -574,11 +574,13 @@
 		break;
 	case V4L2_CID_VBLANK:
 		vts = ctrl->val + gc2053->cur_mode->height;
-		ret = gc2053_write_reg(gc2053->client, GC2053_REG_VTS_H, (vts >> 8) & 0x3f);
-		ret |= gc2053_write_reg(gc2053->client, GC2053_REG_VTS_L, vts & 0xff);
 		/* Note: In master-slave mode, Galaxycore request slave sensor frame rate bigger than master. */
 		if (gc2053->sync_mode == INTERNAL_MASTER_MODE)
-			gc2053_write_reg(gc2053->client, GC2053_REG_VTS_L, (vts & 0xff) + 10);
+			vts += 10;
+		ret = gc2053_write_reg(gc2053->client, GC2053_REG_VTS_H, (vts >> 8) & 0x3f);
+		ret |= gc2053_write_reg(gc2053->client, GC2053_REG_VTS_L, vts & 0xff);
+		/* TBD: master and slave not sync to streaming, but except sleep 20ms below */
+		usleep_range(20000, 50000);
 		break;
 	case V4L2_CID_HFLIP:
 		if (ctrl->val)
diff --git a/kernel/drivers/media/i2c/gc2093.c b/kernel/drivers/media/i2c/gc2093.c
index 696dfbe..aa87a73 100644
--- a/kernel/drivers/media/i2c/gc2093.c
+++ b/kernel/drivers/media/i2c/gc2093.c
@@ -82,6 +82,8 @@
 
 #define GC2093_LANES		2
 
+#define OF_CAMERA_HDR_MODE		"rockchip,camera-hdr-mode"
+
 static const char * const gc2093_supply_names[] = {
 	"dovdd",    /* Digital I/O power */
 	"avdd",     /* Analog power */
@@ -142,7 +144,6 @@
 	struct mutex        lock;
 	bool		    streaming;
 	bool		    power_on;
-	unsigned int        cfg_num;
 	const struct gc2093_mode *cur_mode;
 
 	u32		module_index;
@@ -421,6 +422,138 @@
 	{0x024d, 0x01},
 };
 
+/*
+ * window size=1920*1080 mipi@2lane
+ * mclk=27M mipi_clk=792Mbps
+ * pixel_line_total=2640 line_frame_total=1500
+ * row_time=20us frame_rate=50fps
+ */
+static const struct reg_sequence gc2093_1080p_25fps_hdr_settings[] = {
+	/* System */
+	{0x03fe, 0x80},
+	{0x03fe, 0x80},
+	{0x03fe, 0x80},
+	{0x03fe, 0x00},
+	{0x03f2, 0x00},
+	{0x03f3, 0x00},
+	{0x03f4, 0x36},
+	{0x03f5, 0xc0},
+	{0x03f6, 0x0B},
+	{0x03f7, 0x01},
+	{0x03f8, 0x58},
+	{0x03f9, 0x40},
+	{0x03fc, 0x8e},
+	/* Cisctl & Analog */
+	{0x0087, 0x18},
+	{0x00ee, 0x30},
+	{0x00d0, 0xbf},
+	{0x01a0, 0x00},
+	{0x01a4, 0x40},
+	{0x01a5, 0x40},
+	{0x01a6, 0x40},
+	{0x01af, 0x09},
+	{0x0001, 0x00},
+	{0x0002, 0x02},
+	{0x0003, 0x04},
+	{0x0004, 0x02},
+	{0x0005, 0x02},
+	{0x0006, 0x94},
+	{0x0007, 0x00},
+	{0x0008, 0x11},
+	{0x0009, 0x00},
+	{0x000a, 0x02},
+	{0x000b, 0x00},
+	{0x000c, 0x04},
+	{0x000d, 0x04},
+	{0x000e, 0x40},
+	{0x000f, 0x07},
+	{0x0010, 0x8c},
+	{0x0013, 0x15},
+	{0x0019, 0x0c},
+	{0x0041, 0x05},
+	{0x0042, 0xdc},
+	{0x0053, 0x60},
+	{0x008d, 0x92},
+	{0x0090, 0x00},
+	{0x00c7, 0xe1},
+	{0x001b, 0x73},
+	{0x0028, 0x0d},
+	{0x0029, 0x24},
+	{0x002b, 0x04},
+	{0x002e, 0x23},
+	{0x0037, 0x03},
+	{0x0043, 0x04},
+	{0x0044, 0x20},
+	{0x004a, 0x01},
+	{0x004b, 0x20},
+	{0x0055, 0x30},
+	{0x006b, 0x44},
+	{0x0077, 0x00},
+	{0x0078, 0x20},
+	{0x007c, 0xa1},
+	{0x00d3, 0xd4},
+	{0x00e6, 0x50},
+	/* Gain */
+	{0x00b6, 0xc0},
+	{0x00b0, 0x60},
+	/* Isp */
+	{0x0102, 0x89},
+	{0x0104, 0x01},
+	{0x010e, 0x01},
+	{0x0158, 0x00},
+	{0x0183, 0x01},
+	{0x0187, 0x50},
+	/* Dark sun*/
+	{0x0123, 0x08},
+	{0x0123, 0x00},
+	{0x0120, 0x01},
+	{0x0121, 0x00},
+	{0x0122, 0x10},
+	{0x0124, 0x03},
+	{0x0125, 0xff},
+	{0x0126, 0x3c},
+	{0x001a, 0x8c},
+	{0x00c6, 0xe0},
+	/* Blk */
+	{0x0026, 0x30},
+	{0x0142, 0x00},
+	{0x0149, 0x1e},
+	{0x014a, 0x0f},
+	{0x014b, 0x00},
+	{0x0155, 0x00},
+	{0x0414, 0x78},
+	{0x0415, 0x78},
+	{0x0416, 0x78},
+	{0x0417, 0x78},
+	{0x0454, 0x78},
+	{0x0455, 0x78},
+	{0x0456, 0x78},
+	{0x0457, 0x78},
+	{0x04e0, 0x18},
+	/* Window */
+	{0x0192, 0x02},
+	{0x0194, 0x03},
+	{0x0195, 0x04},
+	{0x0196, 0x38},
+	{0x0197, 0x07},
+	{0x0198, 0x80},
+	/* MIPI */
+	{0x019a, 0x06},
+	{0x007b, 0x2a},
+	{0x0023, 0x2d},
+	{0x0201, 0x27},
+	{0x0202, 0x56},
+	{0x0203, 0xb6},
+	{0x0212, 0x80},
+	{0x0213, 0x07},
+	{0x0215, 0x12},
+	{0x003e, 0x91},
+	/* HDR En */
+	{0x0027, 0x71},
+	{0x0215, 0x92},
+	{0x024d, 0x01},
+};
+
 static const struct gc2093_mode supported_modes[] = {
 	{
 		.width = 1920,
@@ -451,6 +584,25 @@
 		.link_freq_index = LINK_FREQ_396M_INDEX,
 		.reg_list = gc2093_1080p_hdr_settings,
 		.reg_num = ARRAY_SIZE(gc2093_1080p_hdr_settings),
+		.hdr_mode = HDR_X2,
+		.vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_1,
+		.vc[PAD1] = V4L2_MBUS_CSI2_CHANNEL_0,//L->csi wr0
+		.vc[PAD2] = V4L2_MBUS_CSI2_CHANNEL_1,
+		.vc[PAD3] = V4L2_MBUS_CSI2_CHANNEL_1,//M->csi wr2
+	},
+	{
+		.width = 1920,
+		.height = 1080,
+		.max_fps = {
+			.numerator = 10000,
+			.denominator = 250000,
+		},
+		.exp_def = 0x460,
+		.hts_def = 0xa50,
+		.vts_def = 0x5dc,
+		.link_freq_index = LINK_FREQ_396M_INDEX,
+		.reg_list = gc2093_1080p_25fps_hdr_settings,
+		.reg_num = ARRAY_SIZE(gc2093_1080p_25fps_hdr_settings),
 		.hdr_mode = HDR_X2,
 		.vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_1,
 		.vc[PAD1] = V4L2_MBUS_CSI2_CHANNEL_0,//L->csi wr0
@@ -585,6 +737,7 @@
 
 	switch (ctrl->id) {
 	case V4L2_CID_EXPOSURE:
+		dev_dbg(gc2093->dev, "set exposure value 0x%x\n", ctrl->val);
 		if (gc2093->cur_mode->hdr_mode != NO_HDR)
 			goto ctrl_end;
 		dev_dbg(gc2093->dev, "set exposure value 0x%x\n", ctrl->val);
@@ -594,29 +747,36 @@
 					ctrl->val & 0xff);
 		break;
 	case V4L2_CID_ANALOGUE_GAIN:
+		dev_dbg(gc2093->dev, "set gain value 0x%x, mode: %d\n",
+				ctrl->val, gc2093->cur_mode->hdr_mode);
 		if (gc2093->cur_mode->hdr_mode != NO_HDR)
 			goto ctrl_end;
 		dev_dbg(gc2093->dev, "set gain value 0x%x\n", ctrl->val);
 		gc2093_set_gain(gc2093, ctrl->val);
 		break;
 	case V4L2_CID_VBLANK:
+		dev_dbg(gc2093->dev, "set blank value 0x%x\n", ctrl->val);
 		vts = gc2093->cur_mode->height + ctrl->val;
 		gc2093->cur_vts = vts;
 		ret = gc2093_write_reg(gc2093, GC2093_REG_VTS_H,
 				       (vts >> 8) & 0x3f);
 		ret |= gc2093_write_reg(gc2093, GC2093_REG_VTS_L,
 					vts & 0xff);
+		if (!ret)
+			gc2093->cur_vts = ctrl->val + gc2093->cur_mode->height;
 		if (gc2093->cur_vts != gc2093->cur_mode->vts_def)
 			gc2093_modify_fps_info(gc2093);
 		dev_dbg(gc2093->dev, " set blank value 0x%x\n", ctrl->val);
 		break;
 	case V4L2_CID_HFLIP:
-			regmap_update_bits(gc2093->regmap, GC2093_MIRROR_FLIP_REG,
-					   MIRROR_MASK, ctrl->val ? MIRROR_MASK : 0);
+		dev_dbg(gc2093->dev, "set hflip 0x%x\n", ctrl->val);
+		regmap_update_bits(gc2093->regmap, GC2093_MIRROR_FLIP_REG,
+				   MIRROR_MASK, ctrl->val ? MIRROR_MASK : 0);
 		break;
 	case V4L2_CID_VFLIP:
-			regmap_update_bits(gc2093->regmap, GC2093_MIRROR_FLIP_REG,
-					   FLIP_MASK,  ctrl->val ? FLIP_MASK : 0);
+		dev_dbg(gc2093->dev, "set vflip 0x%x\n", ctrl->val);
+		regmap_update_bits(gc2093->regmap, GC2093_MIRROR_FLIP_REG,
+				   FLIP_MASK,  ctrl->val ? FLIP_MASK : 0);
 		break;
 	default:
 		dev_warn(gc2093->dev, "%s Unhandled id:0x%x, val:0x%x\n",
@@ -673,7 +833,7 @@
 					   h_blank, h_blank, 1, h_blank);
 	if (gc2093->hblank)
 		gc2093->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
-
+	gc2093->cur_fps = mode->max_fps;
 	vblank_def = mode->vts_def - mode->height;
 	gc2093->cur_vts = mode->vts_def;
 	gc2093->vblank = v4l2_ctrl_new_std(handler, &gc2093_ctrl_ops,
@@ -820,11 +980,24 @@
 	strlcpy(inf->base.module, gc2093->module_name, sizeof(inf->base.module));
 }
 
+static int gc2093_get_channel_info(struct gc2093 *gc2093,
+				   struct rkmodule_channel_info *ch_info)
+{
+	if (ch_info->index < PAD0 || ch_info->index >= PAD_MAX)
+		return -EINVAL;
+	ch_info->vc = gc2093->cur_mode->vc[ch_info->index];
+	ch_info->width = gc2093->cur_mode->width;
+	ch_info->height = gc2093->cur_mode->height;
+	ch_info->bus_fmt = GC2093_MEDIA_BUS_FMT;
+	return 0;
+}
+
 static long gc2093_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
 {
 	struct gc2093 *gc2093 = to_gc2093(sd);
 	struct preisp_hdrae_exp_s *hdrae_exp = arg;
 	struct rkmodule_hdr_cfg *hdr_cfg;
+	struct rkmodule_channel_info *ch_info;
 	long ret = 0;
 	u32 i, h, w;
 	u32 stream = 0;
@@ -914,15 +1087,18 @@
 		hdr_cfg = (struct rkmodule_hdr_cfg *)arg;
 		w = gc2093->cur_mode->width;
 		h = gc2093->cur_mode->height;
-		for (i = 0; i < gc2093->cfg_num; i++) {
+		for (i = 0; i < ARRAY_SIZE(supported_modes); i++) {
 			if (w == supported_modes[i].width &&
 			h == supported_modes[i].height &&
 			supported_modes[i].hdr_mode == hdr_cfg->hdr_mode) {
 				gc2093->cur_mode = &supported_modes[i];
 				break;
 			}
+			dev_err(gc2093->dev, "i:%d,w:%d, h:%d, hdr:%d\n",
+					i, supported_modes[i].width, supported_modes[i].height,
+					supported_modes[i].hdr_mode);
 		}
-		if (i == gc2093->cfg_num) {
+		if (i == ARRAY_SIZE(supported_modes)) {
 			dev_err(gc2093->dev, "not find hdr mode:%d %dx%d config\n",
 				hdr_cfg->hdr_mode, w, h);
 			ret = -EINVAL;
@@ -958,6 +1134,10 @@
 			usleep_range(delay_us, delay_us + 2000);
 		}
 		break;
+	case RKMODULE_GET_CHANNEL_INFO:
+		ch_info = (struct rkmodule_channel_info *)arg;
+		ret = gc2093_get_channel_info(gc2093, ch_info);
+		break;
 	default:
 		ret = -ENOIOCTLCMD;
 		break;
@@ -990,8 +1170,16 @@
 			}
 		}
 	}
+	dev_info(gc2093->dev,
+		 "%dx%d@%d, mode %d, vts 0x%x\n",
+		 gc2093->cur_mode->width,
+		 gc2093->cur_mode->height,
+		 gc2093->cur_fps.denominator / gc2093->cur_fps.numerator,
+		 gc2093->cur_mode->hdr_mode,
+		 gc2093->cur_vts);
+	dev_info(gc2093->dev, "is_tb:%d\n", gc2093->is_thunderboot);
 	return gc2093_write_reg(gc2093, GC2093_REG_CTRL_MODE,
-				GC2093_MODE_STREAMING);
+							GC2093_MODE_STREAMING);
 }
 
 static int __gc2093_stop_stream(struct gc2093 *gc2093)
@@ -1013,6 +1201,7 @@
 	struct rkmodule_inf *inf;
 	struct rkmodule_hdr_cfg *hdr;
 	struct preisp_hdrae_exp_s *hdrae;
+	struct rkmodule_channel_info *ch_info;
 	long ret = 0;
 	u32 stream = 0;
 
@@ -1082,6 +1271,21 @@
 		else
 			ret = -EFAULT;
 		break;
+	case RKMODULE_GET_CHANNEL_INFO:
+		ch_info = kzalloc(sizeof(*ch_info), GFP_KERNEL);
+		if (!ch_info) {
+			ret = -ENOMEM;
+			return ret;
+		}
+
+		ret = gc2093_ioctl(sd, cmd, ch_info);
+		if (!ret) {
+			ret = copy_to_user(up, ch_info, sizeof(*ch_info));
+			if (ret)
+				ret = -EFAULT;
+		}
+		kfree(ch_info);
+		break;
 	default:
 		ret = -ENOIOCTLCMD;
 		break;
@@ -1100,11 +1304,17 @@
 	fps = DIV_ROUND_CLOSEST(gc2093->cur_mode->max_fps.denominator,
 					gc2093->cur_mode->max_fps.numerator);
 
-	dev_info(gc2093->dev, "%s: on: %d, %dx%d@%d\n", __func__, on,
-				gc2093->cur_mode->width,
-				gc2093->cur_mode->height,
-				fps);
+	dev_info(gc2093->dev,
+		 "%dx%d@%d, mode %d, vts 0x%x\n",
+		 gc2093->cur_mode->width,
+		 gc2093->cur_mode->height,
+		 gc2093->cur_fps.denominator / gc2093->cur_fps.numerator,
+		 gc2093->cur_mode->hdr_mode,
+		 gc2093->cur_vts);
 
+	dev_info(gc2093->dev,
+		 "stream:%d\n, on:%d",
+		 gc2093->streaming, on);
 	mutex_lock(&gc2093->lock);
 	on = !!on;
 	if (on == gc2093->streaming)
@@ -1151,7 +1361,10 @@
 	struct gc2093 *gc2093 = to_gc2093(sd);
 	const struct gc2093_mode *mode = gc2093->cur_mode;
 
-	fi->interval = mode->max_fps;
+	if (gc2093->streaming)
+		fi->interval = gc2093->cur_fps;
+	else
+		fi->interval = mode->max_fps;
 
 	return 0;
 }
@@ -1184,9 +1397,7 @@
 				   struct v4l2_subdev_pad_config *cfg,
 				   struct v4l2_subdev_frame_size_enum *fse)
 {
-	struct gc2093 *gc2093 = to_gc2093(sd);
-
-	if (fse->index >= gc2093->cfg_num)
+	if (fse->index >= ARRAY_SIZE(supported_modes))
 		return -EINVAL;
 
 	if (fse->code != GC2093_MEDIA_BUS_FMT)
@@ -1203,9 +1414,7 @@
 						  struct v4l2_subdev_pad_config *cfg,
 						  struct v4l2_subdev_frame_interval_enum *fie)
 {
-	struct gc2093 *gc2093 = to_gc2093(sd);
-
-	if (fie->index >= gc2093->cfg_num)
+	if (fie->index >= ARRAY_SIZE(supported_modes))
 		return -EINVAL;
 
 	fie->code = GC2093_MEDIA_BUS_FMT;
@@ -1401,6 +1610,136 @@
 			   gc2093_runtime_resume, NULL)
 };
 
+
+#ifdef CONFIG_VIDEO_ROCKCHIP_THUNDER_BOOT_ISP
+static u32 rk_cam_hdr;
+static u32 rk_cam_w;
+static u32 rk_cam_h;
+static u32 rk_cam_fps;
+
+static int __init __maybe_unused rk_cam_hdr_setup(char *str)
+{
+	int ret = 0;
+	unsigned long val = 0;
+
+	ret = kstrtoul(str, 0, &val);
+	if (!ret)
+		rk_cam_hdr = (u32)val;
+	else
+		pr_err("get rk_cam_hdr fail\n");
+	return 1;
+}
+
+static int __init __maybe_unused rk_cam_w_setup(char *str)
+{
+	int ret = 0;
+	unsigned long val = 0;
+
+	ret = kstrtoul(str, 0, &val);
+	if (!ret)
+		rk_cam_w = (u32)val;
+	else
+		pr_err("get rk_cam_w fail\n");
+	return 1;
+}
+
+static int __init __maybe_unused rk_cam_h_setup(char *str)
+{
+	int ret = 0;
+	unsigned long val = 0;
+
+	ret = kstrtoul(str, 0, &val);
+	if (!ret)
+		rk_cam_h = (u32)val;
+	else
+		pr_err("get rk_cam_h fail\n");
+	return 1;
+}
+
+static int __init __maybe_unused rk_cam_fps_setup(char *str)
+{
+	int ret = 0;
+	unsigned long val = 0;
+
+	ret = kstrtoul(str, 0, &val);
+	if (!ret)
+		rk_cam_fps = (u32)val;
+	else
+		pr_err("get rk_cam_fps fail\n");
+	return 1;
+}
+
+__setup("rk_cam_hdr=", rk_cam_hdr_setup);
+__setup("rk_cam_w=", rk_cam_w_setup);
+__setup("rk_cam_h=", rk_cam_h_setup);
+__setup("rk_cam_fps=", rk_cam_fps_setup);
+
+static void find_terminal_resolution(struct gc2093 *gc2093)
+{
+	int i = 0;
+	const struct gc2093_mode *mode = NULL;
+	const struct gc2093_mode *fit_mode = NULL;
+	u32 cur_fps = 0;
+	u32 dst_fps = 0;
+	u32 tmp_fps = 0;
+
+	if (rk_cam_w == 0 || rk_cam_h == 0 ||
+	    rk_cam_fps == 0)
+		goto err_find_res;
+
+	dev_info(gc2093->dev, "find resolution width: %d, height: %d, hdr: %d, fps: %d\n",
+		 rk_cam_w, rk_cam_h, rk_cam_hdr, rk_cam_fps);
+	dst_fps = rk_cam_fps;
+	for (i = 0; i < ARRAY_SIZE(supported_modes); i++) {
+		mode = &supported_modes[i];
+		cur_fps = mode->max_fps.denominator / mode->max_fps.numerator;
+		if (mode->width == rk_cam_w && mode->height == rk_cam_h &&
+		    mode->hdr_mode == rk_cam_hdr) {
+			if (cur_fps == dst_fps) {
+				gc2093->cur_mode = mode;
+				return;
+			}
+			if (cur_fps >= dst_fps) {
+				if (fit_mode) {
+					tmp_fps = fit_mode->max_fps.denominator /
+							  fit_mode->max_fps.numerator;
+					if (tmp_fps - dst_fps > cur_fps - dst_fps)
+						fit_mode = mode;
+				} else {
+					fit_mode = mode;
+				}
+			}
+		}
+	}
+	if (fit_mode) {
+		gc2093->cur_mode = fit_mode;
+		return;
+	}
+err_find_res:
+	dev_err(gc2093->dev, "not match %dx%d@%dfps mode %d\n!",
+		rk_cam_w, rk_cam_h, dst_fps, rk_cam_hdr);
+	gc2093->cur_mode = &supported_modes[0];
+}
+#else
+static void find_terminal_resolution(struct gc2093 *gc2093)
+{
+	u32 hdr_mode = 0;
+	struct device_node *node = gc2093->dev->of_node;
+	int i = 0;
+
+	of_property_read_u32(node, OF_CAMERA_HDR_MODE, &hdr_mode);
+	for (i = 0; i < ARRAY_SIZE(supported_modes); i++) {
+		if (hdr_mode == supported_modes[i].hdr_mode) {
+			gc2093->cur_mode = &supported_modes[i];
+			break;
+		}
+	}
+	if (i == ARRAY_SIZE(supported_modes))
+		gc2093->cur_mode = &supported_modes[0];
+
+}
+#endif
+
 static int gc2093_probe(struct i2c_client *client,
 			 const struct i2c_device_id *id)
 {
@@ -1448,6 +1787,8 @@
 		return -EINVAL;
 	}
 
+	find_terminal_resolution(gc2093);
+
 	gc2093->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_ASIS);
 	if (IS_ERR(gc2093->reset_gpio))
 		dev_warn(dev, "Failed to get reset-gpios\n");
@@ -1463,11 +1804,6 @@
 	}
 
 	mutex_init(&gc2093->lock);
-
-	/* set default mode */
-	gc2093->cur_mode = &supported_modes[0];
-	gc2093->cfg_num = ARRAY_SIZE(supported_modes);
-	gc2093->cur_vts = gc2093->cur_mode->vts_def;
 
 	sd = &gc2093->subdev;
 	v4l2_i2c_subdev_init(sd, client, &gc2093_subdev_ops);
diff --git a/kernel/drivers/media/i2c/gc8034.c b/kernel/drivers/media/i2c/gc8034.c
index 2c1dd37..caa71e5 100644
--- a/kernel/drivers/media/i2c/gc8034.c
+++ b/kernel/drivers/media/i2c/gc8034.c
@@ -1468,6 +1468,7 @@
 		inf->pdaf.flag = 1;
 		inf->pdaf.gainmap_width = otp->pdaf_data.gainmap_width;
 		inf->pdaf.gainmap_height = otp->pdaf_data.gainmap_height;
+		inf->pdaf.pd_offset = otp->pdaf_data.pd_offset;
 		inf->pdaf.dcc_mode = otp->pdaf_data.dcc_mode;
 		inf->pdaf.dcc_dir = otp->pdaf_data.dcc_dir;
 		inf->pdaf.dccmap_width = otp->pdaf_data.dccmap_width;
diff --git a/kernel/drivers/media/i2c/imx415.c b/kernel/drivers/media/i2c/imx415.c
index cda7a78..0bd0431 100644
--- a/kernel/drivers/media/i2c/imx415.c
+++ b/kernel/drivers/media/i2c/imx415.c
@@ -1771,8 +1771,7 @@
 	case RKMODULE_GET_CSI_DPHY_PARAM:
 		if (imx415->cur_mode->hdr_mode == HDR_X2) {
 			dphy_param = (struct rkmodule_csi_dphy_param *)arg;
-			if (dphy_param->vendor == dcphy_param.vendor)
-				*dphy_param = dcphy_param;
+			*dphy_param = dcphy_param;
 			dev_info(&imx415->client->dev,
 				 "get sensor dphy param\n");
 		} else
diff --git a/kernel/drivers/media/i2c/imx586.c b/kernel/drivers/media/i2c/imx586.c
index c0808d0..7948fe0 100644
--- a/kernel/drivers/media/i2c/imx586.c
+++ b/kernel/drivers/media/i2c/imx586.c
@@ -1206,6 +1206,7 @@
 		inf->pdaf.flag = 1;
 		inf->pdaf.gainmap_width = otp->pdaf_data.gainmap_width;
 		inf->pdaf.gainmap_height = otp->pdaf_data.gainmap_height;
+		inf->pdaf.pd_offset = otp->pdaf_data.pd_offset;
 		inf->pdaf.dcc_mode = otp->pdaf_data.dcc_mode;
 		inf->pdaf.dcc_dir = otp->pdaf_data.dcc_dir;
 		inf->pdaf.dccmap_width = otp->pdaf_data.dccmap_width;
diff --git a/kernel/drivers/media/i2c/it6616.c b/kernel/drivers/media/i2c/it6616.c
index 6928a11..22c610f 100644
--- a/kernel/drivers/media/i2c/it6616.c
+++ b/kernel/drivers/media/i2c/it6616.c
@@ -20,6 +20,7 @@
 #include <linux/module.h>
 #include <linux/of_graph.h>
 #include <linux/rk-camera-module.h>
+#include <linux/rk_hdmirx_class.h>
 #include <linux/slab.h>
 #include <linux/timer.h>
 #include <linux/v4l2-dv-timings.h>
@@ -471,7 +472,6 @@
 	struct regmap *mipi_regmap;
 	struct regmap *edid_regmap;
 	u8 attr_hdmi_reg_bank;
-	struct class *hdmirx_class;
 	struct device *dev;
 	struct device *classdev;
 	struct v4l2_fwnode_bus_mipi_csi2 bus;
@@ -3519,6 +3519,16 @@
 	return 0;
 }
 
+static void it6616_detect_hot_plug(struct v4l2_subdev *sd)
+{
+	struct it6616 *it6616 = to_it6616(sd);
+
+	if (it6616->mipi_tx_video_stable && it6616_hdmi_is_5v_on(it6616))
+		v4l2_ctrl_s_ctrl(it6616->detect_tx_5v_ctrl, 1);
+	else
+		v4l2_ctrl_s_ctrl(it6616->detect_tx_5v_ctrl, 0);
+}
+
 static void it6616_work_i2c_poll(struct work_struct *work)
 {
 	struct delayed_work *dwork = to_delayed_work(work);
@@ -3526,8 +3536,8 @@
 			struct it6616, work_i2c_poll);
 	bool handled;
 
-	it6616_s_ctrl_detect_tx_5v(&it6616->sd);
 	it6616_isr(&it6616->sd, 0, &handled);
+	it6616_detect_hot_plug(&it6616->sd);
 	schedule_delayed_work(&it6616->work_i2c_poll,
 			msecs_to_jiffies(POLL_INTERVAL_MS));
 }
@@ -4212,54 +4222,29 @@
 static DEVICE_ATTR_RO(audio_present);
 static DEVICE_ATTR_RO(audio_rate);
 
+static struct attribute *it6616_audio_attrs[] = {
+	&dev_attr_audio_rate.attr,
+	&dev_attr_audio_present.attr,
+	NULL
+};
+ATTRIBUTE_GROUPS(it6616_audio);
+
 static int it6616_create_class_attr(struct it6616 *it6616)
 {
-	int ret = 0;
-
-	it6616->hdmirx_class = class_create(THIS_MODULE, "hdmirx_it6616");
-	if (IS_ERR(it6616->hdmirx_class)) {
-		ret = -ENOMEM;
-		dev_err(it6616->dev, "failed to create hdmirx_it6616 class!\n");
-		return ret;
-	}
-
-	it6616->classdev = device_create(it6616->hdmirx_class, NULL,
-					MKDEV(0, 0), NULL, "hdmirx_it6616");
-	if (IS_ERR(it6616->classdev)) {
-		ret = PTR_ERR(it6616->classdev);
-		dev_err(it6616->dev, "Failed to create device\n");
-		goto err1;
-	}
-
-	ret = device_create_file(it6616->classdev,
-				&dev_attr_audio_present);
-	if (ret) {
-		dev_err(it6616->dev, "failed to create attr audio_present!\n");
-		goto err1;
-	}
-
-	ret = device_create_file(it6616->classdev,
-				&dev_attr_audio_rate);
-	if (ret) {
-		dev_err(it6616->dev,
-			"failed to create attr audio_rate!\n");
-		goto err;
-	}
-
-	return ret;
-
-err:
-	device_remove_file(it6616->classdev, &dev_attr_audio_present);
-err1:
-	class_destroy(it6616->hdmirx_class);
-	return ret;
+	it6616->classdev = device_create_with_groups(rk_hdmirx_class(),
+						     it6616->dev, MKDEV(0, 0),
+						     it6616,
+						     it6616_audio_groups,
+						     "it6616");
+	if (IS_ERR(it6616->classdev))
+		return IS_ERR(it6616->classdev);
+	return 0;
 }
 
 static void it6616_remove_class_attr(struct it6616 *it6616)
 {
 	device_remove_file(it6616->classdev, &dev_attr_audio_rate);
 	device_remove_file(it6616->classdev, &dev_attr_audio_present);
-	class_destroy(it6616->hdmirx_class);
 }
 
 static int it6616_probe(struct i2c_client *client,
diff --git a/kernel/drivers/media/i2c/lt6911uxc.c b/kernel/drivers/media/i2c/lt6911uxc.c
index 23da66e..c801d29 100644
--- a/kernel/drivers/media/i2c/lt6911uxc.c
+++ b/kernel/drivers/media/i2c/lt6911uxc.c
@@ -18,6 +18,7 @@
 #include <linux/module.h>
 #include <linux/of_graph.h>
 #include <linux/rk-camera-module.h>
+#include <linux/rk_hdmirx_class.h>
 #include <linux/slab.h>
 #include <linux/timer.h>
 #include <linux/v4l2-dv-timings.h>
@@ -89,6 +90,7 @@
 	u32 module_index;
 	u32 csi_lanes_in_use;
 	u32 audio_sampling_rate;
+	struct device *classdev;
 };
 
 struct lt6911uxc_mode {
@@ -1279,6 +1281,34 @@
 }
 #endif
 
+static ssize_t audio_rate_show(struct device *dev,
+			       struct device_attribute *attr, char *buf)
+{
+	struct lt6911uxc *lt6911uxc = dev_get_drvdata(dev);
+
+	return snprintf(buf, PAGE_SIZE, "%d", lt6911uxc->audio_sampling_rate);
+}
+
+static ssize_t audio_present_show(struct device *dev,
+				  struct device_attribute *attr, char *buf)
+{
+	struct lt6911uxc *lt6911uxc = dev_get_drvdata(dev);
+
+	return snprintf(buf, PAGE_SIZE, "%d",
+			tx_5v_power_present(&lt6911uxc->sd) ?
+			lt6911uxc->is_audio_present : 0);
+}
+
+static DEVICE_ATTR_RO(audio_rate);
+static DEVICE_ATTR_RO(audio_present);
+
+static struct attribute *lt6911_attrs[] = {
+	&dev_attr_audio_rate.attr,
+	&dev_attr_audio_present.attr,
+	NULL
+};
+ATTRIBUTE_GROUPS(lt6911);
+
 static int lt6911uxc_probe(struct i2c_client *client,
 		const struct i2c_device_id *id)
 {
@@ -1355,6 +1385,14 @@
 		goto err_clean_entity;
 	}
 
+	lt6911uxc->classdev = device_create_with_groups(rk_hdmirx_class(),
+							dev, MKDEV(0, 0),
+							lt6911uxc,
+							lt6911_groups,
+							"lt6911");
+	if (IS_ERR(lt6911uxc->classdev))
+		goto err_clean_entity;
+
 	INIT_DELAYED_WORK(&lt6911uxc->delayed_work_enable_hotplug,
 			lt6911uxc_delayed_work_enable_hotplug);
 	INIT_DELAYED_WORK(&lt6911uxc->delayed_work_res_change,
diff --git a/kernel/drivers/media/i2c/lt6911uxe.c b/kernel/drivers/media/i2c/lt6911uxe.c
index 50cf292..e167aae 100644
--- a/kernel/drivers/media/i2c/lt6911uxe.c
+++ b/kernel/drivers/media/i2c/lt6911uxe.c
@@ -223,9 +223,9 @@
 static struct rkmodule_csi_dphy_param rk3588_dcphy_param = {
 	.vendor = PHY_VENDOR_SAMSUNG,
 	.lp_vol_ref = 3,
-	.lp_hys_sw = {3, 0, 0, 0},
-	.lp_escclk_pol_sel = {1, 0, 0, 0},
-	.skew_data_cal_clk = {0, 3, 3, 3},
+	.lp_hys_sw = {3, 0, 3, 0},
+	.lp_escclk_pol_sel = {1, 1, 0, 0},
+	.skew_data_cal_clk = {0, 13, 0, 13},
 	.clk_hs_term_sel = 2,
 	.data_hs_term_sel = {2, 2, 2, 2},
 	.reserved = {0},
@@ -1395,15 +1395,14 @@
 		break;
 	case RKMODULE_SET_CSI_DPHY_PARAM:
 		dphy_param = (struct rkmodule_csi_dphy_param *)arg;
-		if (dphy_param->vendor == rk3588_dcphy_param.vendor)
+		if (dphy_param->vendor == PHY_VENDOR_SAMSUNG)
 			rk3588_dcphy_param = *dphy_param;
 		dev_dbg(&lt6911uxe->i2c_client->dev,
 			"sensor set dphy param\n");
 		break;
 	case RKMODULE_GET_CSI_DPHY_PARAM:
 		dphy_param = (struct rkmodule_csi_dphy_param *)arg;
-		if (dphy_param->vendor == rk3588_dcphy_param.vendor)
-			*dphy_param = rk3588_dcphy_param;
+		*dphy_param = rk3588_dcphy_param;
 		dev_dbg(&lt6911uxe->i2c_client->dev,
 			"sensor get dphy param\n");
 		break;
diff --git a/kernel/drivers/media/i2c/lt7911uxc.c b/kernel/drivers/media/i2c/lt7911uxc.c
index a39f8f8..20c9d26 100644
--- a/kernel/drivers/media/i2c/lt7911uxc.c
+++ b/kernel/drivers/media/i2c/lt7911uxc.c
@@ -1178,15 +1178,14 @@
 		break;
 	case RKMODULE_SET_CSI_DPHY_PARAM:
 		dphy_param = (struct rkmodule_csi_dphy_param *)arg;
-		if (dphy_param->vendor == rk3588_dcphy_param.vendor)
+		if (dphy_param->vendor == PHY_VENDOR_SAMSUNG)
 			rk3588_dcphy_param = *dphy_param;
 		dev_dbg(&lt7911uxc->i2c_client->dev,
 			"sensor set dphy param\n");
 		break;
 	case RKMODULE_GET_CSI_DPHY_PARAM:
 		dphy_param = (struct rkmodule_csi_dphy_param *)arg;
-		if (dphy_param->vendor == rk3588_dcphy_param.vendor)
-			*dphy_param = rk3588_dcphy_param;
+		*dphy_param = rk3588_dcphy_param;
 		dev_dbg(&lt7911uxc->i2c_client->dev,
 			"sensor get dphy param\n");
 		break;
diff --git a/kernel/drivers/media/i2c/max96712.c b/kernel/drivers/media/i2c/max96712.c
index aac7a63..727765c 100644
--- a/kernel/drivers/media/i2c/max96712.c
+++ b/kernel/drivers/media/i2c/max96712.c
@@ -17,6 +17,7 @@
  *         support for GMSL1 Link.
  * V1.5.00 only check max96712 chipid when probe.
  *         enable stream out if not all link are locked.
+ * V1.6.00 serdes read /write api depend on i2c id index.
  *
  */
 
@@ -45,7 +46,7 @@
 #include <media/v4l2-fwnode.h>
 #include <media/v4l2-subdev.h>
 
-#define DRIVER_VERSION			KERNEL_VERSION(1, 0x05, 0x00)
+#define DRIVER_VERSION			KERNEL_VERSION(1, 0x06, 0x00)
 
 #ifndef V4L2_CID_DIGITAL_GAIN
 #define V4L2_CID_DIGITAL_GAIN		V4L2_CID_GAIN
@@ -63,48 +64,48 @@
 #define MAX96717_CHIP_ID		0xBF
 #define MAX96717_REG_CHIP_ID		0x0D
 
-#define MAX96712_REMOTE_CTRL		0x0003
-#define MAX96712_REMOTE_DISABLE		0xFF
-
 /* max96712->link mask: link type = bit[7:4], link mask = bit[3:0] */
-#define MAX96712_GMSL_TYPE_LINK_A	BIT(4)
-#define MAX96712_GMSL_TYPE_LINK_B	BIT(5)
-#define MAX96712_GMSL_TYPE_LINK_C	BIT(6)
-#define MAX96712_GMSL_TYPE_LINK_D	BIT(7)
-#define MAX96712_GMSL_TYPE_MASK		0xF0 /* bit[7:4], GMSL link type: 0 = GMSL1, 1 = GMSL2 */
+#define MAXIM_GMSL_TYPE_LINK_A		BIT(4)
+#define MAXIM_GMSL_TYPE_LINK_B		BIT(5)
+#define MAXIM_GMSL_TYPE_LINK_C		BIT(6)
+#define MAXIM_GMSL_TYPE_LINK_D		BIT(7)
+#define MAXIM_GMSL_TYPE_MASK		0xF0 /* bit[7:4], GMSL link type: 0 = GMSL1, 1 = GMSL2 */
 
-#define MAX96712_LOCK_STATE_LINK_A	BIT(0)
-#define MAX96712_LOCK_STATE_LINK_B	BIT(1)
-#define MAX96712_LOCK_STATE_LINK_C	BIT(2)
-#define MAX96712_LOCK_STATE_LINK_D	BIT(3)
-#define MAX96712_LOCK_STATE_MASK	0x0F /* bit[3:0], GMSL link mask: 1 = disable, 1 = enable */
+#define MAXIM_GMSL_LOCK_LINK_A		BIT(0)
+#define MAXIM_GMSL_LOCK_LINK_B		BIT(1)
+#define MAXIM_GMSL_LOCK_LINK_C		BIT(2)
+#define MAXIM_GMSL_LOCK_LINK_D		BIT(3)
+#define MAXIM_GMSL_LOCK_MASK		0x0F /* bit[3:0], GMSL link mask: 1 = disable, 1 = enable */
 
-#define MAX96712_FORCE_ALL_CLOCK_EN	1 /* 1: enable, 0: disable */
-
-#define REG_NULL			0xFFFF
+#define MAXIM_FORCE_ALL_CLOCK_EN	1 /* 1: enable, 0: disable */
 
 #define OF_CAMERA_PINCTRL_STATE_DEFAULT	"rockchip,camera_default"
 #define OF_CAMERA_PINCTRL_STATE_SLEEP	"rockchip,camera_sleep"
 
 #define MAX96712_NAME			"max96712"
 
+#define REG_NULL			0xFFFF
+
 /* register length: 8bit or 16bit */
-#define MAX96712_REG_LENGTH_08BIT	1
-#define MAX96712_REG_LENGTH_16BIT	2
+#define DEV_REG_LENGTH_08BITS		1
+#define DEV_REG_LENGTH_16BITS		2
 
 /* register value: 8bit or 16bit or 24bit */
-#define MAX96712_REG_VALUE_08BIT	1
-#define MAX96712_REG_VALUE_16BIT	2
-#define MAX96712_REG_VALUE_24BIT	3
+#define DEV_REG_VALUE_08BITS		1
+#define DEV_REG_VALUE_16BITS		2
+#define DEV_REG_VALUE_24BITS		3
 
-#define MAX96712_I2C_ADDR		(0x29)
-#define MAX96715_I2C_ADDR		(0x40)
-#define MAX96717_I2C_ADDR		(0x40)
-#define CAMERA_I2C_ADDR			(0x30)
+/* i2c device default address */
+#define SER_I2C_ADDR			(0x40)
+#define CAM_I2C_ADDR			(0x30)
 
-#define MAX96712_GET_BIT(x, bit)	((x & (1 << bit)) >> bit)
-#define MAX96712_GET_BIT_M_TO_N(x, m, n)	\
-		((unsigned int)(x << (31 - (n))) >> ((31 - (n)) + (m)))
+/* Maxim Serdes I2C Device ID */
+enum {
+	I2C_DEV_DES = 0,
+	I2C_DEV_SER,
+	I2C_DEV_CAM,
+	I2C_DEV_MAX
+};
 
 enum max96712_rx_rate {
 	MAX96712_RX_RATE_3GBPS = 0,
@@ -120,7 +121,7 @@
 #define MAX96712_NUM_SUPPLIES		ARRAY_SIZE(max96712_supply_names)
 
 struct regval {
-	u16 i2c_addr;
+	u16 i2c_id;
 	u16 reg_len;
 	u16 reg;
 	u8 val;
@@ -144,6 +145,7 @@
 
 struct max96712 {
 	struct i2c_client *client;
+	u16 i2c_addr[I2C_DEV_MAX];
 	struct clk *xvclk;
 	struct gpio_desc *power_gpio;
 	struct gpio_desc *reset_gpio;
@@ -206,91 +208,93 @@
 	.reserved = {0},
 };
 
+/* max96717 */
 static const struct regval max96712_mipi_4lane_1920x1440_30fps[] = {
 	// Link A/B/C/D all use GMSL2, and disabled
-	{ 0x29, 2, 0x0006, 0xf0, 0x00, 0x00 }, // Link A/B/C/D: select GMSL2, Disabled
+	{ I2C_DEV_DES, 2, 0x0006, 0xf0, 0x00, 0x00 }, // Link A/B/C/D: select GMSL2, Disabled
 	// Disable MIPI CSI output
-	{ 0x29, 2, 0x040B, 0x00, 0x00, 0x00 }, // CSI_OUT_EN=0, CSI output disabled
+	{ I2C_DEV_DES, 2, 0x040B, 0x00, 0x00, 0x00 }, // CSI_OUT_EN=0, CSI output disabled
 	// Increase CMU voltage
-	{ 0x29, 2, 0x06C2, 0x10, 0x00, 0x0a }, // Increase CMU voltage to for wide temperature range
+	{ I2C_DEV_DES, 2, 0x06C2, 0x10, 0x00, 0x0a }, // Increase CMU voltage to for wide temperature range
 	// VGAHiGain
-	{ 0x29, 2, 0x14D1, 0x03, 0x00, 0x00 }, // VGAHiGain
-	{ 0x29, 2, 0x15D1, 0x03, 0x00, 0x00 }, // VGAHiGain
-	{ 0x29, 2, 0x16D1, 0x03, 0x00, 0x00 }, // VGAHiGain
-	{ 0x29, 2, 0x17D1, 0x03, 0x00, 0x0a }, // VGAHiGain
+	{ I2C_DEV_DES, 2, 0x14D1, 0x03, 0x00, 0x00 }, // VGAHiGain
+	{ I2C_DEV_DES, 2, 0x15D1, 0x03, 0x00, 0x00 }, // VGAHiGain
+	{ I2C_DEV_DES, 2, 0x16D1, 0x03, 0x00, 0x00 }, // VGAHiGain
+	{ I2C_DEV_DES, 2, 0x17D1, 0x03, 0x00, 0x0a }, // VGAHiGain
 	// SSC Configuration
-	{ 0x29, 2, 0x1445, 0x00, 0x00, 0x00 }, // Disable SSC
-	{ 0x29, 2, 0x1545, 0x00, 0x00, 0x00 }, // Disable SSC
-	{ 0x29, 2, 0x1645, 0x00, 0x00, 0x00 }, // Disable SSC
-	{ 0x29, 2, 0x1745, 0x00, 0x00, 0x0a }, // Disable SSC
+	{ I2C_DEV_DES, 2, 0x1445, 0x00, 0x00, 0x00 }, // Disable SSC
+	{ I2C_DEV_DES, 2, 0x1545, 0x00, 0x00, 0x00 }, // Disable SSC
+	{ I2C_DEV_DES, 2, 0x1645, 0x00, 0x00, 0x00 }, // Disable SSC
+	{ I2C_DEV_DES, 2, 0x1745, 0x00, 0x00, 0x0a }, // Disable SSC
 	// GMSL2 Link Video Pipe Selection
-	{ 0x29, 2, 0x00F0, 0x62, 0x00, 0x00 }, // Phy A -> Pipe Z -> Pipe 0; Phy B -> Pipe Z -> Pipe 1
-	{ 0x29, 2, 0x00F1, 0xea, 0x00, 0x00 }, // Phy C -> Pipe Z -> Pipe 2; Phy D -> Pipe Z -> Pipe 3
-	{ 0x29, 2, 0x00F4, 0x0f, 0x00, 0x00 }, // Enable all 4 Pipes
+	{ I2C_DEV_DES, 2, 0x00F0, 0x62, 0x00, 0x00 }, // Phy A -> Pipe Z -> Pipe 0; Phy B -> Pipe Z -> Pipe 1
+	{ I2C_DEV_DES, 2, 0x00F1, 0xea, 0x00, 0x00 }, // Phy C -> Pipe Z -> Pipe 2; Phy D -> Pipe Z -> Pipe 3
+	{ I2C_DEV_DES, 2, 0x00F4, 0x0f, 0x00, 0x00 }, // Enable all 4 Pipes
 	// Send YUV422, FS, and FE from Video Pipe 0 to Controller 1
-	{ 0x29, 2, 0x090B, 0x07, 0x00, 0x00 }, // Enable 0/1/2 SRC/DST Mappings
-	{ 0x29, 2, 0x092D, 0x15, 0x00, 0x00 }, // SRC/DST 0/1/2 -> CSI2 Controller 1;
+	{ I2C_DEV_DES, 2, 0x090B, 0x07, 0x00, 0x00 }, // Enable 0/1/2 SRC/DST Mappings
+	{ I2C_DEV_DES, 2, 0x092D, 0x15, 0x00, 0x00 }, // SRC/DST 0/1/2 -> CSI2 Controller 1;
 	// For the following MSB 2 bits = VC, LSB 6 bits = DT
-	{ 0x29, 2, 0x090D, 0x1e, 0x00, 0x00 }, // SRC0 VC = 0, DT = YUV422 8bit
-	{ 0x29, 2, 0x090E, 0x1e, 0x00, 0x00 }, // DST0 VC = 0, DT = YUV422 8bit
-	{ 0x29, 2, 0x090F, 0x00, 0x00, 0x00 }, // SRC1 VC = 0, DT = Frame Start
-	{ 0x29, 2, 0x0910, 0x00, 0x00, 0x00 }, // DST1 VC = 0, DT = Frame Start
-	{ 0x29, 2, 0x0911, 0x01, 0x00, 0x00 }, // SRC2 VC = 0, DT = Frame End
-	{ 0x29, 2, 0x0912, 0x01, 0x00, 0x00 }, // DST2 VC = 0, DT = Frame End
+	{ I2C_DEV_DES, 2, 0x090D, 0x1e, 0x00, 0x00 }, // SRC0 VC = 0, DT = YUV422 8bit
+	{ I2C_DEV_DES, 2, 0x090E, 0x1e, 0x00, 0x00 }, // DST0 VC = 0, DT = YUV422 8bit
+	{ I2C_DEV_DES, 2, 0x090F, 0x00, 0x00, 0x00 }, // SRC1 VC = 0, DT = Frame Start
+	{ I2C_DEV_DES, 2, 0x0910, 0x00, 0x00, 0x00 }, // DST1 VC = 0, DT = Frame Start
+	{ I2C_DEV_DES, 2, 0x0911, 0x01, 0x00, 0x00 }, // SRC2 VC = 0, DT = Frame End
+	{ I2C_DEV_DES, 2, 0x0912, 0x01, 0x00, 0x00 }, // DST2 VC = 0, DT = Frame End
 	// Send YUV422, FS, and FE from Video Pipe 1 to Controller 1
-	{ 0x29, 2, 0x094B, 0x07, 0x00, 0x00 }, // Enable 0/1/2 SRC/DST Mappings
-	{ 0x29, 2, 0x096D, 0x15, 0x00, 0x00 }, // SRC/DST 0/1/2 -> CSI2 Controller 1;
+	{ I2C_DEV_DES, 2, 0x094B, 0x07, 0x00, 0x00 }, // Enable 0/1/2 SRC/DST Mappings
+	{ I2C_DEV_DES, 2, 0x096D, 0x15, 0x00, 0x00 }, // SRC/DST 0/1/2 -> CSI2 Controller 1;
 	// For the following MSB 2 bits = VC, LSB 6 bits = DT
-	{ 0x29, 2, 0x094D, 0x1e, 0x00, 0x00 }, // SRC0 VC = 0, DT = YUV422 8bit
-	{ 0x29, 2, 0x094E, 0x5e, 0x00, 0x00 }, // DST0 VC = 1, DT = YUV422 8bit
-	{ 0x29, 2, 0x094F, 0x00, 0x00, 0x00 }, // SRC1 VC = 0, DT = Frame Start
-	{ 0x29, 2, 0x0950, 0x40, 0x00, 0x00 }, // DST1 VC = 1, DT = Frame Start
-	{ 0x29, 2, 0x0951, 0x01, 0x00, 0x00 }, // SRC2 VC = 0, DT = Frame End
-	{ 0x29, 2, 0x0952, 0x41, 0x00, 0x00 }, // DST2 VC = 1, DT = Frame End
+	{ I2C_DEV_DES, 2, 0x094D, 0x1e, 0x00, 0x00 }, // SRC0 VC = 0, DT = YUV422 8bit
+	{ I2C_DEV_DES, 2, 0x094E, 0x5e, 0x00, 0x00 }, // DST0 VC = 1, DT = YUV422 8bit
+	{ I2C_DEV_DES, 2, 0x094F, 0x00, 0x00, 0x00 }, // SRC1 VC = 0, DT = Frame Start
+	{ I2C_DEV_DES, 2, 0x0950, 0x40, 0x00, 0x00 }, // DST1 VC = 1, DT = Frame Start
+	{ I2C_DEV_DES, 2, 0x0951, 0x01, 0x00, 0x00 }, // SRC2 VC = 0, DT = Frame End
+	{ I2C_DEV_DES, 2, 0x0952, 0x41, 0x00, 0x00 }, // DST2 VC = 1, DT = Frame End
 	// Send YUV422, FS, and FE from Video Pipe 2 to Controller 1
-	{ 0x29, 2, 0x098B, 0x07, 0x00, 0x00 }, // Enable 0/1/2 SRC/DST Mappings
-	{ 0x29, 2, 0x09AD, 0x15, 0x00, 0x00 }, // SRC/DST 0/1/2 -> CSI2 Controller 1;
+	{ I2C_DEV_DES, 2, 0x098B, 0x07, 0x00, 0x00 }, // Enable 0/1/2 SRC/DST Mappings
+	{ I2C_DEV_DES, 2, 0x09AD, 0x15, 0x00, 0x00 }, // SRC/DST 0/1/2 -> CSI2 Controller 1;
 	// For the following MSB 2 bits = VC, LSB 6 bits = DT
-	{ 0x29, 2, 0x098D, 0x1e, 0x00, 0x00 }, // SRC0 VC = 0, DT = YUV422 8bit
-	{ 0x29, 2, 0x098E, 0x9e, 0x00, 0x00 }, // DST0 VC = 2, DT = YUV422 8bit
-	{ 0x29, 2, 0x098F, 0x00, 0x00, 0x00 }, // SRC1 VC = 0, DT = Frame Start
-	{ 0x29, 2, 0x0990, 0x80, 0x00, 0x00 }, // DST1 VC = 2, DT = Frame Start
-	{ 0x29, 2, 0x0991, 0x01, 0x00, 0x00 }, // SRC2 VC = 0, DT = Frame End
-	{ 0x29, 2, 0x0992, 0x81, 0x00, 0x00 }, // DST2 VC = 2, DT = Frame End
+	{ I2C_DEV_DES, 2, 0x098D, 0x1e, 0x00, 0x00 }, // SRC0 VC = 0, DT = YUV422 8bit
+	{ I2C_DEV_DES, 2, 0x098E, 0x9e, 0x00, 0x00 }, // DST0 VC = 2, DT = YUV422 8bit
+	{ I2C_DEV_DES, 2, 0x098F, 0x00, 0x00, 0x00 }, // SRC1 VC = 0, DT = Frame Start
+	{ I2C_DEV_DES, 2, 0x0990, 0x80, 0x00, 0x00 }, // DST1 VC = 2, DT = Frame Start
+	{ I2C_DEV_DES, 2, 0x0991, 0x01, 0x00, 0x00 }, // SRC2 VC = 0, DT = Frame End
+	{ I2C_DEV_DES, 2, 0x0992, 0x81, 0x00, 0x00 }, // DST2 VC = 2, DT = Frame End
 	// Send YUV422, FS, and FE from Video Pipe 3 to Controller 1
-	{ 0x29, 2, 0x09CB, 0x07, 0x00, 0x00 }, // Enable 0/1/2 SRC/DST Mappings
-	{ 0x29, 2, 0x09ED, 0x15, 0x00, 0x00 }, // SRC/DST 0/1/2 -> CSI2 Controller 1;
+	{ I2C_DEV_DES, 2, 0x09CB, 0x07, 0x00, 0x00 }, // Enable 0/1/2 SRC/DST Mappings
+	{ I2C_DEV_DES, 2, 0x09ED, 0x15, 0x00, 0x00 }, // SRC/DST 0/1/2 -> CSI2 Controller 1;
 	// For the following MSB 2 bits = VC, LSB 6 bits = DT
-	{ 0x29, 2, 0x09CD, 0x1e, 0x00, 0x00 }, // SRC0 VC = 0, DT = YUV422 8bit
-	{ 0x29, 2, 0x09CE, 0xde, 0x00, 0x00 }, // DST0 VC = 3, DT = YUV422 8bit
-	{ 0x29, 2, 0x09CF, 0x00, 0x00, 0x00 }, // SRC1 VC = 0, DT = Frame Start
-	{ 0x29, 2, 0x09D0, 0xc0, 0x00, 0x00 }, // DST1 VC = 3, DT = Frame Start
-	{ 0x29, 2, 0x09D1, 0x01, 0x00, 0x00 }, // SRC2 VC = 0, DT = Frame End
-	{ 0x29, 2, 0x09D2, 0xc1, 0x00, 0x00 }, // DST2 VC = 3, DT = Frame End
+	{ I2C_DEV_DES, 2, 0x09CD, 0x1e, 0x00, 0x00 }, // SRC0 VC = 0, DT = YUV422 8bit
+	{ I2C_DEV_DES, 2, 0x09CE, 0xde, 0x00, 0x00 }, // DST0 VC = 3, DT = YUV422 8bit
+	{ I2C_DEV_DES, 2, 0x09CF, 0x00, 0x00, 0x00 }, // SRC1 VC = 0, DT = Frame Start
+	{ I2C_DEV_DES, 2, 0x09D0, 0xc0, 0x00, 0x00 }, // DST1 VC = 3, DT = Frame Start
+	{ I2C_DEV_DES, 2, 0x09D1, 0x01, 0x00, 0x00 }, // SRC2 VC = 0, DT = Frame End
+	{ I2C_DEV_DES, 2, 0x09D2, 0xc1, 0x00, 0x00 }, // DST2 VC = 3, DT = Frame End
 	// MIPI PHY Setting
-	{ 0x29, 2, 0x08A0, 0x24, 0x00, 0x00 }, // DPHY0 enabled as clock, MIPI PHY Mode: 2x4 mode
+	{ I2C_DEV_DES, 2, 0x08A0, 0x24, 0x00, 0x00 }, // DPHY0 enabled as clock, MIPI PHY Mode: 2x4 mode
 	// Set Lane Mapping for 4-lane port A
-	{ 0x29, 2, 0x08A3, 0xe4, 0x00, 0x00 }, // PHY1 D1->D3, D0->D2; PHY0 D1->D1, D0->D0
+	{ I2C_DEV_DES, 2, 0x08A3, 0xe4, 0x00, 0x00 }, // PHY1 D1->D3, D0->D2; PHY0 D1->D1, D0->D0
 	// Set 4 lane D-PHY, 2bit VC
-	{ 0x29, 2, 0x090A, 0xc0, 0x00, 0x00 }, // MIPI PHY 0: 4 lanes, DPHY, 2bit VC
-	{ 0x29, 2, 0x094A, 0xc0, 0x00, 0x00 }, // MIPI PHY 1: 4 lanes, DPHY, 2bit VC
+	{ I2C_DEV_DES, 2, 0x090A, 0xc0, 0x00, 0x00 }, // MIPI PHY 0: 4 lanes, DPHY, 2bit VC
+	{ I2C_DEV_DES, 2, 0x094A, 0xc0, 0x00, 0x00 }, // MIPI PHY 1: 4 lanes, DPHY, 2bit VC
 	// Turn on MIPI PHYs
-	{ 0x29, 2, 0x08A2, 0x34, 0x00, 0x00 }, // Enable MIPI PHY 0/1, t_lpx = 106.7ns
+	{ I2C_DEV_DES, 2, 0x08A2, 0x34, 0x00, 0x00 }, // Enable MIPI PHY 0/1, t_lpx = 106.7ns
 	// YUV422 8bit software override for all pipes since connected GMSL1 is under parallel mode
-	{ 0x29, 2, 0x040B, 0x80, 0x00, 0x00 }, // pipe 0 bpp=0x10: Datatypes = 0x22, 0x1E, 0x2E
-	{ 0x29, 2, 0x040E, 0x5e, 0x00, 0x00 }, // pipe 0 DT=0x1E: YUV422 8-bit
-	{ 0x29, 2, 0x040F, 0x7e, 0x00, 0x00 }, // pipe 1 DT=0x1E: YUV422 8-bit
-	{ 0x29, 2, 0x0410, 0x7a, 0x00, 0x00 }, // pipe 2 DT=0x1E, pipe 3 DT=0x1E: YUV422 8-bit
-	{ 0x29, 2, 0x0411, 0x90, 0x00, 0x00 }, // pipe 1 bpp=0x10: Datatypes = 0x22, 0x1E, 0x2E
-	{ 0x29, 2, 0x0412, 0x40, 0x00, 0x00 }, // pipe 2 bpp=0x10, pipe 3 bpp=0x10: Datatypes = 0x22, 0x1E, 0x2E
+	{ I2C_DEV_DES, 2, 0x040B, 0x80, 0x00, 0x00 }, // pipe 0 bpp=0x10: Datatypes = 0x22, 0x1E, 0x2E
+	{ I2C_DEV_DES, 2, 0x040E, 0x5e, 0x00, 0x00 }, // pipe 0 DT=0x1E: YUV422 8-bit
+	{ I2C_DEV_DES, 2, 0x040F, 0x7e, 0x00, 0x00 }, // pipe 1 DT=0x1E: YUV422 8-bit
+	{ I2C_DEV_DES, 2, 0x0410, 0x7a, 0x00, 0x00 }, // pipe 2 DT=0x1E, pipe 3 DT=0x1E: YUV422 8-bit
+	{ I2C_DEV_DES, 2, 0x0411, 0x90, 0x00, 0x00 }, // pipe 1 bpp=0x10: Datatypes = 0x22, 0x1E, 0x2E
+	{ I2C_DEV_DES, 2, 0x0412, 0x40, 0x00, 0x00 }, // pipe 2 bpp=0x10, pipe 3 bpp=0x10: Datatypes = 0x22, 0x1E, 0x2E
 	// Enable all links and pipes
-	{ 0x29, 2, 0x0003, 0xaa, 0x00, 0x00 }, // Enable Remote Control Channel Link A/B/C/D for Port 0
-	{ 0x29, 2, 0x0006, 0xff, 0x00, 0x64 }, // Enable all links and pipes
+	{ I2C_DEV_DES, 2, 0x0003, 0xaa, 0x00, 0x00 }, // Enable Remote Control Channel Link A/B/C/D for Port 0
+	{ I2C_DEV_DES, 2, 0x0006, 0xff, 0x00, 0x64 }, // Enable all links and pipes
 	// Serializer Setting
-	{ 0x40, 2, 0x0302, 0x10, 0x00, 0x00 }, // improve CMU voltage performance to improve link robustness
-	{ 0x40, 2, 0x1417, 0x00, 0x00, 0x00 }, // Errata
-	{ 0x40, 2, 0x1432, 0x7f, 0x00, 0x00 },
-	{ 0x29, 2, REG_NULL, 0x00, 0x00, 0x00 },
+	{ I2C_DEV_SER, 2, 0x0302, 0x10, 0x00, 0x00 }, // improve CMU voltage performance to improve link robustness
+	{ I2C_DEV_SER, 2, 0x1417, 0x00, 0x00, 0x00 }, // Errata
+	{ I2C_DEV_SER, 2, 0x1432, 0x7f, 0x00, 0x00 },
+	// End register setting
+	{ I2C_DEV_DES, 2, REG_NULL, 0x00, 0x00, 0x00 },
 };
 
 static const struct max96712_mode supported_modes_4lane[] = {
@@ -342,15 +346,17 @@
 	MAX96712_LINK_FREQ_MHZ(1250),
 };
 
-static int max96712_write_reg(struct i2c_client *client,
-			u16 client_addr, u16 reg, u16 reg_len, u16 val_len, u32 val)
+static int max96712_write_reg(struct max96712 *max96712, u8 i2c_id,
+			u16 reg, u16 reg_len, u16 val_len, u32 val)
 {
+	struct i2c_client *client = max96712->client;
+	u16 client_addr = max96712->i2c_addr[i2c_id];
 	u32 buf_i, val_i;
 	u8 buf[6];
 	u8 *val_p;
 	__be32 val_be;
 
-	dev_info(&client->dev, "addr(0x%02x) write reg(0x%04x, %d, 0x%02x)\n", \
+	dev_info(&client->dev, "addr(0x%02x) write reg(0x%04x, %d, 0x%02x)\n",
 		client_addr, reg, reg_len, val);
 
 	if (val_len > 4)
@@ -386,9 +392,11 @@
 	return 0;
 }
 
-static int max96712_read_reg(struct i2c_client *client,
-			u16 client_addr, u16 reg, u16 reg_len, u16 val_len, u8 *val)
+static int max96712_read_reg(struct max96712 *max96712, u8 i2c_id,
+			u16 reg, u16 reg_len, u16 val_len, u8 *val)
 {
+	struct i2c_client *client = max96712->client;
+	u16 client_addr = max96712->i2c_addr[i2c_id];
 	struct i2c_msg msgs[2];
 	u8 *data_be_p;
 	__be32 data_be = 0;
@@ -426,34 +434,34 @@
 	*val = be32_to_cpu(data_be);
 
 #if 0
-	dev_info(&client->dev, "addr(0x%02x) read reg(0x%04x, %d, 0x%02x)\n", \
+	dev_info(&client->dev, "addr(0x%02x) read reg(0x%04x, %d, 0x%02x)\n",
 		client_addr, reg, reg_len, *val);
 #endif
 
 	return 0;
 }
 
-static int max96712_update_reg_bits(struct i2c_client *client,
-			u16 client_addr, u16 reg, u16 reg_len, u8 mask, u8 val)
+static int max96712_update_reg_bits(struct max96712 *max96712, u8 i2c_id,
+				u16 reg, u16 reg_len, u8 mask, u8 val)
 {
 	u8 value;
-	u32 val_len = MAX96712_REG_VALUE_08BIT;
+	u32 val_len = DEV_REG_VALUE_08BITS;
 	int ret;
 
-	ret = max96712_read_reg(client, client_addr, reg, reg_len, val_len, &value);
+	ret = max96712_read_reg(max96712, i2c_id, reg, reg_len, val_len, &value);
 	if (ret)
 		return ret;
 
 	value &= ~mask;
 	value |= (val & mask);
-	ret = max96712_write_reg(client, client_addr, reg, reg_len, val_len, value);
+	ret = max96712_write_reg(max96712, i2c_id, reg, reg_len, val_len, value);
 	if (ret)
 		return ret;
 
 	return 0;
 }
 
-static int max96712_write_array(struct i2c_client *client,
+static int max96712_write_array(struct max96712 *max96712,
 				const struct regval *regs)
 {
 	u32 i;
@@ -461,13 +469,13 @@
 
 	for (i = 0; ret == 0 && regs[i].reg != REG_NULL; i++) {
 		if (regs[i].mask != 0)
-			ret = max96712_update_reg_bits(client, regs[i].i2c_addr,
+			ret = max96712_update_reg_bits(max96712, regs[i].i2c_id,
 					regs[i].reg, regs[i].reg_len,
 					regs[i].mask, regs[i].val);
 		else
-			ret = max96712_write_reg(client, regs[i].i2c_addr,
+			ret = max96712_write_reg(max96712, regs[i].i2c_id,
 						regs[i].reg, regs[i].reg_len,
-						MAX96712_REG_VALUE_08BIT, regs[i].val);
+						DEV_REG_VALUE_08BITS, regs[i].val);
 
 		if (regs[i].delay != 0)
 			msleep(regs[i].delay);
@@ -478,14 +486,13 @@
 
 static int max96712_check_local_chipid(struct max96712 *max96712)
 {
-	struct i2c_client *client = max96712->client;
 	struct device *dev = &max96712->client->dev;
 	int ret;
 	u8 id = 0;
 
-	ret = max96712_read_reg(client, MAX96712_I2C_ADDR,
-				MAX96712_REG_CHIP_ID, MAX96712_REG_LENGTH_16BIT,
-				MAX96712_REG_VALUE_08BIT, &id);
+	ret = max96712_read_reg(max96712, I2C_DEV_DES,
+			MAX96712_REG_CHIP_ID, DEV_REG_LENGTH_16BITS,
+			DEV_REG_VALUE_08BITS, &id);
 	if ((ret != 0) || (id != MAX96712_CHIP_ID)) {
 		dev_err(dev, "Unexpected MAX96712 chip id(%02x), ret(%d)\n", id, ret);
 		return -ENODEV;
@@ -507,9 +514,9 @@
 	id = 0;
 #if 0
 	// max96717
-	ret = max96712_read_reg(max96712->client, MAX96717_I2C_ADDR,
-				MAX96717_REG_CHIP_ID, MAX96712_REG_LENGTH_16BIT,
-				MAX96712_REG_VALUE_08BIT, &id);
+	ret = max96712_read_reg(max96712, I2C_DEV_SER,
+			MAX96717_REG_CHIP_ID, DEV_REG_LENGTH_16BITS,
+			DEV_REG_VALUE_08BITS, &id);
 	if ((ret != 0) || (id != MAX96717_CHIP_ID)) {
 		dev_err(dev, "Unexpected MAX96717 chip id(%02x), ret(%d)\n", id, ret);
 		return -ENODEV;
@@ -519,9 +526,9 @@
 
 #if 0
 	// max96715
-	ret = max96712_read_reg(max96712->client, MAX96715_I2C_ADDR,
-				MAX96715_REG_CHIP_ID, MAX96712_REG_LENGTH_08BIT,
-				MAX96712_REG_VALUE_08BIT, &id);
+	ret = max96712_read_reg(max96712, I2C_DEV_SER,
+			MAX96715_REG_CHIP_ID, DEV_REG_LENGTH_08BITS,
+			DEV_REG_VALUE_08BITS, &id);
 	if ((ret != 0) || (id != MAX96715_CHIP_ID)) {
 		dev_err(dev, "Unexpected MAX96715 chip id(%02x), ret(%d)\n", id, ret);
 		return -ENODEV;
@@ -534,96 +541,95 @@
 
 static u8 max96712_get_link_lock_state(struct max96712 *max96712, u8 link_mask)
 {
-	struct i2c_client *client = max96712->client;
 	struct device *dev = &max96712->client->dev;
 	u8 lock = 0, lock_state = 0;
 	u8 link_type = 0;
 
-	link_type = max96712->link_mask & MAX96712_GMSL_TYPE_MASK;
+	link_type = max96712->link_mask & MAXIM_GMSL_TYPE_MASK;
 
-	if (link_mask & MAX96712_LOCK_STATE_LINK_A) {
-		if (link_type & MAX96712_GMSL_TYPE_LINK_A) {
+	if (link_mask & MAXIM_GMSL_LOCK_LINK_A) {
+		if (link_type & MAXIM_GMSL_TYPE_LINK_A) {
 			// GMSL2 LinkA
-			max96712_read_reg(client, MAX96712_I2C_ADDR,
-					0x001a, MAX96712_REG_LENGTH_16BIT,
-					MAX96712_REG_VALUE_08BIT, &lock);
+			max96712_read_reg(max96712, I2C_DEV_DES,
+				0x001a, DEV_REG_LENGTH_16BITS,
+				DEV_REG_VALUE_08BITS, &lock);
 			if (lock & BIT(3)) {
-				lock_state |= MAX96712_LOCK_STATE_LINK_A;
+				lock_state |= MAXIM_GMSL_LOCK_LINK_A;
 				dev_info(dev, "GMSL2 LinkA locked\n");
 			}
 		} else {
 			// GMSL1 LinkA
-			max96712_read_reg(client, MAX96712_I2C_ADDR,
-					0x0bcb, MAX96712_REG_LENGTH_16BIT,
-					MAX96712_REG_VALUE_08BIT, &lock);
+			max96712_read_reg(max96712, I2C_DEV_DES,
+				0x0bcb, DEV_REG_LENGTH_16BITS,
+				DEV_REG_VALUE_08BITS, &lock);
 			if (lock & BIT(0)) {
-				lock_state |= MAX96712_LOCK_STATE_LINK_A;
+				lock_state |= MAXIM_GMSL_LOCK_LINK_A;
 				dev_info(dev, "GMSL1 LinkA locked\n");
 			}
 		}
 	}
 
-	if (link_mask & MAX96712_LOCK_STATE_LINK_B) {
-		if (link_type & MAX96712_GMSL_TYPE_LINK_B) {
+	if (link_mask & MAXIM_GMSL_LOCK_LINK_B) {
+		if (link_type & MAXIM_GMSL_TYPE_LINK_B) {
 			// GMSL2 LinkB
-			max96712_read_reg(client, MAX96712_I2C_ADDR,
-					0x000a, MAX96712_REG_LENGTH_16BIT,
-					MAX96712_REG_VALUE_08BIT, &lock);
+			max96712_read_reg(max96712, I2C_DEV_DES,
+				0x000a, DEV_REG_LENGTH_16BITS,
+				DEV_REG_VALUE_08BITS, &lock);
 			if (lock & BIT(3)) {
-				lock_state |= MAX96712_LOCK_STATE_LINK_B;
+				lock_state |= MAXIM_GMSL_LOCK_LINK_B;
 				dev_info(dev, "GMSL2 LinkB locked\n");
 			}
 		} else {
 			// GMSL1 LinkB
-			max96712_read_reg(client, MAX96712_I2C_ADDR,
-					0x0ccb, MAX96712_REG_LENGTH_16BIT,
-					MAX96712_REG_VALUE_08BIT, &lock);
+			max96712_read_reg(max96712, I2C_DEV_DES,
+				0x0ccb, DEV_REG_LENGTH_16BITS,
+				DEV_REG_VALUE_08BITS, &lock);
 			if (lock & BIT(0)) {
-				lock_state |= MAX96712_LOCK_STATE_LINK_B;
+				lock_state |= MAXIM_GMSL_LOCK_LINK_B;
 				dev_info(dev, "GMSL1 LinkB locked\n");
 			}
 		}
 	}
 
-	if (link_mask & MAX96712_LOCK_STATE_LINK_C) {
-		if (link_type & MAX96712_GMSL_TYPE_LINK_C) {
+	if (link_mask & MAXIM_GMSL_LOCK_LINK_C) {
+		if (link_type & MAXIM_GMSL_TYPE_LINK_C) {
 			// GMSL2 LinkC
-			max96712_read_reg(client, MAX96712_I2C_ADDR,
-					0x000b, MAX96712_REG_LENGTH_16BIT,
-					MAX96712_REG_VALUE_08BIT, &lock);
+			max96712_read_reg(max96712, I2C_DEV_DES,
+				0x000b, DEV_REG_LENGTH_16BITS,
+				DEV_REG_VALUE_08BITS, &lock);
 			if (lock & BIT(3)) {
-				lock_state |= MAX96712_LOCK_STATE_LINK_C;
+				lock_state |= MAXIM_GMSL_LOCK_LINK_C;
 				dev_info(dev, "GMSL2 LinkC locked\n");
 			}
 		} else {
 			// GMSL1 LinkC
-			max96712_read_reg(client, MAX96712_I2C_ADDR,
-					0x0dcb, MAX96712_REG_LENGTH_16BIT,
-					MAX96712_REG_VALUE_08BIT, &lock);
+			max96712_read_reg(max96712, I2C_DEV_DES,
+				0x0dcb, DEV_REG_LENGTH_16BITS,
+				DEV_REG_VALUE_08BITS, &lock);
 			if (lock & BIT(0)) {
-				lock_state |= MAX96712_LOCK_STATE_LINK_C;
+				lock_state |= MAXIM_GMSL_LOCK_LINK_C;
 				dev_info(dev, "GMSL1 LinkC locked\n");
 			}
 		}
 	}
 
-	if (link_mask & MAX96712_LOCK_STATE_LINK_D) {
-		if (link_type & MAX96712_GMSL_TYPE_LINK_D) {
+	if (link_mask & MAXIM_GMSL_LOCK_LINK_D) {
+		if (link_type & MAXIM_GMSL_TYPE_LINK_D) {
 			// GMSL2 LinkD
-			max96712_read_reg(client, MAX96712_I2C_ADDR,
-					0x000c, MAX96712_REG_LENGTH_16BIT,
-					MAX96712_REG_VALUE_08BIT, &lock);
+			max96712_read_reg(max96712, I2C_DEV_DES,
+				0x000c, DEV_REG_LENGTH_16BITS,
+				DEV_REG_VALUE_08BITS, &lock);
 			if (lock & BIT(3)) {
-				lock_state |= MAX96712_LOCK_STATE_LINK_D;
+				lock_state |= MAXIM_GMSL_LOCK_LINK_D;
 				dev_info(dev, "GMSL2 LinkD locked\n");
 			}
 		} else {
 			// GMSL1 LinkD
-			max96712_read_reg(client, MAX96712_I2C_ADDR,
-					0x0ecb, MAX96712_REG_LENGTH_16BIT,
-					MAX96712_REG_VALUE_08BIT, &lock);
+			max96712_read_reg(max96712, I2C_DEV_DES,
+				0x0ecb, DEV_REG_LENGTH_16BITS,
+				DEV_REG_VALUE_08BITS, &lock);
 			if (lock & BIT(0)) {
-				lock_state |= MAX96712_LOCK_STATE_LINK_D;
+				lock_state |= MAXIM_GMSL_LOCK_LINK_D;
 				dev_info(dev, "GMSL1 LinkD locked\n");
 			}
 		}
@@ -634,7 +640,6 @@
 
 static int max96712_check_link_lock_state(struct max96712 *max96712)
 {
-	struct i2c_client *client = max96712->client;
 	struct device *dev = &max96712->client->dev;
 	u8 lock_state = 0, link_mask = 0, link_type = 0;
 	int ret, i, time_ms;
@@ -647,99 +652,99 @@
 	 *	CTRL0: Enable REG_ENABLE
 	 *	CTRL2: Enable REG_MNL
 	 */
-	max96712_update_reg_bits(client, MAX96712_I2C_ADDR,
-				0x0017, MAX96712_REG_LENGTH_16BIT, BIT(2), BIT(2));
-	max96712_update_reg_bits(client, MAX96712_I2C_ADDR,
-				0x0019, MAX96712_REG_LENGTH_16BIT, BIT(4), BIT(4));
+	max96712_update_reg_bits(max96712, I2C_DEV_DES,
+			0x0017, DEV_REG_LENGTH_16BITS, BIT(2), BIT(2));
+	max96712_update_reg_bits(max96712, I2C_DEV_DES,
+			0x0019, DEV_REG_LENGTH_16BITS, BIT(4), BIT(4));
 
 	// CSI output disabled
-	max96712_write_reg(client, MAX96712_I2C_ADDR,
-				0x040B, MAX96712_REG_LENGTH_16BIT,
-				MAX96712_REG_VALUE_08BIT, 0x00);
+	max96712_write_reg(max96712, I2C_DEV_DES,
+			0x040B, DEV_REG_LENGTH_16BITS,
+			DEV_REG_VALUE_08BITS, 0x00);
 
 	// All links select mode by link_type and disable at beginning.
-	link_type = max96712->link_mask & MAX96712_GMSL_TYPE_MASK;
-	max96712_write_reg(client, MAX96712_I2C_ADDR,
-				0x0006, MAX96712_REG_LENGTH_16BIT,
-				MAX96712_REG_VALUE_08BIT, link_type);
+	link_type = max96712->link_mask & MAXIM_GMSL_TYPE_MASK;
+	max96712_write_reg(max96712, I2C_DEV_DES,
+			0x0006, DEV_REG_LENGTH_16BITS,
+			DEV_REG_VALUE_08BITS, link_type);
 
 	// Link Rate
 	if (max96712->rx_rate == MAX96712_RX_RATE_3GBPS) {
 		// Link A ~ Link D Transmitter Rate: 187.5Mbps, Receiver Rate: 3Gbps
-		max96712_write_reg(client, MAX96712_I2C_ADDR,
-					0x0010, MAX96712_REG_LENGTH_16BIT,
-					MAX96712_REG_VALUE_08BIT, 0x11);
-		max96712_write_reg(client, MAX96712_I2C_ADDR,
-					0x0011, MAX96712_REG_LENGTH_16BIT,
-					MAX96712_REG_VALUE_08BIT, 0x11);
+		max96712_write_reg(max96712, I2C_DEV_DES,
+				0x0010, DEV_REG_LENGTH_16BITS,
+				DEV_REG_VALUE_08BITS, 0x11);
+		max96712_write_reg(max96712, I2C_DEV_DES,
+				0x0011, DEV_REG_LENGTH_16BITS,
+				DEV_REG_VALUE_08BITS, 0x11);
 	} else {
 		// Link A ~ Link D Transmitter Rate: 187.5Mbps, Receiver Rate: 6Gbps
-		max96712_write_reg(client, MAX96712_I2C_ADDR,
-					0x0010, MAX96712_REG_LENGTH_16BIT,
-					MAX96712_REG_VALUE_08BIT, 0x22);
-		max96712_write_reg(client, MAX96712_I2C_ADDR,
-					0x0011, MAX96712_REG_LENGTH_16BIT,
-					MAX96712_REG_VALUE_08BIT, 0x22);
+		max96712_write_reg(max96712, I2C_DEV_DES,
+				0x0010, DEV_REG_LENGTH_16BITS,
+				DEV_REG_VALUE_08BITS, 0x22);
+		max96712_write_reg(max96712, I2C_DEV_DES,
+				0x0011, DEV_REG_LENGTH_16BITS,
+				DEV_REG_VALUE_08BITS, 0x22);
 	}
 
 	// GMSL1: Enable HIM on deserializer on Link A/B/C/D
-	if ((link_type & MAX96712_GMSL_TYPE_LINK_A) == 0) {
-		max96712_write_reg(client, MAX96712_I2C_ADDR,
-					0x0B06, MAX96712_REG_LENGTH_16BIT,
-					MAX96712_REG_VALUE_08BIT, 0xEF);
+	if ((link_type & MAXIM_GMSL_TYPE_LINK_A) == 0) {
+		max96712_write_reg(max96712, I2C_DEV_DES,
+				0x0B06, DEV_REG_LENGTH_16BITS,
+				DEV_REG_VALUE_08BITS, 0xEF);
 	}
-	if ((link_type & MAX96712_GMSL_TYPE_LINK_B) == 0) {
-		max96712_write_reg(client, MAX96712_I2C_ADDR,
-					0x0C06, MAX96712_REG_LENGTH_16BIT,
-					MAX96712_REG_VALUE_08BIT, 0xEF);
+	if ((link_type & MAXIM_GMSL_TYPE_LINK_B) == 0) {
+		max96712_write_reg(max96712, I2C_DEV_DES,
+				0x0C06, DEV_REG_LENGTH_16BITS,
+				DEV_REG_VALUE_08BITS, 0xEF);
 	}
-	if ((link_type & MAX96712_GMSL_TYPE_LINK_C) == 0) {
-		max96712_write_reg(client, MAX96712_I2C_ADDR,
-					0x0D06, MAX96712_REG_LENGTH_16BIT,
-					MAX96712_REG_VALUE_08BIT, 0xEF);
+	if ((link_type & MAXIM_GMSL_TYPE_LINK_C) == 0) {
+		max96712_write_reg(max96712, I2C_DEV_DES,
+				0x0D06, DEV_REG_LENGTH_16BITS,
+				DEV_REG_VALUE_08BITS, 0xEF);
 	}
-	if ((link_type & MAX96712_GMSL_TYPE_LINK_D) == 0) {
-		max96712_write_reg(client, MAX96712_I2C_ADDR,
-					0x0E06, MAX96712_REG_LENGTH_16BIT,
-					MAX96712_REG_VALUE_08BIT, 0xEF);
+	if ((link_type & MAXIM_GMSL_TYPE_LINK_D) == 0) {
+		max96712_write_reg(max96712, I2C_DEV_DES,
+				0x0E06, DEV_REG_LENGTH_16BITS,
+				DEV_REG_VALUE_08BITS, 0xEF);
 	}
 
 	// Link A ~ Link D One-Shot Reset depend on link_mask
-	link_mask = max96712->link_mask & MAX96712_LOCK_STATE_MASK;
-	max96712_write_reg(client, MAX96712_I2C_ADDR,
-				0x0018, MAX96712_REG_LENGTH_16BIT,
-				MAX96712_REG_VALUE_08BIT, link_mask);
+	link_mask = max96712->link_mask & MAXIM_GMSL_LOCK_MASK;
+	max96712_write_reg(max96712, I2C_DEV_DES,
+			0x0018, DEV_REG_LENGTH_16BITS,
+			DEV_REG_VALUE_08BITS, link_mask);
 
 	// Link A ~ Link D enable depend on link_type and link_mask
-	max96712_write_reg(client, MAX96712_I2C_ADDR,
-			0x0006, MAX96712_REG_LENGTH_16BIT,
-			MAX96712_REG_VALUE_08BIT, link_type | link_mask);
+	max96712_write_reg(max96712, I2C_DEV_DES,
+			0x0006, DEV_REG_LENGTH_16BITS,
+			DEV_REG_VALUE_08BITS, link_type | link_mask);
 
 	time_ms = 50;
 	msleep(time_ms);
 
 	for (i = 0; i < 20; i++) {
-		if ((lock_state & MAX96712_LOCK_STATE_LINK_A) == 0)
-			if (max96712_get_link_lock_state(max96712, MAX96712_LOCK_STATE_LINK_A)) {
-				lock_state |= MAX96712_LOCK_STATE_LINK_A;
+		if ((lock_state & MAXIM_GMSL_LOCK_LINK_A) == 0)
+			if (max96712_get_link_lock_state(max96712, MAXIM_GMSL_LOCK_LINK_A)) {
+				lock_state |= MAXIM_GMSL_LOCK_LINK_A;
 				dev_info(dev, "LinkA locked time: %d ms\n", time_ms);
 			}
 
-		if ((lock_state & MAX96712_LOCK_STATE_LINK_B) == 0)
-			if (max96712_get_link_lock_state(max96712, MAX96712_LOCK_STATE_LINK_B)) {
-				lock_state |= MAX96712_LOCK_STATE_LINK_B;
+		if ((lock_state & MAXIM_GMSL_LOCK_LINK_B) == 0)
+			if (max96712_get_link_lock_state(max96712, MAXIM_GMSL_LOCK_LINK_B)) {
+				lock_state |= MAXIM_GMSL_LOCK_LINK_B;
 				dev_info(dev, "LinkB locked time: %d ms\n", time_ms);
 			}
 
-		if ((lock_state & MAX96712_LOCK_STATE_LINK_C) == 0)
-			if (max96712_get_link_lock_state(max96712, MAX96712_LOCK_STATE_LINK_C)) {
-				lock_state |= MAX96712_LOCK_STATE_LINK_C;
+		if ((lock_state & MAXIM_GMSL_LOCK_LINK_C) == 0)
+			if (max96712_get_link_lock_state(max96712, MAXIM_GMSL_LOCK_LINK_C)) {
+				lock_state |= MAXIM_GMSL_LOCK_LINK_C;
 				dev_info(dev, "LinkC locked time: %d ms\n", time_ms);
 			}
 
-		if ((lock_state & MAX96712_LOCK_STATE_LINK_D) == 0)
-			if (max96712_get_link_lock_state(max96712, MAX96712_LOCK_STATE_LINK_D)) {
-				lock_state |= MAX96712_LOCK_STATE_LINK_D;
+		if ((lock_state & MAXIM_GMSL_LOCK_LINK_D) == 0)
+			if (max96712_get_link_lock_state(max96712, MAXIM_GMSL_LOCK_LINK_D)) {
+				lock_state |= MAXIM_GMSL_LOCK_LINK_D;
 				dev_info(dev, "LinkD locked time: %d ms\n", time_ms);
 			}
 
@@ -770,7 +775,7 @@
 	struct device *dev = &max96712->client->dev;
 	u8 lock_state = 0, link_mask = 0;
 
-	link_mask = max96712->link_mask & MAX96712_LOCK_STATE_MASK;
+	link_mask = max96712->link_mask & MAXIM_GMSL_LOCK_MASK;
 	if (max96712->streaming) {
 		lock_state = max96712_get_link_lock_state(max96712, link_mask);
 		if (lock_state == link_mask) {
@@ -783,21 +788,21 @@
 	return IRQ_HANDLED;
 }
 
-static int __maybe_unused max96712_dphy_dpll_predef_set(struct i2c_client *client,
-				u32 link_freq_mhz)
+static int max96712_dphy_dpll_predef_set(struct max96712 *max96712, u32 link_freq_mhz)
 {
+	struct device *dev = &max96712->client->dev;
 	int ret = 0;
 	u8 dpll_val = 0, dpll_lock = 0;
 	u8 mipi_tx_phy_enable = 0;
 
-	ret = max96712_read_reg(client, MAX96712_I2C_ADDR,
-			0x08A2, MAX96712_REG_LENGTH_16BIT,
-			MAX96712_REG_VALUE_08BIT, &mipi_tx_phy_enable);
+	ret = max96712_read_reg(max96712, I2C_DEV_DES,
+			0x08A2, DEV_REG_LENGTH_16BITS,
+			DEV_REG_VALUE_08BITS, &mipi_tx_phy_enable);
 	if (ret)
 		return ret;
 	mipi_tx_phy_enable = (mipi_tx_phy_enable & 0xF0) >> 4;
 
-	dev_info(&client->dev, "DPLL predef set: mipi_tx_phy_enable = 0x%02x, link_freq_mhz = %d\n",
+	dev_info(dev, "DPLL predef set: mipi_tx_phy_enable = 0x%02x, link_freq_mhz = %d\n",
 			mipi_tx_phy_enable, link_freq_mhz);
 
 	// dphy max data rate is 2500MHz
@@ -811,116 +816,117 @@
 	// MIPI PHY0
 	if (mipi_tx_phy_enable & BIT(0)) {
 		// Hold DPLL in reset (config_soft_rst_n = 0) before changing the rate
-		ret |= max96712_write_reg(client, MAX96712_I2C_ADDR,
-			0x1C00, MAX96712_REG_LENGTH_16BIT,
-			MAX96712_REG_VALUE_08BIT,
-			0xf4);
+		ret |= max96712_write_reg(max96712, I2C_DEV_DES,
+				0x1C00, DEV_REG_LENGTH_16BITS,
+				DEV_REG_VALUE_08BITS, 0xf4);
 		// Set data rate and enable software override
-		ret |= max96712_update_reg_bits(client, MAX96712_I2C_ADDR,
-			0x0415, MAX96712_REG_LENGTH_16BIT, 0x3F, dpll_val);
+		ret |= max96712_update_reg_bits(max96712, I2C_DEV_DES,
+				0x0415, DEV_REG_LENGTH_16BITS, 0x3F, dpll_val);
 		// Release reset to DPLL (config_soft_rst_n = 1)
-		ret |= max96712_write_reg(client, MAX96712_I2C_ADDR,
-			0x1C00, MAX96712_REG_LENGTH_16BIT,
-			MAX96712_REG_VALUE_08BIT, 0xf5);
+		ret |= max96712_write_reg(max96712, I2C_DEV_DES,
+				0x1C00, DEV_REG_LENGTH_16BITS,
+				DEV_REG_VALUE_08BITS, 0xf5);
 	}
 
 	// MIPI PHY1
 	if (mipi_tx_phy_enable & BIT(1)) {
 		// Hold DPLL in reset (config_soft_rst_n = 0) before changing the rate
-		ret |= max96712_write_reg(client, MAX96712_I2C_ADDR,
-			0x1D00, MAX96712_REG_LENGTH_16BIT,
-			MAX96712_REG_VALUE_08BIT, 0xf4);
+		ret |= max96712_write_reg(max96712, I2C_DEV_DES,
+				0x1D00, DEV_REG_LENGTH_16BITS,
+				DEV_REG_VALUE_08BITS, 0xf4);
 		// Set data rate and enable software override
-		ret |= max96712_update_reg_bits(client, MAX96712_I2C_ADDR,
-			0x0418, MAX96712_REG_LENGTH_16BIT, 0x3F, dpll_val);
+		ret |= max96712_update_reg_bits(max96712, I2C_DEV_DES,
+				0x0418, DEV_REG_LENGTH_16BITS, 0x3F, dpll_val);
 		// Release reset to DPLL (config_soft_rst_n = 1)
-		ret |= max96712_write_reg(client, MAX96712_I2C_ADDR,
-			0x1D00, MAX96712_REG_LENGTH_16BIT,
-			MAX96712_REG_VALUE_08BIT, 0xf5);
+		ret |= max96712_write_reg(max96712, I2C_DEV_DES,
+				0x1D00, DEV_REG_LENGTH_16BITS,
+				DEV_REG_VALUE_08BITS, 0xf5);
 	}
 
 	// MIPI PHY2
 	if (mipi_tx_phy_enable & BIT(2)) {
 		// Hold DPLL in reset (config_soft_rst_n = 0) before changing the rate
-		ret |= max96712_write_reg(client, MAX96712_I2C_ADDR,
-			0x1E00, MAX96712_REG_LENGTH_16BIT,
-			MAX96712_REG_VALUE_08BIT, 0xf4);
+		ret |= max96712_write_reg(max96712, I2C_DEV_DES,
+				0x1E00, DEV_REG_LENGTH_16BITS,
+				DEV_REG_VALUE_08BITS, 0xf4);
 		// Set data rate and enable software override
-		ret |= max96712_update_reg_bits(client, MAX96712_I2C_ADDR,
-			0x041B, MAX96712_REG_LENGTH_16BIT, 0x3F, dpll_val);
+		ret |= max96712_update_reg_bits(max96712, I2C_DEV_DES,
+				0x041B, DEV_REG_LENGTH_16BITS, 0x3F, dpll_val);
 		// Release reset to DPLL (config_soft_rst_n = 1)
-		ret |= max96712_write_reg(client, MAX96712_I2C_ADDR,
-			0x1E00, MAX96712_REG_LENGTH_16BIT,
-			MAX96712_REG_VALUE_08BIT, 0xf5);
+		ret |= max96712_write_reg(max96712, I2C_DEV_DES,
+				0x1E00, DEV_REG_LENGTH_16BITS,
+				DEV_REG_VALUE_08BITS, 0xf5);
 	}
 
 	// MIPI PHY3
 	if (mipi_tx_phy_enable & BIT(3)) {
 		// Hold DPLL in reset (config_soft_rst_n = 0) before changing the rate
-		ret |= max96712_write_reg(client, MAX96712_I2C_ADDR,
-			0x1F00, MAX96712_REG_LENGTH_16BIT,
-			MAX96712_REG_VALUE_08BIT, 0xf4);
+		ret |= max96712_write_reg(max96712, I2C_DEV_DES,
+				0x1F00, DEV_REG_LENGTH_16BITS,
+				DEV_REG_VALUE_08BITS, 0xf4);
 		// Set data rate and enable software override
-		ret |= max96712_update_reg_bits(client, MAX96712_I2C_ADDR,
-			0x041E, MAX96712_REG_LENGTH_16BIT, 0x3F, dpll_val);
+		ret |= max96712_update_reg_bits(max96712, I2C_DEV_DES,
+				0x041E, DEV_REG_LENGTH_16BITS, 0x3F, dpll_val);
 		// Release reset to DPLL (config_soft_rst_n = 1)
-		ret |= max96712_write_reg(client, MAX96712_I2C_ADDR,
-			0x1F00, MAX96712_REG_LENGTH_16BIT,
-			MAX96712_REG_VALUE_08BIT, 0xf5);
+		ret |= max96712_write_reg(max96712, I2C_DEV_DES,
+				0x1F00, DEV_REG_LENGTH_16BITS,
+				DEV_REG_VALUE_08BITS, 0xf5);
 	}
 
 	if (ret) {
-		dev_err(&client->dev, "DPLL predef set error!\n");
+		dev_err(dev, "DPLL predef set error!\n");
 		return ret;
 	}
 
 	ret = read_poll_timeout(max96712_read_reg, ret,
 				!(ret < 0) && (dpll_lock & 0xF0),
 				1000, 10000, false,
-				client, MAX96712_I2C_ADDR,
-				0x0400, MAX96712_REG_LENGTH_16BIT,
-				MAX96712_REG_VALUE_08BIT, &dpll_lock);
+				max96712, I2C_DEV_DES,
+				0x0400, DEV_REG_LENGTH_16BITS,
+				DEV_REG_VALUE_08BITS, &dpll_lock);
 	if (ret < 0) {
-		dev_err(&client->dev, "DPLL is not locked, dpll_lock = 0x%02x\n", dpll_lock);
+		dev_err(dev, "DPLL is not locked, dpll_lock = 0x%02x\n", dpll_lock);
 		return ret;
 	} else {
-		dev_err(&client->dev, "DPLL is locked, dpll_lock = 0x%02x\n", dpll_lock);
+		dev_err(dev, "DPLL is locked, dpll_lock = 0x%02x\n", dpll_lock);
 		return 0;
 	}
 }
 
-static int max96712_auto_init_deskew(struct i2c_client *client, u32 deskew_mask)
+static int max96712_auto_init_deskew(struct max96712 *max96712, u32 deskew_mask)
 {
+	struct device *dev = &max96712->client->dev;
 	int ret = 0;
 
-	dev_info(&client->dev, "Auto initial deskew: deskew_mask = 0x%02x\n", deskew_mask);
+	dev_info(dev, "Auto initial deskew: deskew_mask = 0x%02x\n", deskew_mask);
 
 	// D-PHY Deskew Initial Calibration Control
 	if (deskew_mask & BIT(0)) // MIPI PHY0
-		ret |= max96712_write_reg(client, MAX96712_I2C_ADDR,
-			0x0903, MAX96712_REG_LENGTH_16BIT,
-			MAX96712_REG_VALUE_08BIT, 0x80);
+		ret |= max96712_write_reg(max96712, I2C_DEV_DES,
+				0x0903, DEV_REG_LENGTH_16BITS,
+				DEV_REG_VALUE_08BITS, 0x80);
 
 	if (deskew_mask & BIT(1)) // MIPI PHY1
-		ret |= max96712_write_reg(client, MAX96712_I2C_ADDR,
-			0x0943, MAX96712_REG_LENGTH_16BIT,
-			MAX96712_REG_VALUE_08BIT, 0x80);
+		ret |= max96712_write_reg(max96712, I2C_DEV_DES,
+				0x0943, DEV_REG_LENGTH_16BITS,
+				DEV_REG_VALUE_08BITS, 0x80);
 
 	if (deskew_mask & BIT(2)) // MIPI PHY2
-		ret |= max96712_write_reg(client, MAX96712_I2C_ADDR,
-			0x0983, MAX96712_REG_LENGTH_16BIT,
-			MAX96712_REG_VALUE_08BIT, 0x80);
+		ret |= max96712_write_reg(max96712, I2C_DEV_DES,
+				0x0983, DEV_REG_LENGTH_16BITS,
+				DEV_REG_VALUE_08BITS, 0x80);
 
 	if (deskew_mask & BIT(3)) // MIPI PHY3
-		ret |= max96712_write_reg(client, MAX96712_I2C_ADDR,
-			0x09C3, MAX96712_REG_LENGTH_16BIT,
-			MAX96712_REG_VALUE_08BIT, 0x80);
+		ret |= max96712_write_reg(max96712, I2C_DEV_DES,
+				0x09C3, DEV_REG_LENGTH_16BITS,
+				DEV_REG_VALUE_08BITS, 0x80);
 
 	return ret;
 }
 
-static int max96712_frame_sync_period(struct i2c_client *client, u32 period)
+static int max96712_frame_sync_period(struct max96712 *max96712, u32 period)
 {
+	struct device *dev = &max96712->client->dev;
 	u32 pclk, fsync_peroid;
 	u8 fsync_peroid_h, fsync_peroid_m, fsync_peroid_l;
 	int ret = 0;
@@ -928,30 +934,30 @@
 	if (period == 0)
 		return 0;
 
-	dev_info(&client->dev, "Frame sync period = %d\n", period);
+	dev_info(dev, "Frame sync period = %d\n", period);
 
-#if 1 // TODO: Sensor
+#if 1 // TODO: Sensor slave mode
 	// SC320AT slave mode enable
-	ret |= max96712_write_reg(client, 0x30,
-		0x3222, MAX96712_REG_LENGTH_16BIT,
-		MAX96712_REG_VALUE_08BIT, 0x01);
+	ret |= max96712_write_reg(max96712, I2C_DEV_CAM,
+			0x3222, DEV_REG_LENGTH_16BITS,
+			DEV_REG_VALUE_08BITS, 0x01);
 	// Increase the allowable error range of the trigger signal
-	ret |= max96712_write_reg(client, 0x30,
-		0x32e2, MAX96712_REG_LENGTH_16BIT,
-		MAX96712_REG_VALUE_08BIT, 0x19);
+	ret |= max96712_write_reg(max96712, I2C_DEV_CAM,
+			0x32e2, DEV_REG_LENGTH_16BITS,
+			DEV_REG_VALUE_08BITS, 0x19);
 #endif
 
 	// Master link Video 0 for frame sync generation
-	ret |= max96712_write_reg(client, MAX96712_I2C_ADDR,
-		0x04A2, MAX96712_REG_LENGTH_16BIT,
-		MAX96712_REG_VALUE_08BIT, 0x00);
+	ret |= max96712_write_reg(max96712, I2C_DEV_DES,
+			0x04A2, DEV_REG_LENGTH_16BITS,
+			DEV_REG_VALUE_08BITS, 0x00);
 	// Disable Vsync-Fsync overlap window
-	ret |= max96712_write_reg(client, MAX96712_I2C_ADDR,
-		0x04AA, MAX96712_REG_LENGTH_16BIT,
-		MAX96712_REG_VALUE_08BIT, 0x00);
-	ret |= max96712_write_reg(client, MAX96712_I2C_ADDR,
-		0x04AB, MAX96712_REG_LENGTH_16BIT,
-		MAX96712_REG_VALUE_08BIT, 0x00);
+	ret |= max96712_write_reg(max96712, I2C_DEV_DES,
+			0x04AA, DEV_REG_LENGTH_16BITS,
+			DEV_REG_VALUE_08BITS, 0x00);
+	ret |= max96712_write_reg(max96712, I2C_DEV_DES,
+			0x04AB, DEV_REG_LENGTH_16BITS,
+			DEV_REG_VALUE_08BITS, 0x00);
 
 	// Set FSYNC period to 25M/30 clock cycles. PCLK = 25MHz. Sync freq = 30Hz
 	pclk = 25 * 1000 * 1000;
@@ -959,72 +965,70 @@
 	fsync_peroid_l = (fsync_peroid >> 0) & 0xFF;
 	fsync_peroid_m = (fsync_peroid >> 8) & 0xFF;
 	fsync_peroid_h = (fsync_peroid >> 16) & 0xFF;
-	dev_info(&client->dev, "Frame sync period: H = 0x%02x, M = 0x%02x, L = 0x%02x\n",
+	dev_info(dev, "Frame sync period: H = 0x%02x, M = 0x%02x, L = 0x%02x\n",
 			fsync_peroid_h, fsync_peroid_m, fsync_peroid_l);
 	// FSYNC_PERIOD_H
-	ret |= max96712_write_reg(client, MAX96712_I2C_ADDR,
-		0x04A7, MAX96712_REG_LENGTH_16BIT,
-		MAX96712_REG_VALUE_08BIT, fsync_peroid_h);
+	ret |= max96712_write_reg(max96712, I2C_DEV_DES,
+			0x04A7, DEV_REG_LENGTH_16BITS,
+			DEV_REG_VALUE_08BITS, fsync_peroid_h);
 	// FSYNC_PERIOD_M
-	ret |= max96712_write_reg(client, MAX96712_I2C_ADDR,
-		0x04A6, MAX96712_REG_LENGTH_16BIT,
-		MAX96712_REG_VALUE_08BIT, fsync_peroid_m);
+	ret |= max96712_write_reg(max96712, I2C_DEV_DES,
+			0x04A6, DEV_REG_LENGTH_16BITS,
+			DEV_REG_VALUE_08BITS, fsync_peroid_m);
 	// FSYNC_PERIOD_L
-	ret |= max96712_write_reg(client, MAX96712_I2C_ADDR,
-		0x04A5, MAX96712_REG_LENGTH_16BIT,
-		MAX96712_REG_VALUE_08BIT, fsync_peroid_l);
+	ret |= max96712_write_reg(max96712, I2C_DEV_DES,
+			0x04A5, DEV_REG_LENGTH_16BITS,
+			DEV_REG_VALUE_08BITS, fsync_peroid_l);
 
 	// FSYNC is GMSL2 type, use osc for fsync, include all links/pipes in fsync gen
-	ret |= max96712_write_reg(client, MAX96712_I2C_ADDR,
-		0x04AF, MAX96712_REG_LENGTH_16BIT,
-		MAX96712_REG_VALUE_08BIT, 0xcf);
+	ret |= max96712_write_reg(max96712, I2C_DEV_DES,
+			0x04AF, DEV_REG_LENGTH_16BITS,
+			DEV_REG_VALUE_08BITS, 0xcf);
 
+#if 1 // TODO: Desrializer MFP
 	// FSYNC_TX_ID: set 4 to match MFP4 on serializer side
-	ret |= max96712_write_reg(client, MAX96712_I2C_ADDR,
-		0x04B1, MAX96712_REG_LENGTH_16BIT,
-		MAX96712_REG_VALUE_08BIT, 0x20);
+	ret |= max96712_write_reg(max96712, I2C_DEV_DES,
+			0x04B1, DEV_REG_LENGTH_16BITS,
+			DEV_REG_VALUE_08BITS, 0x20);
+#endif
 
-#if 1 // TODO: Serializer
+#if 1 // TODO: Serializer MFP
 	// Enable GPIO_RX_EN on serializer MFP4
-	ret |= max96712_write_reg(client, 0x40,
-		0x02CA, MAX96712_REG_LENGTH_16BIT,
-		MAX96712_REG_VALUE_08BIT, 0x84);
+	ret |= max96712_write_reg(max96712, I2C_DEV_SER,
+			0x02CA, DEV_REG_LENGTH_16BITS,
+			DEV_REG_VALUE_08BITS, 0x84);
 #endif
 
 	// MFP2, VS not gen internally, GPIO not used to gen fsync, manual mode
-	ret |= max96712_write_reg(client, MAX96712_I2C_ADDR,
-		0x04A0, MAX96712_REG_LENGTH_16BIT,
-		MAX96712_REG_VALUE_08BIT, 0x04);
+	ret |= max96712_write_reg(max96712, I2C_DEV_DES,
+			0x04A0, DEV_REG_LENGTH_16BITS,
+			DEV_REG_VALUE_08BITS, 0x04);
 
 	return ret;
 }
 
-static int max96712_mipi_enable(struct i2c_client *client, bool enable)
+static int max96712_mipi_enable(struct max96712 *max96712, bool enable)
 {
 	int ret = 0;
 
 	if (enable) {
-#if MAX96712_FORCE_ALL_CLOCK_EN
+#if MAXIM_FORCE_ALL_CLOCK_EN
 		// Force all MIPI clocks running
-		ret |= max96712_update_reg_bits(client,
-				MAX96712_I2C_ADDR,
-				0x08A0, MAX96712_REG_LENGTH_16BIT, BIT(7), BIT(7));
+		ret |= max96712_update_reg_bits(max96712, I2C_DEV_DES,
+				0x08A0, DEV_REG_LENGTH_16BITS, BIT(7), BIT(7));
 #endif
 		// CSI output enabled
-		ret |= max96712_update_reg_bits(client,
-				MAX96712_I2C_ADDR,
-				0x040B, MAX96712_REG_LENGTH_16BIT, BIT(1), BIT(1));
+		ret |= max96712_update_reg_bits(max96712, I2C_DEV_DES,
+				0x040B, DEV_REG_LENGTH_16BITS, BIT(1), BIT(1));
 	} else {
-#if MAX96712_FORCE_ALL_CLOCK_EN
+#if MAXIM_FORCE_ALL_CLOCK_EN
 		// Normal mode
-		ret |= max96712_update_reg_bits(client,
-				MAX96712_I2C_ADDR,
-				0x08A0, MAX96712_REG_LENGTH_16BIT, BIT(7), 0x00);
+		ret |= max96712_update_reg_bits(max96712, I2C_DEV_DES,
+				0x08A0, DEV_REG_LENGTH_16BITS, BIT(7), 0x00);
 #endif
 		// CSI output disabled
-		ret |= max96712_update_reg_bits(client,
-				MAX96712_I2C_ADDR,
-				0x040B, MAX96712_REG_LENGTH_16BIT, BIT(1), 0x00);
+		ret |= max96712_update_reg_bits(max96712, I2C_DEV_DES,
+				0x040B, DEV_REG_LENGTH_16BITS, BIT(1), 0x00);
 	}
 
 	return ret;
@@ -1229,9 +1233,9 @@
 		stream = *((u32 *)arg);
 
 		if (stream)
-			ret = max96712_mipi_enable(max96712->client, true);
+			ret = max96712_mipi_enable(max96712, true);
 		else
-			ret = max96712_mipi_enable(max96712->client, false);
+			ret = max96712_mipi_enable(max96712, false);
 		break;
 	case RKMODULE_GET_VICAP_RST_INFO:
 		max96712_get_vicap_rst_inf(
@@ -1408,26 +1412,26 @@
 	if (max96712->hot_plug_irq > 0)
 		enable_irq(max96712->hot_plug_irq);
 
-	ret = max96712_write_array(max96712->client,
+	ret = max96712_write_array(max96712,
 				   max96712->cur_mode->reg_list);
 	if (ret)
 		return ret;
 
 	link_freq_idx = max96712->cur_mode->link_freq_idx;
 	link_freq_mhz = (u32)div_s64(link_freq_items[link_freq_idx], 1000000L);
-	ret = max96712_dphy_dpll_predef_set(max96712->client, link_freq_mhz);
+	ret = max96712_dphy_dpll_predef_set(max96712, link_freq_mhz);
 	if (ret)
 		return ret;
 
 	if (max96712->auto_init_deskew_mask != 0) {
-		ret = max96712_auto_init_deskew(max96712->client,
+		ret = max96712_auto_init_deskew(max96712,
 					max96712->auto_init_deskew_mask);
 		if (ret)
 			return ret;
 	}
 
 	if (max96712->frame_sync_period != 0) {
-		ret = max96712_frame_sync_period(max96712->client,
+		ret = max96712_frame_sync_period(max96712,
 					max96712->frame_sync_period);
 		if (ret)
 			return ret;
@@ -1440,7 +1444,7 @@
 	if (ret)
 		return ret;
 
-	return max96712_mipi_enable(max96712->client, true);
+	return max96712_mipi_enable(max96712, true);
 
 }
 
@@ -1449,7 +1453,7 @@
 	if (max96712->hot_plug_irq > 0)
 		disable_irq(max96712->hot_plug_irq);
 
-	return max96712_mipi_enable(max96712->client, false);
+	return max96712_mipi_enable(max96712, false);
 }
 
 static int max96712_s_stream(struct v4l2_subdev *sd, int on)
@@ -1823,7 +1827,18 @@
 	struct device *dev = &max96712->client->dev;
 	struct device_node *node = dev->of_node;
 	u8 mipi_data_lanes = max96712->bus_cfg.bus.mipi_csi2.num_data_lanes;
+	u32 value = 0;
 	int ret = 0;
+
+	/* serializer i2c address */
+	ret = of_property_read_u32(node, "ser-i2c-addr", &value);
+	if (ret) {
+		max96712->i2c_addr[I2C_DEV_SER] = SER_I2C_ADDR;
+	} else {
+		dev_info(dev, "ser-i2c-addr property: %d\n", value);
+		max96712->i2c_addr[I2C_DEV_SER] = value;
+	}
+	dev_info(dev, "serializer i2c address: 0x%02x\n", max96712->i2c_addr[I2C_DEV_SER]);
 
 	/* max96712 link Receiver Rate: 3G or 6G */
 	ret = of_property_read_u32(node, "link-rx-rate",
@@ -1855,14 +1870,14 @@
 
 	/* auto initial deskew mask */
 	ret = of_property_read_u32(node, "auto-init-deskew-mask",
-			   &max96712->auto_init_deskew_mask);
+				&max96712->auto_init_deskew_mask);
 	if (ret)
 		max96712->auto_init_deskew_mask = 0x0F; // 0x0F: default enable all
 	dev_info(dev, "auto init deskew mask: 0x%02x\n", max96712->auto_init_deskew_mask);
 
 	/* FSYNC period config */
 	ret = of_property_read_u32(node, "frame-sync-period",
-			   &max96712->frame_sync_period);
+				&max96712->frame_sync_period);
 	if (ret)
 		max96712->frame_sync_period = 0; // 0: disable (default)
 	dev_info(dev, "frame sync period: %d\n", max96712->frame_sync_period);
@@ -1910,6 +1925,11 @@
 
 	max96712->client = client;
 	i2c_set_clientdata(client, max96712);
+
+	/* i2c default address init */
+	max96712->i2c_addr[I2C_DEV_DES] = client->addr;
+	max96712->i2c_addr[I2C_DEV_SER] = SER_I2C_ADDR;
+	max96712->i2c_addr[I2C_DEV_CAM] = CAM_I2C_ADDR;
 
 	endpoint = of_graph_get_next_endpoint(dev->of_node, NULL);
 	if (!endpoint) {
@@ -2023,19 +2043,23 @@
 
 	if (!IS_ERR(max96712->lock_gpio)) {
 		max96712->hot_plug_irq = gpiod_to_irq(max96712->lock_gpio);
-		if (max96712->hot_plug_irq < 0)
+		if (max96712->hot_plug_irq < 0) {
 			dev_err(dev, "failed to get hot plug irq\n");
-
-		ret = devm_request_threaded_irq(dev,
-				max96712->hot_plug_irq,
-				NULL,
-				max96712_hot_plug_detect_irq_handler,
-				IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | IRQF_ONESHOT,
-				"max96712_hot_plug",
-				max96712);
-		if (ret)
-			dev_err(dev, "failed to request hot plug irq (%d)\n", ret);
-		disable_irq(max96712->hot_plug_irq);
+		} else {
+			ret = devm_request_threaded_irq(dev,
+					max96712->hot_plug_irq,
+					NULL,
+					max96712_hot_plug_detect_irq_handler,
+					IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+					"max96712_hot_plug",
+					max96712);
+			if (ret) {
+				dev_err(dev, "failed to request hot plug irq (%d)\n", ret);
+				max96712->hot_plug_irq = -1;
+			} else {
+				disable_irq(max96712->hot_plug_irq);
+			}
+		}
 	}
 
 	pm_runtime_set_active(dev);
diff --git a/kernel/drivers/media/i2c/max96722.c b/kernel/drivers/media/i2c/max96722.c
index a5a054a..c2c7a03 100644
--- a/kernel/drivers/media/i2c/max96722.c
+++ b/kernel/drivers/media/i2c/max96722.c
@@ -2,17 +2,22 @@
 /*
  * max96722 GMSL2/GMSL1 to CSI-2 Deserializer driver
  *
- * Copyright (C) 2022 Rockchip Electronics Co., Ltd.
+ * Copyright (C) 2023 Rockchip Electronics Co., Ltd.
  *
  * V0.0X01.0X00 first version.
+ * V1.0X00.0X00 Support New Driver Framework.
+ * V1.0X01.0X00 serdes read /write api depend on i2c id index.
  *
  */
 
 #include <linux/clk.h>
 #include <linux/device.h>
 #include <linux/delay.h>
+#include <linux/iopoll.h>
 #include <linux/gpio/consumer.h>
+#include <linux/pinctrl/consumer.h>
 #include <linux/i2c.h>
+#include <linux/regmap.h>
 #include <linux/module.h>
 #include <linux/pm_runtime.h>
 #include <linux/regulator/consumer.h>
@@ -21,59 +26,94 @@
 #include <linux/version.h>
 #include <linux/compat.h>
 #include <linux/rk-camera-module.h>
+#include <linux/of_graph.h>
 #include <media/media-entity.h>
 #include <media/v4l2-async.h>
 #include <media/v4l2-ctrls.h>
 #include <media/v4l2-subdev.h>
-#include <linux/pinctrl/consumer.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-subdev.h>
 
-#define DRIVER_VERSION			KERNEL_VERSION(0, 0x01, 0x00)
+#define DRIVER_VERSION			KERNEL_VERSION(1, 0x01, 0x00)
 
 #ifndef V4L2_CID_DIGITAL_GAIN
 #define V4L2_CID_DIGITAL_GAIN		V4L2_CID_GAIN
 #endif
 
-#define MAX96722_LINK_FREQ_400MHZ	400000000UL
-/* pixel rate = link frequency * 2 * lanes / BITS_PER_SAMPLE */
-#define MAX96722_PIXEL_RATE		(MAX96722_LINK_FREQ_400MHZ * 2LL * 4LL / 24LL)
-#define MAX96722_XVCLK_FREQ		24000000
+#define MAX96722_LINK_FREQ_MHZ(x)	((x) * 1000000UL)
+#define MAX96722_XVCLK_FREQ		25000000
 
-#define CHIP_ID				0xA1
+#define MAX96722_CHIP_ID		0xA1
 #define MAX96722_REG_CHIP_ID		0x0D
 
-#define MAX96722_REG_CTRL_MODE		0x08a0
-#define MAX96722_MODE_SW_STANDBY	0x4
-#define MAX96722_MODE_STREAMING		0xa4
+#define MAX96715_CHIP_ID		0x45
+#define MAX96715_REG_CHIP_ID		0x1E
 
-#define MAX96722_REMOTE_CTRL		0x0003
-#define MAX96722_REMOTE_DISABLE		0xFF
+#define MAX9295_CHIP_ID			0x91
+#define MAX9295_REG_CHIP_ID		0x0D
 
-#define REG_NULL			0xFFFF
+#define MAX96717_CHIP_ID		0xBF
+#define MAX96717_REG_CHIP_ID		0x0D
 
-#define MAX96722_LANES			4
+/* max96722->link mask: link type = bit[7:4], link mask = bit[3:0] */
+#define MAXIM_GMSL_TYPE_LINK_A		BIT(4)
+#define MAXIM_GMSL_TYPE_LINK_B		BIT(5)
+#define MAXIM_GMSL_TYPE_LINK_C		BIT(6)
+#define MAXIM_GMSL_TYPE_LINK_D		BIT(7)
+#define MAXIM_GMSL_TYPE_MASK		0xF0 /* bit[7:4], GMSL link type: 0 = GMSL1, 1 = GMSL2 */
+
+#define MAXIM_GMSL_LOCK_LINK_A		BIT(0)
+#define MAXIM_GMSL_LOCK_LINK_B		BIT(1)
+#define MAXIM_GMSL_LOCK_LINK_C		BIT(2)
+#define MAXIM_GMSL_LOCK_LINK_D		BIT(3)
+#define MAXIM_GMSL_LOCK_MASK		0x0F /* bit[3:0], GMSL link mask: 1 = disable, 1 = enable */
+
+#define MAXIM_FORCE_ALL_CLOCK_EN	1 /* 1: enable, 0: disable */
 
 #define OF_CAMERA_PINCTRL_STATE_DEFAULT	"rockchip,camera_default"
 #define OF_CAMERA_PINCTRL_STATE_SLEEP	"rockchip,camera_sleep"
 
-#define MAX96722_REG_VALUE_08BIT	1
-#define MAX96722_REG_VALUE_16BIT	2
-#define MAX96722_REG_VALUE_24BIT	3
-
 #define MAX96722_NAME			"max96722"
-#define MAX96722_MEDIA_BUS_FMT		MEDIA_BUS_FMT_RGB888_1X24
 
-static const char * const max96722_supply_names[] = {
-	"avdd",		/* Analog power */
-	"dovdd",	/* Digital I/O power */
-	"dvdd",		/* Digital core power */
+#define REG_NULL			0xFFFF
+
+/* register length: 8bit or 16bit */
+#define DEV_REG_LENGTH_08BITS		1
+#define DEV_REG_LENGTH_16BITS		2
+
+/* register value: 8bit or 16bit or 24bit */
+#define DEV_REG_VALUE_08BITS		1
+#define DEV_REG_VALUE_16BITS		2
+#define DEV_REG_VALUE_24BITS		3
+
+/* i2c device default address */
+#define SER_I2C_ADDR			(0x40)
+#define CAM_I2C_ADDR			(0x30)
+
+/* Maxim Serdes I2C Device ID */
+enum {
+	I2C_DEV_DES = 0,
+	I2C_DEV_SER,
+	I2C_DEV_CAM,
+	I2C_DEV_MAX
 };
 
-#define MAX96722_NUM_SUPPLIES ARRAY_SIZE(max96722_supply_names)
+
+static const char *const max96722_supply_names[] = {
+	"avdd", /* Analog power */
+	"dovdd", /* Digital I/O power */
+	"dvdd", /* Digital core power */
+};
+
+#define MAX96722_NUM_SUPPLIES		ARRAY_SIZE(max96722_supply_names)
 
 struct regval {
-	u16 i2c_addr;
-	u16 addr;
+	u16 i2c_id;
+	u16 reg_len;
+	u16 reg;
 	u8 val;
+	u8 mask;
 	u16 delay;
 };
 
@@ -85,227 +125,935 @@
 	u32 vts_def;
 	u32 exp_def;
 	u32 link_freq_idx;
+	u32 bus_fmt;
 	u32 bpp;
 	const struct regval *reg_list;
+	u32 vc[PAD_MAX];
 };
 
 struct max96722 {
-	struct i2c_client	*client;
-	struct clk		*xvclk;
-	struct gpio_desc	*power_gpio;
-	struct gpio_desc	*reset_gpio;
-	struct gpio_desc	*pwdn_gpio;
+	struct i2c_client *client;
+	u16 i2c_addr[I2C_DEV_MAX];
+	struct clk *xvclk;
+	struct gpio_desc *power_gpio;
+	struct gpio_desc *reset_gpio;
+	struct gpio_desc *pwdn_gpio;
+	struct gpio_desc *pocen_gpio;
+	struct gpio_desc *lock_gpio;
 	struct regulator_bulk_data supplies[MAX96722_NUM_SUPPLIES];
 
-	struct pinctrl		*pinctrl;
-	struct pinctrl_state	*pins_default;
-	struct pinctrl_state	*pins_sleep;
+	struct pinctrl *pinctrl;
+	struct pinctrl_state *pins_default;
+	struct pinctrl_state *pins_sleep;
 
-	struct v4l2_subdev	subdev;
-	struct media_pad	pad;
+	struct v4l2_subdev subdev;
+	struct media_pad pad;
 	struct v4l2_ctrl_handler ctrl_handler;
-	struct v4l2_ctrl	*exposure;
-	struct v4l2_ctrl	*anal_gain;
-	struct v4l2_ctrl	*digi_gain;
-	struct v4l2_ctrl	*hblank;
-	struct v4l2_ctrl	*vblank;
-	struct v4l2_ctrl	*pixel_rate;
-	struct v4l2_ctrl	*link_freq;
-	struct v4l2_ctrl	*test_pattern;
-	struct mutex		mutex;
-	bool			streaming;
-	bool			power_on;
-	bool			hot_plug;
-	u8			is_reset;
+	struct v4l2_ctrl *exposure;
+	struct v4l2_ctrl *anal_gain;
+	struct v4l2_ctrl *digi_gain;
+	struct v4l2_ctrl *hblank;
+	struct v4l2_ctrl *vblank;
+	struct v4l2_ctrl *pixel_rate;
+	struct v4l2_ctrl *link_freq;
+	struct v4l2_ctrl *test_pattern;
+	struct v4l2_fwnode_endpoint bus_cfg;
+
+	struct mutex mutex;
+	bool streaming;
+	bool power_on;
+	bool hot_plug;
+	u8 is_reset;
+	int hot_plug_irq;
+	u32 link_mask;
+	const struct max96722_mode *supported_modes;
 	const struct max96722_mode *cur_mode;
-	u32			module_index;
-	const char		*module_facing;
-	const char		*module_name;
-	const char		*len_name;
+	u32 cfg_modes_num;
+	u32 module_index;
+	u32 auto_init_deskew_mask;
+	u32 frame_sync_period;
+	const char *module_facing;
+	const char *module_name;
+	const char *len_name;
+	struct regmap *regmap;
 };
 
-#define to_max96722(sd) container_of(sd, struct max96722, subdev)
+static const struct regmap_config max96722_regmap_config = {
+	.reg_bits = 16,
+	.val_bits = 8,
+	.max_register = 0x1F17,
+};
 
-static const struct regval max96722_mipi_init[] = {
-	{0x6b, 0x0006, 0xF0, 0x00},
-	// Disable MIPI output
-	{0x6b, 0x040B, 0x00, 0x00},
-	// RGB888 software override for all pipes since connected GMSL1 is under parallel mode
-	{0x6b, 0x040B, 0xC0, 0x00},	//0b11000-000, bpp0=0x18
-	{0x6b, 0x040E, 0xA4, 0x00},	//0b10-100100, DT0=0x24
-	{0x6b, 0x040F, 0x04, 0x00},	//0b0000-0100, DT1=0x24
-	{0x6b, 0x0411, 0x18, 0x00},	//0b000-11000, bpp1=0x18
-	//Video pipe sel
-	{0x6b, 0x00F0, 0x40, 0x00},	//LINKA-pipex=pipe0, LINKB-pipex=pipe1
-	// Send RGB888, FS, and FE from Pipe 0 to Controller 1
-	{0x6b, 0x090B, 0x07, 0x00}, // Enable 3 Mappings
-	{0x6b, 0x092D, 0x15, 0x00},	//Map Data to Port A
-	// For the following MSB 2 bits = VC, LSB 6 bits =DT
-	{0x6b, 0x090D, 0x24, 0x00}, // SRC DT = RGB888
-	{0x6b, 0x090E, 0x24, 0x00}, // DEST DT = RGB888
-	{0x6b, 0x090F, 0x00, 0x00}, // SRC DT = Frame Start
-	{0x6b, 0x0910, 0x00, 0x00}, // DEST DT = Frame Start
-	{0x6b, 0x0911, 0x01, 0x00}, // SRC DT = Frame End
-	{0x6b, 0x0912, 0x01, 0x00}, // DEST DT = Frame End
-	//Send RGB888, FS, and FE from Pipe 1 to Controller 2
-	{0x6b, 0x094B, 0x07, 0x00},
-	{0x6b, 0x096D, 0xAA, 0x00}, // map to MIPI Controller 2
-	// For the following MSB 2 bits = VC, LSB 6 bits =DT
-	{0x6b, 0x094D, 0x24, 0x00},
-	{0x6b, 0x094E, 0x24, 0x00}, // map to VC0
-	{0x6b, 0x094F, 0x00, 0x00}, // frame start
-	{0x6b, 0x0950, 0x00, 0x00},
-	{0x6b, 0x0951, 0x01, 0x00},
-	{0x6b, 0x0952, 0x01, 0x00},
+static struct rkmodule_csi_dphy_param rk3588_dcphy_param = {
+	.vendor = PHY_VENDOR_SAMSUNG,
+	.lp_vol_ref = 3,
+	.lp_hys_sw = {3, 0, 0, 0},
+	.lp_escclk_pol_sel = {1, 0, 0, 0},
+	.skew_data_cal_clk = {0, 0, 0, 0},
+	.clk_hs_term_sel = 2,
+	.data_hs_term_sel = {2, 2, 2, 2},
+	.reserved = {0},
+};
+
+/* Max96715 */
+static const struct regval max96722_mipi_4lane_1280x800_30fps[] = {
+	// Link A/B/C/D all use GMSL1, and disabled
+	{ I2C_DEV_DES, 2, 0x0006, 0x00, 0x00, 0x00 }, // Link A/B/C/D: select GMSL1, Disabled
+	// Disable MIPI CSI output
+	{ I2C_DEV_DES, 2, 0x040B, 0x00, 0x00, 0x00 }, // CSI_OUT_EN=0, CSI output disabled
+	// Increase CMU voltage
+	{ I2C_DEV_DES, 2, 0x06C2, 0x10, 0x00, 0x0a }, // Increase CMU voltage to for wide temperature range
+	// VGAHiGain
+	{ I2C_DEV_DES, 2, 0x14D1, 0x03, 0x00, 0x00 }, // VGAHiGain
+	{ I2C_DEV_DES, 2, 0x15D1, 0x03, 0x00, 0x00 }, // VGAHiGain
+	{ I2C_DEV_DES, 2, 0x16D1, 0x03, 0x00, 0x00 }, // VGAHiGain
+	{ I2C_DEV_DES, 2, 0x17D1, 0x03, 0x00, 0x0a }, // VGAHiGain
+	// SSC Configuration
+	{ I2C_DEV_DES, 2, 0x1445, 0x00, 0x00, 0x00 }, // Disable SSC
+	{ I2C_DEV_DES, 2, 0x1545, 0x00, 0x00, 0x00 }, // Disable SSC
+	{ I2C_DEV_DES, 2, 0x1645, 0x00, 0x00, 0x00 }, // Disable SSC
+	{ I2C_DEV_DES, 2, 0x1745, 0x00, 0x00, 0x0a }, // Disable SSC
+	// GMSL1 configuration to match serializer
+	{ I2C_DEV_DES, 2, 0x0B07, 0x84, 0x00, 0x00 }, // Enable HVEN and DBL (application specific)
+	{ I2C_DEV_DES, 2, 0x0C07, 0x84, 0x00, 0x00 }, // Enable HVEN and DBL (application specific)
+	{ I2C_DEV_DES, 2, 0x0D07, 0x84, 0x00, 0x00 }, // Enable HVEN and DBL (application specific)
+	{ I2C_DEV_DES, 2, 0x0E07, 0x84, 0x00, 0x00 }, // Enable HVEN and DBL (application specific)
+	{ I2C_DEV_DES, 2, 0x0B0F, 0x01, 0x00, 0x00 }, // Disable processing HS and DE signals(required when paring with GMSL1 parallel serializers)
+	{ I2C_DEV_DES, 2, 0x0C0F, 0x01, 0x00, 0x00 }, // Disable processing HS and DE signals(required when paring with GMSL1 parallel serializers)
+	{ I2C_DEV_DES, 2, 0x0D0F, 0x01, 0x00, 0x00 }, // Disable processing HS and DE signals(required when paring with GMSL1 parallel serializers)
+	{ I2C_DEV_DES, 2, 0x0E0F, 0x01, 0x00, 0x00 }, // Disable processing HS and DE signals(required when paring with GMSL1 parallel serializers)
+	// Send YUV422, FS, and FE from Video Pipe 0 to Controller 1
+	{ I2C_DEV_DES, 2, 0x090B, 0x07, 0x00, 0x00 }, // Enable 0/1/2 SRC/DST Mappings
+	{ I2C_DEV_DES, 2, 0x092D, 0x15, 0x00, 0x00 }, // SRC/DST 0/1/2 -> CSI2 Controller 1;
+	// For the following MSB 2 bits = VC, LSB 6 bits = DT
+	{ I2C_DEV_DES, 2, 0x090D, 0x1e, 0x00, 0x00 }, // SRC0 VC = 0, DT = YUV422 8bit
+	{ I2C_DEV_DES, 2, 0x090E, 0x1e, 0x00, 0x00 }, // DST0 VC = 0, DT = YUV422 8bit
+	{ I2C_DEV_DES, 2, 0x090F, 0x00, 0x00, 0x00 }, // SRC1 VC = 0, DT = Frame Start
+	{ I2C_DEV_DES, 2, 0x0910, 0x00, 0x00, 0x00 }, // DST1 VC = 0, DT = Frame Start
+	{ I2C_DEV_DES, 2, 0x0911, 0x01, 0x00, 0x00 }, // SRC2 VC = 0, DT = Frame End
+	{ I2C_DEV_DES, 2, 0x0912, 0x01, 0x00, 0x00 }, // DST2 VC = 0, DT = Frame End
+	// Send YUV422, FS, and FE from Video Pipe 1 to Controller 1
+	{ I2C_DEV_DES, 2, 0x094B, 0x07, 0x00, 0x00 }, // Enable 0/1/2 SRC/DST Mappings
+	{ I2C_DEV_DES, 2, 0x096D, 0x15, 0x00, 0x00 }, // SRC/DST 0/1/2 -> CSI2 Controller 1;
+	// For the following MSB 2 bits = VC, LSB 6 bits = DT
+	{ I2C_DEV_DES, 2, 0x094D, 0x1e, 0x00, 0x00 }, // SRC0 VC = 0, DT = YUV422 8bit
+	{ I2C_DEV_DES, 2, 0x094E, 0x5e, 0x00, 0x00 }, // DST0 VC = 1, DT = YUV422 8bit
+	{ I2C_DEV_DES, 2, 0x094F, 0x00, 0x00, 0x00 }, // SRC1 VC = 0, DT = Frame Start
+	{ I2C_DEV_DES, 2, 0x0950, 0x40, 0x00, 0x00 }, // DST1 VC = 1, DT = Frame Start
+	{ I2C_DEV_DES, 2, 0x0951, 0x01, 0x00, 0x00 }, // SRC2 VC = 0, DT = Frame End
+	{ I2C_DEV_DES, 2, 0x0952, 0x41, 0x00, 0x00 }, // DST2 VC = 1, DT = Frame End
+	// Send YUV422, FS, and FE from Video Pipe 2 to Controller 1
+	{ I2C_DEV_DES, 2, 0x098B, 0x07, 0x00, 0x00 }, // Enable 0/1/2 SRC/DST Mappings
+	{ I2C_DEV_DES, 2, 0x09AD, 0x15, 0x00, 0x00 }, // SRC/DST 0/1/2 -> CSI2 Controller 1;
+	// For the following MSB 2 bits = VC, LSB 6 bits = DT
+	{ I2C_DEV_DES, 2, 0x098D, 0x1e, 0x00, 0x00 }, // SRC0 VC = 0, DT = YUV422 8bit
+	{ I2C_DEV_DES, 2, 0x098E, 0x9e, 0x00, 0x00 }, // DST0 VC = 2, DT = YUV422 8bit
+	{ I2C_DEV_DES, 2, 0x098F, 0x00, 0x00, 0x00 }, // SRC1 VC = 0, DT = Frame Start
+	{ I2C_DEV_DES, 2, 0x0990, 0x80, 0x00, 0x00 }, // DST1 VC = 2, DT = Frame Start
+	{ I2C_DEV_DES, 2, 0x0991, 0x01, 0x00, 0x00 }, // SRC2 VC = 0, DT = Frame End
+	{ I2C_DEV_DES, 2, 0x0992, 0x81, 0x00, 0x00 }, // DST2 VC = 2, DT = Frame End
+	// Send YUV422, FS, and FE from Video Pipe 3 to Controller 1
+	{ I2C_DEV_DES, 2, 0x09CB, 0x07, 0x00, 0x00 }, // Enable 0/1/2 SRC/DST Mappings
+	{ I2C_DEV_DES, 2, 0x09ED, 0x15, 0x00, 0x00 }, // SRC/DST 0/1/2 -> CSI2 Controller 1;
+	// For the following MSB 2 bits = VC, LSB 6 bits = DT
+	{ I2C_DEV_DES, 2, 0x09CD, 0x1e, 0x00, 0x00 }, // SRC0 VC = 0, DT = YUV422 8bit
+	{ I2C_DEV_DES, 2, 0x09CE, 0xde, 0x00, 0x00 }, // DST0 VC = 3, DT = YUV422 8bit
+	{ I2C_DEV_DES, 2, 0x09CF, 0x00, 0x00, 0x00 }, // SRC1 VC = 0, DT = Frame Start
+	{ I2C_DEV_DES, 2, 0x09D0, 0xc0, 0x00, 0x00 }, // DST1 VC = 3, DT = Frame Start
+	{ I2C_DEV_DES, 2, 0x09D1, 0x01, 0x00, 0x00 }, // SRC2 VC = 0, DT = Frame End
+	{ I2C_DEV_DES, 2, 0x09D2, 0xc1, 0x00, 0x00 }, // DST2 VC = 3, DT = Frame End
 	// MIPI PHY Setting
-	// Set Des in 2x4 mode
-	{0x6b, 0x08A0, 0x04, 0x00},
+	{ I2C_DEV_DES, 2, 0x08A0, 0x24, 0x00, 0x00 }, // DPHY0 enabled as clock, MIPI PHY Mode: 2x4 mode
 	// Set Lane Mapping for 4-lane port A
-	{0x6b, 0x08A3, 0xE4, 0x00},
-	{0x6b, 0x08A4, 0xE4, 0x00},
-	// Set 4 lane D-PHY
-	{0x6b, 0x090A, 0xC0, 0x00},
-	{0x6b, 0x094A, 0xC0, 0x00},
-	{0x6b, 0x098A, 0xC0, 0x00},
-	{0x6b, 0x09CA, 0xC0, 0x00},
+	{ I2C_DEV_DES, 2, 0x08A3, 0xe4, 0x00, 0x00 }, // PHY1 D1->D3, D0->D2; PHY0 D1->D1, D0->D0
+	// Set 4 lane D-PHY, 2bit VC
+	{ I2C_DEV_DES, 2, 0x090A, 0xc0, 0x00, 0x00 }, // MIPI PHY 0: 4 lanes, DPHY, 2bit VC
+	{ I2C_DEV_DES, 2, 0x094A, 0xc0, 0x00, 0x00 }, // MIPI PHY 1: 4 lanes, DPHY, 2bit VC
 	// Turn on MIPI PHYs
-	{0x6b, 0x08A2, 0xF0, 0x00},
-	// Hold DPLL in reset (config_soft_rst_n = 0) before changing the rate
-	{0x6b, 0x1C00, 0xF4, 0x00},
-	{0x6b, 0x1D00, 0xF4, 0x00},
-	{0x6b, 0x1E00, 0xF4, 0x00},
-	{0x6b, 0x1F00, 0xF4, 0x00},
-	// Set Data rate to be 800Mbps/lane for port A and enable software override
-	{0x6b, 0x0415, 0xE8, 0x00},	//override pipe0/1
-	{0x6b, 0x0418, 0x28, 0x00},
-	{0x6b, 0x041B, 0x28, 0x00},
-	{0x6b, 0x041E, 0x28, 0x00},
-	// Release reset to DPLL (config_soft_rst_n = 1)
-	{0x6b, 0x1C00, 0xF5, 0x00},
-	{0x6b, 0x1D00, 0xF5, 0x00},
-	{0x6b, 0x1E00, 0xF5, 0x00},
-	{0x6b, 0x1F00, 0xF5, 0x00},
-	{0x6b, 0x0003, 0xFF, 0x00},
-	{0x6b, 0x0006, 0xF3, 0x0a},
-	// {0x6b, 0x08A0, 0x84},
-	{0x6b, REG_NULL, 0x00, 0x00},
+	{ I2C_DEV_DES, 2, 0x08A2, 0x34, 0x00, 0x00 }, // Enable MIPI PHY 0/1, t_lpx = 106.7ns
+	// Enable software override for all pipes since GMSL1 data is parallel mode, bpp=8, dt=0x1e(yuv-8)
+	{ I2C_DEV_DES, 2, 0x040B, 0x40, 0x00, 0x00 }, // pipe 0 bpp=0x08: Datatypes = 0x2A, 0x10-12, 0x31-37
+	{ I2C_DEV_DES, 2, 0x040C, 0x00, 0x00, 0x00 }, // pipe 0 and 1 VC software override: 0x00
+	{ I2C_DEV_DES, 2, 0x040D, 0x00, 0x00, 0x00 }, // pipe 2 and 3 VC software override: 0x00
+	{ I2C_DEV_DES, 2, 0x040E, 0x5e, 0x00, 0x00 }, // pipe 0 DT=0x1E: YUV422 8-bit
+	{ I2C_DEV_DES, 2, 0x040F, 0x7e, 0x00, 0x00 }, // pipe 1 DT=0x1E: YUV422 8-bit
+	{ I2C_DEV_DES, 2, 0x0410, 0x7a, 0x00, 0x00 }, // pipe 2 DT=0x1E, pipe 3 DT=0x1E: YUV422 8-bit
+	{ I2C_DEV_DES, 2, 0x0411, 0x48, 0x00, 0x00 }, // pipe 1 bpp=0x08: Datatypes = 0x2A, 0x10-12, 0x31-37
+	{ I2C_DEV_DES, 2, 0x0412, 0x20, 0x00, 0x00 }, // pipe 2 bpp=0x08, pipe 3 bpp=0x08: Datatypes = 0x2A, 0x10-12, 0x31-37
+	{ I2C_DEV_DES, 2, 0x0415, 0xc0, 0xc0, 0x00 }, // pipe 0/1 enable software overide
+	{ I2C_DEV_DES, 2, 0x0418, 0xc0, 0xc0, 0x00 }, // pipe 2/3 enable software overide
+	{ I2C_DEV_DES, 2, 0x041A, 0xf0, 0x00, 0x00 }, // pipe 0/1/2/3: Enable YUV8-/10-bit mux mode
+	// Enable all links and pipes
+	{ I2C_DEV_DES, 2, 0x0003, 0xaa, 0x00, 0x00 }, // Enable Remote Control Channel Link A/B/C/D for Port 0
+	{ I2C_DEV_DES, 2, 0x0006, 0x0f, 0x00, 0x64 }, // Enable all links and pipes
+	// Serializer Setting
+	{ I2C_DEV_SER, 1, 0x04, 0x47, 0x00, 0x05 }, // main_control: Enable CLINK
+	{ I2C_DEV_SER, 1, 0x07, 0x84, 0x00, 0x00 }, // Config SerDes: DBL=1, BWS=0, HIBW=0, PXL_CRC=0, HVEN=1
+	{ I2C_DEV_SER, 1, 0x67, 0xc4, 0x00, 0x00 }, // Double Alignment Mode: Align at each rising edge of HS
+	{ I2C_DEV_SER, 1, 0x0F, 0xbf, 0x00, 0x00 }, // Enable Set GPO, GPO Output High
+	{ I2C_DEV_SER, 1, 0x3F, 0x08, 0x00, 0x00 }, // Crossbar HS: DIN8
+	{ I2C_DEV_SER, 1, 0x40, 0x2d, 0x00, 0x00 }, // Crossbar VS: DIN13, INVERT_MUX_VS
+	{ I2C_DEV_SER, 1, 0x20, 0x10, 0x00, 0x00 },
+	{ I2C_DEV_SER, 1, 0x21, 0x11, 0x00, 0x00 },
+	{ I2C_DEV_SER, 1, 0x22, 0x12, 0x00, 0x00 },
+	{ I2C_DEV_SER, 1, 0x23, 0x13, 0x00, 0x00 },
+	{ I2C_DEV_SER, 1, 0x24, 0x14, 0x00, 0x00 },
+	{ I2C_DEV_SER, 1, 0x25, 0x15, 0x00, 0x00 },
+	{ I2C_DEV_SER, 1, 0x26, 0x16, 0x00, 0x00 },
+	{ I2C_DEV_SER, 1, 0x27, 0x17, 0x00, 0x00 },
+	{ I2C_DEV_SER, 1, 0x30, 0x00, 0x00, 0x00 },
+	{ I2C_DEV_SER, 1, 0x31, 0x01, 0x00, 0x00 },
+	{ I2C_DEV_SER, 1, 0x32, 0x02, 0x00, 0x00 },
+	{ I2C_DEV_SER, 1, 0x33, 0x03, 0x00, 0x00 },
+	{ I2C_DEV_SER, 1, 0x34, 0x04, 0x00, 0x00 },
+	{ I2C_DEV_SER, 1, 0x35, 0x05, 0x00, 0x00 },
+	{ I2C_DEV_SER, 1, 0x36, 0x06, 0x00, 0x00 },
+	{ I2C_DEV_SER, 1, 0x37, 0x07, 0x00, 0x00 },
+	{ I2C_DEV_SER, 1, 0x04, 0x87, 0x00, 0x05 }, // main_control: Enable Serialization
+	{ I2C_DEV_DES, 2, REG_NULL, 0x00, 0x00, 0x00 },
 };
 
-static const struct max96722_mode supported_modes[] = {
+static const struct max96722_mode supported_modes_4lane[] = {
 	{
-		.width = 1920,
-		.height = 1080,
+		.width = 1280,
+		.height = 800,
 		.max_fps = {
 			.numerator = 10000,
 			.denominator = 300000,
 		},
-		.reg_list = max96722_mipi_init,
-		.link_freq_idx = 0,
+		.reg_list = max96722_mipi_4lane_1280x800_30fps,
+		.link_freq_idx = 20,
+		.bus_fmt = MEDIA_BUS_FMT_UYVY8_2X8,
+		.bpp = 16,
+		.vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_0,
+		.vc[PAD1] = V4L2_MBUS_CSI2_CHANNEL_1,
+		.vc[PAD2] = V4L2_MBUS_CSI2_CHANNEL_2,
+		.vc[PAD3] = V4L2_MBUS_CSI2_CHANNEL_3,
 	},
 };
 
+/* link freq = index * MAX96722_LINK_FREQ_MHZ(50) */
 static const s64 link_freq_items[] = {
-	MAX96722_LINK_FREQ_400MHZ,
+	MAX96722_LINK_FREQ_MHZ(0),
+	MAX96722_LINK_FREQ_MHZ(50),
+	MAX96722_LINK_FREQ_MHZ(100),
+	MAX96722_LINK_FREQ_MHZ(150),
+	MAX96722_LINK_FREQ_MHZ(200),
+	MAX96722_LINK_FREQ_MHZ(250),
+	MAX96722_LINK_FREQ_MHZ(300),
+	MAX96722_LINK_FREQ_MHZ(350),
+	MAX96722_LINK_FREQ_MHZ(400),
+	MAX96722_LINK_FREQ_MHZ(450),
+	MAX96722_LINK_FREQ_MHZ(500),
+	MAX96722_LINK_FREQ_MHZ(550),
+	MAX96722_LINK_FREQ_MHZ(600),
+	MAX96722_LINK_FREQ_MHZ(650),
+	MAX96722_LINK_FREQ_MHZ(700),
+	MAX96722_LINK_FREQ_MHZ(750),
+	MAX96722_LINK_FREQ_MHZ(800),
+	MAX96722_LINK_FREQ_MHZ(850),
+	MAX96722_LINK_FREQ_MHZ(900),
+	MAX96722_LINK_FREQ_MHZ(950),
+	MAX96722_LINK_FREQ_MHZ(1000),
+	MAX96722_LINK_FREQ_MHZ(1050),
+	MAX96722_LINK_FREQ_MHZ(1100),
+	MAX96722_LINK_FREQ_MHZ(1150),
+	MAX96722_LINK_FREQ_MHZ(1200),
+	MAX96722_LINK_FREQ_MHZ(1250),
 };
 
-/* Write registers up to 4 at a time */
-static int max96722_write_reg(struct i2c_client *client, u16 reg,
-			     u32 len, u32 val)
+static int max96722_write_reg(struct max96722 *max96722, u8 i2c_id,
+			u16 reg, u16 reg_len, u16 val_len, u32 val)
 {
+	struct i2c_client *client = max96722->client;
+	u16 client_addr = max96722->i2c_addr[i2c_id];
 	u32 buf_i, val_i;
 	u8 buf[6];
 	u8 *val_p;
 	__be32 val_be;
 
-	dev_dbg(&client->dev, "write reg(0x%x val:0x%x)!\n", reg, val);
+	dev_info(&client->dev, "addr(0x%02x) write reg(0x%04x, %d, 0x%02x)\n",
+		client_addr, reg, reg_len, val);
 
-	if (len > 4)
+	if (val_len > 4)
 		return -EINVAL;
 
-	buf[0] = reg >> 8;
-	buf[1] = reg & 0xff;
+	if (reg_len == 2) {
+		buf[0] = reg >> 8;
+		buf[1] = reg & 0xff;
+
+		buf_i = 2;
+	} else {
+		buf[0] = reg & 0xff;
+
+		buf_i = 1;
+	}
 
 	val_be = cpu_to_be32(val);
 	val_p = (u8 *)&val_be;
-	buf_i = 2;
-	val_i = 4 - len;
+	val_i = 4 - val_len;
 
 	while (val_i < 4)
 		buf[buf_i++] = val_p[val_i++];
 
-	if (i2c_master_send(client, buf, len + 2) != len + 2) {
-		dev_err(&client->dev, "%s: writing register 0x%x from 0x%x failed\n",
-				__func__, reg, client->addr);
+	client->addr = client_addr;
+
+	if (i2c_master_send(client, buf, (val_len + reg_len)) != (val_len + reg_len)) {
+		dev_err(&client->dev,
+			"%s: writing register 0x%04x from 0x%02x failed\n",
+			__func__, reg, client->addr);
 		return -EIO;
 	}
 
 	return 0;
 }
 
-static int max96722_write_array(struct i2c_client *client,
-			       const struct regval *regs)
+static int max96722_read_reg(struct max96722 *max96722, u8 i2c_id,
+			u16 reg, u16 reg_len, u16 val_len, u8 *val)
 {
-	u32 i;
-	int ret = 0;
-
-	for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++) {
-		client->addr = regs[i].i2c_addr;
-		ret = max96722_write_reg(client, regs[i].addr,
-					MAX96722_REG_VALUE_08BIT,
-					regs[i].val);
-		msleep(regs[i].delay);
-	}
-
-	return ret;
-}
-
-/* Read registers up to 4 at a time */
-static int max96722_read_reg(struct i2c_client *client, u16 reg,
-			    unsigned int len, u32 *val)
-{
+	struct i2c_client *client = max96722->client;
+	u16 client_addr = max96722->i2c_addr[i2c_id];
 	struct i2c_msg msgs[2];
 	u8 *data_be_p;
 	__be32 data_be = 0;
 	__be16 reg_addr_be = cpu_to_be16(reg);
+	u8 *reg_be_p;
 	int ret;
 
-	if (len > 4 || !len)
+	if (val_len > 4 || !val_len)
 		return -EINVAL;
 
+	client->addr = client_addr;
 	data_be_p = (u8 *)&data_be;
+	reg_be_p = (u8 *)&reg_addr_be;
+
 	/* Write register address */
 	msgs[0].addr = client->addr;
 	msgs[0].flags = 0;
-	msgs[0].len = 2;
-	msgs[0].buf = (u8 *)&reg_addr_be;
+	msgs[0].len = reg_len;
+	msgs[0].buf = &reg_be_p[2 - reg_len];
 
 	/* Read data from register */
 	msgs[1].addr = client->addr;
 	msgs[1].flags = I2C_M_RD;
-	msgs[1].len = len;
-	msgs[1].buf = &data_be_p[4 - len];
+	msgs[1].len = val_len;
+	msgs[1].buf = &data_be_p[4 - val_len];
 
 	ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
 	if (ret != ARRAY_SIZE(msgs)) {
-		dev_err(&client->dev, "%s: reading register 0x%x from 0x%x failed\n",
-				__func__, reg, client->addr);
+		dev_err(&client->dev,
+			"%s: reading register 0x%x from 0x%x failed\n",
+			__func__, reg, client->addr);
 		return -EIO;
 	}
 
 	*val = be32_to_cpu(data_be);
 
+#if 0
+	dev_info(&client->dev, "addr(0x%02x) read reg(0x%04x, %d, 0x%02x)\n",
+		client_addr, reg, reg_len, *val);
+#endif
+
 	return 0;
 }
 
+static int max96722_update_reg_bits(struct max96722 *max96722, u8 i2c_id,
+				u16 reg, u16 reg_len, u8 mask, u8 val)
+{
+	u8 value;
+	u32 val_len = DEV_REG_VALUE_08BITS;
+	int ret;
+
+	ret = max96722_read_reg(max96722, i2c_id, reg, reg_len, val_len, &value);
+	if (ret)
+		return ret;
+
+	value &= ~mask;
+	value |= (val & mask);
+	ret = max96722_write_reg(max96722, i2c_id, reg, reg_len, val_len, value);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int max96722_write_array(struct max96722 *max96722,
+				const struct regval *regs)
+{
+	u32 i;
+	int ret = 0;
+
+	for (i = 0; ret == 0 && regs[i].reg != REG_NULL; i++) {
+		if (regs[i].mask != 0)
+			ret = max96722_update_reg_bits(max96722, regs[i].i2c_id,
+					regs[i].reg, regs[i].reg_len,
+					regs[i].mask, regs[i].val);
+		else
+			ret = max96722_write_reg(max96722, regs[i].i2c_id,
+					regs[i].reg, regs[i].reg_len,
+					DEV_REG_VALUE_08BITS, regs[i].val);
+
+		if (regs[i].delay != 0)
+			msleep(regs[i].delay);
+	}
+
+	return ret;
+}
+
+static int max96722_check_local_chipid(struct max96722 *max96722)
+{
+	struct device *dev = &max96722->client->dev;
+	int ret;
+	u8 id = 0;
+
+	ret = max96722_read_reg(max96722, I2C_DEV_DES,
+			MAX96722_REG_CHIP_ID, DEV_REG_LENGTH_16BITS,
+			DEV_REG_VALUE_08BITS, &id);
+	if ((ret != 0) || (id != MAX96722_CHIP_ID)) {
+		dev_err(dev, "Unexpected MAX96722 chip id(%02x), ret(%d)\n", id, ret);
+		return -ENODEV;
+	}
+
+	dev_info(dev, "Detected MAX96722 chipid: %02x\n", id);
+
+	return 0;
+}
+
+static int __maybe_unused max96722_check_remote_chipid(struct max96722 *max96722)
+{
+	struct device *dev = &max96722->client->dev;
+	int ret = 0;
+	u8 id;
+
+	dev_info(dev, "Check remote chipid\n");
+
+	id = 0;
+#if 0
+	// max96717
+	ret = max96722_read_reg(max96722, I2C_DEV_SER,
+			MAX96717_REG_CHIP_ID, DEV_REG_LENGTH_16BITS,
+			DEV_REG_VALUE_08BITS, &id);
+	if ((ret != 0) || (id != MAX96717_CHIP_ID)) {
+		dev_err(dev, "Unexpected MAX96717 chip id(%02x), ret(%d)\n", id, ret);
+		return -ENODEV;
+	}
+	dev_info(dev, "Detected MAX96717 chipid: 0x%02x\n", id);
+#endif
+
+#if 0
+	// max9295
+	ret = max96722_read_reg(max96722, I2C_DEV_SER,
+			MAX9295_REG_CHIP_ID, DEV_REG_LENGTH_16BITS,
+			DEV_REG_VALUE_08BITS, &id);
+	if ((ret != 0) || (id != MAX9295_CHIP_ID)) {
+		dev_err(dev, "Unexpected MAX9295 chip id(%02x), ret(%d)\n", id, ret);
+		return -ENODEV;
+	}
+	dev_info(dev, "Detected MAX9295 chipid: 0x%02x\n", id);
+#endif
+
+#if 0
+	// max96715
+	ret = max96722_read_reg(max96722, I2C_DEV_SER,
+			MAX96715_REG_CHIP_ID, DEV_REG_LENGTH_08BITS,
+			DEV_REG_VALUE_08BITS, &id);
+	if ((ret != 0) || (id != MAX96715_CHIP_ID)) {
+		dev_err(dev, "Unexpected MAX96715 chip id(%02x), ret(%d)\n", id, ret);
+		return -ENODEV;
+	}
+	dev_info(dev, "Detected MAX96715 chipid: 0x%02x\n", id);
+#endif
+
+	return ret;
+}
+
+static u8 max96722_get_link_lock_state(struct max96722 *max96722, u8 link_mask)
+{
+	struct device *dev = &max96722->client->dev;
+	u8 lock = 0, lock_state = 0;
+	u8 link_type = 0;
+
+	link_type = max96722->link_mask & MAXIM_GMSL_TYPE_MASK;
+
+	if (link_mask & MAXIM_GMSL_LOCK_LINK_A) {
+		if (link_type & MAXIM_GMSL_TYPE_LINK_A) {
+			// GMSL2 LinkA
+			max96722_read_reg(max96722, I2C_DEV_DES,
+				0x001a, DEV_REG_LENGTH_16BITS,
+				DEV_REG_VALUE_08BITS, &lock);
+			if (lock & BIT(3)) {
+				lock_state |= MAXIM_GMSL_LOCK_LINK_A;
+				dev_info(dev, "GMSL2 LinkA locked\n");
+			}
+		} else {
+			// GMSL1 LinkA
+			max96722_read_reg(max96722, I2C_DEV_DES,
+				0x0bcb, DEV_REG_LENGTH_16BITS,
+				DEV_REG_VALUE_08BITS, &lock);
+			if (lock & BIT(0)) {
+				lock_state |= MAXIM_GMSL_LOCK_LINK_A;
+				dev_info(dev, "GMSL1 LinkA locked\n");
+			}
+		}
+	}
+
+	if (link_mask & MAXIM_GMSL_LOCK_LINK_B) {
+		if (link_type & MAXIM_GMSL_TYPE_LINK_B) {
+			// GMSL2 LinkB
+			max96722_read_reg(max96722, I2C_DEV_DES,
+				0x000a, DEV_REG_LENGTH_16BITS,
+				DEV_REG_VALUE_08BITS, &lock);
+			if (lock & BIT(3)) {
+				lock_state |= MAXIM_GMSL_LOCK_LINK_B;
+				dev_info(dev, "GMSL2 LinkB locked\n");
+			}
+		} else {
+			// GMSL1 LinkB
+			max96722_read_reg(max96722, I2C_DEV_DES,
+				0x0ccb, DEV_REG_LENGTH_16BITS,
+				DEV_REG_VALUE_08BITS, &lock);
+			if (lock & BIT(0)) {
+				lock_state |= MAXIM_GMSL_LOCK_LINK_B;
+				dev_info(dev, "GMSL1 LinkB locked\n");
+			}
+		}
+	}
+
+	if (link_mask & MAXIM_GMSL_LOCK_LINK_C) {
+		if (link_type & MAXIM_GMSL_TYPE_LINK_C) {
+			// GMSL2 LinkC
+			max96722_read_reg(max96722, I2C_DEV_DES,
+				0x000b, DEV_REG_LENGTH_16BITS,
+				DEV_REG_VALUE_08BITS, &lock);
+			if (lock & BIT(3)) {
+				lock_state |= MAXIM_GMSL_LOCK_LINK_C;
+				dev_info(dev, "GMSL2 LinkC locked\n");
+			}
+		} else {
+			// GMSL1 LinkC
+			max96722_read_reg(max96722, I2C_DEV_DES,
+				0x0dcb, DEV_REG_LENGTH_16BITS,
+				DEV_REG_VALUE_08BITS, &lock);
+			if (lock & BIT(0)) {
+				lock_state |= MAXIM_GMSL_LOCK_LINK_C;
+				dev_info(dev, "GMSL1 LinkC locked\n");
+			}
+		}
+	}
+
+	if (link_mask & MAXIM_GMSL_LOCK_LINK_D) {
+		if (link_type & MAXIM_GMSL_TYPE_LINK_D) {
+			// GMSL2 LinkD
+			max96722_read_reg(max96722, I2C_DEV_DES,
+				0x000c, DEV_REG_LENGTH_16BITS,
+				DEV_REG_VALUE_08BITS, &lock);
+			if (lock & BIT(3)) {
+				lock_state |= MAXIM_GMSL_LOCK_LINK_D;
+				dev_info(dev, "GMSL2 LinkD locked\n");
+			}
+		} else {
+			// GMSL1 LinkD
+			max96722_read_reg(max96722, I2C_DEV_DES,
+				0x0ecb, DEV_REG_LENGTH_16BITS,
+				DEV_REG_VALUE_08BITS, &lock);
+			if (lock & BIT(0)) {
+				lock_state |= MAXIM_GMSL_LOCK_LINK_D;
+				dev_info(dev, "GMSL1 LinkD locked\n");
+			}
+		}
+	}
+
+	return lock_state;
+}
+
+static int max96722_check_link_lock_state(struct max96722 *max96722)
+{
+	struct device *dev = &max96722->client->dev;
+	u8 lock_state = 0, link_mask = 0, link_type = 0;
+	int ret, i, time_ms;
+
+	ret = max96722_check_local_chipid(max96722);
+	if (ret)
+		return ret;
+
+	/* IF VDD = 1.2V: Enable REG_ENABLE and REG_MNL
+	 *	CTRL0: Enable REG_ENABLE
+	 *	CTRL2: Enable REG_MNL
+	 */
+	max96722_update_reg_bits(max96722, I2C_DEV_DES,
+			0x0017, DEV_REG_LENGTH_16BITS, BIT(2), BIT(2));
+	max96722_update_reg_bits(max96722, I2C_DEV_DES,
+			0x0019, DEV_REG_LENGTH_16BITS, BIT(4), BIT(4));
+
+	// CSI output disabled
+	max96722_write_reg(max96722, I2C_DEV_DES,
+			0x040B, DEV_REG_LENGTH_16BITS,
+			DEV_REG_VALUE_08BITS, 0x00);
+
+	// All links select mode by link_type and disable at beginning.
+	link_type = max96722->link_mask & MAXIM_GMSL_TYPE_MASK;
+	max96722_write_reg(max96722, I2C_DEV_DES,
+			0x0006, DEV_REG_LENGTH_16BITS,
+			DEV_REG_VALUE_08BITS, link_type);
+
+	// Link Rate
+	// Link A ~ Link D Transmitter Rate: 187.5Mbps, Receiver Rate: 3Gbps
+	max96722_write_reg(max96722, I2C_DEV_DES,
+				0x0010, DEV_REG_LENGTH_16BITS,
+				DEV_REG_VALUE_08BITS, 0x11);
+	max96722_write_reg(max96722, I2C_DEV_DES,
+				0x0011, DEV_REG_LENGTH_16BITS,
+				DEV_REG_VALUE_08BITS, 0x11);
+
+	// GMSL1: Enable HIM on deserializer on Link A/B/C/D
+	if ((link_type & MAXIM_GMSL_TYPE_LINK_A) == 0) {
+		max96722_write_reg(max96722, I2C_DEV_DES,
+				0x0B06, DEV_REG_LENGTH_16BITS,
+				DEV_REG_VALUE_08BITS, 0xEF);
+	}
+	if ((link_type & MAXIM_GMSL_TYPE_LINK_B) == 0) {
+		max96722_write_reg(max96722, I2C_DEV_DES,
+				0x0C06, DEV_REG_LENGTH_16BITS,
+				DEV_REG_VALUE_08BITS, 0xEF);
+	}
+	if ((link_type & MAXIM_GMSL_TYPE_LINK_C) == 0) {
+		max96722_write_reg(max96722, I2C_DEV_DES,
+				0x0D06, DEV_REG_LENGTH_16BITS,
+				DEV_REG_VALUE_08BITS, 0xEF);
+	}
+	if ((link_type & MAXIM_GMSL_TYPE_LINK_D) == 0) {
+		max96722_write_reg(max96722, I2C_DEV_DES,
+				0x0E06, DEV_REG_LENGTH_16BITS,
+				DEV_REG_VALUE_08BITS, 0xEF);
+	}
+
+	// Link A ~ Link D One-Shot Reset depend on link_mask
+	link_mask = max96722->link_mask & MAXIM_GMSL_LOCK_MASK;
+	max96722_write_reg(max96722, I2C_DEV_DES,
+			0x0018, DEV_REG_LENGTH_16BITS,
+			DEV_REG_VALUE_08BITS, link_mask);
+
+	// Link A ~ Link D enable depend on link_type and link_mask
+	max96722_write_reg(max96722, I2C_DEV_DES,
+			0x0006, DEV_REG_LENGTH_16BITS,
+			DEV_REG_VALUE_08BITS, link_type | link_mask);
+
+	time_ms = 50;
+	msleep(time_ms);
+
+	for (i = 0; i < 20; i++) {
+		if ((lock_state & MAXIM_GMSL_LOCK_LINK_A) == 0)
+			if (max96722_get_link_lock_state(max96722, MAXIM_GMSL_LOCK_LINK_A)) {
+				lock_state |= MAXIM_GMSL_LOCK_LINK_A;
+				dev_info(dev, "LinkA locked time: %d ms\n", time_ms);
+			}
+
+		if ((lock_state & MAXIM_GMSL_LOCK_LINK_B) == 0)
+			if (max96722_get_link_lock_state(max96722, MAXIM_GMSL_LOCK_LINK_B)) {
+				lock_state |= MAXIM_GMSL_LOCK_LINK_B;
+				dev_info(dev, "LinkB locked time: %d ms\n", time_ms);
+			}
+
+		if ((lock_state & MAXIM_GMSL_LOCK_LINK_C) == 0)
+			if (max96722_get_link_lock_state(max96722, MAXIM_GMSL_LOCK_LINK_C)) {
+				lock_state |= MAXIM_GMSL_LOCK_LINK_C;
+				dev_info(dev, "LinkC locked time: %d ms\n", time_ms);
+			}
+
+		if ((lock_state & MAXIM_GMSL_LOCK_LINK_D) == 0)
+			if (max96722_get_link_lock_state(max96722, MAXIM_GMSL_LOCK_LINK_D)) {
+				lock_state |= MAXIM_GMSL_LOCK_LINK_D;
+				dev_info(dev, "LinkD locked time: %d ms\n", time_ms);
+			}
+
+		if ((lock_state & link_mask) == link_mask) {
+			dev_info(dev, "All Links are locked: 0x%x, time_ms = %d\n", lock_state, time_ms);
+#if 0
+			max96722_check_remote_chipid(max96722);
+#endif
+			return 0;
+		}
+
+		msleep(10);
+		time_ms += 10;
+	}
+
+	if ((lock_state & link_mask) != 0) {
+		dev_info(dev, "Partial links are locked: 0x%x, time_ms = %d\n", lock_state, time_ms);
+		return 0;
+	} else {
+		dev_err(dev, "Failed to detect camera link, time_ms = %d!\n", time_ms);
+		return -ENODEV;
+	}
+}
+
+static irqreturn_t max96722_hot_plug_detect_irq_handler(int irq, void *dev_id)
+{
+	struct max96722 *max96722 = dev_id;
+	struct device *dev = &max96722->client->dev;
+	u8 lock_state = 0, link_mask = 0;
+
+	link_mask = max96722->link_mask & MAXIM_GMSL_LOCK_MASK;
+	if (max96722->streaming) {
+		lock_state = max96722_get_link_lock_state(max96722, link_mask);
+		if (lock_state == link_mask) {
+			dev_info(dev, "serializer plug in, lock_state = 0x%02x\n", lock_state);
+		} else {
+			dev_info(dev, "serializer plug out, lock_state = 0x%02x\n", lock_state);
+		}
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int max96722_dphy_dpll_predef_set(struct max96722 *max96722, u32 link_freq_mhz)
+{
+	struct device *dev = &max96722->client->dev;
+	int ret = 0;
+	u8 dpll_val = 0, dpll_lock = 0;
+	u8 mipi_tx_phy_enable = 0;
+
+	ret = max96722_read_reg(max96722, I2C_DEV_DES,
+			0x08A2, DEV_REG_LENGTH_16BITS,
+			DEV_REG_VALUE_08BITS, &mipi_tx_phy_enable);
+	if (ret)
+		return ret;
+	mipi_tx_phy_enable = (mipi_tx_phy_enable & 0xF0) >> 4;
+
+	dev_info(dev, "DPLL predef set: mipi_tx_phy_enable = 0x%02x, link_freq_mhz = %d\n",
+			mipi_tx_phy_enable, link_freq_mhz);
+
+	// dphy max data rate is 2500MHz
+	if (link_freq_mhz > (2500 >> 1))
+		link_freq_mhz = (2500 >> 1);
+
+	dpll_val = DIV_ROUND_UP(link_freq_mhz * 2, 100) & 0x1F;
+	// Disable software override for frequency fine tuning
+	dpll_val |= BIT(5);
+
+	// MIPI PHY0
+	if (mipi_tx_phy_enable & BIT(0)) {
+		// Hold DPLL in reset (config_soft_rst_n = 0) before changing the rate
+		ret |= max96722_write_reg(max96722, I2C_DEV_DES,
+				0x1C00, DEV_REG_LENGTH_16BITS,
+				DEV_REG_VALUE_08BITS, 0xf4);
+		// Set data rate and enable software override
+		ret |= max96722_update_reg_bits(max96722, I2C_DEV_DES,
+				0x0415, DEV_REG_LENGTH_16BITS, 0x3F, dpll_val);
+		// Release reset to DPLL (config_soft_rst_n = 1)
+		ret |= max96722_write_reg(max96722, I2C_DEV_DES,
+				0x1C00, DEV_REG_LENGTH_16BITS,
+				DEV_REG_VALUE_08BITS, 0xf5);
+	}
+
+	// MIPI PHY1
+	if (mipi_tx_phy_enable & BIT(1)) {
+		// Hold DPLL in reset (config_soft_rst_n = 0) before changing the rate
+		ret |= max96722_write_reg(max96722, I2C_DEV_DES,
+				0x1D00, DEV_REG_LENGTH_16BITS,
+				DEV_REG_VALUE_08BITS, 0xf4);
+		// Set data rate and enable software override
+		ret |= max96722_update_reg_bits(max96722, I2C_DEV_DES,
+				0x0418, DEV_REG_LENGTH_16BITS, 0x3F, dpll_val);
+		// Release reset to DPLL (config_soft_rst_n = 1)
+		ret |= max96722_write_reg(max96722, I2C_DEV_DES,
+				0x1D00, DEV_REG_LENGTH_16BITS,
+				DEV_REG_VALUE_08BITS, 0xf5);
+	}
+
+	// MIPI PHY2
+	if (mipi_tx_phy_enable & BIT(2)) {
+		// Hold DPLL in reset (config_soft_rst_n = 0) before changing the rate
+		ret |= max96722_write_reg(max96722, I2C_DEV_DES,
+				0x1E00, DEV_REG_LENGTH_16BITS,
+				DEV_REG_VALUE_08BITS, 0xf4);
+		// Set data rate and enable software override
+		ret |= max96722_update_reg_bits(max96722, I2C_DEV_DES,
+				0x041B, DEV_REG_LENGTH_16BITS, 0x3F, dpll_val);
+		// Release reset to DPLL (config_soft_rst_n = 1)
+		ret |= max96722_write_reg(max96722, I2C_DEV_DES,
+				0x1E00, DEV_REG_LENGTH_16BITS,
+				DEV_REG_VALUE_08BITS, 0xf5);
+	}
+
+	// MIPI PHY3
+	if (mipi_tx_phy_enable & BIT(3)) {
+		// Hold DPLL in reset (config_soft_rst_n = 0) before changing the rate
+		ret |= max96722_write_reg(max96722, I2C_DEV_DES,
+				0x1F00, DEV_REG_LENGTH_16BITS,
+				DEV_REG_VALUE_08BITS, 0xf4);
+		// Set data rate and enable software override
+		ret |= max96722_update_reg_bits(max96722, I2C_DEV_DES,
+				0x041E, DEV_REG_LENGTH_16BITS, 0x3F, dpll_val);
+		// Release reset to DPLL (config_soft_rst_n = 1)
+		ret |= max96722_write_reg(max96722, I2C_DEV_DES,
+				0x1F00, DEV_REG_LENGTH_16BITS,
+				DEV_REG_VALUE_08BITS, 0xf5);
+	}
+
+	if (ret) {
+		dev_err(dev, "DPLL predef set error!\n");
+		return ret;
+	}
+
+	ret = read_poll_timeout(max96722_read_reg, ret,
+				!(ret < 0) && (dpll_lock & 0xF0),
+				1000, 10000, false,
+				max96722, I2C_DEV_DES,
+				0x0400, DEV_REG_LENGTH_16BITS,
+				DEV_REG_VALUE_08BITS, &dpll_lock);
+	if (ret < 0) {
+		dev_err(dev, "DPLL is not locked, dpll_lock = 0x%02x\n", dpll_lock);
+		return ret;
+	} else {
+		dev_err(dev, "DPLL is locked, dpll_lock = 0x%02x\n", dpll_lock);
+		return 0;
+	}
+}
+
+static int max96722_auto_init_deskew(struct max96722 *max96722, u32 deskew_mask)
+{
+	struct device *dev = &max96722->client->dev;
+	int ret = 0;
+
+	dev_info(dev, "Auto initial deskew: deskew_mask = 0x%02x\n", deskew_mask);
+
+	// D-PHY Deskew Initial Calibration Control
+	if (deskew_mask & BIT(0)) // MIPI PHY0
+		ret |= max96722_write_reg(max96722, I2C_DEV_DES,
+				0x0903, DEV_REG_LENGTH_16BITS,
+				DEV_REG_VALUE_08BITS, 0x80);
+
+	if (deskew_mask & BIT(1)) // MIPI PHY1
+		ret |= max96722_write_reg(max96722, I2C_DEV_DES,
+				0x0943, DEV_REG_LENGTH_16BITS,
+				DEV_REG_VALUE_08BITS, 0x80);
+
+	if (deskew_mask & BIT(2)) // MIPI PHY2
+		ret |= max96722_write_reg(max96722, I2C_DEV_DES,
+				0x0983, DEV_REG_LENGTH_16BITS,
+				DEV_REG_VALUE_08BITS, 0x80);
+
+	if (deskew_mask & BIT(3)) // MIPI PHY3
+		ret |= max96722_write_reg(max96722, I2C_DEV_DES,
+				0x09C3, DEV_REG_LENGTH_16BITS,
+				DEV_REG_VALUE_08BITS, 0x80);
+
+	return ret;
+}
+
+static int max96722_frame_sync_period(struct max96722 *max96722, u32 period)
+{
+	struct device *dev = &max96722->client->dev;
+	u32 pclk, fsync_peroid;
+	u8 fsync_peroid_h, fsync_peroid_m, fsync_peroid_l;
+	int ret = 0;
+
+	if (period == 0)
+		return 0;
+
+	dev_info(dev, "Frame sync period = %d\n", period);
+
+#if 1 // TODO: Sensor slave mode
+	// sendor slave mode enable
+#endif
+
+	// Master link Video 0 for frame sync generation
+	ret |= max96722_write_reg(max96722, I2C_DEV_DES,
+			0x04A2, DEV_REG_LENGTH_16BITS,
+			DEV_REG_VALUE_08BITS, 0x00);
+	// Disable Vsync-Fsync overlap window
+	ret |= max96722_write_reg(max96722, I2C_DEV_DES,
+			0x04AA, DEV_REG_LENGTH_16BITS,
+			DEV_REG_VALUE_08BITS, 0x00);
+	ret |= max96722_write_reg(max96722, I2C_DEV_DES,
+			0x04AB, DEV_REG_LENGTH_16BITS,
+			DEV_REG_VALUE_08BITS, 0x00);
+
+	// Set FSYNC period to 25M/30 clock cycles. PCLK = 25MHz. Sync freq = 30Hz
+	pclk = 25 * 1000 * 1000;
+	fsync_peroid = DIV_ROUND_UP(pclk, period) - 1;
+	fsync_peroid_l = (fsync_peroid >> 0) & 0xFF;
+	fsync_peroid_m = (fsync_peroid >> 8) & 0xFF;
+	fsync_peroid_h = (fsync_peroid >> 16) & 0xFF;
+	dev_info(dev, "Frame sync period: H = 0x%02x, M = 0x%02x, L = 0x%02x\n",
+			fsync_peroid_h, fsync_peroid_m, fsync_peroid_l);
+	// FSYNC_PERIOD_H
+	ret |= max96722_write_reg(max96722, I2C_DEV_DES,
+			0x04A7, DEV_REG_LENGTH_16BITS,
+			DEV_REG_VALUE_08BITS, fsync_peroid_h);
+	// FSYNC_PERIOD_M
+	ret |= max96722_write_reg(max96722, I2C_DEV_DES,
+			0x04A6, DEV_REG_LENGTH_16BITS,
+			DEV_REG_VALUE_08BITS, fsync_peroid_m);
+	// FSYNC_PERIOD_L
+	ret |= max96722_write_reg(max96722, I2C_DEV_DES,
+			0x04A5, DEV_REG_LENGTH_16BITS,
+			DEV_REG_VALUE_08BITS, fsync_peroid_l);
+
+	// FSYNC is GMSL2 type, use osc for fsync, include all links/pipes in fsync gen
+	ret |= max96722_write_reg(max96722, I2C_DEV_DES,
+			0x04AF, DEV_REG_LENGTH_16BITS,
+			DEV_REG_VALUE_08BITS, 0xcf);
+
+#if 1 // TODO: Desrializer MFP
+	// FSYNC_TX_ID: set 4 to match MFP4 on serializer side
+	ret |= max96722_write_reg(max96722, I2C_DEV_DES,
+			0x04B1, DEV_REG_LENGTH_16BITS,
+			DEV_REG_VALUE_08BITS, 0x20);
+#endif
+
+#if 1 // TODO: Serializer MFP
+	// Enable GPIO_RX_EN on serializer MFP4
+	ret |= max96722_write_reg(max96722, I2C_DEV_SER,
+			0x02CA, DEV_REG_LENGTH_16BITS,
+			DEV_REG_VALUE_08BITS, 0x84);
+#endif
+
+	// MFP2, VS not gen internally, GPIO not used to gen fsync, manual mode
+	ret |= max96722_write_reg(max96722, I2C_DEV_DES,
+			0x04A0, DEV_REG_LENGTH_16BITS,
+			DEV_REG_VALUE_08BITS, 0x04);
+
+	return ret;
+}
+
+static int max96722_mipi_enable(struct max96722 *max96722, bool enable)
+{
+	int ret = 0;
+
+	if (enable) {
+#if MAXIM_FORCE_ALL_CLOCK_EN
+		// Force all MIPI clocks running
+		ret |= max96722_update_reg_bits(max96722, I2C_DEV_DES,
+				0x08A0, DEV_REG_LENGTH_16BITS, BIT(7), BIT(7));
+#endif
+		// CSI output enabled
+		ret |= max96722_update_reg_bits(max96722, I2C_DEV_DES,
+				0x040B, DEV_REG_LENGTH_16BITS, BIT(1), BIT(1));
+	} else {
+#if MAXIM_FORCE_ALL_CLOCK_EN
+		// Normal mode
+		ret |= max96722_update_reg_bits(max96722, I2C_DEV_DES,
+				0x08A0, DEV_REG_LENGTH_16BITS, BIT(7), 0x00);
+#endif
+		// CSI output disabled
+		ret |= max96722_update_reg_bits(max96722, I2C_DEV_DES,
+				0x040B, DEV_REG_LENGTH_16BITS, BIT(1), 0x00);
+	}
+
+	return ret;
+}
+
 static int max96722_get_reso_dist(const struct max96722_mode *mode,
-				 struct v4l2_mbus_framefmt *framefmt)
+				  struct v4l2_mbus_framefmt *framefmt)
 {
 	return abs(mode->width - framefmt->width) +
-		abs(mode->height - framefmt->height);
+	       abs(mode->height - framefmt->height);
 }
 
 static const struct max96722_mode *
-max96722_find_best_fit(struct v4l2_subdev_format *fmt)
+max96722_find_best_fit(struct max96722 *max96722, struct v4l2_subdev_format *fmt)
 {
 	struct v4l2_mbus_framefmt *framefmt = &fmt->format;
 	int dist;
@@ -313,28 +1061,32 @@
 	int cur_best_fit_dist = -1;
 	unsigned int i;
 
-	for (i = 0; i < ARRAY_SIZE(supported_modes); i++) {
-		dist = max96722_get_reso_dist(&supported_modes[i], framefmt);
-		if (cur_best_fit_dist == -1 || dist < cur_best_fit_dist) {
+	for (i = 0; i < max96722->cfg_modes_num; i++) {
+		dist = max96722_get_reso_dist(&max96722->supported_modes[i], framefmt);
+		if ((cur_best_fit_dist == -1 || dist < cur_best_fit_dist)
+				&& (max96722->supported_modes[i].bus_fmt == framefmt->code)) {
 			cur_best_fit_dist = dist;
 			cur_best_fit = i;
 		}
 	}
 
-	return &supported_modes[cur_best_fit];
+	return &max96722->supported_modes[cur_best_fit];
 }
 
 static int max96722_set_fmt(struct v4l2_subdev *sd,
-			   struct v4l2_subdev_pad_config *cfg,
-			  struct v4l2_subdev_format *fmt)
+			    struct v4l2_subdev_pad_config *cfg,
+			    struct v4l2_subdev_format *fmt)
 {
-	struct max96722 *max96722 = to_max96722(sd);
+	struct max96722 *max96722 = v4l2_get_subdevdata(sd);
 	const struct max96722_mode *mode;
+	u64 pixel_rate = 0;
+	u8 data_lanes;
 
 	mutex_lock(&max96722->mutex);
 
-	mode = max96722_find_best_fit(fmt);
-	fmt->format.code = MAX96722_MEDIA_BUS_FMT;
+	mode = max96722_find_best_fit(max96722, fmt);
+
+	fmt->format.code = mode->bus_fmt;
 	fmt->format.width = mode->width;
 	fmt->format.height = mode->height;
 	fmt->format.field = V4L2_FIELD_NONE;
@@ -350,6 +1102,19 @@
 			mutex_unlock(&max96722->mutex);
 			return -EBUSY;
 		}
+
+		max96722->cur_mode = mode;
+
+		__v4l2_ctrl_s_ctrl(max96722->link_freq, mode->link_freq_idx);
+		/* pixel rate = link frequency * 2 * lanes / BITS_PER_SAMPLE */
+		data_lanes = max96722->bus_cfg.bus.mipi_csi2.num_data_lanes;
+		pixel_rate = (u32)link_freq_items[mode->link_freq_idx] / mode->bpp * 2 * data_lanes;
+		__v4l2_ctrl_s_ctrl_int64(max96722->pixel_rate, pixel_rate);
+
+		dev_info(&max96722->client->dev, "mipi_freq_idx = %d, mipi_link_freq = %lld\n",
+				mode->link_freq_idx, link_freq_items[mode->link_freq_idx]);
+		dev_info(&max96722->client->dev, "pixel_rate = %lld, bpp = %d\n",
+				pixel_rate, mode->bpp);
 	}
 
 	mutex_unlock(&max96722->mutex);
@@ -358,10 +1123,10 @@
 }
 
 static int max96722_get_fmt(struct v4l2_subdev *sd,
-			   struct v4l2_subdev_pad_config *cfg,
-			   struct v4l2_subdev_format *fmt)
+			    struct v4l2_subdev_pad_config *cfg,
+			    struct v4l2_subdev_format *fmt)
 {
-	struct max96722 *max96722 = to_max96722(sd);
+	struct max96722 *max96722 = v4l2_get_subdevdata(sd);
 	const struct max96722_mode *mode = max96722->cur_mode;
 
 	mutex_lock(&max96722->mutex);
@@ -375,8 +1140,12 @@
 	} else {
 		fmt->format.width = mode->width;
 		fmt->format.height = mode->height;
-		fmt->format.code = MAX96722_MEDIA_BUS_FMT;
+		fmt->format.code = mode->bus_fmt;
 		fmt->format.field = V4L2_FIELD_NONE;
+		if (fmt->pad < PAD_MAX && fmt->pad >= PAD0)
+			fmt->reserved[0] = mode->vc[fmt->pad];
+		else
+			fmt->reserved[0] = mode->vc[PAD0];
 	}
 	mutex_unlock(&max96722->mutex);
 
@@ -384,38 +1153,43 @@
 }
 
 static int max96722_enum_mbus_code(struct v4l2_subdev *sd,
-				  struct v4l2_subdev_pad_config *cfg,
-				  struct v4l2_subdev_mbus_code_enum *code)
+				   struct v4l2_subdev_pad_config *cfg,
+				   struct v4l2_subdev_mbus_code_enum *code)
 {
+	struct max96722 *max96722 = v4l2_get_subdevdata(sd);
+	const struct max96722_mode *mode = max96722->cur_mode;
+
 	if (code->index != 0)
 		return -EINVAL;
-	code->code = MAX96722_MEDIA_BUS_FMT;
+	code->code = mode->bus_fmt;
 
 	return 0;
 }
 
 static int max96722_enum_frame_sizes(struct v4l2_subdev *sd,
-				    struct v4l2_subdev_pad_config *cfg,
-				   struct v4l2_subdev_frame_size_enum *fse)
+				     struct v4l2_subdev_pad_config *cfg,
+				     struct v4l2_subdev_frame_size_enum *fse)
 {
-	if (fse->index >= ARRAY_SIZE(supported_modes))
+	struct max96722 *max96722 = v4l2_get_subdevdata(sd);
+
+	if (fse->index >= max96722->cfg_modes_num)
 		return -EINVAL;
 
-	if (fse->code != MAX96722_MEDIA_BUS_FMT)
+	if (fse->code != max96722->supported_modes[fse->index].bus_fmt)
 		return -EINVAL;
 
-	fse->min_width  = supported_modes[fse->index].width;
-	fse->max_width  = supported_modes[fse->index].width;
-	fse->max_height = supported_modes[fse->index].height;
-	fse->min_height = supported_modes[fse->index].height;
+	fse->min_width  = max96722->supported_modes[fse->index].width;
+	fse->max_width  = max96722->supported_modes[fse->index].width;
+	fse->max_height = max96722->supported_modes[fse->index].height;
+	fse->min_height = max96722->supported_modes[fse->index].height;
 
 	return 0;
 }
 
 static int max96722_g_frame_interval(struct v4l2_subdev *sd,
-				    struct v4l2_subdev_frame_interval *fi)
+				     struct v4l2_subdev_frame_interval *fi)
 {
-	struct max96722 *max96722 = to_max96722(sd);
+	struct max96722 *max96722 = v4l2_get_subdevdata(sd);
 	const struct max96722_mode *mode = max96722->cur_mode;
 
 	mutex_lock(&max96722->mutex);
@@ -426,7 +1200,7 @@
 }
 
 static void max96722_get_module_inf(struct max96722 *max96722,
-				   struct rkmodule_inf *inf)
+				    struct rkmodule_inf *inf)
 {
 	memset(inf, 0, sizeof(*inf));
 	strscpy(inf->base.sensor, MAX96722_NAME, sizeof(inf->base.sensor));
@@ -435,26 +1209,30 @@
 	strscpy(inf->base.lens, max96722->len_name, sizeof(inf->base.lens));
 }
 
-static void max96722_get_vicap_rst_inf(struct max96722 *max96722,
-				   struct rkmodule_vicap_reset_info *rst_info)
+static void
+max96722_get_vicap_rst_inf(struct max96722 *max96722,
+			   struct rkmodule_vicap_reset_info *rst_info)
 {
 	struct i2c_client *client = max96722->client;
 
 	rst_info->is_reset = max96722->hot_plug;
 	max96722->hot_plug = false;
 	rst_info->src = RKCIF_RESET_SRC_ERR_HOTPLUG;
-	dev_info(&client->dev, "%s: rst_info->is_reset:%d.\n", __func__, rst_info->is_reset);
+	dev_info(&client->dev, "%s: rst_info->is_reset:%d.\n", __func__,
+		 rst_info->is_reset);
 }
 
-static void max96722_set_vicap_rst_inf(struct max96722 *max96722,
-				   struct rkmodule_vicap_reset_info rst_info)
+static void
+max96722_set_vicap_rst_inf(struct max96722 *max96722,
+			   struct rkmodule_vicap_reset_info rst_info)
 {
 	max96722->is_reset = rst_info.is_reset;
 }
 
 static long max96722_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
 {
-	struct max96722 *max96722 = to_max96722(sd);
+	struct max96722 *max96722 = v4l2_get_subdevdata(sd);
+	struct rkmodule_csi_dphy_param *dphy_param;
 	long ret = 0;
 	u32 stream = 0;
 
@@ -463,30 +1241,34 @@
 		max96722_get_module_inf(max96722, (struct rkmodule_inf *)arg);
 		break;
 	case RKMODULE_SET_QUICK_STREAM:
-
 		stream = *((u32 *)arg);
 
 		if (stream)
-			ret = max96722_write_reg(max96722->client,
-				 MAX96722_REG_CTRL_MODE,
-				 MAX96722_REG_VALUE_08BIT,
-				 MAX96722_MODE_STREAMING);
+			ret = max96722_mipi_enable(max96722, true);
 		else
-			ret = max96722_write_reg(max96722->client,
-				 MAX96722_REG_CTRL_MODE,
-				 MAX96722_REG_VALUE_08BIT,
-				 MAX96722_MODE_SW_STANDBY);
+			ret = max96722_mipi_enable(max96722, false);
 		break;
 	case RKMODULE_GET_VICAP_RST_INFO:
-		max96722_get_vicap_rst_inf(max96722,
-			(struct rkmodule_vicap_reset_info *)arg);
+		max96722_get_vicap_rst_inf(
+			max96722, (struct rkmodule_vicap_reset_info *)arg);
 		break;
 	case RKMODULE_SET_VICAP_RST_INFO:
-		max96722_set_vicap_rst_inf(max96722,
-			*(struct rkmodule_vicap_reset_info *)arg);
+		max96722_set_vicap_rst_inf(
+			max96722, *(struct rkmodule_vicap_reset_info *)arg);
 		break;
-	case RKMODULE_GET_CSI_DSI_INFO:
-		*(int *)arg = RKMODULE_CSI_INPUT;
+	case RKMODULE_GET_START_STREAM_SEQ:
+		break;
+	case RKMODULE_SET_CSI_DPHY_PARAM:
+		dphy_param = (struct rkmodule_csi_dphy_param *)arg;
+		if (dphy_param->vendor == rk3588_dcphy_param.vendor)
+			rk3588_dcphy_param = *dphy_param;
+		dev_dbg(&max96722->client->dev, "sensor set dphy param\n");
+		break;
+	case RKMODULE_GET_CSI_DPHY_PARAM:
+		dphy_param = (struct rkmodule_csi_dphy_param *)arg;
+		if (dphy_param->vendor == rk3588_dcphy_param.vendor)
+			*dphy_param = rk3588_dcphy_param;
+		dev_dbg(&max96722->client->dev, "sensor get dphy param\n");
 		break;
 	default:
 		ret = -ENOIOCTLCMD;
@@ -497,13 +1279,14 @@
 }
 
 #ifdef CONFIG_COMPAT
-static long max96722_compat_ioctl32(struct v4l2_subdev *sd,
-				   unsigned int cmd, unsigned long arg)
+static long max96722_compat_ioctl32(struct v4l2_subdev *sd, unsigned int cmd,
+				    unsigned long arg)
 {
 	void __user *up = compat_ptr(arg);
 	struct rkmodule_inf *inf;
 	struct rkmodule_awb_cfg *cfg;
 	struct rkmodule_vicap_reset_info *vicap_rst_inf;
+	struct rkmodule_csi_dphy_param *dphy_param;
 	long ret = 0;
 	int *seq;
 	u32 stream = 0;
@@ -547,7 +1330,8 @@
 
 		ret = max96722_ioctl(sd, cmd, vicap_rst_inf);
 		if (!ret) {
-			ret = copy_to_user(up, vicap_rst_inf, sizeof(*vicap_rst_inf));
+			ret = copy_to_user(up, vicap_rst_inf,
+					   sizeof(*vicap_rst_inf));
 			if (ret)
 				ret = -EFAULT;
 		}
@@ -589,20 +1373,34 @@
 		else
 			ret = -EFAULT;
 		break;
-	case RKMODULE_GET_CSI_DSI_INFO:
-		seq = kzalloc(sizeof(*seq), GFP_KERNEL);
-		if (!seq) {
+	case RKMODULE_SET_CSI_DPHY_PARAM:
+		dphy_param = kzalloc(sizeof(*dphy_param), GFP_KERNEL);
+		if (!dphy_param) {
 			ret = -ENOMEM;
 			return ret;
 		}
 
-		ret = max96722_ioctl(sd, cmd, seq);
+		ret = copy_from_user(dphy_param, up, sizeof(*dphy_param));
+		if (!ret)
+			ret = max96722_ioctl(sd, cmd, dphy_param);
+		else
+			ret = -EFAULT;
+		kfree(dphy_param);
+		break;
+	case RKMODULE_GET_CSI_DPHY_PARAM:
+		dphy_param = kzalloc(sizeof(*dphy_param), GFP_KERNEL);
+		if (!dphy_param) {
+			ret = -ENOMEM;
+			return ret;
+		}
+
+		ret = max96722_ioctl(sd, cmd, dphy_param);
 		if (!ret) {
-			ret = copy_to_user(up, seq, sizeof(*seq));
+			ret = copy_to_user(up, dphy_param, sizeof(*dphy_param));
 			if (ret)
 				ret = -EFAULT;
 		}
-		kfree(seq);
+		kfree(dphy_param);
 		break;
 	default:
 		ret = -ENOIOCTLCMD;
@@ -616,10 +1414,39 @@
 static int __max96722_start_stream(struct max96722 *max96722)
 {
 	int ret;
+	u32 link_freq_mhz, link_freq_idx;
 
-	ret = max96722_write_array(max96722->client, max96722->cur_mode->reg_list);
+	ret = max96722_check_link_lock_state(max96722);
 	if (ret)
 		return ret;
+
+	if (max96722->hot_plug_irq > 0)
+		enable_irq(max96722->hot_plug_irq);
+
+	ret = max96722_write_array(max96722,
+				   max96722->cur_mode->reg_list);
+	if (ret)
+		return ret;
+
+	link_freq_idx = max96722->cur_mode->link_freq_idx;
+	link_freq_mhz = (u32)div_s64(link_freq_items[link_freq_idx], 1000000L);
+	ret = max96722_dphy_dpll_predef_set(max96722, link_freq_mhz);
+	if (ret)
+		return ret;
+
+	if (max96722->auto_init_deskew_mask != 0) {
+		ret = max96722_auto_init_deskew(max96722,
+					max96722->auto_init_deskew_mask);
+		if (ret)
+			return ret;
+	}
+
+	if (max96722->frame_sync_period != 0) {
+		ret = max96722_frame_sync_period(max96722,
+					max96722->frame_sync_period);
+		if (ret)
+			return ret;
+	}
 
 	/* In case these controls are set before streaming */
 	mutex_unlock(&max96722->mutex);
@@ -628,31 +1455,28 @@
 	if (ret)
 		return ret;
 
-	return max96722_write_reg(max96722->client,
-				 MAX96722_REG_CTRL_MODE,
-				 MAX96722_REG_VALUE_08BIT,
-				 MAX96722_MODE_STREAMING);
+	return max96722_mipi_enable(max96722, true);
+
 }
 
 static int __max96722_stop_stream(struct max96722 *max96722)
 {
-	return max96722_write_reg(max96722->client,
-				 MAX96722_REG_CTRL_MODE,
-				 MAX96722_REG_VALUE_08BIT,
-				 MAX96722_MODE_SW_STANDBY);
+	if (max96722->hot_plug_irq > 0)
+		disable_irq(max96722->hot_plug_irq);
+
+	return max96722_mipi_enable(max96722, false);
 }
 
 static int max96722_s_stream(struct v4l2_subdev *sd, int on)
 {
-	struct max96722 *max96722 = to_max96722(sd);
+	struct max96722 *max96722 = v4l2_get_subdevdata(sd);
 	struct i2c_client *client = max96722->client;
 	int ret = 0;
 
 	dev_info(&client->dev, "%s: on: %d, %dx%d@%d\n", __func__, on,
-				max96722->cur_mode->width,
-				max96722->cur_mode->height,
+		max96722->cur_mode->width, max96722->cur_mode->height,
 		DIV_ROUND_CLOSEST(max96722->cur_mode->max_fps.denominator,
-				  max96722->cur_mode->max_fps.numerator));
+				max96722->cur_mode->max_fps.numerator));
 
 	mutex_lock(&max96722->mutex);
 	on = !!on;
@@ -687,7 +1511,7 @@
 
 static int max96722_s_power(struct v4l2_subdev *sd, int on)
 {
-	struct max96722 *max96722 = to_max96722(sd);
+	struct max96722 *max96722 = v4l2_get_subdevdata(sd);
 	struct i2c_client *client = max96722->client;
 	int ret = 0;
 
@@ -728,14 +1552,19 @@
 	u32 delay_us;
 	struct device *dev = &max96722->client->dev;
 
-	if (!IS_ERR(max96722->power_gpio))
+	if (!IS_ERR(max96722->power_gpio)) {
 		gpiod_set_value_cansleep(max96722->power_gpio, 1);
+		usleep_range(5000, 10000);
+	}
 
-	usleep_range(1000, 2000);
+	if (!IS_ERR(max96722->pocen_gpio)) {
+		gpiod_set_value_cansleep(max96722->pocen_gpio, 1);
+		usleep_range(5000, 10000);
+	}
 
 	if (!IS_ERR_OR_NULL(max96722->pins_default)) {
 		ret = pinctrl_select_state(max96722->pinctrl,
-					   max96722->pins_default);
+					max96722->pins_default);
 		if (ret < 0)
 			dev_err(dev, "could not set pins\n");
 	}
@@ -748,11 +1577,11 @@
 		dev_err(dev, "Failed to enable regulators\n");
 		goto disable_clk;
 	}
-
-	if (!IS_ERR(max96722->reset_gpio))
+	if (!IS_ERR(max96722->reset_gpio)) {
 		gpiod_set_value_cansleep(max96722->reset_gpio, 1);
+		usleep_range(500, 1000);
+	}
 
-	usleep_range(500, 1000);
 	if (!IS_ERR(max96722->pwdn_gpio))
 		gpiod_set_value_cansleep(max96722->pwdn_gpio, 1);
 
@@ -776,6 +1605,7 @@
 	if (!IS_ERR(max96722->pwdn_gpio))
 		gpiod_set_value_cansleep(max96722->pwdn_gpio, 0);
 	clk_disable_unprepare(max96722->xvclk);
+
 	if (!IS_ERR(max96722->reset_gpio))
 		gpiod_set_value_cansleep(max96722->reset_gpio, 0);
 
@@ -785,17 +1615,21 @@
 		if (ret < 0)
 			dev_dbg(dev, "could not set pins\n");
 	}
-	if (!IS_ERR(max96722->power_gpio))
-		gpiod_set_value_cansleep(max96722->power_gpio, 0);
 
 	regulator_bulk_disable(MAX96722_NUM_SUPPLIES, max96722->supplies);
+
+	if (!IS_ERR(max96722->pocen_gpio))
+		gpiod_set_value_cansleep(max96722->pocen_gpio, 0);
+
+	if (!IS_ERR(max96722->power_gpio))
+		gpiod_set_value_cansleep(max96722->power_gpio, 0);
 }
 
 static int max96722_runtime_resume(struct device *dev)
 {
 	struct i2c_client *client = to_i2c_client(dev);
 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
-	struct max96722 *max96722 = to_max96722(sd);
+	struct max96722 *max96722 = v4l2_get_subdevdata(sd);
 
 	return __max96722_power_on(max96722);
 }
@@ -804,7 +1638,7 @@
 {
 	struct i2c_client *client = to_i2c_client(dev);
 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
-	struct max96722 *max96722 = to_max96722(sd);
+	struct max96722 *max96722 = v4l2_get_subdevdata(sd);
 
 	__max96722_power_off(max96722);
 
@@ -814,16 +1648,16 @@
 #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
 static int max96722_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
 {
-	struct max96722 *max96722 = to_max96722(sd);
+	struct max96722 *max96722 = v4l2_get_subdevdata(sd);
 	struct v4l2_mbus_framefmt *try_fmt =
-				v4l2_subdev_get_try_format(sd, fh->pad, 0);
-	const struct max96722_mode *def_mode = &supported_modes[0];
+		v4l2_subdev_get_try_format(sd, fh->pad, 0);
+	const struct max96722_mode *def_mode = &max96722->supported_modes[0];
 
 	mutex_lock(&max96722->mutex);
 	/* Initialize try_fmt */
 	try_fmt->width = def_mode->width;
 	try_fmt->height = def_mode->height;
-	try_fmt->code = MAX96722_MEDIA_BUS_FMT;
+	try_fmt->code = def_mode->bus_fmt;
 	try_fmt->field = V4L2_FIELD_NONE;
 
 	mutex_unlock(&max96722->mutex);
@@ -833,38 +1667,60 @@
 }
 #endif
 
-static int max96722_enum_frame_interval(struct v4l2_subdev *sd,
-				       struct v4l2_subdev_pad_config *cfg,
-				       struct v4l2_subdev_frame_interval_enum *fie)
+static int
+max96722_enum_frame_interval(struct v4l2_subdev *sd,
+			     struct v4l2_subdev_pad_config *cfg,
+			     struct v4l2_subdev_frame_interval_enum *fie)
 {
-	if (fie->index >= ARRAY_SIZE(supported_modes))
+	struct max96722 *max96722 = v4l2_get_subdevdata(sd);
+
+	if (fie->index >= max96722->cfg_modes_num)
 		return -EINVAL;
 
-	fie->code = MAX96722_MEDIA_BUS_FMT;
-
-	fie->width = supported_modes[fie->index].width;
-	fie->height = supported_modes[fie->index].height;
-	fie->interval = supported_modes[fie->index].max_fps;
+	fie->code = max96722->supported_modes[fie->index].bus_fmt;
+	fie->width = max96722->supported_modes[fie->index].width;
+	fie->height = max96722->supported_modes[fie->index].height;
+	fie->interval = max96722->supported_modes[fie->index].max_fps;
 
 	return 0;
 }
 
 static int max96722_g_mbus_config(struct v4l2_subdev *sd, unsigned int pad,
-				struct v4l2_mbus_config *config)
+				  struct v4l2_mbus_config *config)
 {
+	struct max96722 *max96722 = v4l2_get_subdevdata(sd);
+	u32 val = 0;
+	u8 data_lanes = max96722->bus_cfg.bus.mipi_csi2.num_data_lanes;
+
+	val |= V4L2_MBUS_CSI2_CONTINUOUS_CLOCK;
+	val |= (1 << (data_lanes - 1));
+	switch (data_lanes) {
+	case 4:
+		val |= V4L2_MBUS_CSI2_CHANNEL_3;
+		fallthrough;
+	case 3:
+		val |= V4L2_MBUS_CSI2_CHANNEL_2;
+		fallthrough;
+	case 2:
+		val |= V4L2_MBUS_CSI2_CHANNEL_1;
+		fallthrough;
+	case 1:
+	default:
+		val |= V4L2_MBUS_CSI2_CHANNEL_0;
+		break;
+	}
+
 	config->type = V4L2_MBUS_CSI2_DPHY;
-	config->flags = V4L2_MBUS_CSI2_4_LANE |
-			V4L2_MBUS_CSI2_CHANNEL_0 |
-			V4L2_MBUS_CSI2_CONTINUOUS_CLOCK;
+	config->flags = val;
 
 	return 0;
 }
 
 static int max96722_get_selection(struct v4l2_subdev *sd,
-				struct v4l2_subdev_pad_config *cfg,
-				struct v4l2_subdev_selection *sel)
+				  struct v4l2_subdev_pad_config *cfg,
+				  struct v4l2_subdev_selection *sel)
 {
-	struct max96722 *max96722 = to_max96722(sd);
+	struct max96722 *max96722 = v4l2_get_subdevdata(sd);
 
 	if (sel->target == V4L2_SEL_TGT_CROP_BOUNDS) {
 		sel->r.left = 0;
@@ -877,10 +1733,8 @@
 	return -EINVAL;
 }
 
-static const struct dev_pm_ops max96722_pm_ops = {
-	SET_RUNTIME_PM_OPS(max96722_runtime_suspend,
-			   max96722_runtime_resume, NULL)
-};
+static const struct dev_pm_ops max96722_pm_ops = { SET_RUNTIME_PM_OPS(
+	max96722_runtime_suspend, max96722_runtime_resume, NULL) };
 
 #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
 static const struct v4l2_subdev_internal_ops max96722_internal_ops = {
@@ -912,15 +1766,17 @@
 };
 
 static const struct v4l2_subdev_ops max96722_subdev_ops = {
-	.core	= &max96722_core_ops,
-	.video	= &max96722_video_ops,
-	.pad	= &max96722_pad_ops,
+	.core = &max96722_core_ops,
+	.video = &max96722_video_ops,
+	.pad = &max96722_pad_ops,
 };
 
 static int max96722_initialize_controls(struct max96722 *max96722)
 {
 	const struct max96722_mode *mode;
 	struct v4l2_ctrl_handler *handler;
+	u64 pixel_rate;
+	u8 data_lanes;
 	int ret;
 
 	handler = &max96722->ctrl_handler;
@@ -932,21 +1788,25 @@
 	handler->lock = &max96722->mutex;
 
 	max96722->link_freq = v4l2_ctrl_new_int_menu(handler, NULL,
-			V4L2_CID_LINK_FREQ,
-			1, 0, link_freq_items);
+				V4L2_CID_LINK_FREQ,
+				ARRAY_SIZE(link_freq_items) - 1, 0,
+				link_freq_items);
+	__v4l2_ctrl_s_ctrl(max96722->link_freq, mode->link_freq_idx);
+	dev_info(&max96722->client->dev, "mipi_freq_idx = %d, mipi_link_freq = %lld\n",
+			mode->link_freq_idx, link_freq_items[mode->link_freq_idx]);
 
-	max96722->pixel_rate = v4l2_ctrl_new_std(handler, NULL,
-			V4L2_CID_PIXEL_RATE,
-			0, MAX96722_PIXEL_RATE,
-			1, MAX96722_PIXEL_RATE);
-
-	__v4l2_ctrl_s_ctrl(max96722->link_freq,
-			   mode->link_freq_idx);
+	/* pixel rate = link frequency * 2 * lanes / BITS_PER_SAMPLE */
+	data_lanes = max96722->bus_cfg.bus.mipi_csi2.num_data_lanes;
+	pixel_rate = (u32)link_freq_items[mode->link_freq_idx] / mode->bpp * 2 * data_lanes;
+	max96722->pixel_rate =
+		v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE, 0,
+				  pixel_rate, 1, pixel_rate);
+	dev_info(&max96722->client->dev, "pixel_rate = %lld, bpp = %d\n",
+			pixel_rate, mode->bpp);
 
 	if (handler->error) {
 		ret = handler->error;
-		dev_err(&max96722->client->dev,
-			"Failed to init controls(%d)\n", ret);
+		dev_err(&max96722->client->dev, "Failed to init controls(%d)\n", ret);
 		goto err_free_handler;
 	}
 
@@ -960,25 +1820,6 @@
 	return ret;
 }
 
-static int max96722_check_sensor_id(struct max96722 *max96722,
-				   struct i2c_client *client)
-{
-	struct device *dev = &max96722->client->dev;
-	u32 id = 0;
-	int ret;
-
-	ret = max96722_read_reg(client, MAX96722_REG_CHIP_ID,
-			       MAX96722_REG_VALUE_08BIT, &id);
-	if (id != CHIP_ID) {
-		dev_err(dev, "Unexpected sensor id(%02x), ret(%d)\n", id, ret);
-		return -ENODEV;
-	}
-
-	dev_info(dev, "Detected %02x sensor\n", id);
-
-	return 0;
-}
-
 static int max96722_configure_regulators(struct max96722 *max96722)
 {
 	unsigned int i;
@@ -987,44 +1828,132 @@
 		max96722->supplies[i].supply = max96722_supply_names[i];
 
 	return devm_regulator_bulk_get(&max96722->client->dev,
-					MAX96722_NUM_SUPPLIES,
-					max96722->supplies);
+				       MAX96722_NUM_SUPPLIES,
+				       max96722->supplies);
+}
+
+static int max96722_parse_dt(struct max96722 *max96722)
+{
+	struct device *dev = &max96722->client->dev;
+	struct device_node *node = dev->of_node;
+	u8 mipi_data_lanes = max96722->bus_cfg.bus.mipi_csi2.num_data_lanes;
+	u32 value = 0;
+	int ret = 0;
+
+	/* serializer i2c address */
+	ret = of_property_read_u32(node, "ser-i2c-addr", &value);
+	if (ret) {
+		max96722->i2c_addr[I2C_DEV_SER] = SER_I2C_ADDR;
+	} else {
+		dev_info(dev, "ser-i2c-addr property: %d\n", value);
+		max96722->i2c_addr[I2C_DEV_SER] = value;
+	}
+	dev_info(dev, "serializer i2c address: 0x%02x\n", max96722->i2c_addr[I2C_DEV_SER]);
+
+	/* max96722 link mask:
+	 *     bit[3:0] = link enable mask: 0 = disable, 1 = enable:
+	 *         bit0 - LinkA, bit1 - LinkB, bit2 - LinkC, bit3 - LinkD
+	 *     bit[7:4] = link type, 0 = GMSL1, 1 = GMSL2:
+	 *         bit4 - LinkA, bit5 - LinkB, bit6 - LinkC, bit7 = LinkD
+	 */
+	ret = of_property_read_u32(node, "link-mask", &max96722->link_mask);
+	if (ret) {
+		/* default link mask */
+		if (mipi_data_lanes == 4)
+			max96722->link_mask = 0xFF; /* Link A/B/C/D: GMSL2 and enable */
+		else
+			max96722->link_mask = 0x33; /* Link A/B: GMSL2 and enable */
+	} else {
+		dev_info(dev, "link-mask property: 0x%08x\n", max96722->link_mask);
+	}
+	dev_info(dev, "serdes link mask: 0x%02x\n", max96722->link_mask);
+
+	/* auto initial deskew mask */
+	ret = of_property_read_u32(node, "auto-init-deskew-mask",
+				&max96722->auto_init_deskew_mask);
+	if (ret)
+		max96722->auto_init_deskew_mask = 0x0F; // 0x0F: default enable all
+	dev_info(dev, "auto init deskew mask: 0x%02x\n", max96722->auto_init_deskew_mask);
+
+	/* FSYNC period config */
+	ret = of_property_read_u32(node, "frame-sync-period",
+				&max96722->frame_sync_period);
+	if (ret)
+		max96722->frame_sync_period = 0; // 0: disable (default)
+	dev_info(dev, "frame sync period: %d\n", max96722->frame_sync_period);
+
+	return 0;
 }
 
 static int max96722_probe(struct i2c_client *client,
-			 const struct i2c_device_id *id)
+			  const struct i2c_device_id *id)
 {
 	struct device *dev = &client->dev;
 	struct device_node *node = dev->of_node;
 	struct max96722 *max96722;
 	struct v4l2_subdev *sd;
+	struct device_node *endpoint;
 	char facing[2];
+	u8 mipi_data_lanes;
 	int ret;
 
-	dev_info(dev, "driver version: %02x.%02x.%02x",
-		DRIVER_VERSION >> 16,
-		(DRIVER_VERSION & 0xff00) >> 8,
-		DRIVER_VERSION & 0x00ff);
+	dev_info(dev, "driver version: %02x.%02x.%02x", DRIVER_VERSION >> 16,
+		 (DRIVER_VERSION & 0xff00) >> 8, DRIVER_VERSION & 0x00ff);
 
 	max96722 = devm_kzalloc(dev, sizeof(*max96722), GFP_KERNEL);
 	if (!max96722)
 		return -ENOMEM;
 
 	ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX,
-				   &max96722->module_index);
+					&max96722->module_index);
 	ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING,
-				       &max96722->module_facing);
+					&max96722->module_facing);
 	ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME,
-				       &max96722->module_name);
+					&max96722->module_name);
 	ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME,
-				       &max96722->len_name);
+					&max96722->len_name);
 	if (ret) {
 		dev_err(dev, "could not get module information!\n");
 		return -EINVAL;
 	}
 
+	max96722->regmap = devm_regmap_init_i2c(client, &max96722_regmap_config);
+	if (IS_ERR(max96722->regmap)) {
+		dev_err(dev, "Failed to regmap initialize I2C\n");
+		return PTR_ERR(max96722->regmap);
+	}
+
 	max96722->client = client;
-	max96722->cur_mode = &supported_modes[0];
+	i2c_set_clientdata(client, max96722);
+
+	/* i2c default address init */
+	max96722->i2c_addr[I2C_DEV_DES] = client->addr;
+	max96722->i2c_addr[I2C_DEV_SER] = SER_I2C_ADDR;
+	max96722->i2c_addr[I2C_DEV_CAM] = CAM_I2C_ADDR;
+
+	endpoint = of_graph_get_next_endpoint(dev->of_node, NULL);
+	if (!endpoint) {
+		dev_err(dev, "Failed to get endpoint\n");
+		return -EINVAL;
+	}
+
+	ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(endpoint),
+				&max96722->bus_cfg);
+	if (ret) {
+		dev_err(dev, "Failed to get bus config\n");
+		return -EINVAL;
+	}
+	mipi_data_lanes = max96722->bus_cfg.bus.mipi_csi2.num_data_lanes;
+	dev_info(dev, "mipi csi2 phy data lanes %d\n", mipi_data_lanes);
+
+	if (mipi_data_lanes == 4) {
+		max96722->supported_modes = supported_modes_4lane;
+		max96722->cfg_modes_num = ARRAY_SIZE(supported_modes_4lane);
+	} else {
+		dev_err(dev, "Not support mipi data lane: %d\n", mipi_data_lanes);
+		return -EINVAL;
+	}
+	max96722->cur_mode = &max96722->supported_modes[0];
 
 	max96722->power_gpio = devm_gpiod_get(dev, "power", GPIOD_OUT_LOW);
 	if (IS_ERR(max96722->power_gpio))
@@ -1038,6 +1967,14 @@
 	if (IS_ERR(max96722->pwdn_gpio))
 		dev_warn(dev, "Failed to get pwdn-gpios\n");
 
+	max96722->pocen_gpio = devm_gpiod_get(dev, "pocen", GPIOD_OUT_LOW);
+	if (IS_ERR(max96722->pocen_gpio))
+		dev_warn(dev, "Failed to get pocen-gpios\n");
+
+	max96722->lock_gpio = devm_gpiod_get(dev, "lock", GPIOD_IN);
+	if (IS_ERR(max96722->lock_gpio))
+		dev_warn(dev, "Failed to get lock-gpios\n");
+
 	ret = max96722_configure_regulators(max96722);
 	if (ret) {
 		dev_err(dev, "Failed to get power regulators\n");
@@ -1046,18 +1983,18 @@
 
 	max96722->pinctrl = devm_pinctrl_get(dev);
 	if (!IS_ERR(max96722->pinctrl)) {
-		max96722->pins_default =
-			pinctrl_lookup_state(max96722->pinctrl,
-					     OF_CAMERA_PINCTRL_STATE_DEFAULT);
+		max96722->pins_default = pinctrl_lookup_state(
+			max96722->pinctrl, OF_CAMERA_PINCTRL_STATE_DEFAULT);
 		if (IS_ERR(max96722->pins_default))
 			dev_err(dev, "could not get default pinstate\n");
 
-		max96722->pins_sleep =
-			pinctrl_lookup_state(max96722->pinctrl,
-					     OF_CAMERA_PINCTRL_STATE_SLEEP);
+		max96722->pins_sleep = pinctrl_lookup_state(
+			max96722->pinctrl, OF_CAMERA_PINCTRL_STATE_SLEEP);
 		if (IS_ERR(max96722->pins_sleep))
 			dev_err(dev, "could not get sleep pinstate\n");
 	}
+
+	max96722_parse_dt(max96722);
 
 	mutex_init(&max96722->mutex);
 
@@ -1071,16 +2008,7 @@
 	if (ret)
 		goto err_free_handler;
 
-	ret = max96722_write_reg(max96722->client,
-				 MAX96722_REMOTE_CTRL,
-				 MAX96722_REG_VALUE_08BIT,
-				 MAX96722_REMOTE_DISABLE);
-	if (ret) {
-		dev_err(dev, "disable i2c remote control error\n");
-		goto err_power_off;
-	}
-
-	ret = max96722_check_sensor_id(max96722, client);
+	ret = max96722_check_local_chipid(max96722);
 	if (ret)
 		goto err_power_off;
 
@@ -1102,13 +2030,36 @@
 	else
 		facing[0] = 'f';
 
+	v4l2_set_subdevdata(sd, max96722);
+
 	snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s",
-		 max96722->module_index, facing,
-		 MAX96722_NAME, dev_name(sd->dev));
+		max96722->module_index, facing, MAX96722_NAME,
+		dev_name(sd->dev));
 	ret = v4l2_async_register_subdev_sensor_common(sd);
 	if (ret) {
 		dev_err(dev, "v4l2 async register subdev failed\n");
 		goto err_clean_entity;
+	}
+
+	if (!IS_ERR(max96722->lock_gpio)) {
+		max96722->hot_plug_irq = gpiod_to_irq(max96722->lock_gpio);
+		if (max96722->hot_plug_irq < 0) {
+			dev_err(dev, "failed to get hot plug irq\n");
+		} else {
+			ret = devm_request_threaded_irq(dev,
+					max96722->hot_plug_irq,
+					NULL,
+					max96722_hot_plug_detect_irq_handler,
+					IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+					"max96722_hot_plug",
+					max96722);
+			if (ret) {
+				dev_err(dev, "failed to request hot plug irq (%d)\n", ret);
+				max96722->hot_plug_irq = -1;
+			} else {
+				disable_irq(max96722->hot_plug_irq);
+			}
+		}
 	}
 
 	pm_runtime_set_active(dev);
@@ -1134,7 +2085,7 @@
 static int max96722_remove(struct i2c_client *client)
 {
 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
-	struct max96722 *max96722 = to_max96722(sd);
+	struct max96722 *max96722 = v4l2_get_subdevdata(sd);
 
 	v4l2_async_unregister_subdev(sd);
 #if defined(CONFIG_MEDIA_CONTROLLER)
@@ -1185,7 +2136,7 @@
 	i2c_del_driver(&max96722_i2c_driver);
 }
 
-device_initcall_sync(sensor_mod_init);
+module_init(sensor_mod_init);
 module_exit(sensor_mod_exit);
 
 MODULE_DESCRIPTION("Maxim max96722 deserializer driver");
diff --git a/kernel/drivers/media/i2c/maxim4c/Kconfig b/kernel/drivers/media/i2c/maxim4c/Kconfig
new file mode 100644
index 0000000..667ce82
--- /dev/null
+++ b/kernel/drivers/media/i2c/maxim4c/Kconfig
@@ -0,0 +1,46 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Maxim Quad GMSL deserializer and serializer devices
+#
+config VIDEO_DES_MAXIM4C
+	tristate "Maxim Quad GMSL deserializer support"
+	depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+	depends on MEDIA_CAMERA_SUPPORT
+	select V4L2_FWNODE
+	help
+	  This driver supports the Maxim Quad GMSL2/GMSL1 deserializer.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called maxim4c.
+
+menu "Maxim Quad GMSL serializer devices support"
+	visible if VIDEO_DES_MAXIM4C
+
+config MAXIM4C_SER_MAX9295
+	tristate "Maxim GMSL2 serializer max9295 support"
+	depends on VIDEO_DES_MAXIM4C
+	help
+	  This driver supports the Maxim GMSL2 max9295 serializer.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called remote_max9295.
+
+config MAXIM4C_SER_MAX96715
+	tristate "Maxim GMSL1 Serializer max96715 support"
+	depends on VIDEO_DES_MAXIM4C
+	help
+	  This driver supports the Maxim GMSL1 max96715 serializer.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called remote_max96715.
+
+config MAXIM4C_SER_MAX96717
+	tristate "Maxim GMSL2 Serializer max96717 support"
+	depends on VIDEO_DES_MAXIM4C
+	help
+	  This driver supports the Maxim GMSL2 max96717 serializer.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called remote_max96717.
+
+endmenu
diff --git a/kernel/drivers/media/i2c/maxim4c/Makefile b/kernel/drivers/media/i2c/maxim4c/Makefile
new file mode 100644
index 0000000..f1ee630
--- /dev/null
+++ b/kernel/drivers/media/i2c/maxim4c/Makefile
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_VIDEO_DES_MAXIM4C)	+= maxim4c.o
+maxim4c-objs	+= maxim4c_i2c.o \
+		   maxim4c_mipi_txphy.o \
+		   maxim4c_video_pipe.o \
+		   maxim4c_link.o \
+		   maxim4c_remote.o \
+		   maxim4c_pattern.o \
+		   maxim4c_v4l2.o \
+		   maxim4c_drv.o
+
+obj-$(CONFIG_MAXIM4C_SER_MAX9295)	+= remote_max9295.o
+obj-$(CONFIG_MAXIM4C_SER_MAX96715)	+= remote_max96715.o
+obj-$(CONFIG_MAXIM4C_SER_MAX96717)	+= remote_max96717.o
diff --git a/kernel/drivers/media/i2c/maxim4c/maxim4c_api.h b/kernel/drivers/media/i2c/maxim4c/maxim4c_api.h
new file mode 100644
index 0000000..6dd3bbc
--- /dev/null
+++ b/kernel/drivers/media/i2c/maxim4c/maxim4c_api.h
@@ -0,0 +1,101 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Maxim Quad GMSL Deserializer driver API function declaration
+ *
+ * Copyright (c) 2023 Rockchip Electronics Co., Ltd.
+ *
+ * Author: Cai Wenzhong <cwz@rock-chips.com>
+ */
+
+#ifndef __MAXIM4C_API_H__
+#define __MAXIM4C_API_H__
+
+#include "maxim4c_i2c.h"
+#include "maxim4c_link.h"
+#include "maxim4c_video_pipe.h"
+#include "maxim4c_mipi_txphy.h"
+#include "maxim4c_remote.h"
+#include "maxim4c_pattern.h"
+#include "maxim4c_drv.h"
+
+#define MAXIM4C_NAME			"maxim4c"
+
+/* Maxim Deserializer Test Pattern */
+#define MAXIM4C_TEST_PATTERN		0
+
+/* Maxim Deserializer pwdn on/off enable */
+#define MAXIM4C_LOCAL_DES_ON_OFF_EN	0
+
+/* maxim4c i2c api */
+int maxim4c_i2c_write_reg(struct i2c_client *client,
+		u16 reg_addr, u16 reg_len, u16 val_len, u32 reg_val);
+int maxim4c_i2c_read_reg(struct i2c_client *client,
+		u16 reg_addr, u16 reg_len, u16 val_len, u32 *reg_val);
+int maxim4c_i2c_update_reg(struct i2c_client *client,
+		u16 reg_addr, u16 reg_len,
+		u32 val_len, u32 val_mask, u32 reg_val);
+
+int maxim4c_i2c_write_byte(struct i2c_client *client,
+		u16 reg_addr, u16 reg_len, u8 reg_val);
+int maxim4c_i2c_read_byte(struct i2c_client *client,
+		u16 reg_addr, u16 reg_len, u8 *reg_val);
+int maxim4c_i2c_update_byte(struct i2c_client *client,
+		u16 reg_addr, u16 reg_len, u8 val_mask, u8 reg_val);
+
+int maxim4c_i2c_write_array(struct i2c_client *client,
+				const struct maxim4c_i2c_regval *regs);
+int maxim4c_i2c_load_init_seq(struct device *dev,
+		struct device_node *node, struct maxim4c_i2c_init_seq *init_seq);
+int maxim4c_i2c_run_init_seq(struct i2c_client *client,
+			struct maxim4c_i2c_init_seq *init_seq);
+
+/* maxim4c link api */
+u8 maxim4c_link_get_lock_state(maxim4c_t *maxim4c, u8 link_mask);
+int maxim4c_link_oneshot_reset(maxim4c_t *maxim4c, u8 link_mask);
+int maxim4c_link_mask_enable(maxim4c_t *maxim4c, u8 link_mask, bool enable);
+int maxim4c_link_wait_linklock(maxim4c_t *maxim4c, u8 link_mask);
+int maxim4c_link_select_remote_enable(maxim4c_t *maxim4c, u8 link_mask);
+int maxim4c_link_select_remote_control(maxim4c_t *maxim4c, u8 link_mask);
+int maxim4c_link_hw_init(maxim4c_t *maxim4c);
+void maxim4c_link_data_init(maxim4c_t *maxim4c);
+int maxim4c_link_parse_dt(maxim4c_t *maxim4c, struct device_node *of_node);
+
+/* maxim4c video pipe api */
+int maxim4c_video_pipe_hw_init(maxim4c_t *maxim4c);
+int maxim4c_video_pipe_mask_enable(maxim4c_t *maxim4c, u8 video_pipe_mask, bool enable);
+int maxim4c_video_pipe_linkid_enable(maxim4c_t *maxim4c, u8 link_id, bool enable);
+void maxim4c_video_pipe_data_init(maxim4c_t *maxim4c);
+int maxim4c_video_pipe_parse_dt(maxim4c_t *maxim4c, struct device_node *of_node);
+
+/* maxim4c mipi txphy api */
+int maxim4c_mipi_txphy_hw_init(maxim4c_t *maxim4c);
+void maxim4c_mipi_txphy_data_init(maxim4c_t *maxim4c);
+int maxim4c_mipi_txphy_parse_dt(maxim4c_t *maxim4c, struct device_node *of_node);
+int maxim4c_mipi_txphy_enable(maxim4c_t *maxim4c, bool enable);
+int maxim4c_dphy_dpll_predef_set(maxim4c_t *maxim4c, s64 link_freq_hz);
+int maxim4c_mipi_csi_output(maxim4c_t *maxim4c, bool enable);
+
+/* maxim4c remote api */
+int maxim4c_remote_mfd_add_devices(maxim4c_t *maxim4c);
+int maxim4c_remote_devices_init(maxim4c_t *maxim4c, u8 link_init_mask);
+int maxim4c_remote_devices_deinit(maxim4c_t *maxim4c, u8 link_init_mask);
+int maxim4c_remote_load_init_seq(maxim4c_remote_t *remote_device);
+int maxim4c_remote_i2c_addr_select(maxim4c_remote_t *remote_device, u32 i2c_id);
+int maxim4c_remote_i2c_client_init(maxim4c_remote_t *remote_device,
+				struct i2c_client *des_client);
+int maxim4c_remote_device_register(maxim4c_t *maxim4c,
+				maxim4c_remote_t *remote_device);
+
+/* maxim4c v4l2 subdev api */
+int maxim4c_v4l2_subdev_init(maxim4c_t *maxim4c);
+void maxim4c_v4l2_subdev_deinit(maxim4c_t *maxim4c);
+
+int maxim4c_module_hw_init(maxim4c_t *maxim4c);
+
+/* maxim4c pattern api */
+int maxim4c_pattern_hw_init(maxim4c_t *maxim4c);
+int maxim4c_pattern_support_mode_init(maxim4c_t *maxim4c);
+int maxim4c_pattern_data_init(maxim4c_t *maxim4c);
+int maxim4c_pattern_enable(maxim4c_t *maxim4c, bool enable);
+
+#endif /* __MAXIM4C_API_H__ */
diff --git a/kernel/drivers/media/i2c/maxim4c/maxim4c_drv.c b/kernel/drivers/media/i2c/maxim4c/maxim4c_drv.c
new file mode 100644
index 0000000..88924dc
--- /dev/null
+++ b/kernel/drivers/media/i2c/maxim4c/maxim4c_drv.c
@@ -0,0 +1,737 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Maxim Quad GMSL2/GMSL1 to CSI-2 Deserializer driver
+ *
+ * Copyright (C) 2023 Rockchip Electronics Co., Ltd.
+ *
+ * Author: Cai Wenzhong <cwz@rock-chips.com>
+ *
+ * V2.00.00 maxim serdes quad GMSL2/GMSL1 driver framework.
+ *     1. local deserializer support: max96712/max96722
+ *     2. remote serializer support: max9295/max96715/max96717
+ *     3. support deserializer and serializer auto adaptive
+ *     4. support deserializer output test pattern
+ *     5. support remote serializer I2c address mapping
+ *     6. support remote serializer hot plug detection and recovery
+ *
+ * V2.01.00
+ *     1. remote device and local link are bound through link id
+ *     2. support local and remote port chain check
+ *     3. drivers/media/i2c/maxim4c/Kconfig support menu select
+ *     4. optimize delay time and error messages
+ *     5. power control: local by pwdn gpio, remote by pocen gpio
+ *     6. local pwdn on/off enable depend on MAXIM4C_LOCAL_DES_ON_OFF_EN
+ *
+ */
+#include <linux/clk.h>
+#include <linux/i2c.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/sysfs.h>
+#include <linux/slab.h>
+#include <linux/version.h>
+#include <linux/compat.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of_graph.h>
+#include <linux/pm_runtime.h>
+#include <linux/workqueue.h>
+#include <linux/rk-camera-module.h>
+#include <linux/gpio/consumer.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/regulator/consumer.h>
+#include <media/media-entity.h>
+#include <media/v4l2-async.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-subdev.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-subdev.h>
+
+#include "maxim4c_api.h"
+
+#define DRIVER_VERSION			KERNEL_VERSION(2, 0x01, 0x00)
+
+#define MAXIM4C_XVCLK_FREQ		25000000
+
+static int maxim4c_check_local_chipid(maxim4c_t *maxim4c)
+{
+	struct i2c_client *client = maxim4c->client;
+	struct device *dev = &client->dev;
+	int ret = 0, loop = 0;
+	u8 chipid = 0;
+
+	for (loop = 0; loop < 5; loop++) {
+		if (loop != 0) {
+			dev_info(dev, "check local chipid retry (%d)", loop);
+			msleep(10);
+		}
+
+		ret = maxim4c_i2c_read_byte(client,
+				MAXIM4C_REG_CHIP_ID, MAXIM4C_I2C_REG_ADDR_16BITS,
+				&chipid);
+		if (ret == 0) {
+			if (chipid == maxim4c->chipid) {
+				if (chipid == MAX96712_CHIP_ID) {
+					dev_info(dev, "MAX96712 is Detected\n");
+					return 0;
+				}
+
+				if (chipid == MAX96722_CHIP_ID) {
+					dev_info(dev, "MAX96722 is Detected\n");
+					return 0;
+				}
+			} else {
+				dev_err(dev, "Unexpected maxim chipid = %02x\n", chipid);
+				return -ENODEV;
+			}
+		}
+	}
+
+	dev_err(dev, "maxim check chipid error, ret(%d)\n", ret);
+
+	return -ENODEV;
+}
+
+static irqreturn_t maxim4c_hot_plug_detect_irq_handler(int irq, void *dev_id)
+{
+	struct maxim4c *maxim4c = dev_id;
+	struct device *dev = &maxim4c->client->dev;
+	int lock_gpio_level = 0;
+
+	mutex_lock(&maxim4c->mutex);
+	if (maxim4c->streaming) {
+		lock_gpio_level = gpiod_get_value_cansleep(maxim4c->lock_gpio);
+		if (lock_gpio_level == 0) {
+			dev_info(dev, "serializer hot plug out\n");
+
+			maxim4c->hot_plug_state = MAXIM4C_HOT_PLUG_OUT;
+		} else {
+			dev_info(dev, "serializer hot plug in\n");
+
+			maxim4c->hot_plug_state = MAXIM4C_HOT_PLUG_IN;
+		}
+
+		queue_delayed_work(maxim4c->hot_plug_work.state_check_wq,
+					&maxim4c->hot_plug_work.state_d_work,
+					msecs_to_jiffies(50));
+	}
+	mutex_unlock(&maxim4c->mutex);
+
+	return IRQ_HANDLED;
+}
+
+static void maxim4c_lock_irq_init(maxim4c_t *maxim4c)
+{
+	struct device *dev = &maxim4c->client->dev;
+	int ret = 0;
+
+	if (!IS_ERR(maxim4c->lock_gpio)) {
+		maxim4c->hot_plug_irq = gpiod_to_irq(maxim4c->lock_gpio);
+		if (maxim4c->hot_plug_irq < 0) {
+			dev_err(dev, "failed to get hot plug irq\n");
+		} else {
+			ret = devm_request_threaded_irq(dev,
+					maxim4c->hot_plug_irq,
+					NULL,
+					maxim4c_hot_plug_detect_irq_handler,
+					IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+					"maxim4c_hot_plug",
+					maxim4c);
+			if (ret) {
+				dev_err(dev, "failed to request hot plug irq (%d)\n", ret);
+				maxim4c->hot_plug_irq = -1;
+			} else {
+				disable_irq(maxim4c->hot_plug_irq);
+			}
+		}
+	}
+}
+
+static void maxim4c_hot_plug_state_check_work(struct work_struct *work)
+{
+	struct maxim4c_hot_plug_work *hot_plug_work =
+		container_of(work, struct maxim4c_hot_plug_work, state_d_work.work);
+	struct maxim4c *maxim4c =
+		container_of(hot_plug_work, struct maxim4c, hot_plug_work);
+	struct device *dev = &maxim4c->client->dev;
+	u8 curr_lock_state = 0, last_lock_state = 0, link_lock_change = 0;
+	u8 link_enable_mask = 0, link_id = 0;
+
+	dev_dbg(dev, "%s\n", __func__);
+
+	mutex_lock(&maxim4c->mutex);
+	if (maxim4c->streaming == 0) {
+		mutex_unlock(&maxim4c->mutex);
+		return;
+	}
+
+	link_enable_mask = maxim4c->gmsl_link.link_enable_mask;
+	last_lock_state = maxim4c->link_lock_state;
+	if ((maxim4c->hot_plug_state == MAXIM4C_HOT_PLUG_OUT)
+			&& (last_lock_state == link_enable_mask)) {
+		maxim4c_link_select_remote_control(maxim4c, 0);
+	}
+
+	curr_lock_state = maxim4c_link_get_lock_state(maxim4c, link_enable_mask);
+	link_lock_change = (last_lock_state ^ curr_lock_state);
+	if (link_lock_change) {
+		dev_dbg(dev, "lock state: current = 0x%02x, last = 0x%02x\n",
+			curr_lock_state, last_lock_state);
+
+		maxim4c->link_lock_state = curr_lock_state;
+	}
+
+	if (link_lock_change & MAXIM4C_LINK_MASK_A) {
+		link_id = MAXIM4C_LINK_ID_A;
+
+		if (curr_lock_state & MAXIM4C_LINK_MASK_A) {
+			dev_info(dev, "Link A plug in\n");
+
+			maxim4c_remote_devices_init(maxim4c, MAXIM4C_LINK_MASK_A);
+
+			maxim4c_video_pipe_linkid_enable(maxim4c, link_id, true);
+		} else {
+			dev_info(dev, "Link A plug out\n");
+
+			maxim4c_video_pipe_linkid_enable(maxim4c, link_id, false);
+		}
+	}
+
+	if (link_lock_change & MAXIM4C_LINK_MASK_B) {
+		link_id = MAXIM4C_LINK_ID_B;
+
+		if (curr_lock_state & MAXIM4C_LINK_MASK_B) {
+			dev_info(dev, "Link B plug in\n");
+
+			maxim4c_remote_devices_init(maxim4c, MAXIM4C_LINK_MASK_B);
+
+			maxim4c_video_pipe_linkid_enable(maxim4c, link_id, true);
+		} else {
+			dev_info(dev, "Link B plug out\n");
+
+			maxim4c_video_pipe_linkid_enable(maxim4c, link_id, false);
+		}
+	}
+
+	if (link_lock_change & MAXIM4C_LINK_MASK_C) {
+		link_id = MAXIM4C_LINK_ID_C;
+
+		if (curr_lock_state & MAXIM4C_LINK_MASK_C) {
+			dev_info(dev, "Link C plug in\n");
+
+			maxim4c_remote_devices_init(maxim4c, MAXIM4C_LINK_MASK_C);
+
+			maxim4c_video_pipe_linkid_enable(maxim4c, link_id, true);
+		} else {
+			dev_info(dev, "Link C plug out\n");
+
+			maxim4c_video_pipe_linkid_enable(maxim4c, link_id, false);
+		}
+	}
+
+	if (link_lock_change & MAXIM4C_LINK_MASK_D) {
+		link_id = MAXIM4C_LINK_ID_D;
+
+		if (curr_lock_state & MAXIM4C_LINK_MASK_D) {
+			dev_info(dev, "Link D plug in\n");
+
+			maxim4c_remote_devices_init(maxim4c, MAXIM4C_LINK_MASK_D);
+
+			maxim4c_video_pipe_linkid_enable(maxim4c, link_id, true);
+		} else {
+			dev_info(dev, "Link D plug out\n");
+
+			maxim4c_video_pipe_linkid_enable(maxim4c, link_id, false);
+		}
+	}
+
+	if (curr_lock_state == link_enable_mask) {
+		// remote control mask enable
+		maxim4c_link_select_remote_control(maxim4c, link_enable_mask);
+	} else {
+		queue_delayed_work(maxim4c->hot_plug_work.state_check_wq,
+				&maxim4c->hot_plug_work.state_d_work,
+				msecs_to_jiffies(100));
+	}
+
+	mutex_unlock(&maxim4c->mutex);
+}
+
+static int maxim4c_lock_state_work_init(maxim4c_t *maxim4c)
+{
+	struct device *dev = &maxim4c->client->dev;
+
+	INIT_DELAYED_WORK(&maxim4c->hot_plug_work.state_d_work,
+			maxim4c_hot_plug_state_check_work);
+	maxim4c->hot_plug_work.state_check_wq =
+		create_singlethread_workqueue("maxim4c work queue");
+	if (maxim4c->hot_plug_work.state_check_wq == NULL) {
+		dev_err(dev, "failed to create hot plug work queue\n");
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static int maxim4c_lock_state_work_deinit(maxim4c_t *maxim4c)
+{
+	if (maxim4c->hot_plug_work.state_check_wq) {
+		cancel_delayed_work_sync(&maxim4c->hot_plug_work.state_d_work);
+		destroy_workqueue(maxim4c->hot_plug_work.state_check_wq);
+		maxim4c->hot_plug_work.state_check_wq = NULL;
+	}
+
+	return 0;
+}
+
+/* Calculate the delay in us by clock rate and clock cycles */
+static inline u32 maxim4c_cal_delay(u32 cycles)
+{
+	return DIV_ROUND_UP(cycles, MAXIM4C_XVCLK_FREQ / 1000 / 1000);
+}
+
+static int maxim4c_local_device_power_on(maxim4c_t *maxim4c)
+{
+	struct device *dev = &maxim4c->client->dev;
+
+	if (!IS_ERR(maxim4c->pwdn_gpio)) {
+		dev_info(dev, "local device pwdn gpio on\n");
+
+		gpiod_set_value_cansleep(maxim4c->pwdn_gpio, 1);
+
+		usleep_range(5000, 10000);
+	}
+
+	return 0;
+}
+
+static void maxim4c_local_device_power_off(maxim4c_t *maxim4c)
+{
+	struct device *dev = &maxim4c->client->dev;
+
+	if (!IS_ERR(maxim4c->pwdn_gpio)) {
+		dev_info(dev, "local device pwdn gpio off\n");
+
+		gpiod_set_value_cansleep(maxim4c->pwdn_gpio, 0);
+	}
+}
+
+static int maxim4c_remote_device_power_on(maxim4c_t *maxim4c)
+{
+	struct device *dev = &maxim4c->client->dev;
+
+	// remote PoC enable
+	if (!IS_ERR(maxim4c->pocen_gpio)) {
+		dev_info(dev, "remote device pocen gpio on\n");
+
+		gpiod_set_value_cansleep(maxim4c->pocen_gpio, 1);
+		usleep_range(5000, 10000);
+	}
+
+	return 0;
+}
+
+static int maxim4c_remote_device_power_off(maxim4c_t *maxim4c)
+{
+	struct device *dev = &maxim4c->client->dev;
+
+	// remote PoC enable
+	if (!IS_ERR(maxim4c->pocen_gpio)) {
+		dev_info(dev, "remote device pocen gpio off\n");
+
+		gpiod_set_value_cansleep(maxim4c->pocen_gpio, 0);
+	}
+
+	return 0;
+}
+
+static int maxim4c_runtime_resume(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct maxim4c *maxim4c = v4l2_get_subdevdata(sd);
+	int ret = 0;
+
+#if MAXIM4C_LOCAL_DES_ON_OFF_EN
+	ret |= maxim4c_local_device_power_on(maxim4c);
+#endif /* MAXIM4C_LOCAL_DES_ON_OFF_EN */
+
+	ret |= maxim4c_remote_device_power_on(maxim4c);
+
+	return ret;
+}
+
+static int maxim4c_runtime_suspend(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct maxim4c *maxim4c = v4l2_get_subdevdata(sd);
+	int ret = 0;
+
+	ret |= maxim4c_remote_device_power_off(maxim4c);
+
+#if MAXIM4C_LOCAL_DES_ON_OFF_EN
+	maxim4c_local_device_power_off(maxim4c);
+#endif /* MAXIM4C_LOCAL_DES_ON_OFF_EN */
+
+	return ret;
+}
+
+static const struct dev_pm_ops maxim4c_pm_ops = {
+	SET_RUNTIME_PM_OPS(
+		maxim4c_runtime_suspend, maxim4c_runtime_resume, NULL)
+};
+
+static void maxim4c_module_data_init(maxim4c_t *maxim4c)
+{
+	maxim4c_link_data_init(maxim4c);
+	maxim4c_video_pipe_data_init(maxim4c);
+	maxim4c_mipi_txphy_data_init(maxim4c);
+}
+
+static int maxim4c_extra_init_seq_parse(maxim4c_t *maxim4c, struct device_node *node)
+{
+	struct device *dev = &maxim4c->client->dev;
+	struct device_node *init_seq_node = NULL;
+	struct maxim4c_i2c_init_seq *init_seq = NULL;
+
+	init_seq_node = of_get_child_by_name(node, "extra-init-sequence");
+	if (IS_ERR_OR_NULL(init_seq_node)) {
+		dev_dbg(dev, "%pOF no child node extra-init-sequence\n", node);
+		return 0;
+	}
+
+	if (!of_device_is_available(init_seq_node)) {
+		dev_dbg(dev, "%pOF is disabled\n", init_seq_node);
+
+		of_node_put(init_seq_node);
+		return 0;
+	}
+
+	dev_info(dev, "load extra-init-sequence\n");
+
+	init_seq = &maxim4c->extra_init_seq;
+	maxim4c_i2c_load_init_seq(dev,
+			init_seq_node, init_seq);
+
+	of_node_put(init_seq_node);
+
+	return 0;
+}
+
+static int maxim4c_module_parse_dt(maxim4c_t *maxim4c)
+{
+	struct device *dev = &maxim4c->client->dev;
+	struct device_node *node = NULL;
+
+	// maxim serdes local
+	node = of_get_child_by_name(dev->of_node, "serdes-local-device");
+	if (IS_ERR_OR_NULL(node)) {
+		dev_err(dev, "%pOF has no child node: serdes-local-device\n",
+				dev->of_node);
+
+		return -ENODEV;
+	}
+
+	if (!of_device_is_available(node)) {
+		dev_info(dev, "%pOF is disabled\n", node);
+
+		of_node_put(node);
+		return -ENODEV;
+	}
+
+	/* gmsl link parse dt */
+	maxim4c_link_parse_dt(maxim4c, node);
+
+	/* video pipe parse dt */
+	maxim4c_video_pipe_parse_dt(maxim4c, node);
+
+	/* mipi txphy parse dt */
+	maxim4c_mipi_txphy_parse_dt(maxim4c, node);
+
+	/* extra init seq parse dt */
+	maxim4c_extra_init_seq_parse(maxim4c, node);
+
+	of_node_put(node);
+
+	return 0;
+}
+
+static int maxim4c_run_extra_init_seq(maxim4c_t *maxim4c)
+{
+	struct i2c_client *client = maxim4c->client;
+	struct device *dev = &client->dev;
+	int ret = 0;
+
+	ret = maxim4c_i2c_run_init_seq(client,
+			&maxim4c->extra_init_seq);
+	if (ret) {
+		dev_err(dev, "extra init sequence error\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int maxim4c_module_hw_previnit(maxim4c_t *maxim4c)
+{
+	struct i2c_client *client = maxim4c->client;
+	int ret = 0;
+
+	// All links disable at beginning.
+	ret = maxim4c_i2c_write_byte(client,
+			0x0006, MAXIM4C_I2C_REG_ADDR_16BITS,
+			0xF0);
+	if (ret)
+		return ret;
+
+	// MIPI CSI output disable.
+	ret = maxim4c_i2c_write_byte(client,
+			0x040B, MAXIM4C_I2C_REG_ADDR_16BITS,
+			0x00);
+	if (ret)
+		return ret;
+
+	// MIPI TXPHY standby
+	ret = maxim4c_i2c_update_byte(client,
+			0x08A2, MAXIM4C_I2C_REG_ADDR_16BITS,
+			0xF0, 0x00);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int maxim4c_module_hw_postinit(maxim4c_t *maxim4c)
+{
+	struct i2c_client *client = maxim4c->client;
+	int ret = 0;
+
+	// video pipe disable all
+	ret |= maxim4c_i2c_write_byte(client,
+			0x00F4, MAXIM4C_I2C_REG_ADDR_16BITS,
+			0);
+
+	// remote control disable all
+	ret |= maxim4c_link_select_remote_control(maxim4c, 0);
+
+	return ret;
+}
+
+int maxim4c_module_hw_init(maxim4c_t *maxim4c)
+{
+	struct device *dev = &maxim4c->client->dev;
+	int ret = 0;
+
+	ret = maxim4c_module_hw_previnit(maxim4c);
+	if (ret) {
+		dev_err(dev, "%s: hw prev init error\n", __func__);
+
+		return ret;
+	}
+
+	ret = maxim4c_link_hw_init(maxim4c);
+	if (ret) {
+		dev_err(dev, "%s: hw link init error\n", __func__);
+		return ret;
+	}
+
+	ret = maxim4c_video_pipe_hw_init(maxim4c);
+	if (ret) {
+		dev_err(dev, "%s: hw pipe init error\n", __func__);
+		return ret;
+	}
+
+	ret = maxim4c_mipi_txphy_hw_init(maxim4c);
+	if (ret) {
+		dev_err(dev, "%s: hw txphy init error\n", __func__);
+		return ret;
+	}
+
+	ret = maxim4c_run_extra_init_seq(maxim4c);
+	if (ret) {
+		dev_err(dev, "%s: run extra init seq error\n", __func__);
+		return ret;
+	}
+
+	ret = maxim4c_module_hw_postinit(maxim4c);
+	if (ret) {
+		dev_err(dev, "%s: hw post init error\n", __func__);
+		return ret;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(maxim4c_module_hw_init);
+
+static int maxim4c_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
+{
+	struct device *dev = &client->dev;
+	struct device_node *node = dev->of_node;
+	maxim4c_t *maxim4c = NULL;
+	u32 chip_id;
+	int ret = 0;
+
+	dev_info(dev, "driver version: %02x.%02x.%02x", DRIVER_VERSION >> 16,
+		 (DRIVER_VERSION & 0xff00) >> 8, DRIVER_VERSION & 0x00ff);
+
+	chip_id = (uintptr_t)of_device_get_match_data(dev);
+	if (chip_id == MAX96712_CHIP_ID) {
+		dev_info(dev, "maxim4c driver for max96712\n");
+	} else if (chip_id == MAX96722_CHIP_ID) {
+		dev_info(dev, "maxim4c driver for max96722\n");
+	} else {
+		dev_err(dev, "maxim4c driver unknown chip\n");
+		return -EINVAL;
+	}
+
+	maxim4c = devm_kzalloc(dev, sizeof(*maxim4c), GFP_KERNEL);
+	if (!maxim4c) {
+		dev_err(dev, "maxim4c probe no memory error\n");
+		return -ENOMEM;
+	}
+
+	maxim4c->client = client;
+	maxim4c->chipid = chip_id;
+
+	ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX,
+				   &maxim4c->module_index);
+	ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING,
+				       &maxim4c->module_facing);
+	ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME,
+				       &maxim4c->module_name);
+	ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME,
+				       &maxim4c->len_name);
+	if (ret) {
+		dev_err(dev, "could not get module information!\n");
+		return -EINVAL;
+	}
+
+	maxim4c->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_OUT_LOW);
+	if (IS_ERR(maxim4c->pwdn_gpio))
+		dev_warn(dev, "Failed to get pwdn-gpios, maybe no use\n");
+
+	maxim4c->pocen_gpio = devm_gpiod_get(dev, "pocen", GPIOD_OUT_LOW);
+	if (IS_ERR(maxim4c->pocen_gpio))
+		dev_warn(dev, "Failed to get pocen-gpios\n");
+
+	maxim4c->lock_gpio = devm_gpiod_get(dev, "lock", GPIOD_IN);
+	if (IS_ERR(maxim4c->lock_gpio))
+		dev_warn(dev, "Failed to get lock-gpios\n");
+
+	mutex_init(&maxim4c->mutex);
+
+	ret = maxim4c_local_device_power_on(maxim4c);
+	if (ret)
+		goto err_destroy_mutex;
+
+	ret = maxim4c_check_local_chipid(maxim4c);
+	if (ret)
+		goto err_power_off;
+
+	// client->dev->driver_data = subdev
+	// subdev->dev->driver_data = maxim4c
+	ret = maxim4c_v4l2_subdev_init(maxim4c);
+	if (ret) {
+		dev_err(dev, "maxim4c probe v4l2 subdev init error\n");
+		goto err_power_off;
+	}
+
+#if MAXIM4C_TEST_PATTERN
+	ret = maxim4c_pattern_data_init(maxim4c);
+	if (ret)
+		goto err_power_off;
+
+#if (MAXIM4C_LOCAL_DES_ON_OFF_EN == 0)
+	ret = maxim4c_pattern_hw_init(maxim4c);
+	if (ret)
+		goto err_power_off;
+#endif /* MAXIM4C_LOCAL_DES_ON_OFF_EN */
+
+	pm_runtime_set_active(dev);
+	pm_runtime_enable(dev);
+	pm_runtime_idle(dev);
+
+	return 0;
+#endif /* MAXIM4C_TEST_PATTERN */
+
+	maxim4c_module_data_init(maxim4c);
+	maxim4c_module_parse_dt(maxim4c);
+
+#if (MAXIM4C_LOCAL_DES_ON_OFF_EN == 0)
+	ret = maxim4c_module_hw_init(maxim4c);
+	if (ret)
+		goto err_subdev_deinit;
+#endif /* MAXIM4C_LOCAL_DES_ON_OFF_EN */
+
+	ret = maxim4c_remote_mfd_add_devices(maxim4c);
+	if (ret)
+		goto err_subdev_deinit;
+
+	maxim4c_lock_irq_init(maxim4c);
+	maxim4c_lock_state_work_init(maxim4c);
+
+	pm_runtime_set_active(dev);
+	pm_runtime_enable(dev);
+	pm_runtime_idle(dev);
+
+	return 0;
+
+err_subdev_deinit:
+	maxim4c_v4l2_subdev_deinit(maxim4c);
+err_power_off:
+	maxim4c_local_device_power_off(maxim4c);
+err_destroy_mutex:
+	mutex_destroy(&maxim4c->mutex);
+
+	return ret;
+}
+
+static int maxim4c_remove(struct i2c_client *client)
+{
+	maxim4c_t *maxim4c = i2c_get_clientdata(client);
+
+	maxim4c_lock_state_work_deinit(maxim4c);
+
+	maxim4c_v4l2_subdev_deinit(maxim4c);
+
+	mutex_destroy(&maxim4c->mutex);
+
+	pm_runtime_disable(&client->dev);
+	if (!pm_runtime_status_suspended(&client->dev))
+		maxim4c_local_device_power_off(maxim4c);
+	pm_runtime_set_suspended(&client->dev);
+
+	return 0;
+}
+
+static const struct of_device_id maxim4c_of_match[] = {
+	{
+		.compatible = "maxim4c,max96712",
+		.data = (const void *)MAX96712_CHIP_ID
+	}, {
+		.compatible = "maxim4c,max96722",
+		.data = (const void *)MAX96722_CHIP_ID
+	},
+	{ /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, maxim4c_of_match);
+
+static struct i2c_driver maxim4c_i2c_driver = {
+	.driver = {
+		.name = MAXIM4C_NAME,
+		.pm = &maxim4c_pm_ops,
+		.of_match_table = of_match_ptr(maxim4c_of_match),
+	},
+	.probe		= &maxim4c_probe,
+	.remove		= &maxim4c_remove,
+};
+
+module_i2c_driver(maxim4c_i2c_driver);
+
+MODULE_AUTHOR("Cai Wenzhong <cwz@rock-chips.com>");
+MODULE_DESCRIPTION("Maxim quad gmsl deserializer driver");
+MODULE_LICENSE("GPL");
diff --git a/kernel/drivers/media/i2c/maxim4c/maxim4c_drv.h b/kernel/drivers/media/i2c/maxim4c/maxim4c_drv.h
new file mode 100644
index 0000000..444f20f
--- /dev/null
+++ b/kernel/drivers/media/i2c/maxim4c/maxim4c_drv.h
@@ -0,0 +1,109 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2023 Rockchip Electronics Co., Ltd.
+ *
+ */
+
+#ifndef __MAXIM4C_DRV_H__
+#define __MAXIM4C_DRV_H__
+
+#include <linux/workqueue.h>
+#include <linux/rk-camera-module.h>
+#include <linux/mfd/core.h>
+#include <media/media-entity.h>
+#include <media/v4l2-async.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-subdev.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-subdev.h>
+
+#include "maxim4c_i2c.h"
+#include "maxim4c_link.h"
+#include "maxim4c_mipi_txphy.h"
+#include "maxim4c_remote.h"
+#include "maxim4c_pattern.h"
+
+#define MAXIM4C_REG_CHIP_ID		0x0D
+#define MAX96712_CHIP_ID		0xA0
+#define MAX96722_CHIP_ID		0xA1
+
+enum {
+	MAXIM4C_HOT_PLUG_OUT = 0,
+	MAXIM4C_HOT_PLUG_IN,
+};
+
+struct maxim4c_hot_plug_work {
+	struct workqueue_struct *state_check_wq;
+	struct delayed_work state_d_work;
+	u32 hot_plug_state;
+};
+
+struct maxim4c_mode {
+	u32 width;
+	u32 height;
+	struct v4l2_fract max_fps;
+	u32 hts_def;
+	u32 vts_def;
+	u32 exp_def;
+	u32 link_freq_idx;
+	u32 bus_fmt;
+	u32 bpp;
+	const struct regval *reg_list;
+	u32 vc[PAD_MAX];
+};
+
+typedef struct maxim4c {
+	struct i2c_client *client;
+	struct clk *xvclk;
+	struct gpio_desc *pwdn_gpio;
+	struct gpio_desc *pocen_gpio;
+	struct gpio_desc *lock_gpio;
+
+	struct mutex mutex;
+
+	struct v4l2_subdev subdev;
+	struct media_pad pad;
+	struct v4l2_ctrl_handler ctrl_handler;
+	struct v4l2_ctrl *exposure;
+	struct v4l2_ctrl *anal_gain;
+	struct v4l2_ctrl *digi_gain;
+	struct v4l2_ctrl *hblank;
+	struct v4l2_ctrl *vblank;
+	struct v4l2_ctrl *pixel_rate;
+	struct v4l2_ctrl *link_freq;
+	struct v4l2_fwnode_endpoint bus_cfg;
+
+	u32 chipid;
+
+	bool streaming;
+	bool power_on;
+	bool hot_plug;
+	u8 is_reset;
+	int hot_plug_irq;
+	u32 hot_plug_state;
+	u32 link_lock_state;
+	struct maxim4c_hot_plug_work hot_plug_work;
+
+	struct maxim4c_mode supported_mode;
+	const struct maxim4c_mode *cur_mode;
+	u32 cfg_modes_num;
+
+	u32 module_index;
+	const char *module_facing;
+	const char *module_name;
+	const char *len_name;
+
+	maxim4c_gmsl_link_t gmsl_link;
+	maxim4c_video_pipe_t video_pipe;
+	maxim4c_mipi_txphy_t mipi_txphy;
+
+	struct maxim4c_pattern pattern;
+
+	struct maxim4c_i2c_init_seq extra_init_seq;
+
+	struct mfd_cell remote_mfd_devs[MAXIM4C_LINK_ID_MAX];
+	maxim4c_remote_t *remote_device[MAXIM4C_LINK_ID_MAX];
+} maxim4c_t;
+
+#endif /* __MAXIM4C_DRV_H__ */
diff --git a/kernel/drivers/media/i2c/maxim4c/maxim4c_i2c.c b/kernel/drivers/media/i2c/maxim4c/maxim4c_i2c.c
new file mode 100644
index 0000000..77aba74
--- /dev/null
+++ b/kernel/drivers/media/i2c/maxim4c/maxim4c_i2c.c
@@ -0,0 +1,407 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Maxim Quad GMSL Deserializer I2C read/write driver
+ *
+ * Copyright (C) 2023 Rockchip Electronics Co., Ltd.
+ *
+ * Author: Cai Wenzhong <cwz@rock-chips.com>
+ *
+ */
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/i2c-dev.h>
+#include "maxim4c_i2c.h"
+
+/* Write registers up to 4 at a time */
+int maxim4c_i2c_write_reg(struct i2c_client *client,
+		u16 reg_addr, u16 reg_len, u32 val_len, u32 reg_val)
+{
+	u32 buf_i, val_i;
+	u8 buf[6];
+	u8 *val_p;
+	__be32 val_be;
+
+	dev_info(&client->dev, "i2c addr(0x%02x) write: 0x%04x (%d) = 0x%08x (%d)\n",
+			client->addr, reg_addr, reg_len, reg_val, val_len);
+
+	if (val_len > 4)
+		return -EINVAL;
+
+	if (reg_len == 2) {
+		buf[0] = reg_addr >> 8;
+		buf[1] = reg_addr & 0xff;
+
+		buf_i = 2;
+	} else {
+		buf[0] = reg_addr & 0xff;
+
+		buf_i = 1;
+	}
+
+	val_be = cpu_to_be32(reg_val);
+	val_p = (u8 *)&val_be;
+	val_i = 4 - val_len;
+
+	while (val_i < 4)
+		buf[buf_i++] = val_p[val_i++];
+
+	if (i2c_master_send(client, buf, (val_len + reg_len)) != (val_len + reg_len)) {
+		dev_err(&client->dev,
+			"%s: writing register 0x%04x from 0x%02x failed\n",
+			__func__, reg_addr, client->addr);
+		return -EIO;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(maxim4c_i2c_write_reg);
+
+/* Read registers up to 4 at a time */
+int maxim4c_i2c_read_reg(struct i2c_client *client,
+		u16 reg_addr, u16 reg_len, u32 val_len, u32 *reg_val)
+{
+	struct i2c_msg msgs[2];
+	u8 *data_be_p;
+	__be32 data_be = 0;
+	__be16 reg_addr_be = cpu_to_be16(reg_addr);
+	u8 *reg_be_p;
+	int ret;
+
+	if (val_len > 4 || !val_len)
+		return -EINVAL;
+
+	data_be_p = (u8 *)&data_be;
+	reg_be_p = (u8 *)&reg_addr_be;
+
+	/* Write register address */
+	msgs[0].addr = client->addr;
+	msgs[0].flags = 0;
+	msgs[0].len = reg_len;
+	msgs[0].buf = &reg_be_p[2 - reg_len];
+
+	/* Read data from register */
+	msgs[1].addr = client->addr;
+	msgs[1].flags = I2C_M_RD;
+	msgs[1].len = val_len;
+	msgs[1].buf = &data_be_p[4 - val_len];
+
+	ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+	if (ret != ARRAY_SIZE(msgs)) {
+		dev_err(&client->dev,
+			"%s: reading register 0x%04x from 0x%02x failed\n",
+			__func__, reg_addr, client->addr);
+		return -EIO;
+	}
+
+	*reg_val = be32_to_cpu(data_be);
+
+#if 0
+	dev_info(&client->dev, "i2c addr(0x%02x) read: 0x%04x (%d) = 0x%08x (%d)\n",
+		client->addr, reg_addr, reg_len, *reg_val, val_len);
+#endif
+
+	return 0;
+}
+EXPORT_SYMBOL(maxim4c_i2c_read_reg);
+
+/* Update registers up to 4 at a time */
+int maxim4c_i2c_update_reg(struct i2c_client *client,
+		u16 reg_addr, u16 reg_len, u32 val_len, u32 val_mask, u32 reg_val)
+{
+	u32 value;
+	int ret;
+
+	ret = maxim4c_i2c_read_reg(client, reg_addr, reg_len, val_len, &value);
+	if (ret)
+		return ret;
+
+	value &= ~val_mask;
+	value |= (reg_val & val_mask);
+	ret = maxim4c_i2c_write_reg(client, reg_addr, reg_len, val_len, value);
+
+	return ret;
+}
+EXPORT_SYMBOL(maxim4c_i2c_update_reg);
+
+int maxim4c_i2c_write_byte(struct i2c_client *client,
+		u16 reg_addr, u16 reg_len, u8 reg_val)
+{
+	int ret = 0;
+
+	ret = maxim4c_i2c_write_reg(client,
+			reg_addr, reg_len,
+			MAXIM4C_I2C_REG_VALUE_08BITS, reg_val);
+
+	return ret;
+}
+EXPORT_SYMBOL(maxim4c_i2c_write_byte);
+
+int maxim4c_i2c_read_byte(struct i2c_client *client,
+		u16 reg_addr, u16 reg_len, u8 *reg_val)
+{
+	int ret = 0;
+	u32 value = 0;
+	u8 *value_be_p = (u8 *)&value;
+
+	ret = maxim4c_i2c_read_reg(client,
+			reg_addr, reg_len,
+			MAXIM4C_I2C_REG_VALUE_08BITS, &value);
+
+	*reg_val = *value_be_p;
+
+	return ret;
+}
+EXPORT_SYMBOL(maxim4c_i2c_read_byte);
+
+int maxim4c_i2c_update_byte(struct i2c_client *client,
+		u16 reg_addr, u16 reg_len, u8 val_mask, u8 reg_val)
+{
+	u8 value;
+	int ret;
+
+	ret = maxim4c_i2c_read_byte(client, reg_addr, reg_len, &value);
+	if (ret)
+		return ret;
+
+	value &= ~val_mask;
+	value |= (reg_val & val_mask);
+	ret = maxim4c_i2c_write_byte(client, reg_addr, reg_len, value);
+
+	return ret;
+}
+EXPORT_SYMBOL(maxim4c_i2c_update_byte);
+
+int maxim4c_i2c_write_array(struct i2c_client *client,
+				const struct maxim4c_i2c_regval *regs)
+{
+	u32 i = 0;
+	int ret = 0;
+
+	for (i = 0; (ret == 0) && (regs[i].reg_addr != MAXIM4C_REG_NULL); i++) {
+		if (regs[i].val_mask != 0)
+			ret = maxim4c_i2c_update_reg(client,
+					regs[i].reg_addr, regs[i].reg_len,
+					regs[i].val_len, regs[i].val_mask, regs[i].reg_val);
+		else
+			ret = maxim4c_i2c_write_reg(client,
+					regs[i].reg_addr, regs[i].reg_len,
+					regs[i].val_len, regs[i].reg_val);
+
+		if (regs[i].delay != 0)
+			usleep_range(regs[i].delay * 1000, regs[i].delay * 1000 + 100);
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(maxim4c_i2c_write_array);
+
+static int maxim4c_i2c_parse_init_seq(struct device *dev,
+		const u8 *seq_data, int data_len, struct maxim4c_i2c_init_seq *init_seq)
+{
+	struct maxim4c_i2c_regval *reg_val = NULL;
+	u8 *data_buf = NULL, *d8 = NULL;
+	u32 i = 0;
+
+	if ((seq_data == NULL) || (init_seq == NULL)) {
+		dev_err(dev, "%s: input parameter = NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	if ((init_seq->seq_item_size == 0)
+			|| (data_len == 0)
+			|| (init_seq->reg_len == 0)
+			|| (init_seq->val_len == 0)) {
+		dev_err(dev, "%s: input parameter size zero\n", __func__);
+		return -EINVAL;
+	}
+
+	// data_len = seq_item_size * N
+	if (data_len % init_seq->seq_item_size) {
+		dev_err(dev, "%s: data_len or seq_item_size error\n", __func__);
+		return -EINVAL;
+	}
+
+	// seq_item_size = reg_len + val_len * 2 + 1
+	if (init_seq->seq_item_size !=
+			(init_seq->reg_len + init_seq->val_len * 2 + 1)) {
+		dev_err(dev, "%s: seq_item_size or reg_len or val_len error\n", __func__);
+		return -EINVAL;
+	}
+
+	data_buf = devm_kmemdup(dev, seq_data, data_len, GFP_KERNEL);
+	if (!data_buf) {
+		dev_err(dev, "%s data buf error\n", __func__);
+		return -ENOMEM;
+	}
+
+	d8 = data_buf;
+
+	init_seq->reg_seq_size = data_len / init_seq->seq_item_size;
+	init_seq->reg_seq_size += 1; // add 1 for end register setting
+
+	init_seq->reg_init_seq = devm_kcalloc(dev, init_seq->reg_seq_size,
+					sizeof(struct maxim4c_i2c_regval), GFP_KERNEL);
+	if (!init_seq->reg_init_seq) {
+		dev_err(dev, "%s init seq buffer error\n", __func__);
+		return -ENOMEM;
+	}
+
+	for (i = 0; i < init_seq->reg_seq_size - 1; i++) {
+		reg_val = &init_seq->reg_init_seq[i];
+
+		reg_val->reg_len = init_seq->reg_len;
+		reg_val->val_len = init_seq->val_len;
+
+		reg_val->reg_addr = 0;
+		switch (init_seq->reg_len) {
+		case 4:
+			reg_val->reg_addr |= (*d8 << 24);
+			d8 += 1;
+			fallthrough;
+		case 3:
+			reg_val->reg_addr |= (*d8 << 16);
+			d8 += 1;
+			fallthrough;
+		case 2:
+			reg_val->reg_addr |= (*d8 << 8);
+			d8 += 1;
+			fallthrough;
+		case 1:
+			reg_val->reg_addr |= (*d8 << 0);
+			d8 += 1;
+			break;
+		}
+
+		reg_val->reg_val = 0;
+		switch (init_seq->val_len) {
+		case 4:
+			reg_val->reg_val |= (*d8 << 24);
+			d8 += 1;
+			fallthrough;
+		case 3:
+			reg_val->reg_val |= (*d8 << 16);
+			d8 += 1;
+			fallthrough;
+		case 2:
+			reg_val->reg_val |= (*d8 << 8);
+			d8 += 1;
+			fallthrough;
+		case 1:
+			reg_val->reg_val |= (*d8 << 0);
+			d8 += 1;
+			break;
+		}
+
+		reg_val->val_mask = 0;
+		switch (init_seq->val_len) {
+		case 4:
+			reg_val->val_mask |= (*d8 << 24);
+			d8 += 1;
+			fallthrough;
+		case 3:
+			reg_val->val_mask |= (*d8 << 16);
+			d8 += 1;
+			fallthrough;
+		case 2:
+			reg_val->val_mask |= (*d8 << 8);
+			d8 += 1;
+			fallthrough;
+		case 1:
+			reg_val->val_mask |= (*d8 << 0);
+			d8 += 1;
+			break;
+		}
+
+		reg_val->delay = *d8;
+		d8 += 1;
+	}
+
+	// End register setting
+	init_seq->reg_init_seq[init_seq->reg_seq_size - 1].reg_len = init_seq->reg_len;
+	init_seq->reg_init_seq[init_seq->reg_seq_size - 1].reg_addr = MAXIM4C_REG_NULL;
+
+	return 0;
+}
+
+int maxim4c_i2c_load_init_seq(struct device *dev,
+		struct device_node *node, struct maxim4c_i2c_init_seq *init_seq)
+{
+	const void *init_seq_data = NULL;
+	u32 seq_data_len = 0, value = 0;
+	int ret = 0;
+
+	if ((node == NULL) || (init_seq == NULL)) {
+		dev_err(dev, "%s input parameter error\n", __func__);
+		return -EINVAL;
+	}
+
+	init_seq->reg_init_seq = NULL;
+	init_seq->reg_seq_size = 0;
+
+	if (!of_device_is_available(node)) {
+		dev_info(dev, "%pOF is disabled\n", node);
+
+		return 0;
+	}
+
+	init_seq_data = of_get_property(node, "init-sequence", &seq_data_len);
+	if (!init_seq_data) {
+		dev_err(dev, "failed to get property init-sequence\n");
+		return -EINVAL;
+	}
+	if (seq_data_len == 0) {
+		dev_err(dev, "init-sequence date is empty\n");
+		return -EINVAL;
+	}
+
+	ret = of_property_read_u32(node, "seq-item-size", &value);
+	if (ret) {
+		dev_err(dev, "failed to get property seq-item-size\n");
+		return -EINVAL;
+	} else {
+		dev_info(dev, "seq-item-size property: %d", value);
+		init_seq->seq_item_size = value;
+	}
+
+	ret = of_property_read_u32(node, "reg-addr-len", &value);
+	if (ret) {
+		dev_err(dev, "failed to get property reg-addr-len\n");
+		return -EINVAL;
+	} else {
+		dev_info(dev, "reg-addr-len property: %d", value);
+		init_seq->reg_len = value;
+	}
+
+	ret = of_property_read_u32(node, "reg-val-len", &value);
+	if (ret) {
+		dev_err(dev, "failed to get property reg-val-len\n");
+		return -EINVAL;
+	} else {
+		dev_info(dev, "reg-val-len property: %d", value);
+		init_seq->val_len = value;
+	}
+
+	ret = maxim4c_i2c_parse_init_seq(dev,
+			init_seq_data, seq_data_len, init_seq);
+	if (ret) {
+		dev_err(dev, "failed to parse init-sequence\n");
+		return ret;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(maxim4c_i2c_load_init_seq);
+
+int maxim4c_i2c_run_init_seq(struct i2c_client *client,
+			struct maxim4c_i2c_init_seq *init_seq)
+{
+	int ret = 0;
+
+	if (init_seq == NULL || init_seq->reg_init_seq == NULL)
+		return 0;
+
+	ret = maxim4c_i2c_write_array(client,
+			init_seq->reg_init_seq);
+	return ret;
+}
+EXPORT_SYMBOL(maxim4c_i2c_run_init_seq);
diff --git a/kernel/drivers/media/i2c/maxim4c/maxim4c_i2c.h b/kernel/drivers/media/i2c/maxim4c/maxim4c_i2c.h
new file mode 100644
index 0000000..024e260
--- /dev/null
+++ b/kernel/drivers/media/i2c/maxim4c/maxim4c_i2c.h
@@ -0,0 +1,55 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2023 Rockchip Electronics Co., Ltd.
+ *
+ */
+
+#ifndef __MAXIM4C_I2C_H__
+#define __MAXIM4C_I2C_H__
+
+#include <linux/i2c.h>
+
+/* register address: 8bit or 16bit */
+#define MAXIM4C_I2C_REG_ADDR_08BITS	1
+#define MAXIM4C_I2C_REG_ADDR_16BITS	2
+
+/* register value: 8bit or 16bit or 24bit */
+#define MAXIM4C_I2C_REG_VALUE_08BITS	1
+#define MAXIM4C_I2C_REG_VALUE_16BITS	2
+#define MAXIM4C_I2C_REG_VALUE_24BITS	3
+
+/* I2C Device ID */
+enum {
+	MAXIM4C_I2C_DES_DEF,	/* Deserializer I2C address: Default */
+
+	MAXIM4C_I2C_SER_DEF,	/* Serializer I2C address: Default */
+	MAXIM4C_I2C_SER_MAP,	/* Serializer I2C address: Mapping */
+
+	MAXIM4C_I2C_CAM_DEF,	/* Camera I2C address: Default */
+	MAXIM4C_I2C_CAM_MAP,	/* Camera I2C address: Mapping */
+
+	MAXIM4C_I2C_DEV_MAX,
+};
+
+/* i2c register array end */
+#define MAXIM4C_REG_NULL		0xFFFF
+
+struct maxim4c_i2c_regval {
+	u16 reg_len;
+	u16 reg_addr;
+	u32 val_len;
+	u32 reg_val;
+	u32 val_mask;
+	u8 delay;
+};
+
+/* seq_item_size = reg_len + val_len * 2 + 1 */
+struct maxim4c_i2c_init_seq {
+	struct maxim4c_i2c_regval *reg_init_seq;
+	u32 reg_seq_size;
+	u32 seq_item_size;
+	u32 reg_len;
+	u32 val_len;
+};
+
+#endif /* __MAXIM4C_I2C_H__ */
diff --git a/kernel/drivers/media/i2c/maxim4c/maxim4c_link.c b/kernel/drivers/media/i2c/maxim4c/maxim4c_link.c
new file mode 100644
index 0000000..688dfe8
--- /dev/null
+++ b/kernel/drivers/media/i2c/maxim4c/maxim4c_link.c
@@ -0,0 +1,754 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Maxim Quad GMSL Deserializer Link driver
+ *
+ * Copyright (C) 2023 Rockchip Electronics Co., Ltd.
+ *
+ * Author: Cai Wenzhong <cwz@rock-chips.com>
+ *
+ */
+#include <linux/delay.h>
+#include "maxim4c_api.h"
+
+static int maxim4c_link_enable_vdd_ldo1(maxim4c_t *maxim4c)
+{
+	struct i2c_client *client = maxim4c->client;
+	int ret = 0;
+
+	/* IF VDD = 1.2V: Enable REG_ENABLE and REG_MNL
+	 *	CTRL0: Enable REG_ENABLE
+	 *	CTRL2: Enable REG_MNL
+	 */
+	ret |= maxim4c_i2c_update_byte(client,
+			0x0017, MAXIM4C_I2C_REG_ADDR_16BITS, BIT(2), BIT(2));
+	ret |= maxim4c_i2c_update_byte(client,
+			0x0019, MAXIM4C_I2C_REG_ADDR_16BITS, BIT(4), BIT(4));
+
+	return ret;
+}
+
+static int maxim4c_link_enable_vdd_ldo2(maxim4c_t *maxim4c)
+{
+	struct i2c_client *client = maxim4c->client;
+	int ret = 0;
+
+	ret |= maxim4c_i2c_write_byte(client,
+			0x06C2, MAXIM4C_I2C_REG_ADDR_16BITS,
+			0x10);
+	// delay 10ms
+	msleep(10);
+
+	return ret;
+}
+
+static int maxim4c_link_set_rate(maxim4c_t *maxim4c)
+{
+	struct i2c_client *client = maxim4c->client;
+	maxim4c_gmsl_link_t *gmsl_link = &maxim4c->gmsl_link;
+	struct maxim4c_link_cfg *link_cfg = NULL;
+	u8 link_rate = 0;
+	int ret = 0;
+
+	/* Link A/B rate setting */
+	link_rate = 0; /* default Transmitter Rate is 187.5Mbps */
+	link_cfg = &gmsl_link->link_cfg[MAXIM4C_LINK_ID_A];
+	if (link_cfg->link_enable) {
+		/* Link A: Receiver Rate */
+		if (link_cfg->link_rx_rate == MAXIM4C_LINK_RX_RATE_3GBPS)
+			link_rate |= (0x1 << 0);
+		else
+			link_rate |= (0x2 << 0);
+	}
+	link_cfg = &gmsl_link->link_cfg[MAXIM4C_LINK_ID_B];
+	if (link_cfg->link_enable) {
+		/* Link B: Receiver Rate */
+		if (link_cfg->link_rx_rate == MAXIM4C_LINK_RX_RATE_3GBPS)
+			link_rate |= (0x1 << 4);
+		else
+			link_rate |= (0x2 << 4);
+	}
+	if (link_rate != 0) {
+		ret |= maxim4c_i2c_write_byte(client,
+				0x0010, MAXIM4C_I2C_REG_ADDR_16BITS,
+				link_rate);
+	}
+
+	/* Link C/D rate setting */
+	link_rate = 0;
+	link_cfg = &gmsl_link->link_cfg[MAXIM4C_LINK_ID_C];
+	if (link_cfg->link_enable) {
+		/* Link C: Receiver Rate */
+		if (link_cfg->link_rx_rate == MAXIM4C_LINK_RX_RATE_3GBPS)
+			link_rate |= (0x1 << 0);
+		else
+			link_rate |= (0x2 << 0);
+	}
+	link_cfg = &gmsl_link->link_cfg[MAXIM4C_LINK_ID_D];
+	if (link_cfg->link_enable) {
+		/* Link D: Receiver Rate */
+		if (link_cfg->link_rx_rate == MAXIM4C_LINK_RX_RATE_3GBPS)
+			link_rate |= (0x1 << 4);
+		else
+			link_rate |= (0x2 << 4);
+	}
+	if (link_rate != 0) {
+		ret |= maxim4c_i2c_write_byte(client,
+				0x0011, MAXIM4C_I2C_REG_ADDR_16BITS,
+				link_rate);
+	}
+
+	return ret;
+}
+
+static int maxim4c_link_run_init_seq(maxim4c_t *maxim4c)
+{
+	struct i2c_client *client = maxim4c->client;
+	struct device *dev = &client->dev;
+	maxim4c_gmsl_link_t *gmsl_link = &maxim4c->gmsl_link;
+	struct maxim4c_link_cfg *link_cfg = NULL;
+	struct maxim4c_i2c_init_seq *init_seq = NULL;
+	int link_idx = 0;
+	int ret = 0;
+
+	// link init sequence
+	for (link_idx = 0; link_idx < MAXIM4C_LINK_ID_MAX; link_idx++) {
+		link_cfg = &gmsl_link->link_cfg[link_idx];
+		init_seq = &link_cfg->link_init_seq;
+		ret = maxim4c_i2c_run_init_seq(client, init_seq);
+		if (ret) {
+			dev_err(dev, "link id = %d init sequence error\n", link_idx);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int maxim4c_link_status_init(maxim4c_t *maxim4c)
+{
+	struct i2c_client *client = maxim4c->client;
+	maxim4c_gmsl_link_t *gmsl_link = &maxim4c->gmsl_link;
+	struct maxim4c_link_cfg *link_cfg = NULL;
+	u8 link_type = 0, link_enable = 0;
+	u8 reg_mask = 0, reg_value = 0;
+	u16 reg_addr = 0;
+	int ret = 0, link_idx = 0;
+
+	gmsl_link->link_enable_mask = 0x00;
+	gmsl_link->link_type_mask = 0x0F;
+	gmsl_link->link_locked_mask = 0;
+
+	link_type = 0xF0; /* default GMSL2 */
+	link_enable = 0; /* default disable */
+	for (link_idx = 0; link_idx < MAXIM4C_LINK_ID_MAX; link_idx++) {
+		link_cfg = &gmsl_link->link_cfg[link_idx];
+		if (link_cfg->link_enable) {
+			gmsl_link->link_enable_mask |= BIT(link_idx);
+
+			if (link_cfg->link_type == MAXIM4C_GMSL1) {
+				gmsl_link->link_type_mask &= ~BIT(link_idx);
+				link_type &= ~BIT(4 + link_idx);
+			}
+		}
+	}
+
+	ret = maxim4c_i2c_write_byte(client,
+			0x0006, MAXIM4C_I2C_REG_ADDR_16BITS,
+			link_type | link_enable);
+
+	reg_mask = BIT(1) | BIT(0);
+	reg_value = 0;
+	for (link_idx = 0; link_idx < MAXIM4C_LINK_ID_MAX; link_idx++) {
+		reg_addr = 0x0B04 + 0x100 * link_idx;
+		ret |= maxim4c_i2c_update_byte(client,
+				reg_addr, MAXIM4C_I2C_REG_ADDR_16BITS,
+				reg_mask, reg_value);
+	}
+
+	if (gmsl_link->i2c_ctrl_port == MAXIM4C_I2C_PORT2) {
+		reg_mask = 0x0F;
+		reg_value = 0x0F;
+		ret |= maxim4c_i2c_update_byte(client,
+				0x000E, MAXIM4C_I2C_REG_ADDR_16BITS,
+				reg_mask, reg_value);
+	}
+
+	reg_mask = 0xFF;
+	reg_value = 0xFF;
+	ret |= maxim4c_i2c_update_byte(client,
+			0x0003, MAXIM4C_I2C_REG_ADDR_16BITS,
+			reg_mask, reg_value);
+
+	return ret;
+}
+
+u8 maxim4c_link_get_lock_state(maxim4c_t *maxim4c, u8 link_mask)
+{
+	struct i2c_client *client = maxim4c->client;
+	struct device *dev = &client->dev;
+	maxim4c_gmsl_link_t *gmsl_link = &maxim4c->gmsl_link;
+	u8 link_type = 0, link_lock = 0, lock_state = 0;
+
+	dev_dbg(dev, "%s, link_mask = 0x%x\n", __func__, link_mask);
+
+	// Link A
+	if (link_mask & MAXIM4C_LINK_MASK_A) {
+		link_type = gmsl_link->link_cfg[MAXIM4C_LINK_ID_A].link_type;
+		if (link_type == MAXIM4C_GMSL2) {
+			// GMSL2 Link A
+			maxim4c_i2c_read_byte(client,
+				0x001A, MAXIM4C_I2C_REG_ADDR_16BITS,
+				&link_lock);
+			if (link_lock & BIT(3)) {
+				lock_state |= MAXIM4C_LINK_MASK_A;
+				dev_dbg(dev, "GMSL2 Link A locked\n");
+			}
+		} else {
+			// GMSL1 Link A
+			maxim4c_i2c_read_byte(client,
+				0x0BCB, MAXIM4C_I2C_REG_ADDR_16BITS,
+				&link_lock);
+			if (link_lock & BIT(0)) {
+				lock_state |= MAXIM4C_LINK_MASK_A;
+				dev_dbg(dev, "GMSL1 Link A locked\n");
+			}
+		}
+
+		// record link lock
+		if (lock_state & MAXIM4C_LINK_MASK_A)
+			gmsl_link->link_locked_mask |= MAXIM4C_LINK_MASK_A;
+		else
+			gmsl_link->link_locked_mask &= ~MAXIM4C_LINK_MASK_A;
+	}
+
+	// Link B
+	if (link_mask & MAXIM4C_LINK_MASK_B) {
+		link_type = gmsl_link->link_cfg[MAXIM4C_LINK_ID_B].link_type;
+		if (link_type == MAXIM4C_GMSL2) {
+			// GMSL2 Link B
+			maxim4c_i2c_read_byte(client,
+				0x000A, MAXIM4C_I2C_REG_ADDR_16BITS,
+				&link_lock);
+			if (link_lock & BIT(3)) {
+				lock_state |= MAXIM4C_LINK_MASK_B;
+				dev_dbg(dev, "GMSL2 Link B locked\n");
+			}
+		} else {
+			// GMSL1 Link B
+			maxim4c_i2c_read_byte(client,
+				0x0CCB, MAXIM4C_I2C_REG_ADDR_16BITS,
+				&link_lock);
+			if (link_lock & BIT(0)) {
+				lock_state |= MAXIM4C_LINK_MASK_B;
+				dev_dbg(dev, "GMSL1 Link B locked\n");
+			}
+		}
+
+		// record link lock
+		if (lock_state & MAXIM4C_LINK_MASK_B)
+			gmsl_link->link_locked_mask |= MAXIM4C_LINK_MASK_B;
+		else
+			gmsl_link->link_locked_mask &= ~MAXIM4C_LINK_MASK_B;
+	}
+
+	// Link C
+	if (link_mask & MAXIM4C_LINK_MASK_C) {
+		link_type = gmsl_link->link_cfg[MAXIM4C_LINK_ID_C].link_type;
+		if (link_type == MAXIM4C_GMSL2) {
+			// GMSL2 Link C
+			maxim4c_i2c_read_byte(client,
+				0x000B, MAXIM4C_I2C_REG_ADDR_16BITS,
+				&link_lock);
+			if (link_lock & BIT(3)) {
+				lock_state |= MAXIM4C_LINK_MASK_C;
+				dev_dbg(dev, "GMSL2 Link C locked\n");
+			}
+		} else {
+			// GMSL1 Link C
+			maxim4c_i2c_read_byte(client,
+				0x0DCB, MAXIM4C_I2C_REG_ADDR_16BITS,
+				&link_lock);
+			if (link_lock & BIT(0)) {
+				lock_state |= MAXIM4C_LINK_MASK_C;
+				dev_dbg(dev, "GMSL1 Link C locked\n");
+			}
+		}
+
+		// record link lock
+		if (lock_state & MAXIM4C_LINK_MASK_C)
+			gmsl_link->link_locked_mask |= MAXIM4C_LINK_MASK_C;
+		else
+			gmsl_link->link_locked_mask &= ~MAXIM4C_LINK_MASK_C;
+	}
+
+	// Link D
+	if (link_mask & MAXIM4C_LINK_MASK_D) {
+		link_type = gmsl_link->link_cfg[MAXIM4C_LINK_ID_D].link_type;
+		if (link_type == MAXIM4C_GMSL2) {
+			// GMSL2 Link D
+			maxim4c_i2c_read_byte(client,
+				0x000C, MAXIM4C_I2C_REG_ADDR_16BITS,
+				&link_lock);
+			if (link_lock & BIT(3)) {
+				lock_state |= MAXIM4C_LINK_MASK_D;
+				dev_dbg(dev, "GMSL2 Link D locked\n");
+			}
+		} else {
+			// GMSL1 Link D
+			maxim4c_i2c_read_byte(client,
+				0x0ECB, MAXIM4C_I2C_REG_ADDR_16BITS,
+				&link_lock);
+			if (link_lock & BIT(0)) {
+				lock_state |= MAXIM4C_LINK_MASK_D;
+				dev_dbg(dev, "GMSL1 Link D locked\n");
+			}
+		}
+
+		// record link lock
+		if (lock_state & MAXIM4C_LINK_MASK_D)
+			gmsl_link->link_locked_mask |= MAXIM4C_LINK_MASK_D;
+		else
+			gmsl_link->link_locked_mask &= ~MAXIM4C_LINK_MASK_D;
+	}
+
+	return lock_state;
+}
+EXPORT_SYMBOL(maxim4c_link_get_lock_state);
+
+int maxim4c_link_oneshot_reset(struct maxim4c *maxim4c, u8 link_mask)
+{
+	struct i2c_client *client = maxim4c->client;
+	struct device *dev = &client->dev;
+	maxim4c_gmsl_link_t *gmsl_link = &maxim4c->gmsl_link;
+	struct maxim4c_link_cfg *link_cfg = NULL;
+	u8 oneshot_reset = 0;
+	int ret = 0, link_idx = 0;
+
+	dev_dbg(dev, "%s, link_mask = 0x%x\n", __func__, link_mask);
+
+	oneshot_reset = 0;
+	for (link_idx = 0; link_idx < MAXIM4C_LINK_ID_MAX; link_idx++) {
+		link_cfg = &gmsl_link->link_cfg[link_idx];
+		if (link_cfg->link_enable && (link_mask & BIT(link_idx)))
+			oneshot_reset |= BIT(link_idx);
+	}
+
+	if (oneshot_reset != 0) {
+		// One-Shot Reset
+		ret = maxim4c_i2c_write_byte(client,
+				0x0018, MAXIM4C_I2C_REG_ADDR_16BITS,
+				oneshot_reset);
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(maxim4c_link_oneshot_reset);
+
+int maxim4c_link_mask_enable(struct maxim4c *maxim4c, u8 link_mask, bool enable)
+{
+	struct i2c_client *client = maxim4c->client;
+	struct device *dev = &client->dev;
+	maxim4c_gmsl_link_t *gmsl_link = &maxim4c->gmsl_link;
+	struct maxim4c_link_cfg *link_cfg = NULL;
+	u8 reg_mask = 0, reg_value = 0;
+	int ret = 0, link_idx = 0;
+
+	dev_dbg(dev, "%s, link_mask = 0x%x, enable = %d\n",
+			__func__, link_mask, enable);
+
+	reg_mask = 0;
+	for (link_idx = 0; link_idx < MAXIM4C_LINK_ID_MAX; link_idx++) {
+		link_cfg = &gmsl_link->link_cfg[link_idx];
+		if (link_cfg->link_enable && (link_mask & BIT(link_idx)))
+			reg_mask |= BIT(link_idx);
+	}
+
+	if (reg_mask != 0) {
+		reg_value = enable ? reg_mask : 0;
+
+		ret = maxim4c_i2c_update_byte(client,
+				0x0006, MAXIM4C_I2C_REG_ADDR_16BITS,
+				reg_mask, reg_value);
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(maxim4c_link_mask_enable);
+
+int maxim4c_link_wait_linklock(struct maxim4c *maxim4c, u8 link_mask)
+{
+	struct i2c_client *client = maxim4c->client;
+	struct device *dev = &client->dev;
+	u8 lock_state = 0, link_bit_mask = 0;
+	int loop_idx = 0, time_ms = 0, link_idx = 0;
+
+	time_ms = 50;
+	msleep(time_ms);
+
+	for (loop_idx = 0; loop_idx < 20; loop_idx++) {
+		if (loop_idx != 0) {
+			msleep(10);
+			time_ms += 10;
+		}
+
+		for (link_idx = 0; link_idx < MAXIM4C_LINK_ID_MAX; link_idx++) {
+			link_bit_mask = BIT(link_idx);
+
+			if ((link_mask & link_bit_mask)
+					&& ((lock_state & link_bit_mask) == 0)) {
+				if (maxim4c_link_get_lock_state(maxim4c, link_bit_mask)) {
+					lock_state |= link_bit_mask;
+					dev_info(dev, "Link %c locked time: %d ms\n",
+						'A' + link_idx, time_ms);
+				}
+			}
+		}
+
+		if ((lock_state & link_mask) == link_mask) {
+			dev_info(dev, "All Links are locked: 0x%x, time_ms = %d\n",
+				lock_state, time_ms);
+			maxim4c->link_lock_state = lock_state;
+			return 0;
+		}
+	}
+
+	if ((lock_state & link_mask) != 0) {
+		dev_info(dev, "Partial links are locked: 0x%x, time_ms = %d\n",
+			lock_state, time_ms);
+		maxim4c->link_lock_state = lock_state;
+		return 0;
+	} else {
+		dev_err(dev, "Failed to detect remote link, time_ms = %d!\n", time_ms);
+		maxim4c->link_lock_state = 0;
+		return -ENODEV;
+	}
+}
+EXPORT_SYMBOL(maxim4c_link_wait_linklock);
+
+int maxim4c_link_select_remote_enable(struct maxim4c *maxim4c, u8 link_mask)
+{
+	struct i2c_client *client = maxim4c->client;
+	struct device *dev = &client->dev;
+	maxim4c_gmsl_link_t *gmsl_link = &maxim4c->gmsl_link;
+	u8 link_enable = 0, link_reset = 0, link_bit_mask = 0;
+	int ret = 0, link_idx = 0;
+
+	dev_dbg(dev, "%s, link_mask = 0x%x\n", __func__, link_mask);
+
+	ret = 0;
+	link_enable = 0;
+	link_reset = 0;
+
+	for (link_idx = 0; link_idx < MAXIM4C_LINK_ID_MAX; link_idx++) {
+		link_bit_mask = BIT(link_idx);
+
+		if (link_mask & link_bit_mask) {
+			if (gmsl_link->link_cfg[link_idx].link_enable) {
+				link_enable |= BIT(link_idx);
+				link_reset |= BIT(link_idx);
+			} else {
+				link_mask &= ~link_bit_mask;
+			}
+		}
+	}
+
+	if (link_mask != 0) {
+		// One-Shot Reset
+		ret |= maxim4c_i2c_write_byte(client,
+				0x0018, MAXIM4C_I2C_REG_ADDR_16BITS,
+				link_reset);
+
+		// Link Enable
+		ret |= maxim4c_i2c_update_byte(client,
+				0x0006, MAXIM4C_I2C_REG_ADDR_16BITS,
+				link_enable, link_enable);
+
+		ret |= maxim4c_link_wait_linklock(maxim4c, link_mask);
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(maxim4c_link_select_remote_enable);
+
+int maxim4c_link_select_remote_control(struct maxim4c *maxim4c, u8 link_mask)
+{
+	struct i2c_client *client = maxim4c->client;
+	struct device *dev = &client->dev;
+	maxim4c_gmsl_link_t *gmsl_link = &maxim4c->gmsl_link;
+	struct maxim4c_link_cfg *link_cfg = NULL;
+	u8 link_enable = 0, link_type = 0;
+	u8 reg_mask = 0, reg_value = 0;
+	u16 reg_addr = 0;
+	int link_idx = 0, ret = 0;
+
+	dev_dbg(dev, "%s, link mask = 0x%x\n", __func__, link_mask);
+
+	// GMSL1 Link forward control channel
+	for (link_idx = 0; link_idx < MAXIM4C_LINK_ID_MAX; link_idx++) {
+		link_cfg = &gmsl_link->link_cfg[link_idx];
+
+		link_enable = link_cfg->link_enable;
+		link_type = link_cfg->link_type;
+		if (link_enable && (link_type == MAXIM4C_GMSL1)) {
+			reg_mask = BIT(1) | BIT(0);
+
+			if (link_mask & BIT(link_idx))
+				// GMSL1: Enable forward control channel transmitter
+				reg_value = BIT(1) | BIT(0);
+			else
+				// GMSL1: Disable forward control channel transmitter
+				reg_value = 0;
+
+			reg_addr = 0x0B04 + 0x100 * link_idx;
+			ret |= maxim4c_i2c_update_byte(client,
+					reg_addr, MAXIM4C_I2C_REG_ADDR_16BITS,
+					reg_mask, reg_value);
+
+			link_mask &= ~BIT(link_idx);
+		}
+	}
+
+	// GMSL2 Link
+	if (gmsl_link->i2c_ctrl_port == MAXIM4C_I2C_PORT2) {
+		reg_mask = 0x0F;
+		reg_value = ~link_mask;
+
+		ret |= maxim4c_i2c_update_byte(client,
+				0x000E, MAXIM4C_I2C_REG_ADDR_16BITS,
+				reg_mask, reg_value);
+	} else if (gmsl_link->i2c_ctrl_port == MAXIM4C_I2C_PORT1) {
+		reg_mask = 0xFF;
+		reg_value = 0;
+
+		for (link_idx = 0; link_idx < MAXIM4C_LINK_ID_MAX; link_idx++) {
+			if (link_mask & BIT(link_idx))
+				reg_value |= BIT(1 + 2 * link_idx);
+		}
+		reg_value = ~reg_value;
+
+		ret |= maxim4c_i2c_update_byte(client,
+				0x0003, MAXIM4C_I2C_REG_ADDR_16BITS,
+				reg_mask, reg_value);
+	} else {
+		reg_mask = 0xFF;
+		reg_value = 0;
+
+		for (link_idx = 0; link_idx < MAXIM4C_LINK_ID_MAX; link_idx++) {
+			if (link_mask & BIT(link_idx))
+				reg_value |= BIT(0 + 2 * link_idx);
+		}
+		reg_value = ~reg_value;
+
+		ret |= maxim4c_i2c_update_byte(client,
+				0x0003, MAXIM4C_I2C_REG_ADDR_16BITS,
+				reg_mask, reg_value);
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(maxim4c_link_select_remote_control);
+
+static int maxim4c_gmsl_link_config_parse_dt(struct device *dev,
+			maxim4c_gmsl_link_t *gmsl_link,
+			struct device_node *parent_node)
+{
+	struct device_node *node = NULL;
+	struct device_node *init_seq_node = NULL;
+	struct maxim4c_i2c_init_seq *init_seq = NULL;
+	struct maxim4c_link_cfg *link_cfg = NULL;
+	const char *link_cfg_name = "gmsl-link-config";
+	u32 value = 0;
+	u32 sub_idx = 0, link_id = 0;
+	int ret = 0;
+
+	node = NULL;
+	sub_idx = 0;
+	while ((node = of_get_next_child(parent_node, node))) {
+		if (!strncasecmp(node->name,
+				 link_cfg_name,
+				 strlen(link_cfg_name))) {
+			if (sub_idx >= MAXIM4C_LINK_ID_MAX) {
+				dev_err(dev, "%pOF: Too many matching %s node\n",
+						parent_node, link_cfg_name);
+
+				of_node_put(node);
+				break;
+			}
+
+			if (!of_device_is_available(node)) {
+				dev_info(dev, "%pOF is disabled\n", node);
+
+				sub_idx++;
+
+				continue;
+			}
+
+			/* GMSL LINK: link id */
+			ret = of_property_read_u32(node, "link-id", &link_id);
+			if (ret) {
+				// if link_id is error, parse next node
+				dev_err(dev, "Can not get link-id property!");
+
+				sub_idx++;
+
+				continue;
+			}
+			if (link_id >= MAXIM4C_LINK_ID_MAX) {
+				// if link_id is error, parse next node
+				dev_err(dev, "Error link-id = %d!", link_id);
+
+				sub_idx++;
+
+				continue;
+			}
+
+			link_cfg = &gmsl_link->link_cfg[link_id];
+
+			/* GMSL LINK: link enable */
+			link_cfg->link_enable = 1;
+
+			dev_info(dev, "gmsl link id = %d: link_enable = %d\n",
+					link_id, link_cfg->link_enable);
+
+			/* GMSL LINK: other config */
+			ret = of_property_read_u32(node, "link-type", &value);
+			if (ret == 0) {
+				dev_info(dev, "link-type property: %d", value);
+				link_cfg->link_type = value;
+			}
+
+			ret = of_property_read_u32(node, "link-rx-rate", &value);
+			if (ret == 0) {
+				dev_info(dev, "link-rx-rate property: %d", value);
+				link_cfg->link_rx_rate = value;
+			}
+
+			/* link init sequence */
+			init_seq_node = of_get_child_by_name(node, "link-init-sequence");
+			if (!IS_ERR_OR_NULL(init_seq_node)) {
+				dev_info(dev, "load pipe-init-sequence\n");
+
+				init_seq = &link_cfg->link_init_seq;
+				maxim4c_i2c_load_init_seq(dev,
+						init_seq_node, init_seq);
+
+				of_node_put(init_seq_node);
+			}
+
+			sub_idx++;
+		}
+	}
+
+	return 0;
+}
+
+int maxim4c_link_parse_dt(maxim4c_t *maxim4c, struct device_node *of_node)
+{
+	struct device *dev = &maxim4c->client->dev;
+	struct device_node *node = NULL;
+	maxim4c_gmsl_link_t *gmsl_link = &maxim4c->gmsl_link;
+	u32 value = 0;
+	int ret = 0;
+
+	dev_info(dev, "=== maxim4c link parse dt ===\n");
+
+	node = of_get_child_by_name(of_node, "gmsl-links");
+	if (IS_ERR_OR_NULL(node)) {
+		dev_err(dev, "%pOF has no child node: gmsl-links\n",
+				of_node);
+		return -ENODEV;
+	}
+
+	if (!of_device_is_available(node)) {
+		dev_info(dev, "%pOF is disabled\n", node);
+		of_node_put(node);
+		return -ENODEV;
+	}
+
+	/* vdd 1.2v ldo1 enable */
+	ret = of_property_read_u32(node, "link-vdd-ldo1-en", &value);
+	if (ret == 0) {
+		dev_info(dev, "link-vdd-ldo1-en property: %d\n", value);
+		gmsl_link->link_vdd_ldo1_en = value;
+	}
+
+	/* vdd ldo2 enable */
+	ret = of_property_read_u32(node, "link-vdd-ldo2-en", &value);
+	if (ret == 0) {
+		dev_info(dev, "link-vdd-ldo2-en property: %d\n", value);
+		gmsl_link->link_vdd_ldo2_en = value;
+	}
+
+	ret = maxim4c_gmsl_link_config_parse_dt(dev, gmsl_link, node);
+
+	of_node_put(node);
+
+	return ret;
+}
+EXPORT_SYMBOL(maxim4c_link_parse_dt);
+
+int maxim4c_link_hw_init(maxim4c_t *maxim4c)
+{
+	struct device *dev = &maxim4c->client->dev;
+	maxim4c_gmsl_link_t *gmsl_link = &maxim4c->gmsl_link;
+	int ret = 0;
+
+	// All links disable at beginning.
+	ret = maxim4c_link_status_init(maxim4c);
+	if (ret) {
+		dev_err(dev, "%s: link status error\n", __func__);
+		return ret;
+	}
+
+	if (gmsl_link->link_vdd_ldo1_en)
+		ret |= maxim4c_link_enable_vdd_ldo1(maxim4c);
+
+	if (gmsl_link->link_vdd_ldo2_en)
+		ret |= maxim4c_link_enable_vdd_ldo2(maxim4c);
+	if (ret) {
+		dev_err(dev, "%s: link vdd ldo enable error\n", __func__);
+		return ret;
+	}
+
+	// Link Rate Setting
+	ret = maxim4c_link_set_rate(maxim4c);
+	if (ret) {
+		dev_err(dev, "%s: link set rate error\n", __func__);
+		return ret;
+	}
+
+	// link init sequence
+	ret = maxim4c_link_run_init_seq(maxim4c);
+	if (ret) {
+		dev_err(dev, "%s: link run init seq error\n", __func__);
+		return ret;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(maxim4c_link_hw_init);
+
+void maxim4c_link_data_init(maxim4c_t *maxim4c)
+{
+	maxim4c_gmsl_link_t *gmsl_link = &maxim4c->gmsl_link;
+	struct maxim4c_link_cfg *link_cfg = NULL;
+	int i = 0;
+
+	gmsl_link->link_vdd_ldo1_en = 0;
+	gmsl_link->link_vdd_ldo2_en = 0;
+	gmsl_link->i2c_ctrl_port = MAXIM4C_I2C_PORT0;
+
+	for (i = 0; i < MAXIM4C_LINK_ID_MAX; i++) {
+		link_cfg = &gmsl_link->link_cfg[i];
+
+		link_cfg->link_enable = 0;
+		link_cfg->link_type = MAXIM4C_GMSL2;
+		if (maxim4c->chipid == MAX96722_CHIP_ID)
+			link_cfg->link_rx_rate = MAXIM4C_LINK_RX_RATE_3GBPS;
+		else
+			link_cfg->link_rx_rate = MAXIM4C_LINK_RX_RATE_6GBPS;
+		link_cfg->link_tx_rate = MAXIM4C_LINK_TX_RATE_187_5MPS;
+		link_cfg->link_init_seq.reg_init_seq = NULL;
+	}
+}
+EXPORT_SYMBOL(maxim4c_link_data_init);
diff --git a/kernel/drivers/media/i2c/maxim4c/maxim4c_link.h b/kernel/drivers/media/i2c/maxim4c/maxim4c_link.h
new file mode 100644
index 0000000..f25d4cb
--- /dev/null
+++ b/kernel/drivers/media/i2c/maxim4c/maxim4c_link.h
@@ -0,0 +1,86 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2023 Rockchip Electronics Co., Ltd.
+ *
+ */
+
+#ifndef __MAXIM4C_LINK_H__
+#define __MAXIM4C_LINK_H__
+
+#include "maxim4c_i2c.h"
+
+/* Link cable */
+enum maxim4c_link_cable {
+	MAXIM4C_CABLE_COAX = 0,
+	MAXIM4C_CABLE_STP,
+};
+
+/* Link Type */
+enum maxim4c_link_type {
+	MAXIM4C_GMSL1 = 0,
+	MAXIM4C_GMSL2,
+};
+
+/* Link Mode */
+enum maxim4c_link_mode {
+	MAXIM4C_GMSL_PIXEL = 0,
+	MAXIM4C_GMSL_TUNNEL,
+};
+
+/* I2C Remote Control Port */
+enum {
+	MAXIM4C_I2C_PORT0 = 0,
+	MAXIM4C_I2C_PORT1,
+	MAXIM4C_I2C_PORT2,
+	MAXIM4C_I2C_PORT_MAX,
+};
+
+/* Link SIO ID: 0 ~ 3 */
+enum {
+	MAXIM4C_LINK_ID_A = 0,
+	MAXIM4C_LINK_ID_B,
+	MAXIM4C_LINK_ID_C,
+	MAXIM4C_LINK_ID_D,
+	MAXIM4C_LINK_ID_MAX,
+};
+
+/* Link Bit Mask: bit0 ~ bit3 */
+#define MAXIM4C_LINK_MASK_A		BIT(MAXIM4C_LINK_ID_A)
+#define MAXIM4C_LINK_MASK_B		BIT(MAXIM4C_LINK_ID_B)
+#define MAXIM4C_LINK_MASK_C		BIT(MAXIM4C_LINK_ID_C)
+#define MAXIM4C_LINK_MASK_D		BIT(MAXIM4C_LINK_ID_D)
+
+#define MAXIM4C_LINK_MASK_ALL		GENMASK(MAXIM4C_LINK_ID_D, MAXIM4C_LINK_ID_A)
+
+/* Link Receiver Rate */
+enum maxim4c_link_rx_rate {
+	MAXIM4C_LINK_RX_RATE_3GBPS = 0,
+	MAXIM4C_LINK_RX_RATE_6GBPS,
+};
+
+/* Link Transmitter Rate */
+enum maxim4c_link_tx_rate {
+	MAXIM4C_LINK_TX_RATE_187_5MPS = 0,
+};
+
+struct maxim4c_link_cfg {
+	u8 link_enable;
+	u8 link_type;
+	u8 link_rx_rate;
+	u8 link_tx_rate;
+
+	struct maxim4c_i2c_init_seq link_init_seq;
+};
+
+typedef struct maxim4c_gmsl_link {
+	u8 link_enable_mask;
+	u8 link_type_mask;
+	u8 link_locked_mask;
+	u8 link_vdd_ldo1_en;
+	u8 link_vdd_ldo2_en;
+	u8 i2c_ctrl_port;
+
+	struct maxim4c_link_cfg link_cfg[MAXIM4C_LINK_ID_MAX];
+} maxim4c_gmsl_link_t;
+
+#endif /* __MAXIM4C_LINK_H__ */
diff --git a/kernel/drivers/media/i2c/maxim4c/maxim4c_mipi_txphy.c b/kernel/drivers/media/i2c/maxim4c/maxim4c_mipi_txphy.c
new file mode 100644
index 0000000..5734902
--- /dev/null
+++ b/kernel/drivers/media/i2c/maxim4c/maxim4c_mipi_txphy.c
@@ -0,0 +1,546 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Maxim Quad GMSL Deserializer MIPI txphy driver
+ *
+ * Copyright (C) 2023 Rockchip Electronics Co., Ltd.
+ *
+ * Author: Cai Wenzhong <cwz@rock-chips.com>
+ *
+ */
+#include <linux/iopoll.h>
+
+#include "maxim4c_api.h"
+
+static int maxim4c_txphy_auto_init_deskew(maxim4c_t *maxim4c)
+{
+	struct i2c_client *client = maxim4c->client;
+	maxim4c_mipi_txphy_t *mipi_txphy = &maxim4c->mipi_txphy;
+	struct maxim4c_txphy_cfg *phy_cfg = NULL;
+	u16 reg_addr = 0;
+	u8 phy_idx = 0;
+	int ret = 0;
+
+	// D-PHY Deskew Initial Calibration Control
+	for (phy_idx = 0; phy_idx < MAXIM4C_TXPHY_ID_MAX; phy_idx++) {
+		phy_cfg = &mipi_txphy->phy_cfg[phy_idx];
+		if (phy_cfg->phy_enable && (phy_cfg->auto_deskew & BIT(7))) {
+			reg_addr = 0x0903 + 0x40 * phy_idx;
+			ret |= maxim4c_i2c_write_byte(client,
+					reg_addr, MAXIM4C_I2C_REG_ADDR_16BITS,
+					phy_cfg->auto_deskew);
+		}
+	}
+
+	return ret;
+}
+
+static int maxim4c_mipi_txphy_lane_mapping(maxim4c_t *maxim4c)
+{
+	struct i2c_client *client = maxim4c->client;
+	maxim4c_mipi_txphy_t *mipi_txphy = &maxim4c->mipi_txphy;
+	struct maxim4c_txphy_cfg *phy_cfg = NULL;
+	u8 reg_value = 0, reg_mask = 0;
+	int ret = 0;
+
+	// MIPI TXPHY A/B: data lane mapping
+	reg_mask = 0;
+	reg_value = 0;
+	phy_cfg = &mipi_txphy->phy_cfg[MAXIM4C_TXPHY_ID_A];
+	if (phy_cfg->phy_enable) {
+		reg_mask |= 0x0F;
+		reg_value |= (phy_cfg->data_lane_map << 0);
+	}
+	phy_cfg = &mipi_txphy->phy_cfg[MAXIM4C_TXPHY_ID_B];
+	if (phy_cfg->phy_enable) {
+		reg_mask |= 0xF0;
+		reg_value |= (phy_cfg->data_lane_map << 4);
+	}
+	if (reg_mask != 0) {
+		ret |= maxim4c_i2c_update_byte(client,
+				0x08A3, MAXIM4C_I2C_REG_ADDR_16BITS,
+				reg_mask, reg_value);
+	}
+
+	// MIPI TXPHY C/D: data lane mapping
+	reg_mask = 0;
+	reg_value = 0;
+	phy_cfg = &mipi_txphy->phy_cfg[MAXIM4C_TXPHY_ID_C];
+	if (phy_cfg->phy_enable) {
+		reg_mask |= 0x0F;
+		reg_value |= (phy_cfg->data_lane_map << 0);
+	}
+	phy_cfg = &mipi_txphy->phy_cfg[MAXIM4C_TXPHY_ID_D];
+	if (phy_cfg->phy_enable) {
+		reg_mask |= 0xF0;
+		reg_value |= (phy_cfg->data_lane_map << 4);
+	}
+	if (reg_mask != 0) {
+		ret |= maxim4c_i2c_update_byte(client,
+				0x08A4, MAXIM4C_I2C_REG_ADDR_16BITS,
+				reg_mask, reg_value);
+	}
+
+	return ret;
+}
+
+static int maxim4c_mipi_txphy_type_vcx_lane_num(maxim4c_t *maxim4c)
+{
+	struct i2c_client *client = maxim4c->client;
+	maxim4c_mipi_txphy_t *mipi_txphy = &maxim4c->mipi_txphy;
+	struct maxim4c_txphy_cfg *phy_cfg = NULL;
+	u8 phy_idx = 0;
+	u8 reg_mask = 0, reg_value = 0;
+	u16 reg_addr = 0;
+	int ret = 0;
+
+	for (phy_idx = 0; phy_idx < MAXIM4C_TXPHY_ID_MAX; phy_idx++) {
+		phy_cfg = &mipi_txphy->phy_cfg[phy_idx];
+		if (phy_cfg->phy_enable == 0)
+			continue;
+
+		reg_mask = 0xF0;
+		reg_value = 0;
+
+		if (phy_cfg->phy_type == MAXIM4C_TXPHY_TYPE_CPHY)
+			reg_value |= BIT(5);
+		if (phy_cfg->vc_ext_en)
+			reg_value |= BIT(4);
+
+		reg_value |= ((phy_cfg->data_lane_num - 1) << 6);
+
+		reg_addr = 0x090A + 0x40 * phy_idx;
+		ret |= maxim4c_i2c_update_byte(client,
+				reg_addr, MAXIM4C_I2C_REG_ADDR_16BITS,
+				reg_mask, reg_value);
+	}
+
+	return ret;
+}
+
+int maxim4c_mipi_txphy_enable(maxim4c_t *maxim4c, bool enable)
+{
+	struct i2c_client *client = maxim4c->client;
+	struct device *dev = &client->dev;
+	maxim4c_mipi_txphy_t *mipi_txphy = &maxim4c->mipi_txphy;
+	u8 phy_idx = 0;
+	u8 reg_mask = 0, reg_value = 0;
+	int ret = 0;
+
+	dev_dbg(dev, "%s: enable = %d\n", __func__, enable);
+
+	reg_mask = 0xF0;
+	reg_value = 0;
+
+	if (enable) {
+		for (phy_idx = 0; phy_idx < MAXIM4C_TXPHY_ID_MAX; phy_idx++) {
+			if (mipi_txphy->phy_cfg[phy_idx].phy_enable)
+				reg_value |= BIT(4 + phy_idx);
+		}
+	}
+
+	ret |= maxim4c_i2c_update_byte(client,
+			0x08A2, MAXIM4C_I2C_REG_ADDR_16BITS,
+			reg_mask, reg_value);
+
+	return ret;
+}
+
+int maxim4c_dphy_dpll_predef_set(maxim4c_t *maxim4c, s64 link_freq_hz)
+{
+	struct i2c_client *client = maxim4c->client;
+	struct device *dev = &client->dev;
+	maxim4c_mipi_txphy_t *mipi_txphy = &maxim4c->mipi_txphy;
+	struct maxim4c_txphy_cfg *phy_cfg = NULL;
+	u32 link_freq_mhz = 0;
+	u16 reg_addr = 0;
+	u8 phy_idx = 0;
+	u8 dpll_mask = 0, dpll_val = 0, dpll_lock = 0;
+	int ret = 0;
+
+	dpll_mask = 0;
+
+	link_freq_mhz = (u32)div_s64(link_freq_hz, 1000000L);
+	dpll_val = DIV_ROUND_UP(link_freq_mhz * 2, 100) & 0x1F;
+	if (dpll_val == 0)
+		dpll_val = 15; /* default 1500MBps */
+	// Disable software override for frequency fine tuning
+	dpll_val |= BIT(5);
+
+	for (phy_idx = 0; phy_idx < MAXIM4C_TXPHY_ID_MAX; phy_idx++) {
+		phy_cfg = &mipi_txphy->phy_cfg[phy_idx];
+		if ((phy_cfg->phy_enable == 0) || (phy_cfg->clock_master == 0))
+			continue;
+
+		if (phy_cfg->clock_mode != MAXIM4C_TXPHY_DPLL_PREDEF)
+			continue;
+
+		dpll_mask |= BIT(phy_idx + 4);
+
+		// Hold DPLL in reset (config_soft_rst_n = 0) before changing the rate
+		reg_addr = 0x1C00 + 0x100 * phy_idx;
+		ret |= maxim4c_i2c_write_byte(client,
+				reg_addr, MAXIM4C_I2C_REG_ADDR_16BITS,
+				0xf4);
+
+		// Set dpll data rate
+		reg_addr = 0x0415 + 0x03 * phy_idx;
+		ret |= maxim4c_i2c_update_byte(client,
+				reg_addr, MAXIM4C_I2C_REG_ADDR_16BITS,
+				0x3F, dpll_val);
+
+		// Release reset to DPLL (config_soft_rst_n = 1)
+		reg_addr = 0x1C00 + 0x100 * phy_idx;
+		ret |= maxim4c_i2c_write_byte(client,
+				reg_addr, MAXIM4C_I2C_REG_ADDR_16BITS,
+				0xf5);
+	}
+
+	if (ret) {
+		dev_err(dev, "DPLL predef set error!\n");
+		return ret;
+	}
+
+	ret = read_poll_timeout(maxim4c_i2c_read_byte, ret,
+				!(ret < 0) && (dpll_lock & dpll_mask),
+				1000, 10000, false,
+				client,
+				0x0400, MAXIM4C_I2C_REG_ADDR_16BITS,
+				&dpll_lock);
+	if (ret < 0) {
+		dev_err(dev, "DPLL is unlocked: 0x%02x\n", dpll_lock);
+		return ret;
+	} else {
+		dev_info(dev, "DPLL is locked: 0x%02x\n", dpll_lock);
+		return 0;
+	}
+}
+EXPORT_SYMBOL(maxim4c_dphy_dpll_predef_set);
+
+int maxim4c_mipi_csi_output(maxim4c_t *maxim4c, bool enable)
+{
+	struct i2c_client *client = maxim4c->client;
+	struct device *dev = &client->dev;
+	maxim4c_mipi_txphy_t *mipi_txphy = &maxim4c->mipi_txphy;
+	u8 reg_mask = 0, reg_value = 0;
+	int ret = 0;
+
+	dev_dbg(dev, "%s: enable = %d\n", __func__, enable);
+
+	if (mipi_txphy->force_clock_out_en != 0) {
+		reg_mask = BIT(7);
+		reg_value = enable ? BIT(7) : 0;
+
+		// Force all MIPI clocks running Config
+		ret |= maxim4c_i2c_update_byte(client,
+				0x08A0, MAXIM4C_I2C_REG_ADDR_16BITS,
+				reg_mask, reg_value);
+	}
+
+	/* Bit1 of the register 0x040B: CSI_OUT_EN
+	 *     1 = CSI output enabled
+	 *     0 = CSI output disabled
+	 */
+	reg_mask = BIT(1);
+	reg_value = enable ? BIT(1) : 0;
+
+	// MIPI CSI output Setting
+	ret |= maxim4c_i2c_update_byte(client,
+			0x040B, MAXIM4C_I2C_REG_ADDR_16BITS,
+			reg_mask, reg_value);
+
+	return ret;
+}
+EXPORT_SYMBOL(maxim4c_mipi_csi_output);
+
+static int maxim4c_mipi_txphy_config_parse_dt(struct device *dev,
+				maxim4c_mipi_txphy_t *mipi_txphy,
+				struct device_node *parent_node)
+{
+	struct device_node *node = NULL;
+	struct maxim4c_txphy_cfg *phy_cfg = NULL;
+	const char *txphy_cfg_name = "mipi-txphy-config";
+	u32 value = 0;
+	u32 sub_idx = 0, phy_id = 0;
+	int ret;
+
+	node = NULL;
+	sub_idx = 0;
+	while ((node = of_get_next_child(parent_node, node))) {
+		if (!strncasecmp(node->name,
+				 txphy_cfg_name,
+				 strlen(txphy_cfg_name))) {
+			if (sub_idx >= MAXIM4C_TXPHY_ID_MAX) {
+				dev_err(dev, "%pOF: Too many matching %s node\n",
+						parent_node, txphy_cfg_name);
+
+				of_node_put(node);
+				break;
+			}
+
+			if (!of_device_is_available(node)) {
+				dev_info(dev, "%pOF is disabled\n", node);
+
+				sub_idx++;
+
+				continue;
+			}
+
+			/* MIPI TXPHY: phy id */
+			ret = of_property_read_u32(node, "phy-id", &phy_id);
+			if (ret) {
+				// if mipi txphy phy_id is error, parse next node
+				dev_err(dev, "Can not get phy-id property!");
+
+				sub_idx++;
+
+				continue;
+			}
+			if (phy_id >= MAXIM4C_TXPHY_ID_MAX) {
+				// if mipi txphy phy_id is error, parse next node
+				dev_err(dev, "Error phy-id = %d!", phy_id);
+
+				sub_idx++;
+
+				continue;
+			}
+
+			phy_cfg = &mipi_txphy->phy_cfg[phy_id];
+
+			/* MIPI TXPHY: phy enable */
+			phy_cfg->phy_enable = 1;
+
+			dev_info(dev, "mipi txphy id = %d: phy_enable = %d\n",
+					phy_id, phy_cfg->phy_enable);
+
+			/* MIPI TXPHY: other config */
+			ret = of_property_read_u32(node, "phy-type", &value);
+			if (ret == 0) {
+				dev_info(dev, "phy-type property: %d", value);
+				phy_cfg->phy_type = value;
+			}
+
+			ret = of_property_read_u32(node, "auto-deskew", &value);
+			if (ret == 0) {
+				dev_info(dev, "auto-deskew property: 0x%x", value);
+				phy_cfg->auto_deskew = value;
+			}
+
+			ret = of_property_read_u32(node, "data-lane-num", &value);
+			if (ret == 0) {
+				dev_info(dev, "data-lane-num property: %d", value);
+				phy_cfg->data_lane_num = value;
+			}
+
+			ret = of_property_read_u32(node, "data-lane-map", &value);
+			if (ret == 0) {
+				dev_info(dev, "data-lane-map property: 0x%x", value);
+				phy_cfg->data_lane_map = value;
+			}
+
+			ret = of_property_read_u32(node, "vc-ext-en", &value);
+			if (ret == 0) {
+				dev_info(dev, "vc-ext-en property: %d", value);
+				phy_cfg->vc_ext_en = value;
+			}
+
+			ret = of_property_read_u32(node, "clock-mode", &value);
+			if (ret == 0) {
+				dev_info(dev, "clock-mode property: %d", value);
+				phy_cfg->clock_mode = value;
+			}
+
+			sub_idx++;
+		}
+	}
+
+	return 0;
+}
+
+int maxim4c_mipi_txphy_parse_dt(maxim4c_t *maxim4c, struct device_node *of_node)
+{
+	struct device *dev = &maxim4c->client->dev;
+	struct device_node *node = NULL;
+	maxim4c_mipi_txphy_t *mipi_txphy = &maxim4c->mipi_txphy;
+	u32 value = 0;
+	int ret = 0;
+
+	dev_info(dev, "=== maxim4c mipi txphy parse dt ===\n");
+
+	node = of_get_child_by_name(of_node, "mipi-txphys");
+	if (IS_ERR_OR_NULL(node)) {
+		dev_err(dev, "%pOF has no child node: mipi-txphys\n",
+				of_node);
+		return -ENODEV;
+	}
+
+	if (!of_device_is_available(node)) {
+		dev_info(dev, "%pOF is disabled\n", node);
+		of_node_put(node);
+		return -ENODEV;
+	}
+
+	/* mipi txphy mode */
+	ret = of_property_read_u32(node, "phy-mode", &value);
+	if (ret == 0) {
+		dev_info(dev, "phy-mode property: %d\n", value);
+		mipi_txphy->phy_mode = value;
+	}
+	dev_info(dev, "mipi txphy mode: %d\n", mipi_txphy->phy_mode);
+
+	/* MIPI clocks running mode */
+	ret = of_property_read_u32(node, "phy-force-clock-out", &value);
+	if (ret == 0) {
+		dev_info(dev, "phy-force-clock-out property: %d\n", value);
+		mipi_txphy->force_clock_out_en = value;
+	}
+	dev_info(dev, "mipi txphy force clock out enable: %d\n",
+			mipi_txphy->force_clock_out_en);
+
+	ret = of_property_read_u32(node, "phy-force-clk0-en", &value);
+	if (ret == 0) {
+		dev_info(dev, "phy-force-clk0-en property: %d\n", value);
+		mipi_txphy->force_clk0_en = value;
+	}
+
+	ret = of_property_read_u32(node, "phy-force-clk3-en", &value);
+	if (ret == 0) {
+		dev_info(dev, "phy-force-clk3-en property: %d\n", value);
+		mipi_txphy->force_clk3_en = value;
+	}
+
+	ret = maxim4c_mipi_txphy_config_parse_dt(dev, mipi_txphy, node);
+
+	of_node_put(node);
+
+	return ret;
+}
+EXPORT_SYMBOL(maxim4c_mipi_txphy_parse_dt);
+
+int maxim4c_mipi_txphy_hw_init(maxim4c_t *maxim4c)
+{
+	struct i2c_client *client = maxim4c->client;
+	struct device *dev = &client->dev;
+	maxim4c_mipi_txphy_t *mipi_txphy = &maxim4c->mipi_txphy;
+	struct maxim4c_txphy_cfg *phy_cfg = NULL;
+	u8 mode = 0;
+	int ret = 0, i = 0;
+
+	mode = 0;
+	switch (mipi_txphy->phy_mode) {
+	case MAXIM4C_TXPHY_MODE_4X2LANES:
+		mode |= BIT(0);
+
+		// clock master
+		for (i = 0; i < MAXIM4C_TXPHY_ID_MAX; i++) {
+			if (mipi_txphy->phy_cfg[i].phy_enable)
+				mipi_txphy->phy_cfg[i].clock_master = 1;
+		}
+
+		break;
+	case MAXIM4C_TXPHY_MODE_1X4LANES_2X2LANES:
+		mode |= BIT(3);
+		// MIPI master clock setting
+		if (mipi_txphy->force_clk0_en != 0)
+			mode |= BIT(5);
+
+		// clock master
+		phy_cfg = &mipi_txphy->phy_cfg[MAXIM4C_TXPHY_ID_B];
+		if (phy_cfg->phy_enable)
+			phy_cfg->clock_master = 1;
+
+		phy_cfg = &mipi_txphy->phy_cfg[MAXIM4C_TXPHY_ID_C];
+		if (phy_cfg->phy_enable)
+			phy_cfg->clock_master = 1;
+
+		phy_cfg = &mipi_txphy->phy_cfg[MAXIM4C_TXPHY_ID_D];
+		if (phy_cfg->phy_enable)
+			phy_cfg->clock_master = 1;
+
+		break;
+	case MAXIM4C_TXPHY_MODE_2X2LANES_1X4LANES:
+		mode |= BIT(4);
+		// MIPI master clock setting
+		if (mipi_txphy->force_clk3_en != 0)
+			mode |= BIT(6);
+
+		// clock master
+		phy_cfg = &mipi_txphy->phy_cfg[MAXIM4C_TXPHY_ID_A];
+		if (phy_cfg->phy_enable)
+			phy_cfg->clock_master = 1;
+
+		phy_cfg = &mipi_txphy->phy_cfg[MAXIM4C_TXPHY_ID_B];
+		if (phy_cfg->phy_enable)
+			phy_cfg->clock_master = 1;
+
+		phy_cfg = &mipi_txphy->phy_cfg[MAXIM4C_TXPHY_ID_C];
+		if (phy_cfg->phy_enable)
+			phy_cfg->clock_master = 1;
+
+		break;
+	case MAXIM4C_TXPHY_MODE_2X4LANES:
+	default:
+		mode |= BIT(2);
+		// MIPI master clock setting
+		if (mipi_txphy->force_clk0_en != 0)
+			mode |= BIT(5);
+		if (mipi_txphy->force_clk3_en != 0)
+			mode |= BIT(6);
+		// clock master
+		phy_cfg = &mipi_txphy->phy_cfg[MAXIM4C_TXPHY_ID_B];
+		if (phy_cfg->phy_enable)
+			phy_cfg->clock_master = 1;
+
+		phy_cfg = &mipi_txphy->phy_cfg[MAXIM4C_TXPHY_ID_C];
+		if (phy_cfg->phy_enable)
+			phy_cfg->clock_master = 1;
+		break;
+	}
+
+	// MIPI TXPHY Mode setting
+	ret |= maxim4c_i2c_write_byte(client,
+			0x08A0, MAXIM4C_I2C_REG_ADDR_16BITS,
+			mode);
+
+	// mipi txphy data lane mapping
+	ret |= maxim4c_mipi_txphy_lane_mapping(maxim4c);
+
+	// mipi txphy type, lane number, virtual channel extension
+	ret |= maxim4c_mipi_txphy_type_vcx_lane_num(maxim4c);
+
+	// mipi txphy auto init deskew
+	ret |= maxim4c_txphy_auto_init_deskew(maxim4c);
+
+	if (ret) {
+		dev_err(dev, "%s: txphy hw init error\n", __func__);
+		return ret;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(maxim4c_mipi_txphy_hw_init);
+
+void maxim4c_mipi_txphy_data_init(maxim4c_t *maxim4c)
+{
+	maxim4c_mipi_txphy_t *mipi_txphy = &maxim4c->mipi_txphy;
+	struct maxim4c_txphy_cfg *phy_cfg = NULL;
+	int i = 0;
+
+	mipi_txphy->phy_mode = MAXIM4C_TXPHY_MODE_2X4LANES;
+	mipi_txphy->force_clock_out_en = 1;
+	mipi_txphy->force_clk0_en = 0;
+	mipi_txphy->force_clk3_en = 0;
+
+	for (i = 0; i < MAXIM4C_TXPHY_ID_MAX; i++) {
+		phy_cfg = &mipi_txphy->phy_cfg[i];
+
+		phy_cfg->phy_enable = 0;
+		phy_cfg->phy_type = MAXIM4C_TXPHY_TYPE_DPHY;
+		phy_cfg->auto_deskew = 0;
+		phy_cfg->data_lane_num = 4;
+		phy_cfg->data_lane_map = 0xe4;
+		phy_cfg->vc_ext_en = 0;
+		phy_cfg->clock_master = 0;
+		phy_cfg->clock_mode = MAXIM4C_TXPHY_DPLL_PREDEF;
+	}
+}
+EXPORT_SYMBOL(maxim4c_mipi_txphy_data_init);
diff --git a/kernel/drivers/media/i2c/maxim4c/maxim4c_mipi_txphy.h b/kernel/drivers/media/i2c/maxim4c/maxim4c_mipi_txphy.h
new file mode 100644
index 0000000..759488d
--- /dev/null
+++ b/kernel/drivers/media/i2c/maxim4c/maxim4c_mipi_txphy.h
@@ -0,0 +1,67 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2023 Rockchip Electronics Co., Ltd.
+ *
+ */
+
+#ifndef __MAXIM4C_MIPI_TXPHY_H__
+#define __MAXIM4C_MIPI_TXPHY_H__
+
+/* MIPI TXPHY ID: 0 ~ 3 */
+enum {
+	MAXIM4C_TXPHY_ID_A = 0,
+	MAXIM4C_TXPHY_ID_B,
+	MAXIM4C_TXPHY_ID_C,
+	MAXIM4C_TXPHY_ID_D,
+	MAXIM4C_TXPHY_ID_MAX,
+};
+
+/* MIPI TXPHY Bit Mask: bit0 ~ bit3 */
+#define MAXIM4C_TXPHY_MASK_A		BIT(MAXIM4C_TXPHY_ID_A)
+#define MAXIM4C_TXPHY_MASK_B		BIT(MAXIM4C_TXPHY_ID_B)
+#define MAXIM4C_TXPHY_MASK_C		BIT(MAXIM4C_TXPHY_ID_C)
+#define MAXIM4C_TXPHY_MASK_D		BIT(MAXIM4C_TXPHY_ID_D)
+
+#define MAXIM4C_TXPHY_MASK_ALL		GENMASK(MAXIM4C_TXPHY_ID_D, MAXIM4C_TXPHY_ID_A)
+
+/* MIPI TXPHY Type */
+enum {
+	MAXIM4C_TXPHY_TYPE_DPHY = 0,
+	MAXIM4C_TXPHY_TYPE_CPHY,
+};
+
+/* MIPI TXPHY Mode */
+enum {
+	MAXIM4C_TXPHY_MODE_2X4LANES = 0, /* PortA: 1x4Lanes, PortB: 1x4Lanes */
+	MAXIM4C_TXPHY_MODE_4X2LANES, /* PortA: 2x2Lanes, PortB: 2x2Lanes */
+	MAXIM4C_TXPHY_MODE_1X4LANES_2X2LANES, /* PortA: 1x4Lanes, PortB: 2x2Lanes */
+	MAXIM4C_TXPHY_MODE_2X2LANES_1X4LANES, /* PortA: 2x2Lanes, PortB: 1x4Lanes */
+};
+
+/* MIPI TXPHY DPLL */
+enum {
+	MAXIM4C_TXPHY_DPLL_PREDEF = 0,
+	MAXIM4C_TXPHY_DPLL_FINE_TUNING,
+};
+
+struct maxim4c_txphy_cfg {
+	u8 phy_enable;
+	u8 phy_type;
+	u8 auto_deskew;
+	u8 data_lane_num;
+	u8 data_lane_map;
+	u8 vc_ext_en;
+	u8 clock_master;
+	u8 clock_mode;
+};
+
+typedef struct maxim4c_mipi_txphy {
+	u8 phy_mode; /* mipi txphy mode */
+	u8 force_clock_out_en; /* Force all MIPI clocks running */
+	u8 force_clk0_en; /* DPHY0 enabled as clock */
+	u8 force_clk3_en; /* DPHY3 enabled as clock */
+
+	struct maxim4c_txphy_cfg phy_cfg[MAXIM4C_TXPHY_ID_MAX];
+} maxim4c_mipi_txphy_t;
+
+#endif /* __MAXIM4C_MIPI_TXPHY_H__ */
diff --git a/kernel/drivers/media/i2c/maxim4c/maxim4c_pattern.c b/kernel/drivers/media/i2c/maxim4c/maxim4c_pattern.c
new file mode 100644
index 0000000..1a3de8f
--- /dev/null
+++ b/kernel/drivers/media/i2c/maxim4c/maxim4c_pattern.c
@@ -0,0 +1,374 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Maxim Quad GMSL Deserializer Test Pattern Driver
+ *
+ * Copyright (C) 2023 Rockchip Electronics Co., Ltd.
+ *
+ * Author: Cai Wenzhong <cwz@rock-chips.com>
+ *
+ */
+#include "maxim4c_api.h"
+
+#define PATTERN_WIDTH		1920
+#define PATTERN_HEIGHT		1080
+
+/* pattern generator: 0 or 1 */
+enum {
+	PATTERN_GENERATOR_0 = 0,
+	PATTERN_GENERATOR_1,
+};
+
+/* pattern mode: checkerboard or gradient */
+enum {
+	PATTERN_CHECKERBOARD = 0,
+	PATTERN_GRADIENT,
+};
+
+/* pattern pclk: 25M or 75M 0r 150M or 375M */
+enum {
+	PATTERN_PCLK_25M = 0,
+	PATTERN_PCLK_75M,
+	PATTERN_PCLK_150M,
+	PATTERN_PCLK_375M,
+};
+
+static const struct maxim4c_mode maxim4c_pattern_mode = {
+	.width = PATTERN_WIDTH,
+	.height = PATTERN_HEIGHT,
+	.max_fps = {
+		.numerator = 10000,
+		.denominator = 300000,
+	},
+	.link_freq_idx = 15,
+	.bus_fmt = MEDIA_BUS_FMT_RGB888_1X24,
+	.bpp = 24,
+	.vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_0,
+};
+
+/* VPG0 or VPG1 register */
+#define VPGx_REG(x, reg)	((reg) + 0x30 * (x))
+
+int maxim4c_pattern_enable(maxim4c_t *maxim4c, bool enable)
+{
+	struct i2c_client *client = maxim4c->client;
+	struct device *dev = &client->dev;
+	struct maxim4c_pattern *pattern = &maxim4c->pattern;
+	u32 vpgx;
+	u32 pattern_mode;
+	u8 reg_mask = 0, reg_val = 0;
+	int ret = 0;
+
+	dev_info(dev, "video pattern: enable = %d\n", enable);
+
+	vpgx = pattern->pattern_generator;
+	pattern_mode = pattern->pattern_mode;
+
+	reg_mask = BIT(5) | BIT(4);
+	if (pattern_mode == PATTERN_CHECKERBOARD) {
+		/* Generate checkerboard pattern. */
+		reg_val = enable ? BIT(4) : 0;
+	} else {
+		/* Generate gradient pattern. */
+		reg_val = enable ? BIT(5) : 0;
+	}
+	ret = maxim4c_i2c_update_byte(client,
+			VPGx_REG(vpgx, 0x1051), MAXIM4C_I2C_REG_ADDR_16BITS,
+			reg_mask, reg_val);
+
+	return ret;
+}
+EXPORT_SYMBOL(maxim4c_pattern_enable);
+
+static int maxim4c_pattern_previnit(maxim4c_t *maxim4c)
+{
+	struct i2c_client *client = maxim4c->client;
+	int ret = 0;
+
+	// All links disable at beginning.
+	ret = maxim4c_i2c_write_byte(client,
+			0x0006, MAXIM4C_I2C_REG_ADDR_16BITS,
+			0xF0);
+	if (ret)
+		return ret;
+
+	// video pipe disable.
+	ret = maxim4c_i2c_write_byte(client,
+			0x00F4, MAXIM4C_I2C_REG_ADDR_16BITS,
+			0x00);
+	if (ret)
+		return ret;
+
+	// MIPI CSI output disable.
+	ret = maxim4c_i2c_write_byte(client,
+			0x040B, MAXIM4C_I2C_REG_ADDR_16BITS,
+			0x00);
+	if (ret)
+		return ret;
+
+	// MIPI TXPHY standby
+	ret = maxim4c_i2c_update_byte(client,
+			0x08A2, MAXIM4C_I2C_REG_ADDR_16BITS,
+			0xF0, 0x00);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int maxim4c_pattern_config(maxim4c_t *maxim4c)
+{
+	const u32 h_active = PATTERN_WIDTH;
+	const u32 h_fp = 88;
+	const u32 h_sw = 44;
+	const u32 h_bp = 148;
+	const u32 h_tot = h_active + h_fp + h_sw + h_bp;
+
+	const u32 v_active = PATTERN_HEIGHT;
+	const u32 v_fp = 4;
+	const u32 v_sw = 5;
+	const u32 v_bp = 36;
+	const u32 v_tot = v_active + v_fp + v_sw + v_bp;
+
+	struct i2c_client *client = maxim4c->client;
+	struct maxim4c_pattern *pattern = &maxim4c->pattern;
+	u32 vpgx;
+	u32 pattern_mode;
+	u32 pattern_pclk;
+	u16 reg_addr = 0;
+	u8 reg_mask = 0, reg_val = 0;
+	int ret = 0, i = 0;
+
+	vpgx = pattern->pattern_generator;
+	pattern_mode = pattern->pattern_mode;
+	pattern_pclk = pattern->pattern_pclk;
+
+	// PATGEN_MODE = 0, Pattern generator disabled
+	//	use video from the serializer input
+	ret |= maxim4c_i2c_update_byte(client,
+			VPGx_REG(vpgx, 0x1051), MAXIM4C_I2C_REG_ADDR_16BITS,
+			BIT(5) | BIT(4), 0x00);
+
+	/* Pattern PCLK:
+	 *	0b00 - 25MHz
+	 *	0b01 - 75MHz
+	 *	0b1x - (PATGEN_CLK_SRC: 0 - 150MHz, 1 - 375MHz).
+	 */
+	pattern_pclk = (pattern_pclk & 0x03);
+	ret |= maxim4c_i2c_write_byte(client,
+			0x0009, MAXIM4C_I2C_REG_ADDR_16BITS,
+			pattern_pclk);
+	if (pattern_pclk >= PATTERN_PCLK_150M) {
+		reg_mask = BIT(7);
+		if (pattern_pclk == PATTERN_PCLK_375M)
+			reg_val = BIT(7);
+		else
+			reg_val = 0;
+
+		if (vpgx == PATTERN_GENERATOR_0) {
+			for (i = 0; i < 4; i++) {
+				reg_addr = 0x01DC + i * 0x20;
+				ret |= maxim4c_i2c_update_byte(client,
+						reg_addr, MAXIM4C_I2C_REG_ADDR_16BITS,
+						reg_mask, reg_val);
+			}
+		} else {
+			for (i = 0; i < 4; i++) {
+				reg_addr = 0x025C + i * 0x20;
+				ret |= maxim4c_i2c_update_byte(client,
+						reg_addr, MAXIM4C_I2C_REG_ADDR_16BITS,
+						reg_mask, reg_val);
+			}
+		}
+	}
+
+	/* Configure Video Timing Generator for 1920x1080 @ 30 fps. */
+	// VS_DLY = 0
+	ret |= maxim4c_i2c_write_reg(client,
+			VPGx_REG(vpgx, 0x1052), MAXIM4C_I2C_REG_ADDR_16BITS,
+			MAXIM4C_I2C_REG_VALUE_24BITS, 0x000000);
+	// VS_HIGH = Vsw * Htot
+	ret |= maxim4c_i2c_write_reg(client,
+			VPGx_REG(vpgx, 0x1055), MAXIM4C_I2C_REG_ADDR_16BITS,
+			MAXIM4C_I2C_REG_VALUE_24BITS, v_sw * h_tot);
+	// VS_LOW = (Vactive + Vfp + Vbp) * Htot
+	ret |= maxim4c_i2c_write_reg(client,
+			VPGx_REG(vpgx, 0x1058), MAXIM4C_I2C_REG_ADDR_16BITS,
+			MAXIM4C_I2C_REG_VALUE_24BITS, (v_active + v_fp + v_bp) * h_tot);
+	// V2H = VS_DLY
+	ret |= maxim4c_i2c_write_reg(client,
+			VPGx_REG(vpgx, 0x105b), MAXIM4C_I2C_REG_ADDR_16BITS,
+			MAXIM4C_I2C_REG_VALUE_24BITS, 0x000000);
+	// HS_HIGH = Hsw
+	ret |= maxim4c_i2c_write_reg(client,
+			VPGx_REG(vpgx, 0x105e), MAXIM4C_I2C_REG_ADDR_16BITS,
+			MAXIM4C_I2C_REG_VALUE_16BITS, h_sw);
+	// HS_LOW = Hactive + Hfp + Hbp
+	ret |= maxim4c_i2c_write_reg(client,
+			VPGx_REG(vpgx, 0x1060), MAXIM4C_I2C_REG_ADDR_16BITS,
+			MAXIM4C_I2C_REG_VALUE_16BITS, h_active + h_fp + h_bp);
+	// HS_CNT = Vtot
+	ret |= maxim4c_i2c_write_reg(client,
+			VPGx_REG(vpgx, 0x1062), MAXIM4C_I2C_REG_ADDR_16BITS,
+			MAXIM4C_I2C_REG_VALUE_16BITS, v_tot);
+	// V2D = VS_DLY + Htot * (Vsw + Vbp) + (Hsw + Hbp)
+	ret |= maxim4c_i2c_write_reg(client,
+			VPGx_REG(vpgx, 0x1064),	MAXIM4C_I2C_REG_ADDR_16BITS,
+			MAXIM4C_I2C_REG_VALUE_24BITS, h_tot * (v_sw + v_bp) + (h_sw + h_bp));
+	// DE_HIGH = Hactive
+	ret |= maxim4c_i2c_write_reg(client,
+			VPGx_REG(vpgx, 0x1067),	MAXIM4C_I2C_REG_ADDR_16BITS,
+			MAXIM4C_I2C_REG_VALUE_16BITS, h_active);
+	// DE_LOW = Hfp + Hsw + Hbp
+	ret |= maxim4c_i2c_write_reg(client,
+			VPGx_REG(vpgx, 0x1069),	MAXIM4C_I2C_REG_ADDR_16BITS,
+			MAXIM4C_I2C_REG_VALUE_16BITS, h_fp + h_sw + h_bp);
+	// DE_CNT = Vactive
+	ret |= maxim4c_i2c_write_reg(client,
+			VPGx_REG(vpgx, 0x106b),	MAXIM4C_I2C_REG_ADDR_16BITS,
+			MAXIM4C_I2C_REG_VALUE_16BITS, v_active);
+
+	/* Generate VS, HS and DE in free-running mode, Invert HS and VS. */
+	ret |= maxim4c_i2c_write_byte(client,
+			VPGx_REG(vpgx, 0x1050), MAXIM4C_I2C_REG_ADDR_16BITS,
+			0xfb);
+
+	/* Configure Video Pattern Generator. */
+	if (pattern_mode == PATTERN_CHECKERBOARD) {
+		/* Set checkerboard pattern size. */
+		ret |= maxim4c_i2c_write_reg(client,
+			VPGx_REG(vpgx, 0x1074),	MAXIM4C_I2C_REG_ADDR_16BITS,
+			MAXIM4C_I2C_REG_VALUE_24BITS, 0x3c3c3c);
+
+		/* Set checkerboard pattern colors. */
+		ret |= maxim4c_i2c_write_reg(client,
+			VPGx_REG(vpgx, 0x106e), MAXIM4C_I2C_REG_ADDR_16BITS,
+			MAXIM4C_I2C_REG_VALUE_24BITS, 0xfecc00);
+		ret |= maxim4c_i2c_write_reg(client,
+			VPGx_REG(vpgx, 0x1071), MAXIM4C_I2C_REG_ADDR_16BITS,
+			MAXIM4C_I2C_REG_VALUE_24BITS, 0x006aa7);
+	} else {
+		/* Set gradient increment. */
+		ret |= maxim4c_i2c_write_byte(client,
+				VPGx_REG(vpgx, 0x106d), MAXIM4C_I2C_REG_ADDR_16BITS,
+				0x10);
+	}
+
+	return ret;
+}
+
+int maxim4c_pattern_support_mode_init(maxim4c_t *maxim4c)
+{
+	struct device *dev = &maxim4c->client->dev;
+	struct maxim4c_mode *supported_mode = NULL;
+
+	dev_info(dev, "=== maxim4c pattern support mode init ===\n");
+
+	maxim4c->cfg_modes_num = 1;
+	maxim4c->cur_mode = &maxim4c->supported_mode;
+	supported_mode = &maxim4c->supported_mode;
+
+	// init using def mode
+	memcpy(supported_mode, &maxim4c_pattern_mode, sizeof(struct maxim4c_mode));
+
+	return 0;
+}
+EXPORT_SYMBOL(maxim4c_pattern_support_mode_init);
+
+int maxim4c_pattern_data_init(maxim4c_t *maxim4c)
+{
+	struct device *dev = &maxim4c->client->dev;
+	struct device_node *node = NULL;
+	struct maxim4c_mode *supported_mode = NULL;
+	struct maxim4c_pattern *pattern = NULL;
+	maxim4c_mipi_txphy_t *mipi_txphy = &maxim4c->mipi_txphy;
+	int ret = 0;
+
+	// maxim serdes local
+	node = of_get_child_by_name(dev->of_node, "serdes-local-device");
+	if (IS_ERR_OR_NULL(node)) {
+		dev_err(dev, "%pOF has no child node: serdes-local-device\n",
+				dev->of_node);
+		return -ENODEV;
+	}
+
+	if (!of_device_is_available(node)) {
+		dev_info(dev, "%pOF is disabled\n", node);
+
+		of_node_put(node);
+		return -ENODEV;
+	}
+
+	maxim4c_mipi_txphy_data_init(maxim4c);
+
+	/* mipi txphy parse dt */
+	ret = maxim4c_mipi_txphy_parse_dt(maxim4c, node);
+	if (ret) {
+		dev_err(dev, "%s: txphy parse dt error\n", __func__);
+		return ret;
+	}
+
+	// pattern need enable force_clock_out_en
+	dev_info(dev, "Pattern mode force_clock_out_en default enable\n");
+	mipi_txphy->force_clock_out_en = 1;
+
+	// pattern generator and mode init
+	pattern = &maxim4c->pattern;
+	pattern->pattern_generator = PATTERN_GENERATOR_0;
+	pattern->pattern_mode = PATTERN_CHECKERBOARD;
+	pattern->pattern_pclk = PATTERN_PCLK_75M;
+
+	supported_mode = &maxim4c->supported_mode;
+	switch (pattern->pattern_pclk) {
+	case PATTERN_PCLK_25M:
+		supported_mode->max_fps.denominator = 100000;
+		break;
+	case PATTERN_PCLK_75M:
+		supported_mode->max_fps.denominator = 300000;
+		break;
+	case PATTERN_PCLK_150M:
+		supported_mode->max_fps.denominator = 600000;
+		if (supported_mode->link_freq_idx < 12)
+			dev_warn(dev, "link_freq_idx = %d is too low\n",
+					supported_mode->link_freq_idx);
+		break;
+	case PATTERN_PCLK_375M:
+		supported_mode->max_fps.denominator = 1500000;
+		if (supported_mode->link_freq_idx < 22)
+			dev_warn(dev, "link_freq_idx = %d is too low\n",
+					supported_mode->link_freq_idx);
+		break;
+	}
+
+	dev_info(dev, "video pattern: generator = %d, mode = %d, pclk = %d\n",
+		pattern->pattern_generator, pattern->pattern_mode, pattern->pattern_pclk);
+
+	return 0;
+}
+EXPORT_SYMBOL(maxim4c_pattern_data_init);
+
+int maxim4c_pattern_hw_init(maxim4c_t *maxim4c)
+{
+	struct device *dev = &maxim4c->client->dev;
+	int ret = 0;
+
+	ret = maxim4c_pattern_previnit(maxim4c);
+	if (ret) {
+		dev_err(dev, "%s: pattern previnit error\n", __func__);
+		return ret;
+	}
+
+	ret = maxim4c_mipi_txphy_hw_init(maxim4c);
+	if (ret) {
+		dev_err(dev, "%s: txphy hw init error\n", __func__);
+		return ret;
+	}
+
+	ret = maxim4c_pattern_config(maxim4c);
+	if (ret) {
+		dev_err(dev, "%s: pattern config error\n", __func__);
+		return ret;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(maxim4c_pattern_hw_init);
diff --git a/kernel/drivers/media/i2c/maxim4c/maxim4c_pattern.h b/kernel/drivers/media/i2c/maxim4c/maxim4c_pattern.h
new file mode 100644
index 0000000..3693b4e
--- /dev/null
+++ b/kernel/drivers/media/i2c/maxim4c/maxim4c_pattern.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2023 Rockchip Electronics Co., Ltd.
+ *
+ */
+
+#ifndef __MAXIM4C_PATTERN_H__
+#define __MAXIM4C_PATTERN_H__
+
+struct maxim4c_pattern {
+	u32 pattern_generator;
+	u32 pattern_mode;
+	u32 pattern_pclk;
+};
+
+#endif /* __MAXIM4C_PATTERN_H__ */
diff --git a/kernel/drivers/media/i2c/maxim4c/maxim4c_remote.c b/kernel/drivers/media/i2c/maxim4c/maxim4c_remote.c
new file mode 100644
index 0000000..5835e0b
--- /dev/null
+++ b/kernel/drivers/media/i2c/maxim4c/maxim4c_remote.c
@@ -0,0 +1,434 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Maxim Quad GMSL Deserializer Remode Device Manage
+ *
+ * Copyright (C) 2023 Rockchip Electronics Co., Ltd.
+ *
+ * Author: Cai Wenzhong <cwz@rock-chips.com>
+ *
+ */
+#include <linux/module.h>
+#include <linux/of_graph.h>
+#include <linux/mfd/core.h>
+#include "maxim4c_api.h"
+
+static const char *maxim4c_remote_devs_name[MAXIM4C_LINK_ID_MAX] = {
+	"remote0", "remote1", "remote2", "remote3"
+};
+
+static const char *maxim4c_remote_link_compat[MAXIM4C_LINK_ID_MAX] = {
+	"maxim4c,link0", "maxim4c,link1", "maxim4c,link2", "maxim4c,link3"
+};
+
+static int maxim4c_remote_dev_info_parse(struct device *dev,
+			struct mfd_cell *remote_mfd_dev, u8 link_id)
+{
+	struct device_node *node = NULL;
+	const char *remote_device_name = "serdes-remote-device";
+	const char *prop_str = NULL, *link_compat = NULL;
+	u32 sub_idx = 0, remote_id = 0;
+	int ret = 0;
+
+	node = NULL;
+	sub_idx = 0;
+	while ((node = of_get_next_child(dev->of_node, node))) {
+		if (!strncasecmp(node->name,
+					remote_device_name,
+					strlen(remote_device_name))) {
+			if (sub_idx >= MAXIM4C_LINK_ID_MAX) {
+				dev_err(dev, "%pOF: Too many matching %s node\n",
+						dev->of_node, remote_device_name);
+
+				of_node_put(node);
+				break;
+			}
+
+			if (!of_device_is_available(node)) {
+				dev_info(dev, "%pOF is disabled\n", node);
+
+				sub_idx++;
+
+				continue;
+			}
+
+			/* remote id */
+			ret = of_property_read_u32(node, "remote-id", &remote_id);
+			if (ret) {
+				sub_idx++;
+
+				continue;
+			}
+			if (remote_id >= MAXIM4C_LINK_ID_MAX) {
+				sub_idx++;
+
+				continue;
+			}
+
+			if (remote_id != link_id) {
+				sub_idx++;
+
+				continue;
+			}
+
+			dev_info(dev, "remote device id = %d\n", remote_id);
+
+			ret = of_property_read_string(node, "compatible", &prop_str);
+			if (ret) {
+				dev_err(dev, "%pOF no compatible error\n", node);
+
+				of_node_put(node);
+				return -EINVAL;
+			}
+
+			link_compat = maxim4c_remote_link_compat[remote_id];
+			if (!strncasecmp(prop_str,
+					link_compat, strlen(link_compat))) {
+				dev_info(dev, "compatible property: %s\n", prop_str);
+
+				remote_mfd_dev->name = maxim4c_remote_devs_name[remote_id];
+				remote_mfd_dev->of_compatible = prop_str;
+
+				of_node_put(node);
+				return 0;
+			}
+
+			dev_err(dev, "%pOF compatible and remote_id mismatch\n", node);
+
+			of_node_put(node);
+			return -EINVAL;
+		}
+	}
+
+	return -EINVAL;
+}
+
+static int maxim4c_remote_mfd_devs_init(maxim4c_t *maxim4c)
+{
+	struct device *dev = &maxim4c->client->dev;
+	maxim4c_gmsl_link_t *gmsl_link = &maxim4c->gmsl_link;
+	struct mfd_cell *remote_mfd_dev = NULL;
+	int link_idx = 0, nr_mfd_cell = 0;
+	int ret = 0;
+
+	remote_mfd_dev = maxim4c->remote_mfd_devs;
+	nr_mfd_cell = 0;
+	for (link_idx = 0; link_idx < MAXIM4C_LINK_ID_MAX; link_idx++) {
+		remote_mfd_dev->name = NULL;
+		remote_mfd_dev->of_compatible = NULL;
+
+		if (gmsl_link->link_cfg[link_idx].link_enable == 0) {
+			dev_dbg(dev, "%s: link id = %d is disabled\n",
+					__func__, link_idx);
+			continue;
+		}
+
+		ret = maxim4c_remote_dev_info_parse(dev, remote_mfd_dev, link_idx);
+		if (ret == 0) {
+			remote_mfd_dev++;
+			nr_mfd_cell++;
+		}
+	}
+
+	dev_info(dev, "Total number of remote devices is %d", nr_mfd_cell);
+
+	return nr_mfd_cell;
+}
+
+int maxim4c_remote_mfd_add_devices(maxim4c_t *maxim4c)
+{
+	struct device *dev = &maxim4c->client->dev;
+	int nr_mfd_cell = 0, ret = 0;
+
+	dev_info(dev, "=== maxim4c add remote devices ===");
+
+	nr_mfd_cell = maxim4c_remote_mfd_devs_init(maxim4c);
+	if (nr_mfd_cell == 0) {
+		dev_err(dev, "%s: remote mfd devices init error\n",
+				__func__);
+		return -EINVAL;
+	}
+
+	ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO,
+			maxim4c->remote_mfd_devs, nr_mfd_cell,
+			NULL, 0, NULL);
+	if (ret)
+		dev_err(dev, "%s: add remote mfd devices error: %d\n",
+				__func__, ret);
+
+	return ret;
+}
+EXPORT_SYMBOL(maxim4c_remote_mfd_add_devices);
+
+int maxim4c_remote_devices_init(maxim4c_t *maxim4c, u8 link_init_mask)
+{
+	struct device *dev = &maxim4c->client->dev;
+	struct maxim4c_remote *remote_device = NULL;
+	const struct maxim4c_remote_ops *remote_ops = NULL;
+	u8 link_mask = 0, link_enable = 0, link_locked = 0;
+	int ret = 0, i = 0;
+
+	dev_dbg(dev, "%s: link init mask = 0x%02x\n", __func__, link_init_mask);
+
+	for (i = 0; i < MAXIM4C_LINK_ID_MAX; i++) {
+		if ((link_init_mask & BIT(i)) == 0) {
+			dev_dbg(dev, "link id = %d init mask is disabled\n", i);
+			continue;
+		}
+
+		link_enable = maxim4c->gmsl_link.link_cfg[i].link_enable;
+		if (link_enable == 0) {
+			dev_info(dev, "link id = %d is disabled\n", i);
+			continue;
+		}
+
+		remote_device = maxim4c->remote_device[i];
+		if (remote_device == NULL) {
+			dev_info(dev, "remote device id = %d isn't detected\n", i);
+			continue;
+		}
+
+		if (remote_device->remote_enable == 0) {
+			dev_info(dev, "remote device id = %d isn't enabled\n", i);
+			continue;
+		}
+
+		remote_ops = remote_device->remote_ops;
+		if (remote_ops == NULL) {
+			dev_info(dev, "remote device id = %d is no ops\n", i);
+			continue;
+		}
+
+		link_mask = BIT(i);
+		link_locked = maxim4c_link_get_lock_state(maxim4c, link_mask);
+		if (link_locked != link_mask) {
+			dev_info(dev, "link id = %d is unlocked\n", i);
+			continue;
+		}
+
+		maxim4c_link_select_remote_control(maxim4c, link_mask);
+
+		if (remote_ops->remote_init)
+			ret |= remote_ops->remote_init(remote_device);
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(maxim4c_remote_devices_init);
+
+int maxim4c_remote_devices_deinit(maxim4c_t *maxim4c, u8 link_init_mask)
+{
+	struct device *dev = &maxim4c->client->dev;
+	struct maxim4c_remote *remote_device = NULL;
+	const struct maxim4c_remote_ops *remote_ops = NULL;
+	u8 link_mask = 0, link_enable = 0, link_locked = 0;
+	int ret = 0, i = 0;
+
+	dev_dbg(dev, "%s: link init mask = 0x%02x\n", __func__, link_init_mask);
+
+	for (i = 0; i < MAXIM4C_LINK_ID_MAX; i++) {
+		if ((link_init_mask & BIT(i)) == 0) {
+			dev_dbg(dev, "link id = %d init mask is disabled\n", i);
+			continue;
+		}
+
+		link_enable = maxim4c->gmsl_link.link_cfg[i].link_enable;
+		if (link_enable == 0) {
+			dev_info(dev, "link id = %d is disabled\n", i);
+			continue;
+		}
+
+		remote_device = maxim4c->remote_device[i];
+		if (remote_device == NULL) {
+			dev_info(dev, "remote device id = %d isn't detected\n", i);
+			continue;
+		}
+
+		if (remote_device->remote_enable == 0) {
+			dev_info(dev, "remote device id = %d isn't enabled\n", i);
+			continue;
+		}
+
+		remote_ops = remote_device->remote_ops;
+		if (remote_ops == NULL) {
+			dev_info(dev, "remote device id = %d is no ops\n", i);
+			continue;
+		}
+
+		link_mask = BIT(i);
+		link_locked = maxim4c_link_get_lock_state(maxim4c, link_mask);
+		if (link_locked != link_mask) {
+			dev_info(dev, "link id = %d is unlocked\n", i);
+			continue;
+		}
+
+		maxim4c_link_select_remote_control(maxim4c, link_mask);
+
+		if (remote_ops->remote_deinit)
+			ret |= remote_ops->remote_deinit(remote_device);
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(maxim4c_remote_devices_deinit);
+
+int maxim4c_remote_load_init_seq(maxim4c_remote_t *remote_device)
+{
+	struct device *dev = remote_device->dev;
+	struct device_node *node = NULL;
+	int ret = 0;
+
+	node = of_get_child_by_name(dev->of_node, "remote-init-sequence");
+	if (!IS_ERR_OR_NULL(node)) {
+		dev_info(dev, "load remote-init-sequence\n");
+
+		ret = maxim4c_i2c_load_init_seq(dev, node,
+					&remote_device->remote_init_seq);
+
+		of_node_put(node);
+		return ret;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(maxim4c_remote_load_init_seq);
+
+int maxim4c_remote_i2c_addr_select(maxim4c_remote_t *remote_device, u32 i2c_id)
+{
+	struct device *dev = remote_device->dev;
+	struct i2c_client *client = remote_device->client;
+
+	if (i2c_id == MAXIM4C_I2C_SER_DEF) {
+		client->addr = remote_device->ser_i2c_addr_def;
+		dev_info(dev, "ser select default i2c addr = 0x%02x\n", client->addr);
+	} else if (i2c_id == MAXIM4C_I2C_SER_MAP) {
+		client->addr = remote_device->ser_i2c_addr_map;
+		dev_info(dev, "ser select mapping i2c addr = 0x%02x\n", client->addr);
+	} else {
+		dev_err(dev, "i2c select id = %d error\n", i2c_id);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(maxim4c_remote_i2c_addr_select);
+
+int maxim4c_remote_i2c_client_init(maxim4c_remote_t *remote_device,
+				struct i2c_client *des_client)
+{
+	struct device *dev = remote_device->dev;
+	struct i2c_client *ser_client = NULL;
+	u16 ser_client_addr = 0;
+
+	if (remote_device->ser_i2c_addr_map)
+		ser_client_addr = remote_device->ser_i2c_addr_map;
+	else
+		ser_client_addr = remote_device->ser_i2c_addr_def;
+	ser_client = devm_i2c_new_dummy_device(&des_client->dev,
+				des_client->adapter, ser_client_addr);
+	if (IS_ERR(ser_client)) {
+		dev_err(dev, "failed to alloc i2c client.\n");
+		return -PTR_ERR(ser_client);
+	}
+	ser_client->addr = remote_device->ser_i2c_addr_def;
+
+	remote_device->client = ser_client;
+	i2c_set_clientdata(ser_client, remote_device);
+
+	dev_info(dev, "remote i2c client init, i2c_addr = 0x%02x\n",
+			ser_client_addr);
+
+	return 0;
+}
+EXPORT_SYMBOL(maxim4c_remote_i2c_client_init);
+
+static int maxim4c_remote_device_chain_check(maxim4c_remote_t *remote_device)
+{
+	struct device *dev = NULL;
+	struct device_node *endpoint = NULL;
+	struct device_node *link_node = NULL;
+	u8 remote_id, link_id;
+	u32 value;
+	int ret = 0;
+
+	if (remote_device == NULL) {
+		dev_err(dev, "%s: input parameter is error\n", __func__);
+		return -EINVAL;
+	}
+
+	dev = remote_device->dev;
+	remote_id = remote_device->remote_id;
+
+	endpoint = of_graph_get_next_endpoint(dev->of_node, NULL);
+	if (!endpoint) {
+		dev_err(dev, "%s: no endpoint error\n", __func__);
+		return -EINVAL;
+	}
+
+	link_node = of_graph_get_remote_port_parent(endpoint);
+	if (!link_node) {
+		dev_err(dev, "%pOF: endpoint has no remote port parent error\n",
+				endpoint);
+		return -EINVAL;
+	}
+
+	ret = of_property_read_u32(link_node, "link-id", &value);
+	if (ret) {
+		dev_err(dev, "%pOF: no property link_id error\n", link_node);
+
+		of_node_put(link_node);
+		return -EINVAL;
+	}
+	of_node_put(link_node);
+	link_id = value;
+
+	if (remote_id != link_id) {
+		dev_err(dev, "remote_id (%d) != link_id (%d) of %pOF\n",
+				remote_id, link_id, link_node);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+int maxim4c_remote_device_register(maxim4c_t *maxim4c,
+		maxim4c_remote_t *remote_device)
+{
+	struct device *dev = NULL;
+	u8 remote_id;
+	int ret = 0;
+
+	if ((maxim4c == NULL) || (remote_device == NULL)) {
+		dev_err(dev, "%s: input parameter is error!\n", __func__);
+
+		return -EINVAL;
+	}
+
+	dev = remote_device->dev;
+	remote_id = remote_device->remote_id;
+	if (remote_id >= MAXIM4C_LINK_ID_MAX) {
+		dev_err(dev, "%s: remote_id = %d is error\n",
+				__func__, remote_id);
+
+		return -EINVAL;
+	}
+
+	if (maxim4c->remote_device[remote_id] != NULL) {
+		dev_err(dev, "%s: remote_id = %d is conflict\n",
+				__func__, remote_id);
+
+		return -EINVAL;
+	}
+
+	ret = maxim4c_remote_device_chain_check(remote_device);
+	if (ret) {
+		dev_err(dev, "%s: remote device id = %d chain error\n",
+				__func__, remote_id);
+		return -EINVAL;
+	}
+
+	remote_device->remote_enable = 1;
+	maxim4c->remote_device[remote_id] = remote_device;
+
+	return 0;
+}
+EXPORT_SYMBOL(maxim4c_remote_device_register);
diff --git a/kernel/drivers/media/i2c/maxim4c/maxim4c_remote.h b/kernel/drivers/media/i2c/maxim4c/maxim4c_remote.h
new file mode 100644
index 0000000..f5c73ca
--- /dev/null
+++ b/kernel/drivers/media/i2c/maxim4c/maxim4c_remote.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2023 Rockchip Electronics Co., Ltd.
+ *
+ */
+
+#ifndef __MAXIM4C_REMOTE_H__
+#define __MAXIM4C_REMOTE_H__
+
+#include "maxim4c_i2c.h"
+
+struct maxim4c_remote;
+
+struct maxim4c_remote_ops {
+	int (*remote_init)(struct maxim4c_remote *remote);
+	int (*remote_deinit)(struct maxim4c_remote *remote);
+};
+
+typedef struct maxim4c_remote {
+	struct i2c_client *client;
+	struct device *dev;
+	void *local;
+	const struct maxim4c_remote_ops *remote_ops;
+	struct maxim4c_i2c_init_seq remote_init_seq;
+
+	u8 remote_id;
+	u8 remote_enable;
+
+	u8 ser_i2c_addr_def;
+	u8 ser_i2c_addr_map;
+
+	u8 cam_i2c_addr_def;
+	u8 cam_i2c_addr_map;
+} maxim4c_remote_t;
+
+#endif /* __MAXIM4C_REMOTE_H__ */
diff --git a/kernel/drivers/media/i2c/maxim4c/maxim4c_v4l2.c b/kernel/drivers/media/i2c/maxim4c/maxim4c_v4l2.c
new file mode 100644
index 0000000..37df9a6
--- /dev/null
+++ b/kernel/drivers/media/i2c/maxim4c/maxim4c_v4l2.c
@@ -0,0 +1,999 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Maxim Quad GMSL Deserializer V4L2 driver
+ *
+ * Copyright (C) 2023 Rockchip Electronics Co., Ltd.
+ *
+ * Author: Cai Wenzhong <cwz@rock-chips.com>
+ *
+ */
+#include <linux/interrupt.h>
+#include <linux/of_graph.h>
+#include <linux/pm_runtime.h>
+#include <linux/compat.h>
+#include <linux/rk-camera-module.h>
+#include <media/media-entity.h>
+#include <media/v4l2-async.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-subdev.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-subdev.h>
+
+#include "maxim4c_api.h"
+
+#ifndef V4L2_CID_DIGITAL_GAIN
+#define V4L2_CID_DIGITAL_GAIN		V4L2_CID_GAIN
+#endif
+
+#define MIPI_PHY_FREQ_MHZ(x)		((x) * 1000000UL)
+
+/* link freq = index * MIPI_PHY_FREQ_MHZ(50) */
+static const s64 link_freq_items[] = {
+	MIPI_PHY_FREQ_MHZ(0),
+	MIPI_PHY_FREQ_MHZ(50),
+	MIPI_PHY_FREQ_MHZ(100),
+	MIPI_PHY_FREQ_MHZ(150),
+	MIPI_PHY_FREQ_MHZ(200),
+	MIPI_PHY_FREQ_MHZ(250),
+	MIPI_PHY_FREQ_MHZ(300),
+	MIPI_PHY_FREQ_MHZ(350),
+	MIPI_PHY_FREQ_MHZ(400),
+	MIPI_PHY_FREQ_MHZ(450),
+	MIPI_PHY_FREQ_MHZ(500),
+	MIPI_PHY_FREQ_MHZ(550),
+	MIPI_PHY_FREQ_MHZ(600),
+	MIPI_PHY_FREQ_MHZ(650),
+	MIPI_PHY_FREQ_MHZ(700),
+	MIPI_PHY_FREQ_MHZ(750),
+	MIPI_PHY_FREQ_MHZ(800),
+	MIPI_PHY_FREQ_MHZ(850),
+	MIPI_PHY_FREQ_MHZ(900),
+	MIPI_PHY_FREQ_MHZ(950),
+	MIPI_PHY_FREQ_MHZ(1000),
+	MIPI_PHY_FREQ_MHZ(1050),
+	MIPI_PHY_FREQ_MHZ(1100),
+	MIPI_PHY_FREQ_MHZ(1150),
+	MIPI_PHY_FREQ_MHZ(1200),
+	MIPI_PHY_FREQ_MHZ(1250),
+};
+
+static const struct maxim4c_mode maxim4c_def_mode = {
+	.width = 1920,
+	.height = 1080,
+	.max_fps = {
+		.numerator = 10000,
+		.denominator = 300000,
+	},
+	.link_freq_idx = 15,
+	.bus_fmt = MEDIA_BUS_FMT_UYVY8_2X8,
+	.bpp = 16,
+	.vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_0,
+	.vc[PAD1] = V4L2_MBUS_CSI2_CHANNEL_1,
+	.vc[PAD2] = V4L2_MBUS_CSI2_CHANNEL_2,
+	.vc[PAD3] = V4L2_MBUS_CSI2_CHANNEL_3,
+};
+
+static struct rkmodule_csi_dphy_param rk3588_dcphy_param = {
+	.vendor = PHY_VENDOR_SAMSUNG,
+	.lp_vol_ref = 3,
+	.lp_hys_sw = {3, 0, 0, 0},
+	.lp_escclk_pol_sel = {1, 0, 0, 0},
+	.skew_data_cal_clk = {0, 0, 0, 0},
+	.clk_hs_term_sel = 2,
+	.data_hs_term_sel = {2, 2, 2, 2},
+	.reserved = {0},
+};
+
+static int maxim4c_support_mode_init(maxim4c_t *maxim4c)
+{
+	struct device *dev = &maxim4c->client->dev;
+	struct device_node *node = NULL;
+	struct maxim4c_mode *mode = NULL;
+	u32 value = 0, vc_array[PAD_MAX];
+	int ret = 0, i = 0, array_size = 0;
+
+	dev_info(dev, "=== maxim4c support mode init ===\n");
+
+#if MAXIM4C_TEST_PATTERN
+	ret = maxim4c_pattern_support_mode_init(maxim4c);
+	return ret;
+#endif
+
+	maxim4c->cfg_modes_num = 1;
+	maxim4c->cur_mode = &maxim4c->supported_mode;
+	mode = &maxim4c->supported_mode;
+
+	// init using def mode
+	memcpy(mode, &maxim4c_def_mode, sizeof(struct maxim4c_mode));
+
+	node = of_get_child_by_name(dev->of_node, "support-mode-config");
+	if (IS_ERR_OR_NULL(node)) {
+		dev_info(dev, "no mode config node, using default config.\n");
+
+		return 0;
+	}
+
+	if (!of_device_is_available(node)) {
+		dev_info(dev, "%pOF is disabled, using default config.\n", node);
+
+		of_node_put(node);
+
+		return 0;
+	}
+
+	ret = of_property_read_u32(node, "sensor-width", &value);
+	if (ret == 0) {
+		dev_info(dev, "sensor-width property: %d\n", value);
+		mode->width = value;
+	}
+	dev_info(dev, "support mode: width = %d\n", mode->width);
+
+	ret = of_property_read_u32(node, "sensor-height", &value);
+	if (ret == 0) {
+		dev_info(dev, "sensor-height property: %d\n", value);
+		mode->height = value;
+	}
+	dev_info(dev, "support mode: height = %d\n", mode->height);
+
+	ret = of_property_read_u32(node, "bus-format", &value);
+	if (ret == 0) {
+		dev_info(dev, "bus-format property: %d\n", value);
+		mode->bus_fmt = value;
+	}
+	dev_info(dev, "support mode: bus_fmt = 0x%x\n", mode->bus_fmt);
+
+	ret = of_property_read_u32(node, "bpp", &value);
+	if (ret == 0) {
+		dev_info(dev, "bpp property: %d\n", value);
+		mode->bpp = value;
+	}
+	dev_info(dev, "support mode: bpp = %d\n", mode->bpp);
+
+	ret = of_property_read_u32(node, "max-fps-numerator", &value);
+	if (ret == 0) {
+		dev_info(dev, "max-fps-numerator property: %d\n", value);
+		mode->max_fps.numerator = value;
+	}
+	dev_info(dev, "support mode: numerator = %d\n", mode->max_fps.numerator);
+
+	ret = of_property_read_u32(node, "max-fps-denominator", &value);
+	if (ret == 0) {
+		dev_info(dev, "max-fps-denominator property: %d\n", value);
+		mode->max_fps.denominator = value;
+	}
+	dev_info(dev, "support mode: denominator = %d\n", mode->max_fps.denominator);
+
+	ret = of_property_read_u32(node, "link-freq-idx", &value);
+	if (ret == 0) {
+		dev_info(dev, "link-freq-idx property: %d\n", value);
+		mode->link_freq_idx = value;
+	}
+	dev_info(dev, "support mode: link_freq_idx = %d\n", mode->link_freq_idx);
+
+	ret = of_property_read_u32(node, "hts-def", &value);
+	if (ret == 0) {
+		dev_info(dev, "hts-def property: %d\n", value);
+		mode->hts_def = value;
+	}
+	dev_info(dev, "support mode: hts_def = %d\n", mode->hts_def);
+
+	ret = of_property_read_u32(node, "vts-def", &value);
+	if (ret == 0) {
+		dev_info(dev, "vts-def property: %d\n", value);
+		mode->vts_def = value;
+	}
+	dev_info(dev, "support mode: vts_def = %d\n", mode->vts_def);
+
+	ret = of_property_read_u32(node, "exp-def", &value);
+	if (ret == 0) {
+		dev_info(dev, "exp-def property: %d\n", value);
+		mode->exp_def = value;
+	}
+	dev_info(dev, "support mode: exp_def = %d\n", mode->exp_def);
+
+	array_size = of_property_read_variable_u32_array(node,
+				"vc-array", vc_array, 1, PAD_MAX);
+	if (array_size > 0) {
+		if (array_size > PAD_MAX)
+			array_size = PAD_MAX;
+
+		for (i = 0; i < array_size; i++) {
+			dev_info(dev, "vc-array[%d] property: 0x%x\n", i, vc_array[i]);
+			mode->vc[i] = vc_array[i];
+		}
+	}
+	for (i = 0; i < PAD_MAX; i++)
+		dev_info(dev, "support mode: vc[%d] = 0x%x\n", i, mode->vc[i]);
+
+	of_node_put(node);
+
+	return 0;
+}
+
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+static int maxim4c_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+	struct maxim4c *maxim4c = v4l2_get_subdevdata(sd);
+	struct v4l2_mbus_framefmt *try_fmt =
+		v4l2_subdev_get_try_format(sd, fh->pad, 0);
+	const struct maxim4c_mode *def_mode = &maxim4c->supported_mode;
+
+	mutex_lock(&maxim4c->mutex);
+
+	/* Initialize try_fmt */
+	try_fmt->width = def_mode->width;
+	try_fmt->height = def_mode->height;
+	try_fmt->code = def_mode->bus_fmt;
+	try_fmt->field = V4L2_FIELD_NONE;
+
+	mutex_unlock(&maxim4c->mutex);
+	/* No crop or compose */
+
+	return 0;
+}
+#endif
+
+static int maxim4c_s_power(struct v4l2_subdev *sd, int on)
+{
+	struct maxim4c *maxim4c = v4l2_get_subdevdata(sd);
+	struct i2c_client *client = maxim4c->client;
+	int ret = 0;
+
+	mutex_lock(&maxim4c->mutex);
+
+	/* If the power state is not modified - no work to do. */
+	if (maxim4c->power_on == !!on)
+		goto unlock_and_return;
+
+	if (on) {
+		ret = pm_runtime_get_sync(&client->dev);
+		if (ret < 0) {
+			pm_runtime_put_noidle(&client->dev);
+			goto unlock_and_return;
+		}
+
+		maxim4c->power_on = true;
+	} else {
+		pm_runtime_put(&client->dev);
+		maxim4c->power_on = false;
+	}
+
+unlock_and_return:
+	mutex_unlock(&maxim4c->mutex);
+
+	return ret;
+}
+
+static void maxim4c_get_module_inf(struct maxim4c *maxim4c,
+					struct rkmodule_inf *inf)
+{
+	memset(inf, 0, sizeof(*inf));
+	strscpy(inf->base.sensor, MAXIM4C_NAME, sizeof(inf->base.sensor));
+	strscpy(inf->base.module, maxim4c->module_name,
+		sizeof(inf->base.module));
+	strscpy(inf->base.lens, maxim4c->len_name, sizeof(inf->base.lens));
+}
+
+static void maxim4c_get_vicap_rst_inf(struct maxim4c *maxim4c,
+				struct rkmodule_vicap_reset_info *rst_info)
+{
+	struct i2c_client *client = maxim4c->client;
+
+	rst_info->is_reset = maxim4c->hot_plug;
+	maxim4c->hot_plug = false;
+	rst_info->src = RKCIF_RESET_SRC_ERR_HOTPLUG;
+
+	dev_info(&client->dev, "%s: rst_info->is_reset:%d.\n",
+		__func__, rst_info->is_reset);
+}
+
+static void maxim4c_set_vicap_rst_inf(struct maxim4c *maxim4c,
+				struct rkmodule_vicap_reset_info rst_info)
+{
+	maxim4c->is_reset = rst_info.is_reset;
+}
+
+static long maxim4c_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
+{
+	struct maxim4c *maxim4c = v4l2_get_subdevdata(sd);
+	struct rkmodule_csi_dphy_param *dphy_param;
+	long ret = 0;
+
+	dev_dbg(&maxim4c->client->dev, "ioctl cmd = 0x%08x\n", cmd);
+
+	switch (cmd) {
+	case RKMODULE_GET_MODULE_INFO:
+		maxim4c_get_module_inf(maxim4c, (struct rkmodule_inf *)arg);
+		break;
+	case RKMODULE_GET_VICAP_RST_INFO:
+		maxim4c_get_vicap_rst_inf(maxim4c,
+			(struct rkmodule_vicap_reset_info *)arg);
+		break;
+	case RKMODULE_SET_VICAP_RST_INFO:
+		maxim4c_set_vicap_rst_inf(maxim4c,
+			*(struct rkmodule_vicap_reset_info *)arg);
+		break;
+	case RKMODULE_SET_CSI_DPHY_PARAM:
+		dphy_param = (struct rkmodule_csi_dphy_param *)arg;
+		rk3588_dcphy_param = *dphy_param;
+		dev_dbg(&maxim4c->client->dev, "set dcphy param\n");
+		break;
+	case RKMODULE_GET_CSI_DPHY_PARAM:
+		dphy_param = (struct rkmodule_csi_dphy_param *)arg;
+		*dphy_param = rk3588_dcphy_param;
+		dev_dbg(&maxim4c->client->dev, "get dcphy param\n");
+		break;
+	default:
+		ret = -ENOIOCTLCMD;
+		break;
+	}
+
+	return ret;
+}
+
+#ifdef CONFIG_COMPAT
+static long maxim4c_compat_ioctl32(struct v4l2_subdev *sd, unsigned int cmd,
+					unsigned long arg)
+{
+	void __user *up = compat_ptr(arg);
+	struct rkmodule_inf *inf;
+	struct rkmodule_vicap_reset_info *vicap_rst_inf;
+	struct rkmodule_csi_dphy_param *dphy_param;
+	long ret = 0;
+
+	switch (cmd) {
+	case RKMODULE_GET_MODULE_INFO:
+		inf = kzalloc(sizeof(*inf), GFP_KERNEL);
+		if (!inf) {
+			ret = -ENOMEM;
+			return ret;
+		}
+
+		ret = maxim4c_ioctl(sd, cmd, inf);
+		if (!ret) {
+			ret = copy_to_user(up, inf, sizeof(*inf));
+			if (ret)
+				ret = -EFAULT;
+		}
+		kfree(inf);
+		break;
+	case RKMODULE_GET_VICAP_RST_INFO:
+		vicap_rst_inf = kzalloc(sizeof(*vicap_rst_inf), GFP_KERNEL);
+		if (!vicap_rst_inf) {
+			ret = -ENOMEM;
+			return ret;
+		}
+
+		ret = maxim4c_ioctl(sd, cmd, vicap_rst_inf);
+		if (!ret) {
+			ret = copy_to_user(up, vicap_rst_inf, sizeof(*vicap_rst_inf));
+			if (ret)
+				ret = -EFAULT;
+		}
+		kfree(vicap_rst_inf);
+		break;
+	case RKMODULE_SET_VICAP_RST_INFO:
+		vicap_rst_inf = kzalloc(sizeof(*vicap_rst_inf), GFP_KERNEL);
+		if (!vicap_rst_inf) {
+			ret = -ENOMEM;
+			return ret;
+		}
+
+		ret = copy_from_user(vicap_rst_inf, up, sizeof(*vicap_rst_inf));
+		if (!ret)
+			ret = maxim4c_ioctl(sd, cmd, vicap_rst_inf);
+		else
+			ret = -EFAULT;
+		kfree(vicap_rst_inf);
+		break;
+	case RKMODULE_SET_CSI_DPHY_PARAM:
+		dphy_param = kzalloc(sizeof(*dphy_param), GFP_KERNEL);
+		if (!dphy_param) {
+			ret = -ENOMEM;
+			return ret;
+		}
+
+		ret = copy_from_user(dphy_param, up, sizeof(*dphy_param));
+		if (!ret)
+			ret = maxim4c_ioctl(sd, cmd, dphy_param);
+		else
+			ret = -EFAULT;
+		kfree(dphy_param);
+		break;
+	case RKMODULE_GET_CSI_DPHY_PARAM:
+		dphy_param = kzalloc(sizeof(*dphy_param), GFP_KERNEL);
+		if (!dphy_param) {
+			ret = -ENOMEM;
+			return ret;
+		}
+
+		ret = maxim4c_ioctl(sd, cmd, dphy_param);
+		if (!ret) {
+			ret = copy_to_user(up, dphy_param, sizeof(*dphy_param));
+			if (ret)
+				ret = -EFAULT;
+		}
+		kfree(dphy_param);
+		break;
+	default:
+		ret = -ENOIOCTLCMD;
+		break;
+	}
+
+	return ret;
+}
+#endif /* CONFIG_COMPAT */
+
+static int __maxim4c_start_stream(struct maxim4c *maxim4c)
+{
+	struct device *dev = &maxim4c->client->dev;
+	int ret = 0;
+	s64 link_freq_hz = 0;
+	u8 link_mask = 0, link_freq_idx = 0;
+	u8 video_pipe_mask = 0;
+
+#if MAXIM4C_LOCAL_DES_ON_OFF_EN
+#if MAXIM4C_TEST_PATTERN
+	ret = maxim4c_pattern_hw_init(maxim4c);
+	if (ret) {
+		dev_err(dev, "test pattern hw init error\n");
+		return ret;
+	}
+#else
+	ret = maxim4c_module_hw_init(maxim4c);
+	if (ret) {
+		dev_err(dev, "maxim4c module hw init error\n");
+		return ret;
+	}
+#endif /* MAXIM4C_TEST_PATTERN */
+#endif /* MAXIM4C_LOCAL_DES_ON_OFF_EN */
+
+	link_mask = maxim4c->gmsl_link.link_enable_mask;
+	video_pipe_mask = maxim4c->video_pipe.pipe_enable_mask;
+
+	// disable all remote control
+	ret = maxim4c_link_select_remote_control(maxim4c, 0);
+	if (ret) {
+		dev_err(dev, "link disable remote control error\n");
+		return ret;
+	}
+
+	// disable all video pipe
+	ret = maxim4c_video_pipe_mask_enable(maxim4c, video_pipe_mask, false);
+	if (ret) {
+		dev_err(dev, "video pipe disable error\n");
+		return ret;
+	}
+
+	ret = maxim4c_link_select_remote_enable(maxim4c, link_mask);
+	if (ret) {
+		dev_err(dev, "link select enable error, mask = 0x%x\n", link_mask);
+		return ret;
+	}
+
+	link_mask = maxim4c->gmsl_link.link_locked_mask;
+	ret = maxim4c_remote_devices_init(maxim4c, link_mask);
+	if (ret) {
+		dev_err(dev, "remote devices init error\n");
+		return ret;
+	}
+
+	// mipi txphy enable setting: standby or enable
+	ret = maxim4c_mipi_txphy_enable(maxim4c, true);
+	if (ret) {
+		dev_err(dev, "mipi txphy enable error\n");
+		return ret;
+	}
+
+	// mipi txphy dpll setting
+	link_freq_idx = maxim4c->cur_mode->link_freq_idx;
+	link_freq_hz = link_freq_items[link_freq_idx];
+	ret = maxim4c_dphy_dpll_predef_set(maxim4c, link_freq_hz);
+	if (ret) {
+		dev_err(dev, "mipi txphy dpll setting error\n");
+		return ret;
+	}
+
+	// enable video pipe
+	ret = maxim4c_video_pipe_mask_enable(maxim4c, video_pipe_mask, true);
+	if (ret) {
+		dev_err(dev, "video pipe enable error\n");
+		return ret;
+	}
+
+	ret = maxim4c_link_select_remote_control(maxim4c, link_mask);
+	if (ret) {
+		dev_err(dev, "remote control enable error\n");
+		return ret;
+	}
+
+	/* In case these controls are set before streaming */
+	mutex_unlock(&maxim4c->mutex);
+	ret = v4l2_ctrl_handler_setup(&maxim4c->ctrl_handler);
+	mutex_lock(&maxim4c->mutex);
+	if (ret)
+		return ret;
+
+#if MAXIM4C_TEST_PATTERN
+	ret = maxim4c_pattern_enable(maxim4c, true);
+	if (ret) {
+		dev_err(dev, "test pattern setting error\n");
+		return ret;
+	}
+#endif /* MAXIM4C_TEST_PATTERN */
+
+	ret = maxim4c_mipi_csi_output(maxim4c, true);
+	if (ret) {
+		dev_err(dev, "mipi csi output error\n");
+		return ret;
+	}
+
+	if (maxim4c->hot_plug_irq > 0)
+		enable_irq(maxim4c->hot_plug_irq);
+
+	return 0;
+}
+
+static int __maxim4c_stop_stream(struct maxim4c *maxim4c)
+{
+	struct device *dev = &maxim4c->client->dev;
+	u8 link_mask = 0, pipe_mask = 0;
+	int ret = 0;
+
+	link_mask = maxim4c->gmsl_link.link_enable_mask;
+	pipe_mask = maxim4c->video_pipe.pipe_enable_mask;
+
+	if (maxim4c->hot_plug_irq > 0)
+		disable_irq(maxim4c->hot_plug_irq);
+
+	if (maxim4c->hot_plug_work.state_check_wq)
+		cancel_delayed_work_sync(&maxim4c->hot_plug_work.state_d_work);
+
+	ret |= maxim4c_mipi_csi_output(maxim4c, false);
+	ret |= maxim4c_mipi_txphy_enable(maxim4c, false);
+
+#if MAXIM4C_TEST_PATTERN
+	ret |= maxim4c_pattern_enable(maxim4c, false);
+#endif /* MAXIM4C_TEST_PATTERN */
+
+	ret |= maxim4c_video_pipe_mask_enable(maxim4c, pipe_mask, false);
+
+	ret |= maxim4c_remote_devices_deinit(maxim4c, link_mask);
+
+	ret |= maxim4c_link_select_remote_control(maxim4c, 0);
+	ret |= maxim4c_link_mask_enable(maxim4c, link_mask, false);
+
+	if (ret) {
+		dev_err(dev, "stop stream error\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int maxim4c_s_stream(struct v4l2_subdev *sd, int on)
+{
+	struct maxim4c *maxim4c = v4l2_get_subdevdata(sd);
+	struct i2c_client *client = maxim4c->client;
+	int ret = 0;
+
+	dev_info(&client->dev, "%s: on: %d, %dx%d@%d\n", __func__, on,
+		maxim4c->cur_mode->width, maxim4c->cur_mode->height,
+		DIV_ROUND_CLOSEST(maxim4c->cur_mode->max_fps.denominator,
+				maxim4c->cur_mode->max_fps.numerator));
+
+	mutex_lock(&maxim4c->mutex);
+	on = !!on;
+	if (on == maxim4c->streaming)
+		goto unlock_and_return;
+
+	if (on) {
+		ret = pm_runtime_get_sync(&client->dev);
+		if (ret < 0) {
+			pm_runtime_put_noidle(&client->dev);
+			goto unlock_and_return;
+		}
+
+		ret = __maxim4c_start_stream(maxim4c);
+		if (ret) {
+			v4l2_err(sd, "start stream failed while write regs\n");
+			pm_runtime_put(&client->dev);
+			goto unlock_and_return;
+		}
+	} else {
+		__maxim4c_stop_stream(maxim4c);
+		pm_runtime_put(&client->dev);
+	}
+
+	maxim4c->streaming = on;
+
+unlock_and_return:
+	mutex_unlock(&maxim4c->mutex);
+
+	return ret;
+}
+
+static int maxim4c_g_frame_interval(struct v4l2_subdev *sd,
+				struct v4l2_subdev_frame_interval *fi)
+{
+	struct maxim4c *maxim4c = v4l2_get_subdevdata(sd);
+	const struct maxim4c_mode *mode = maxim4c->cur_mode;
+
+	mutex_lock(&maxim4c->mutex);
+	fi->interval = mode->max_fps;
+	mutex_unlock(&maxim4c->mutex);
+
+	return 0;
+}
+
+static int maxim4c_enum_mbus_code(struct v4l2_subdev *sd,
+				struct v4l2_subdev_pad_config *cfg,
+				struct v4l2_subdev_mbus_code_enum *code)
+{
+	struct maxim4c *maxim4c = v4l2_get_subdevdata(sd);
+	const struct maxim4c_mode *mode = maxim4c->cur_mode;
+
+	if (code->index != 0)
+		return -EINVAL;
+	code->code = mode->bus_fmt;
+
+	return 0;
+}
+
+static int maxim4c_enum_frame_sizes(struct v4l2_subdev *sd,
+				struct v4l2_subdev_pad_config *cfg,
+				struct v4l2_subdev_frame_size_enum *fse)
+{
+	struct maxim4c *maxim4c = v4l2_get_subdevdata(sd);
+
+	if (fse->index >= maxim4c->cfg_modes_num)
+		return -EINVAL;
+
+	if (fse->code != maxim4c->supported_mode.bus_fmt)
+		return -EINVAL;
+
+	fse->min_width  = maxim4c->supported_mode.width;
+	fse->max_width  = maxim4c->supported_mode.width;
+	fse->max_height = maxim4c->supported_mode.height;
+	fse->min_height = maxim4c->supported_mode.height;
+
+	return 0;
+}
+
+static int
+maxim4c_enum_frame_interval(struct v4l2_subdev *sd,
+			struct v4l2_subdev_pad_config *cfg,
+			struct v4l2_subdev_frame_interval_enum *fie)
+{
+	struct maxim4c *maxim4c = v4l2_get_subdevdata(sd);
+
+	if (fie->index >= maxim4c->cfg_modes_num)
+		return -EINVAL;
+
+	fie->code = maxim4c->supported_mode.bus_fmt;
+	fie->width = maxim4c->supported_mode.width;
+	fie->height = maxim4c->supported_mode.height;
+	fie->interval = maxim4c->supported_mode.max_fps;
+
+	return 0;
+}
+
+static int maxim4c_get_fmt(struct v4l2_subdev *sd,
+			struct v4l2_subdev_pad_config *cfg,
+			struct v4l2_subdev_format *fmt)
+{
+	struct maxim4c *maxim4c = v4l2_get_subdevdata(sd);
+	const struct maxim4c_mode *mode = maxim4c->cur_mode;
+
+	mutex_lock(&maxim4c->mutex);
+	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+		fmt->format = *v4l2_subdev_get_try_format(sd, cfg, fmt->pad);
+#else
+		mutex_unlock(&maxim4c->mutex);
+		return -ENOTTY;
+#endif
+	} else {
+		fmt->format.width = mode->width;
+		fmt->format.height = mode->height;
+		fmt->format.code = mode->bus_fmt;
+		fmt->format.field = V4L2_FIELD_NONE;
+		if (fmt->pad < PAD_MAX && fmt->pad >= PAD0)
+			fmt->reserved[0] = mode->vc[fmt->pad];
+		else
+			fmt->reserved[0] = mode->vc[PAD0];
+	}
+	mutex_unlock(&maxim4c->mutex);
+
+	return 0;
+}
+
+static int maxim4c_set_fmt(struct v4l2_subdev *sd,
+			struct v4l2_subdev_pad_config *cfg,
+			struct v4l2_subdev_format *fmt)
+{
+	struct maxim4c *maxim4c = v4l2_get_subdevdata(sd);
+	struct device *dev = &maxim4c->client->dev;
+	const struct maxim4c_mode *mode = NULL;
+	u64 link_freq = 0, pixel_rate = 0;
+	u8 data_lanes;
+
+	mutex_lock(&maxim4c->mutex);
+
+	mode = &maxim4c->supported_mode;
+
+	fmt->format.code = mode->bus_fmt;
+	fmt->format.width = mode->width;
+	fmt->format.height = mode->height;
+	fmt->format.field = V4L2_FIELD_NONE;
+	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+		*v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = fmt->format;
+#else
+		mutex_unlock(&maxim4c->mutex);
+		return -ENOTTY;
+#endif
+	} else {
+		if (maxim4c->streaming) {
+			mutex_unlock(&maxim4c->mutex);
+			return -EBUSY;
+		}
+
+		maxim4c->cur_mode = mode;
+
+		__v4l2_ctrl_s_ctrl(maxim4c->link_freq, mode->link_freq_idx);
+
+		/* pixel rate = link frequency * 2 * lanes / BITS_PER_SAMPLE */
+		link_freq = link_freq_items[mode->link_freq_idx];
+		data_lanes = maxim4c->bus_cfg.bus.mipi_csi2.num_data_lanes;
+		pixel_rate = (u32)link_freq / mode->bpp * 2 * data_lanes;
+		__v4l2_ctrl_s_ctrl_int64(maxim4c->pixel_rate, pixel_rate);
+
+		dev_info(dev, "mipi_freq_idx = %d, mipi_link_freq = %lld\n",
+					mode->link_freq_idx, link_freq);
+		dev_info(dev, "pixel_rate = %lld, bpp = %d\n",
+					pixel_rate, mode->bpp);
+	}
+
+	mutex_unlock(&maxim4c->mutex);
+
+	return 0;
+}
+
+static int maxim4c_get_selection(struct v4l2_subdev *sd,
+				struct v4l2_subdev_pad_config *cfg,
+				struct v4l2_subdev_selection *sel)
+{
+	struct maxim4c *maxim4c = v4l2_get_subdevdata(sd);
+
+	if (sel->target == V4L2_SEL_TGT_CROP_BOUNDS) {
+		sel->r.left = 0;
+		sel->r.width = maxim4c->cur_mode->width;
+		sel->r.top = 0;
+		sel->r.height = maxim4c->cur_mode->height;
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static int maxim4c_g_mbus_config(struct v4l2_subdev *sd, unsigned int pad,
+				struct v4l2_mbus_config *config)
+{
+	struct maxim4c *maxim4c = v4l2_get_subdevdata(sd);
+	u32 val = 0;
+	u8 data_lanes = maxim4c->bus_cfg.bus.mipi_csi2.num_data_lanes;
+
+	val |= V4L2_MBUS_CSI2_CONTINUOUS_CLOCK;
+	val |= (1 << (data_lanes - 1));
+	switch (data_lanes) {
+	case 4:
+		val |= V4L2_MBUS_CSI2_CHANNEL_3;
+		fallthrough;
+	case 3:
+		val |= V4L2_MBUS_CSI2_CHANNEL_2;
+		fallthrough;
+	case 2:
+		val |= V4L2_MBUS_CSI2_CHANNEL_1;
+		fallthrough;
+	case 1:
+	default:
+		val |= V4L2_MBUS_CSI2_CHANNEL_0;
+		break;
+	}
+
+	config->type = V4L2_MBUS_CSI2_DPHY;
+	config->flags = val;
+
+	return 0;
+}
+
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+static const struct v4l2_subdev_internal_ops maxim4c_internal_ops = {
+	.open = maxim4c_open,
+};
+#endif
+
+static const struct v4l2_subdev_core_ops maxim4c_core_ops = {
+	.s_power = maxim4c_s_power,
+	.ioctl = maxim4c_ioctl,
+#ifdef CONFIG_COMPAT
+	.compat_ioctl32 = maxim4c_compat_ioctl32,
+#endif
+};
+
+static const struct v4l2_subdev_video_ops maxim4c_video_ops = {
+	.s_stream = maxim4c_s_stream,
+	.g_frame_interval = maxim4c_g_frame_interval,
+};
+
+static const struct v4l2_subdev_pad_ops maxim4c_pad_ops = {
+	.enum_mbus_code = maxim4c_enum_mbus_code,
+	.enum_frame_size = maxim4c_enum_frame_sizes,
+	.enum_frame_interval = maxim4c_enum_frame_interval,
+	.get_fmt = maxim4c_get_fmt,
+	.set_fmt = maxim4c_set_fmt,
+	.get_selection = maxim4c_get_selection,
+	.get_mbus_config = maxim4c_g_mbus_config,
+};
+
+static const struct v4l2_subdev_ops maxim4c_subdev_ops = {
+	.core = &maxim4c_core_ops,
+	.video = &maxim4c_video_ops,
+	.pad = &maxim4c_pad_ops,
+};
+
+static int maxim4c_initialize_controls(struct maxim4c *maxim4c)
+{
+	struct device *dev = &maxim4c->client->dev;
+	const struct maxim4c_mode *mode;
+	struct v4l2_ctrl_handler *handler;
+	u64 link_freq = 0, pixel_rate = 0;
+	u8 data_lanes;
+	int ret = 0;
+
+	handler = &maxim4c->ctrl_handler;
+
+	ret = v4l2_ctrl_handler_init(handler, 2);
+	if (ret)
+		return ret;
+	handler->lock = &maxim4c->mutex;
+
+	mode = maxim4c->cur_mode;
+	maxim4c->link_freq = v4l2_ctrl_new_int_menu(handler, NULL,
+				V4L2_CID_LINK_FREQ,
+				ARRAY_SIZE(link_freq_items) - 1, 0,
+				link_freq_items);
+	__v4l2_ctrl_s_ctrl(maxim4c->link_freq, mode->link_freq_idx);
+
+	link_freq = link_freq_items[mode->link_freq_idx];
+	dev_info(dev, "mipi_freq_idx = %d, mipi_link_freq = %lld\n",
+				mode->link_freq_idx, link_freq);
+
+	/* pixel rate = link frequency * 2 * lanes / BITS_PER_SAMPLE */
+	data_lanes = maxim4c->bus_cfg.bus.mipi_csi2.num_data_lanes;
+	pixel_rate = (u32)link_freq / mode->bpp * 2 * data_lanes;
+	maxim4c->pixel_rate =
+		v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE, 0,
+				pixel_rate, 1, pixel_rate);
+	dev_info(dev, "pixel_rate = %lld, bpp = %d\n",
+				pixel_rate, mode->bpp);
+
+	if (handler->error) {
+		ret = handler->error;
+		dev_err(dev, "Failed to init controls(%d)\n", ret);
+		goto err_free_handler;
+	}
+
+	maxim4c->subdev.ctrl_handler = handler;
+
+	return 0;
+
+err_free_handler:
+	v4l2_ctrl_handler_free(handler);
+
+	return ret;
+}
+
+static int maxim4c_mipi_data_lanes_parse(maxim4c_t *maxim4c)
+{
+	struct device *dev = &maxim4c->client->dev;
+	struct device_node *endpoint;
+	u8 mipi_data_lanes;
+	int ret = 0;
+
+	endpoint = of_graph_get_next_endpoint(dev->of_node, NULL);
+	if (!endpoint) {
+		dev_err(dev, "Failed to get endpoint\n");
+		return -EINVAL;
+	}
+
+	ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(endpoint),
+		&maxim4c->bus_cfg);
+	if (ret) {
+		dev_err(dev, "Failed to get bus config\n");
+		return -EINVAL;
+	}
+	mipi_data_lanes = maxim4c->bus_cfg.bus.mipi_csi2.num_data_lanes;
+	dev_info(dev, "mipi csi2 phy data lanes = %d\n", mipi_data_lanes);
+
+	return 0;
+}
+
+int maxim4c_v4l2_subdev_init(maxim4c_t *maxim4c)
+{
+	struct i2c_client *client = maxim4c->client;
+	struct device *dev = &client->dev;
+	struct v4l2_subdev *sd = NULL;
+	char facing[2];
+	int ret = 0;
+
+	maxim4c_mipi_data_lanes_parse(maxim4c);
+
+	maxim4c_support_mode_init(maxim4c);
+
+	sd = &maxim4c->subdev;
+	v4l2_i2c_subdev_init(sd, client, &maxim4c_subdev_ops);
+	ret = maxim4c_initialize_controls(maxim4c);
+	if (ret)
+		goto err_free_handler;
+
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+	sd->internal_ops = &maxim4c_internal_ops;
+	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+#endif
+
+#if defined(CONFIG_MEDIA_CONTROLLER)
+	maxim4c->pad.flags = MEDIA_PAD_FL_SOURCE;
+	sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
+	ret = media_entity_pads_init(&sd->entity, 1, &maxim4c->pad);
+	if (ret < 0)
+		goto err_free_handler;
+#endif
+
+	v4l2_set_subdevdata(sd, maxim4c);
+
+	memset(facing, 0, sizeof(facing));
+	if (strcmp(maxim4c->module_facing, "back") == 0)
+		facing[0] = 'b';
+	else
+		facing[0] = 'f';
+
+	snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s",
+		 maxim4c->module_index, facing, MAXIM4C_NAME,
+		 dev_name(sd->dev));
+
+	ret = v4l2_async_register_subdev_sensor_common(sd);
+	if (ret) {
+		dev_err(dev, "v4l2 async register subdev failed\n");
+		goto err_clean_entity;
+	}
+
+	return 0;
+
+err_clean_entity:
+#if defined(CONFIG_MEDIA_CONTROLLER)
+	media_entity_cleanup(&sd->entity);
+#endif
+
+err_free_handler:
+	v4l2_ctrl_handler_free(&maxim4c->ctrl_handler);
+
+	return ret;
+}
+EXPORT_SYMBOL(maxim4c_v4l2_subdev_init);
+
+void maxim4c_v4l2_subdev_deinit(maxim4c_t *maxim4c)
+{
+	struct v4l2_subdev *sd = &maxim4c->subdev;
+
+	v4l2_async_unregister_subdev(sd);
+
+#if defined(CONFIG_MEDIA_CONTROLLER)
+	media_entity_cleanup(&sd->entity);
+#endif
+
+	v4l2_ctrl_handler_free(&maxim4c->ctrl_handler);
+}
+EXPORT_SYMBOL(maxim4c_v4l2_subdev_deinit);
diff --git a/kernel/drivers/media/i2c/maxim4c/maxim4c_video_pipe.c b/kernel/drivers/media/i2c/maxim4c/maxim4c_video_pipe.c
new file mode 100644
index 0000000..e47e778
--- /dev/null
+++ b/kernel/drivers/media/i2c/maxim4c/maxim4c_video_pipe.c
@@ -0,0 +1,356 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Maxim Quad GMSL Deserializer Video Pipe driver
+ *
+ * Copyright (C) 2023 Rockchip Electronics Co., Ltd.
+ *
+ * Author: Cai Wenzhong <cwz@rock-chips.com>
+ *
+ */
+#include "maxim4c_api.h"
+
+static int maxim4c_video_pipe_select(maxim4c_t *maxim4c)
+{
+	struct i2c_client *client = maxim4c->client;
+	maxim4c_video_pipe_t *video_pipe = &maxim4c->video_pipe;
+	struct maxim4c_pipe_cfg *video_pipe_cfg = NULL;
+	u8 reg_mask = 0, reg_value = 0;
+	u16 reg_addr = 0;
+	int i = 0, shift = 0;
+	int ret = 0;
+
+	// video pipe selection
+	reg_mask = 0;
+	reg_value = 0;
+	for (i = 0; i < MAXIM4C_PIPE_O_ID_MAX; i++) {
+		video_pipe_cfg = &video_pipe->pipe_cfg[i];
+
+		shift = (i % 2) ? 4 : 0;
+		if (video_pipe_cfg->pipe_enable) {
+			reg_mask |= (0xF << shift);
+			reg_value |= ((video_pipe_cfg->pipe_idx & 0x3) << (0 + shift));
+			reg_value |= ((video_pipe_cfg->link_idx & 0x3) << (2 + shift));
+		}
+
+		if ((i % 2 == 1) && (reg_mask != 0)) {
+			reg_addr = 0x00F0 + (i / 2);
+
+			ret |= maxim4c_i2c_update_byte(client,
+					reg_addr, MAXIM4C_I2C_REG_ADDR_16BITS,
+					reg_mask, reg_value);
+
+			// Prepare for next register
+			reg_mask = 0;
+			reg_value = 0;
+		}
+	}
+
+	return ret;
+}
+
+static int maxim4c_video_pipe_run_init_seq(maxim4c_t *maxim4c)
+{
+	struct i2c_client *client = maxim4c->client;
+	struct device *dev = &client->dev;
+	maxim4c_video_pipe_t *video_pipe = &maxim4c->video_pipe;
+	struct maxim4c_pipe_cfg *video_pipe_cfg = NULL;
+	struct maxim4c_i2c_init_seq *init_seq = NULL;
+	int i = 0;
+	int ret = 0;
+
+	// video pipe init sequence
+	for (i = 0; i < MAXIM4C_PIPE_O_ID_MAX; i++) {
+		video_pipe_cfg = &video_pipe->pipe_cfg[i];
+		init_seq = &video_pipe_cfg->pipe_init_seq;
+		ret = maxim4c_i2c_run_init_seq(client, init_seq);
+		if (ret) {
+			dev_err(dev, "pipe id = %d init sequence error\n", i);
+			return ret;
+		}
+	}
+
+	// video pipe parallel mode init sequence
+	init_seq = &video_pipe->parallel_init_seq;
+	ret = maxim4c_i2c_run_init_seq(client, init_seq);
+	if (ret) {
+		dev_err(dev, "pipe parallel init sequence error\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int maxim4c_video_pipe_config_parse_dt(struct device *dev,
+		maxim4c_video_pipe_t *video_pipe,
+		struct device_node *parent_node)
+{
+	struct device_node *node = NULL;
+	struct device_node *init_seq_node = NULL;
+	struct maxim4c_i2c_init_seq *init_seq = NULL;
+	struct maxim4c_pipe_cfg *video_pipe_cfg = NULL;
+	const char *pipe_cfg_name = "video-pipe-config";
+	u32 sub_idx = 0, pipe_id = 0;
+	u32 value = 0;
+	int ret = 0;
+
+	node = NULL;
+	sub_idx = 0;
+	while ((node = of_get_next_child(parent_node, node))) {
+		if (!strncasecmp(node->name,
+					pipe_cfg_name,
+					strlen(pipe_cfg_name))) {
+			if (sub_idx >= MAXIM4C_PIPE_O_ID_MAX) {
+				dev_err(dev, "%pOF: Too many matching %s node\n",
+						parent_node, pipe_cfg_name);
+
+				of_node_put(node);
+				break;
+			}
+
+			if (!of_device_is_available(node)) {
+				dev_info(dev, "%pOF is disabled\n", node);
+
+				sub_idx++;
+
+				continue;
+			}
+
+			/* Video Pipe: pipe id */
+			ret = of_property_read_u32(node, "pipe-id", &pipe_id);
+			if (ret) {
+				// if pipe_id is error, parse next node
+				dev_err(dev, "Can not get pipe-id property!");
+
+				sub_idx++;
+
+				continue;
+			}
+			if (pipe_id >= MAXIM4C_PIPE_O_ID_MAX) {
+				// if pipe_id is error, parse next node
+				dev_err(dev, "Error pipe-id = %d!", pipe_id);
+
+				sub_idx++;
+
+				continue;
+			}
+
+			video_pipe_cfg = &video_pipe->pipe_cfg[pipe_id];
+
+			/* Video Pipe: pipe enable */
+			video_pipe_cfg->pipe_enable = 1;
+			video_pipe->pipe_enable_mask |= BIT(pipe_id);
+
+			dev_info(dev, "video pipe id = %d: pipe enable = %d\n",
+				pipe_id, video_pipe_cfg->pipe_enable);
+
+			/* Video Pipe: other config */
+			ret = of_property_read_u32(node, "pipe-idx", &value);
+			if (ret == 0) {
+				dev_info(dev, "pipe-idx property: %d", value);
+				video_pipe_cfg->pipe_idx = value;
+			}
+
+			ret = of_property_read_u32(node, "link-idx", &value);
+			if (ret == 0) {
+				dev_info(dev, "link-idx property: %d", value);
+				video_pipe_cfg->link_idx = value;
+			}
+
+			init_seq_node = of_get_child_by_name(node, "pipe-init-sequence");
+			if (!IS_ERR_OR_NULL(init_seq_node)) {
+				dev_info(dev, "load pipe-init-sequence\n");
+
+				init_seq = &video_pipe_cfg->pipe_init_seq;
+				maxim4c_i2c_load_init_seq(dev, init_seq_node, init_seq);
+
+				of_node_put(init_seq_node);
+			}
+
+			sub_idx++;
+		}
+	}
+
+	node = of_get_child_by_name(parent_node, "parallel-mode-config");
+	if (!IS_ERR_OR_NULL(node)) {
+		if (!of_device_is_available(node)) {
+			dev_info(dev, "%pOF is disabled\n", node);
+
+			of_node_put(node);
+			return 0;
+		}
+
+		init_seq_node = of_get_child_by_name(node, "parallel-init-sequence");
+		if (!IS_ERR_OR_NULL(init_seq_node)) {
+			dev_info(dev, "load parallel-init-sequence\n");
+
+			init_seq = &video_pipe->parallel_init_seq;
+			maxim4c_i2c_load_init_seq(dev, init_seq_node, init_seq);
+
+			of_node_put(init_seq_node);
+		}
+
+		of_node_put(node);
+	}
+
+	return 0;
+}
+
+int maxim4c_video_pipe_parse_dt(maxim4c_t *maxim4c, struct device_node *of_node)
+{
+	struct device *dev = &maxim4c->client->dev;
+	struct device_node *node = NULL;
+	maxim4c_video_pipe_t *video_pipe = &maxim4c->video_pipe;
+	int ret = 0;
+
+	dev_info(dev, "=== maxim4c video pipe parse dt ===\n");
+
+	node = of_get_child_by_name(of_node, "video-pipes");
+	if (IS_ERR_OR_NULL(node)) {
+		dev_err(dev, "%pOF has no child node: video-pipes\n",
+				of_node);
+		return -ENODEV;
+	}
+
+	if (!of_device_is_available(node)) {
+		dev_info(dev, "%pOF is disabled\n", node);
+		of_node_put(node);
+		return -ENODEV;
+	}
+
+	ret = maxim4c_video_pipe_config_parse_dt(dev, video_pipe, node);
+
+	of_node_put(node);
+
+	return ret;
+}
+EXPORT_SYMBOL(maxim4c_video_pipe_parse_dt);
+
+int maxim4c_video_pipe_mask_enable(maxim4c_t *maxim4c, u8 video_pipe_mask, bool enable)
+{
+	struct i2c_client *client = maxim4c->client;
+	struct device *dev = &client->dev;
+	maxim4c_video_pipe_t *video_pipe = &maxim4c->video_pipe;
+	struct maxim4c_pipe_cfg *video_pipe_cfg = NULL;
+	u8 reg_mask = 0, reg_value = 0;
+	int i = 0;
+	int ret = 0;
+
+	dev_dbg(dev, "%s, video_pipe_mask = 0x%x, enable = %d\n",
+			__func__, video_pipe_mask, enable);
+
+	reg_mask = 0;
+	reg_value = 0;
+	// video pipe enable
+	for (i = 0; i < MAXIM4C_PIPE_O_ID_MAX; i++) {
+		video_pipe_cfg = &video_pipe->pipe_cfg[i];
+		if (video_pipe_cfg->pipe_enable
+				&& (video_pipe_mask & BIT(i))) {
+			reg_mask |= BIT(i);
+			if (enable)
+				reg_value |= BIT(i);
+		}
+	}
+
+	if (reg_mask != 0) {
+		ret |= maxim4c_i2c_update_byte(client,
+				0x00F4, MAXIM4C_I2C_REG_ADDR_16BITS,
+				reg_mask, reg_value);
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(maxim4c_video_pipe_mask_enable);
+
+int maxim4c_video_pipe_linkid_enable(maxim4c_t *maxim4c, u8 link_id, bool enable)
+{
+	struct i2c_client *client = maxim4c->client;
+	struct device *dev = &client->dev;
+	maxim4c_video_pipe_t *video_pipe = &maxim4c->video_pipe;
+	struct maxim4c_pipe_cfg *video_pipe_cfg = NULL;
+	u8 reg_mask = 0, reg_value = 0;
+	int i = 0;
+	int ret = 0;
+
+	dev_dbg(dev, "%s, link_id = %d, enable = %d\n",
+			__func__, link_id, enable);
+
+	reg_mask = 0;
+	reg_value = 0;
+	// video pipe enable
+	for (i = 0; i < MAXIM4C_PIPE_O_ID_MAX; i++) {
+		video_pipe_cfg = &video_pipe->pipe_cfg[i];
+		if (video_pipe_cfg->pipe_enable
+				&& (video_pipe_cfg->link_idx == link_id)) {
+			reg_mask = BIT(i);
+			if (enable)
+				reg_value = BIT(i);
+		}
+	}
+
+	if (reg_mask != 0) {
+		ret = maxim4c_i2c_update_byte(client,
+				0x00F4, MAXIM4C_I2C_REG_ADDR_16BITS,
+				reg_mask, reg_value);
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(maxim4c_video_pipe_linkid_enable);
+
+void maxim4c_video_pipe_data_init(maxim4c_t *maxim4c)
+{
+	maxim4c_video_pipe_t *video_pipe = &maxim4c->video_pipe;
+	struct maxim4c_pipe_cfg *video_pipe_cfg = NULL;
+	int i = 0;
+
+	video_pipe->pipe_enable_mask = 0;
+	video_pipe->parallel_init_seq.reg_init_seq = NULL;
+
+	for (i = 0; i < 4; i++) {
+		video_pipe_cfg = &video_pipe->pipe_cfg[i];
+
+		video_pipe_cfg->pipe_enable = 0;
+		video_pipe_cfg->pipe_idx = MAXIM4C_PIPE_I_ID_Z;
+		video_pipe_cfg->link_idx = (i % 4);
+		video_pipe_cfg->pipe_init_seq.reg_init_seq = NULL;
+	}
+
+	for (i = 4; i < MAXIM4C_PIPE_O_ID_MAX; i++) {
+		video_pipe_cfg = &video_pipe->pipe_cfg[i];
+
+		video_pipe_cfg->pipe_enable = 0;
+		video_pipe_cfg->pipe_idx = MAXIM4C_PIPE_I_ID_X;
+		video_pipe_cfg->link_idx = (i % 4);
+		video_pipe_cfg->pipe_init_seq.reg_init_seq = NULL;
+	}
+}
+EXPORT_SYMBOL(maxim4c_video_pipe_data_init);
+
+int maxim4c_video_pipe_hw_init(maxim4c_t *maxim4c)
+{
+	struct device *dev = &maxim4c->client->dev;
+	u8 pipe_enable_mask = 0;
+	int ret = 0;
+
+	ret = maxim4c_video_pipe_select(maxim4c);
+	if (ret) {
+		dev_err(dev, "%s: video pipe select error\n", __func__);
+		return ret;
+	}
+
+	pipe_enable_mask = maxim4c->video_pipe.pipe_enable_mask;
+	ret = maxim4c_video_pipe_mask_enable(maxim4c, pipe_enable_mask, true);
+	if (ret) {
+		dev_err(dev, "%s: video pipe mask enable error\n", __func__);
+		return ret;
+	}
+
+	ret = maxim4c_video_pipe_run_init_seq(maxim4c);
+	if (ret) {
+		dev_err(dev, "%s: video pipe run init seq error\n", __func__);
+		return ret;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(maxim4c_video_pipe_hw_init);
diff --git a/kernel/drivers/media/i2c/maxim4c/maxim4c_video_pipe.h b/kernel/drivers/media/i2c/maxim4c/maxim4c_video_pipe.h
new file mode 100644
index 0000000..d86e5fe
--- /dev/null
+++ b/kernel/drivers/media/i2c/maxim4c/maxim4c_video_pipe.h
@@ -0,0 +1,50 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2023 Rockchip Electronics Co., Ltd.
+ *
+ */
+
+#ifndef __MAXIM4C_VIDEO_PIPE_H__
+#define __MAXIM4C_VIDEO_PIPE_H__
+
+#include "maxim4c_i2c.h"
+
+/* Maxim4c Video Pipe In ID: 0 ~ 3 */
+enum {
+	MAXIM4C_PIPE_I_ID_X = 0,
+	MAXIM4C_PIPE_I_ID_Y,
+	MAXIM4C_PIPE_I_ID_Z,
+	MAXIM4C_PIPE_I_ID_U,
+	MAXIM4C_PIPE_I_ID_MAX,
+};
+
+/* Maxim4c Video Pipe Out ID: 0 ~ 7 */
+enum {
+	MAXIM4C_PIPE_O_ID_0 = 0,
+	MAXIM4C_PIPE_O_ID_1,
+	MAXIM4C_PIPE_O_ID_2,
+	MAXIM4C_PIPE_O_ID_3,
+	MAXIM4C_PIPE_O_ID_4,
+	MAXIM4C_PIPE_O_ID_5,
+	MAXIM4C_PIPE_O_ID_6,
+	MAXIM4C_PIPE_O_ID_7,
+	MAXIM4C_PIPE_O_ID_MAX,
+};
+
+/* Maxim4c Video Pipe Out Config */
+struct maxim4c_pipe_cfg {
+	u8 pipe_enable;
+	u8 pipe_idx;
+	u8 link_idx;
+
+	struct maxim4c_i2c_init_seq pipe_init_seq;
+};
+
+typedef struct maxim4c_video_pipe {
+	u8 pipe_enable_mask;
+
+	struct maxim4c_pipe_cfg pipe_cfg[MAXIM4C_PIPE_O_ID_MAX];
+	struct maxim4c_i2c_init_seq parallel_init_seq;
+} maxim4c_video_pipe_t;
+
+#endif /* __MAXIM4C_VIDEO_PIPE_H__ */
diff --git a/kernel/drivers/media/i2c/maxim4c/remote_max9295.c b/kernel/drivers/media/i2c/maxim4c/remote_max9295.c
new file mode 100644
index 0000000..b050516
--- /dev/null
+++ b/kernel/drivers/media/i2c/maxim4c/remote_max9295.c
@@ -0,0 +1,337 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Maxim Quad GMSL2/GMSL1 to CSI-2 Serializer driver
+ *
+ * Copyright (C) 2023 Rockchip Electronics Co., Ltd.
+ *
+ * Author: Cai Wenzhong <cwz@rock-chips.com>
+ *
+ */
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+
+#include "maxim4c_api.h"
+
+#define MAX9295_I2C_ADDR_DEF		0x40
+
+#define MAX9295_CHIP_ID			0x91
+#define MAX9295_REG_CHIP_ID		0x0D
+
+static int max9295_i2c_addr_remap(maxim4c_remote_t *max9295)
+{
+	struct device *dev = max9295->dev;
+	struct i2c_client *client = max9295->client;
+	u16 i2c_8bit_addr = 0;
+	int ret = 0;
+
+	if (max9295->ser_i2c_addr_map) {
+		dev_info(dev, "Serializer i2c address remap\n");
+
+		maxim4c_remote_i2c_addr_select(max9295, MAXIM4C_I2C_SER_DEF);
+
+		i2c_8bit_addr = (max9295->ser_i2c_addr_map << 1);
+		ret = maxim4c_i2c_write_byte(client,
+				0x0000, MAXIM4C_I2C_REG_ADDR_16BITS,
+				i2c_8bit_addr);
+		if (ret) {
+			dev_err(dev, "ser i2c address map setting error!\n");
+			return ret;
+		}
+
+		maxim4c_remote_i2c_addr_select(max9295, MAXIM4C_I2C_SER_MAP);
+	}
+
+	if (max9295->cam_i2c_addr_map) {
+		dev_info(dev, "Camera i2c address remap\n");
+
+		i2c_8bit_addr = (max9295->cam_i2c_addr_map << 1);
+		ret = maxim4c_i2c_write_byte(client,
+				0x0042, MAXIM4C_I2C_REG_ADDR_16BITS,
+				i2c_8bit_addr);
+		if (ret) {
+			dev_err(dev, "cam i2c address source setting error!\n");
+			return ret;
+		}
+
+		i2c_8bit_addr = (max9295->cam_i2c_addr_def << 1);
+		ret = maxim4c_i2c_write_byte(client,
+				0x0043, MAXIM4C_I2C_REG_ADDR_16BITS,
+				i2c_8bit_addr);
+		if (ret) {
+			dev_err(dev, "cam i2c address destination setting error!\n");
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int max9295_i2c_addr_def(maxim4c_remote_t *max9295)
+{
+	struct device *dev = max9295->dev;
+	struct i2c_client *client = max9295->client;
+	u16 i2c_8bit_addr = 0;
+	int ret = 0;
+
+	if (max9295->ser_i2c_addr_map) {
+		dev_info(dev, "Serializer i2c address def\n");
+
+		maxim4c_remote_i2c_addr_select(max9295, MAXIM4C_I2C_SER_MAP);
+
+		i2c_8bit_addr = (max9295->ser_i2c_addr_def << 1);
+		ret = maxim4c_i2c_write_byte(client,
+				0x0000, MAXIM4C_I2C_REG_ADDR_16BITS,
+				i2c_8bit_addr);
+		if (ret) {
+			dev_err(dev, "ser i2c address def setting error!\n");
+			return ret;
+		}
+
+		maxim4c_remote_i2c_addr_select(max9295, MAXIM4C_I2C_SER_DEF);
+	}
+
+	return 0;
+}
+
+static int max9295_check_chipid(maxim4c_remote_t *max9295)
+{
+	struct device *dev = max9295->dev;
+	struct i2c_client *client = max9295->client;
+	u8 chip_id;
+	int ret = 0;
+
+	// max9295
+	ret = maxim4c_i2c_read_byte(client,
+			MAX9295_REG_CHIP_ID, MAXIM4C_I2C_REG_ADDR_16BITS,
+			&chip_id);
+	if (ret != 0) {
+		dev_info(dev, "Retry check chipid using map address\n");
+		maxim4c_remote_i2c_addr_select(max9295, MAXIM4C_I2C_SER_MAP);
+		ret = maxim4c_i2c_read_byte(client,
+				MAX9295_REG_CHIP_ID, MAXIM4C_I2C_REG_ADDR_16BITS,
+				&chip_id);
+		if (ret != 0) {
+			dev_err(dev, "MAX9295 detect error, ret(%d)\n", ret);
+			maxim4c_remote_i2c_addr_select(max9295, MAXIM4C_I2C_SER_DEF);
+
+			return -ENODEV;
+		}
+
+		max9295_i2c_addr_def(max9295);
+	}
+
+	if (chip_id != MAX9295_CHIP_ID) {
+		dev_err(dev, "Unexpected chip id = %02x\n", chip_id);
+		return -ENODEV;
+	}
+
+	dev_info(dev, "Detected MAX9295 chip id: 0x%02x\n", chip_id);
+
+	return 0;
+}
+
+static int max9295_soft_power_down(maxim4c_remote_t *max9295)
+{
+	struct device *dev = max9295->dev;
+	struct i2c_client *client = max9295->client;
+	int ret = 0;
+
+	ret = maxim4c_i2c_write_byte(client,
+			0x10, MAXIM4C_I2C_REG_ADDR_16BITS,
+			BIT(7));
+	if (ret) {
+		dev_err(dev, "soft power down setting error!\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int max9295_module_init(maxim4c_remote_t *max9295)
+{
+	struct device *dev = max9295->dev;
+	struct i2c_client *client = max9295->client;
+	int ret = 0;
+
+	ret = maxim4c_remote_i2c_addr_select(max9295, MAXIM4C_I2C_SER_DEF);
+	if (ret)
+		return ret;
+
+	ret = max9295_check_chipid(max9295);
+	if (ret)
+		return ret;
+
+	ret = max9295_i2c_addr_remap(max9295);
+	if (ret)
+		return ret;
+
+	ret = maxim4c_i2c_run_init_seq(client,
+			&max9295->remote_init_seq);
+	if (ret) {
+		dev_err(dev, "remote id = %d init sequence error\n",
+				max9295->remote_id);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int max9295_module_deinit(maxim4c_remote_t *max9295)
+{
+	int ret = 0;
+
+#if 0
+	ret |= max9295_i2c_addr_def(max9295);
+#endif
+	ret |= max9295_soft_power_down(max9295);
+
+	return ret;
+}
+
+static const struct maxim4c_remote_ops max9295_ops = {
+	.remote_init = max9295_module_init,
+	.remote_deinit = max9295_module_deinit,
+};
+
+static int max9295_parse_dt(maxim4c_remote_t *max9295)
+{
+	struct device *dev = max9295->dev;
+	struct device_node *of_node = dev->of_node;
+	u32 value = 0;
+	int ret = 0;
+
+	dev_info(dev, "=== maxim4c remote max9295 parse dt ===\n");
+
+	ret = of_property_read_u32(of_node, "remote-id", &value);
+	if (ret == 0) {
+		dev_info(dev, "remote-id property: %d\n", value);
+		max9295->remote_id = value;
+	} else {
+		max9295->remote_id = MAXIM4C_LINK_ID_MAX;
+	}
+
+	dev_info(dev, "max9295 remote id: %d\n", max9295->remote_id);
+
+	ret = of_property_read_u32(of_node, "ser-i2c-addr-def", &value);
+	if (ret == 0) {
+		dev_info(dev, "ser-i2c-addr-def property: 0x%x", value);
+		max9295->ser_i2c_addr_def = value;
+	} else {
+		max9295->ser_i2c_addr_def = MAX9295_I2C_ADDR_DEF;
+	}
+
+	ret = of_property_read_u32(of_node, "ser-i2c-addr-map", &value);
+	if (ret == 0) {
+		dev_info(dev, "ser-i2c-addr-map property: 0x%x", value);
+		max9295->ser_i2c_addr_map = value;
+	}
+
+	ret = of_property_read_u32(of_node, "cam-i2c-addr-def", &value);
+	if (ret == 0) {
+		dev_info(dev, "cam-i2c-addr-def property: 0x%x", value);
+		max9295->cam_i2c_addr_def = value;
+	}
+
+	ret = of_property_read_u32(of_node, "cam-i2c-addr-map", &value);
+	if (ret == 0) {
+		dev_info(dev, "cam-i2c-addr-map property: 0x%x", value);
+		max9295->cam_i2c_addr_map = value;
+	}
+
+	return 0;
+}
+
+static int max9295_probe(struct platform_device *pdev)
+{
+	struct i2c_client *client = to_i2c_client(pdev->dev.parent);
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct maxim4c *maxim4c = v4l2_get_subdevdata(sd);
+	struct maxim4c_remote *max9295 = NULL;
+	u32 link_id = MAXIM4C_LINK_ID_MAX;
+	int ret = 0;
+
+	dev_info(&pdev->dev, "max9295 serializer probe\n");
+
+	link_id = (uintptr_t)of_device_get_match_data(&pdev->dev);
+	link_id = link_id - MAXIM4C_LINK_ID_MAX;
+	if (link_id >= MAXIM4C_LINK_ID_MAX) {
+		dev_err(&pdev->dev, "max9295 probe match data error\n");
+		return -EINVAL;
+	}
+	dev_info(&pdev->dev, "max9295 probe link id = %d\n", link_id);
+
+	max9295 = devm_kzalloc(&pdev->dev, sizeof(*max9295), GFP_KERNEL);
+	if (!max9295) {
+		dev_err(&pdev->dev, "max9295 probe no memory error\n");
+		return -ENOMEM;
+	}
+
+	max9295->dev = &pdev->dev;
+	max9295->remote_ops = &max9295_ops;
+	max9295->local = maxim4c;
+	dev_set_drvdata(max9295->dev, max9295);
+
+	max9295_parse_dt(max9295);
+
+	if (max9295->remote_id != link_id) {
+		dev_err(&pdev->dev, "max9295 probe remote_id error\n");
+		return -EINVAL;
+	}
+
+	ret = maxim4c_remote_i2c_client_init(max9295, client);
+	if (ret) {
+		dev_err(&pdev->dev, "remote i2c client init error\n");
+		return ret;
+	}
+
+	ret = maxim4c_remote_device_register(maxim4c, max9295);
+	if (ret) {
+		dev_err(&pdev->dev, "remote serializer register error\n");
+		return ret;
+	}
+
+	maxim4c_remote_load_init_seq(max9295);
+
+	return 0;
+}
+
+static int max9295_remove(struct platform_device *pdev)
+{
+	return 0;
+}
+
+static const struct of_device_id max9295_of_table[] = {
+	{
+		.compatible = "maxim4c,link0,max9295",
+		.data = (const void *)(MAXIM4C_LINK_ID_MAX + MAXIM4C_LINK_ID_A)
+	}, {
+		.compatible = "maxim4c,link1,max9295",
+		.data = (const void *)(MAXIM4C_LINK_ID_MAX + MAXIM4C_LINK_ID_B)
+	}, {
+		.compatible = "maxim4c,link2,max9295",
+		.data = (const void *)(MAXIM4C_LINK_ID_MAX + MAXIM4C_LINK_ID_C)
+	}, {
+		.compatible = "maxim4c,link3,max9295",
+		.data = (const void *)(MAXIM4C_LINK_ID_MAX + MAXIM4C_LINK_ID_D)
+	},
+	{ /* Sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, max9295_of_table);
+
+static struct platform_driver max9295_driver = {
+	.probe		= max9295_probe,
+	.remove		= max9295_remove,
+	.driver		= {
+		.name	= "max9295",
+		.of_match_table = max9295_of_table,
+	},
+};
+
+module_platform_driver(max9295_driver);
+
+MODULE_AUTHOR("Cai Wenzhong <cwz@rock-chips.com>");
+MODULE_DESCRIPTION("Maxim MAX9295 Serializer Driver");
+MODULE_LICENSE("GPL");
diff --git a/kernel/drivers/media/i2c/maxim4c/remote_max96715.c b/kernel/drivers/media/i2c/maxim4c/remote_max96715.c
new file mode 100644
index 0000000..3f2689a
--- /dev/null
+++ b/kernel/drivers/media/i2c/maxim4c/remote_max96715.c
@@ -0,0 +1,400 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Maxim Quad GMSL2/GMSL1 to CSI-2 Serializer driver
+ *
+ * Copyright (C) 2023 Rockchip Electronics Co., Ltd.
+ *
+ * Author: Cai Wenzhong <cwz@rock-chips.com>
+ *
+ */
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+
+#include "maxim4c_api.h"
+
+#define MAX96715_I2C_ADDR_DEF		0x40
+
+#define MAX96715_CHIP_ID		0x45
+#define MAX96715_REG_CHIP_ID		0x1E
+
+/* Config and Video mode switch */
+#define MAX96715_MODE_SWITCH		1
+
+enum {
+	LINK_MODE_VIDEO = 0,
+	LINK_MODE_CONFIG,
+};
+
+static int __maybe_unused max96715_link_mode_select(maxim4c_remote_t *max96715, u32 mode)
+{
+	struct device *dev = max96715->dev;
+	struct i2c_client *client = max96715->client;
+	u8 reg_mask = 0, reg_value = 0;
+	u32 delay_ms = 0;
+	int ret = 0;
+
+	dev_dbg(dev, "%s: mode = %d\n", __func__, mode);
+
+	reg_mask = BIT(7) | BIT(6);
+	if (mode == LINK_MODE_CONFIG) {
+		reg_value = BIT(6);
+		delay_ms = 5;
+	} else {
+		reg_value = BIT(7);
+		delay_ms = 50;
+	}
+	ret |= maxim4c_i2c_update_byte(client,
+			0x04, MAXIM4C_I2C_REG_ADDR_08BITS,
+			reg_mask, reg_value);
+
+	msleep(delay_ms);
+
+	return ret;
+}
+
+static int max96715_i2c_addr_remap(maxim4c_remote_t *max96715)
+{
+	struct device *dev = max96715->dev;
+	struct i2c_client *client = max96715->client;
+	u16 i2c_8bit_addr = 0;
+	int ret = 0;
+
+	if (max96715->ser_i2c_addr_map) {
+		dev_info(dev, "Serializer i2c address remap\n");
+
+		maxim4c_remote_i2c_addr_select(max96715, MAXIM4C_I2C_SER_DEF);
+
+		i2c_8bit_addr = (max96715->ser_i2c_addr_map << 1);
+		ret = maxim4c_i2c_write_byte(client,
+				0x00, MAXIM4C_I2C_REG_ADDR_08BITS,
+				i2c_8bit_addr);
+		if (ret) {
+			dev_err(dev, "ser i2c address map setting error!\n");
+			return ret;
+		}
+
+		maxim4c_remote_i2c_addr_select(max96715, MAXIM4C_I2C_SER_MAP);
+	}
+
+	if (max96715->cam_i2c_addr_map) {
+		dev_info(dev, "Camera i2c address remap\n");
+
+		i2c_8bit_addr = (max96715->cam_i2c_addr_map << 1);
+		ret = maxim4c_i2c_write_byte(client,
+				0x09, MAXIM4C_I2C_REG_ADDR_08BITS,
+				i2c_8bit_addr);
+		if (ret) {
+			dev_err(dev, "cam i2c address source setting error!\n");
+			return ret;
+		}
+
+		i2c_8bit_addr = (max96715->cam_i2c_addr_def << 1);
+		ret = maxim4c_i2c_write_byte(client,
+				0x0A, MAXIM4C_I2C_REG_ADDR_08BITS,
+				i2c_8bit_addr);
+		if (ret) {
+			dev_err(dev, "cam i2c address destination setting error!\n");
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int max96715_i2c_addr_def(maxim4c_remote_t *max96715)
+{
+	struct device *dev = max96715->dev;
+	struct i2c_client *client = max96715->client;
+	u16 i2c_8bit_addr = 0;
+	int ret = 0;
+
+	if (max96715->ser_i2c_addr_map) {
+		dev_info(dev, "Serializer i2c address def\n");
+
+		maxim4c_remote_i2c_addr_select(max96715, MAXIM4C_I2C_SER_MAP);
+
+		i2c_8bit_addr = (max96715->ser_i2c_addr_def << 1);
+		ret = maxim4c_i2c_write_byte(client,
+				0x00, MAXIM4C_I2C_REG_ADDR_08BITS,
+				i2c_8bit_addr);
+		if (ret) {
+			dev_err(dev, "ser i2c address def setting error!\n");
+			return ret;
+		}
+
+		maxim4c_remote_i2c_addr_select(max96715, MAXIM4C_I2C_SER_DEF);
+	}
+
+	return 0;
+}
+
+static int max96715_check_chipid(maxim4c_remote_t *max96715)
+{
+	struct device *dev = max96715->dev;
+	struct i2c_client *client = max96715->client;
+	u8 chip_id;
+	int ret = 0;
+
+	// max96715
+	ret = maxim4c_i2c_read_byte(client,
+			MAX96715_REG_CHIP_ID, MAXIM4C_I2C_REG_ADDR_08BITS,
+			&chip_id);
+	if (ret != 0) {
+		dev_info(dev, "Retry check chipid using map address\n");
+		maxim4c_remote_i2c_addr_select(max96715, MAXIM4C_I2C_SER_MAP);
+		ret = maxim4c_i2c_read_byte(client,
+				MAX96715_REG_CHIP_ID, MAXIM4C_I2C_REG_ADDR_08BITS,
+				&chip_id);
+		if (ret != 0) {
+			dev_err(dev, "MAX96715 detect error, ret(%d)\n", ret);
+			maxim4c_remote_i2c_addr_select(max96715, MAXIM4C_I2C_SER_DEF);
+
+			return -ENODEV;
+		}
+
+		max96715_i2c_addr_def(max96715);
+	}
+
+	if (chip_id != MAX96715_CHIP_ID) {
+		dev_err(dev, "Unexpected chip id = %02x\n", chip_id);
+		return -ENODEV;
+	}
+
+	dev_info(dev, "Detected MAX96715 chip id: 0x%02x\n", chip_id);
+
+	return 0;
+}
+
+static int max96715_soft_power_down(maxim4c_remote_t *max96715)
+{
+	struct device *dev = max96715->dev;
+	struct i2c_client *client = max96715->client;
+	int ret = 0;
+
+	ret = maxim4c_i2c_write_byte(client,
+			0x13, MAXIM4C_I2C_REG_ADDR_08BITS,
+			BIT(7));
+	if (ret) {
+		dev_err(dev, "soft power down setting error!\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int max96715_module_init(maxim4c_remote_t *max96715)
+{
+	struct device *dev = max96715->dev;
+	struct i2c_client *client = max96715->client;
+	struct maxim4c *maxim4c = max96715->local;
+	int ret = 0;
+
+	ret = maxim4c_remote_i2c_addr_select(max96715, MAXIM4C_I2C_SER_DEF);
+	if (ret)
+		return ret;
+
+	ret = max96715_check_chipid(max96715);
+	if (ret)
+		return ret;
+
+	ret = max96715_i2c_addr_remap(max96715);
+	if (ret)
+		return ret;
+
+#if MAX96715_MODE_SWITCH
+	if (maxim4c->hot_plug_irq > 0)
+		disable_irq(maxim4c->hot_plug_irq);
+
+	ret = max96715_link_mode_select(max96715, LINK_MODE_CONFIG);
+	if (ret) {
+		if (maxim4c->hot_plug_irq > 0)
+			enable_irq(maxim4c->hot_plug_irq);
+
+		return ret;
+	}
+#endif
+
+	ret = maxim4c_i2c_run_init_seq(client,
+			&max96715->remote_init_seq);
+
+	if (ret) {
+		dev_err(dev, "remote id = %d init sequence error\n",
+				max96715->remote_id);
+
+		if (maxim4c->hot_plug_irq > 0)
+			enable_irq(maxim4c->hot_plug_irq);
+
+		return ret;
+	}
+
+#if MAX96715_MODE_SWITCH
+	ret = max96715_link_mode_select(max96715, LINK_MODE_VIDEO);
+	if (maxim4c->hot_plug_irq > 0)
+		enable_irq(maxim4c->hot_plug_irq);
+	if (ret)
+		return ret;
+#endif
+
+	return 0;
+}
+
+static int max96715_module_deinit(maxim4c_remote_t *max96715)
+{
+	int ret = 0;
+
+#if 0
+	ret |= max96715_i2c_addr_def(max96715);
+#endif
+	ret |= max96715_soft_power_down(max96715);
+
+	return ret;
+}
+
+static const struct maxim4c_remote_ops max96715_ops = {
+	.remote_init = max96715_module_init,
+	.remote_deinit = max96715_module_deinit,
+};
+
+static int max96715_parse_dt(maxim4c_remote_t *max96715)
+{
+	struct device *dev = max96715->dev;
+	struct device_node *of_node = dev->of_node;
+	u32 value = 0;
+	int ret = 0;
+
+	dev_info(dev, "=== maxim4c remote max96715 parse dt ===\n");
+
+	ret = of_property_read_u32(of_node, "remote-id", &value);
+	if (ret == 0) {
+		dev_info(dev, "remote-id property: %d\n", value);
+		max96715->remote_id = value;
+	} else {
+		max96715->remote_id = MAXIM4C_LINK_ID_MAX;
+	}
+
+	dev_info(dev, "max96715 remote id: %d\n", max96715->remote_id);
+
+	ret = of_property_read_u32(of_node, "ser-i2c-addr-def", &value);
+	if (ret == 0) {
+		dev_info(dev, "ser-i2c-addr-def property: 0x%x", value);
+		max96715->ser_i2c_addr_def = value;
+	} else {
+		max96715->ser_i2c_addr_def = MAX96715_I2C_ADDR_DEF;
+	}
+
+	ret = of_property_read_u32(of_node, "ser-i2c-addr-map", &value);
+	if (ret == 0) {
+		dev_info(dev, "ser-i2c-addr-map property: 0x%x", value);
+		max96715->ser_i2c_addr_map = value;
+	}
+
+	ret = of_property_read_u32(of_node, "cam-i2c-addr-def", &value);
+	if (ret == 0) {
+		dev_info(dev, "cam-i2c-addr-def property: 0x%x", value);
+		max96715->cam_i2c_addr_def = value;
+	}
+
+	ret = of_property_read_u32(of_node, "cam-i2c-addr-map", &value);
+	if (ret == 0) {
+		dev_info(dev, "cam-i2c-addr-map property: 0x%x", value);
+		max96715->cam_i2c_addr_map = value;
+	}
+
+	return 0;
+}
+
+static int max96715_probe(struct platform_device *pdev)
+{
+	struct i2c_client *client = to_i2c_client(pdev->dev.parent);
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct maxim4c *maxim4c = v4l2_get_subdevdata(sd);
+	struct maxim4c_remote *max96715 = NULL;
+	u32 link_id = MAXIM4C_LINK_ID_MAX;
+	int ret = 0;
+
+	dev_info(&pdev->dev, "max96715 serializer probe\n");
+
+	link_id = (uintptr_t)of_device_get_match_data(&pdev->dev);
+	link_id = link_id - MAXIM4C_LINK_ID_MAX;
+	if (link_id >= MAXIM4C_LINK_ID_MAX) {
+		dev_err(&pdev->dev, "max96715 probe match data error\n");
+		return -EINVAL;
+	}
+	dev_info(&pdev->dev, "max96715 probe link id = %d\n", link_id);
+
+	max96715 = devm_kzalloc(&pdev->dev, sizeof(*max96715), GFP_KERNEL);
+	if (!max96715) {
+		dev_err(&pdev->dev, "max96715 probe no memory error\n");
+		return -ENOMEM;
+	}
+
+	max96715->dev = &pdev->dev;
+	max96715->remote_ops = &max96715_ops;
+	max96715->local = maxim4c;
+	dev_set_drvdata(max96715->dev, max96715);
+
+	max96715_parse_dt(max96715);
+
+	if (max96715->remote_id != link_id) {
+		dev_err(&pdev->dev, "max96715 probe remote_id error\n");
+		return -EINVAL;
+	}
+
+	ret = maxim4c_remote_i2c_client_init(max96715, client);
+	if (ret) {
+		dev_err(&pdev->dev, "remote i2c client init error\n");
+		return ret;
+	}
+
+	ret = maxim4c_remote_device_register(maxim4c, max96715);
+	if (ret) {
+		dev_err(&pdev->dev, "remote serializer register error\n");
+		return ret;
+	}
+
+	maxim4c_remote_load_init_seq(max96715);
+
+	return 0;
+}
+
+static int max96715_remove(struct platform_device *pdev)
+{
+	return 0;
+}
+
+static const struct of_device_id max96715_of_table[] = {
+	{
+		.compatible = "maxim4c,link0,max96715",
+		.data = (const void *)(MAXIM4C_LINK_ID_MAX + MAXIM4C_LINK_ID_A)
+	}, {
+		.compatible = "maxim4c,link1,max96715",
+		.data = (const void *)(MAXIM4C_LINK_ID_MAX + MAXIM4C_LINK_ID_B)
+	}, {
+		.compatible = "maxim4c,link2,max96715",
+		.data = (const void *)(MAXIM4C_LINK_ID_MAX + MAXIM4C_LINK_ID_C)
+	}, {
+		.compatible = "maxim4c,link3,max96715",
+		.data = (const void *)(MAXIM4C_LINK_ID_MAX + MAXIM4C_LINK_ID_D)
+	},
+	{ /* Sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, max96715_of_table);
+
+static struct platform_driver max96715_driver = {
+	.probe		= max96715_probe,
+	.remove		= max96715_remove,
+	.driver		= {
+		.name	= "max96715",
+		.of_match_table = max96715_of_table,
+	},
+};
+
+module_platform_driver(max96715_driver);
+
+MODULE_AUTHOR("Cai Wenzhong <cwz@rock-chips.com>");
+MODULE_DESCRIPTION("Maxim MAX96715 Serializer Driver");
+MODULE_LICENSE("GPL");
diff --git a/kernel/drivers/media/i2c/maxim4c/remote_max96717.c b/kernel/drivers/media/i2c/maxim4c/remote_max96717.c
new file mode 100644
index 0000000..d0a3b7d
--- /dev/null
+++ b/kernel/drivers/media/i2c/maxim4c/remote_max96717.c
@@ -0,0 +1,316 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Maxim Quad GMSL2/GMSL1 to CSI-2 Serializer driver
+ *
+ * Copyright (C) 2023 Rockchip Electronics Co., Ltd.
+ *
+ * Author: Cai Wenzhong <cwz@rock-chips.com>
+ *
+ */
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+
+#include "maxim4c_api.h"
+
+#define MAX96717_I2C_ADDR_DEF		0x40
+
+#define MAX96717_CHIP_ID		0xBF
+#define MAX96717_REG_CHIP_ID		0x0D
+
+static int max96717_i2c_addr_remap(maxim4c_remote_t *max96717)
+{
+	struct device *dev = max96717->dev;
+	struct i2c_client *client = max96717->client;
+	u16 i2c_8bit_addr = 0;
+	int ret = 0;
+
+	if (max96717->ser_i2c_addr_map) {
+		dev_info(dev, "Serializer i2c address remap\n");
+
+		maxim4c_remote_i2c_addr_select(max96717, MAXIM4C_I2C_SER_DEF);
+
+		i2c_8bit_addr = (max96717->ser_i2c_addr_map << 1);
+		ret = maxim4c_i2c_write_byte(client,
+				0x0000, MAXIM4C_I2C_REG_ADDR_16BITS,
+				i2c_8bit_addr);
+		if (ret) {
+			dev_err(dev, "ser i2c address map setting error!\n");
+			return ret;
+		}
+
+		maxim4c_remote_i2c_addr_select(max96717, MAXIM4C_I2C_SER_MAP);
+	}
+
+	if (max96717->cam_i2c_addr_map) {
+		dev_info(dev, "Camera i2c address remap\n");
+
+		i2c_8bit_addr = (max96717->cam_i2c_addr_map << 1);
+		ret = maxim4c_i2c_write_byte(client,
+				0x0042, MAXIM4C_I2C_REG_ADDR_16BITS,
+				i2c_8bit_addr);
+		if (ret) {
+			dev_err(dev, "cam i2c address source setting error!\n");
+			return ret;
+		}
+
+		i2c_8bit_addr = (max96717->cam_i2c_addr_def << 1);
+		ret = maxim4c_i2c_write_byte(client,
+				0x0043, MAXIM4C_I2C_REG_ADDR_16BITS,
+				i2c_8bit_addr);
+		if (ret) {
+			dev_err(dev, "cam i2c address destination setting error!\n");
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int max96717_i2c_addr_def(maxim4c_remote_t *max96717)
+{
+	struct device *dev = max96717->dev;
+	struct i2c_client *client = max96717->client;
+	u16 i2c_8bit_addr = 0;
+	int ret = 0;
+
+	if (max96717->ser_i2c_addr_map) {
+		dev_info(dev, "Serializer i2c address def\n");
+
+		maxim4c_remote_i2c_addr_select(max96717, MAXIM4C_I2C_SER_MAP);
+
+		i2c_8bit_addr = (max96717->ser_i2c_addr_def << 1);
+		ret = maxim4c_i2c_write_byte(client,
+				0x0000, MAXIM4C_I2C_REG_ADDR_16BITS,
+				i2c_8bit_addr);
+		if (ret) {
+			dev_err(dev, "ser i2c address def setting error!\n");
+			return ret;
+		}
+
+		maxim4c_remote_i2c_addr_select(max96717, MAXIM4C_I2C_SER_DEF);
+	}
+
+	return 0;
+}
+
+static int max96717_check_chipid(maxim4c_remote_t *max96717)
+{
+	struct device *dev = max96717->dev;
+	struct i2c_client *client = max96717->client;
+	u8 chip_id;
+	int ret = 0;
+
+	// max96717
+	ret = maxim4c_i2c_read_byte(client,
+			MAX96717_REG_CHIP_ID, MAXIM4C_I2C_REG_ADDR_16BITS,
+			&chip_id);
+	if (ret != 0) {
+		dev_info(dev, "Retry check chipid using map address\n");
+		maxim4c_remote_i2c_addr_select(max96717, MAXIM4C_I2C_SER_MAP);
+		ret = maxim4c_i2c_read_byte(client,
+				MAX96717_REG_CHIP_ID, MAXIM4C_I2C_REG_ADDR_16BITS,
+				&chip_id);
+		if (ret != 0) {
+			dev_err(dev, "MAX96717 detect error, ret(%d)\n", ret);
+			maxim4c_remote_i2c_addr_select(max96717, MAXIM4C_I2C_SER_DEF);
+
+			return -ENODEV;
+		}
+
+		max96717_i2c_addr_def(max96717);
+	}
+
+	if (chip_id != MAX96717_CHIP_ID) {
+		dev_err(dev, "Unexpected chip id = %02x\n", chip_id);
+		return -ENODEV;
+	}
+	dev_info(dev, "Detected MAX96717 chip id: 0x%02x\n", chip_id);
+
+	return 0;
+}
+
+static int max96717_module_init(maxim4c_remote_t *max96717)
+{
+	struct device *dev = max96717->dev;
+	struct i2c_client *client = max96717->client;
+	int ret = 0;
+
+	ret = maxim4c_remote_i2c_addr_select(max96717, MAXIM4C_I2C_SER_DEF);
+	if (ret)
+		return ret;
+
+	ret = max96717_check_chipid(max96717);
+	if (ret)
+		return ret;
+
+	ret = max96717_i2c_addr_remap(max96717);
+	if (ret)
+		return ret;
+
+	ret = maxim4c_i2c_run_init_seq(client,
+			&max96717->remote_init_seq);
+	if (ret) {
+		dev_err(dev, "remote id = %d init sequence error\n",
+				max96717->remote_id);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int max96717_module_deinit(maxim4c_remote_t *max96717)
+{
+	int ret = 0;
+
+	ret |= max96717_i2c_addr_def(max96717);
+
+	return ret;
+}
+
+static const struct maxim4c_remote_ops max96717_ops = {
+	.remote_init = max96717_module_init,
+	.remote_deinit = max96717_module_deinit,
+};
+
+static int max96717_parse_dt(maxim4c_remote_t *max96717)
+{
+	struct device *dev = max96717->dev;
+	struct device_node *of_node = dev->of_node;
+	u32 value = 0;
+	int ret = 0;
+
+	dev_info(dev, "=== maxim4c remote max96717 parse dt ===\n");
+
+	ret = of_property_read_u32(of_node, "remote-id", &value);
+	if (ret == 0) {
+		dev_info(dev, "remote-id property: %d\n", value);
+		max96717->remote_id = value;
+	} else {
+		max96717->remote_id = MAXIM4C_LINK_ID_MAX;
+	}
+
+	dev_info(dev, "max96717 remote id: %d\n", max96717->remote_id);
+
+	ret = of_property_read_u32(of_node, "ser-i2c-addr-def", &value);
+	if (ret == 0) {
+		dev_info(dev, "ser-i2c-addr-def property: 0x%x", value);
+		max96717->ser_i2c_addr_def = value;
+	} else {
+		max96717->ser_i2c_addr_def = MAX96717_I2C_ADDR_DEF;
+	}
+
+	ret = of_property_read_u32(of_node, "ser-i2c-addr-map", &value);
+	if (ret == 0) {
+		dev_info(dev, "ser-i2c-addr-map property: 0x%x", value);
+		max96717->ser_i2c_addr_map = value;
+	}
+
+	ret = of_property_read_u32(of_node, "cam-i2c-addr-def", &value);
+	if (ret == 0) {
+		dev_info(dev, "cam-i2c-addr-def property: 0x%x", value);
+		max96717->cam_i2c_addr_def = value;
+	}
+
+	ret = of_property_read_u32(of_node, "cam-i2c-addr-map", &value);
+	if (ret == 0) {
+		dev_info(dev, "cam-i2c-addr-map property: 0x%x", value);
+		max96717->cam_i2c_addr_map = value;
+	}
+
+	return 0;
+}
+
+static int max96717_probe(struct platform_device *pdev)
+{
+	struct i2c_client *client = to_i2c_client(pdev->dev.parent);
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct maxim4c *maxim4c = v4l2_get_subdevdata(sd);
+	struct maxim4c_remote *max96717 = NULL;
+	u32 link_id = MAXIM4C_LINK_ID_MAX;
+	int ret = 0;
+
+	dev_info(&pdev->dev, "max96717 serializer probe\n");
+
+	link_id = (uintptr_t)of_device_get_match_data(&pdev->dev);
+	link_id = link_id - MAXIM4C_LINK_ID_MAX;
+	if (link_id >= MAXIM4C_LINK_ID_MAX) {
+		dev_err(&pdev->dev, "max96717 probe match data error\n");
+		return -EINVAL;
+	}
+	dev_info(&pdev->dev, "max96717 probe link id = %d\n", link_id);
+
+	max96717 = devm_kzalloc(&pdev->dev, sizeof(*max96717), GFP_KERNEL);
+	if (!max96717) {
+		dev_err(&pdev->dev, "max96717 probe no memory error\n");
+		return -ENOMEM;
+	}
+
+	max96717->dev = &pdev->dev;
+	max96717->remote_ops = &max96717_ops;
+	max96717->local = maxim4c;
+	dev_set_drvdata(max96717->dev, max96717);
+
+	max96717_parse_dt(max96717);
+
+	if (max96717->remote_id != link_id) {
+		dev_err(&pdev->dev, "max96717 probe remote_id error\n");
+		return -EINVAL;
+	}
+
+	ret = maxim4c_remote_i2c_client_init(max96717, client);
+	if (ret) {
+		dev_err(&pdev->dev, "remote i2c client init error\n");
+		return ret;
+	}
+
+	ret = maxim4c_remote_device_register(maxim4c, max96717);
+	if (ret) {
+		dev_err(&pdev->dev, "remote serializer register error\n");
+		return ret;
+	}
+
+	maxim4c_remote_load_init_seq(max96717);
+
+	return 0;
+}
+
+static int max96717_remove(struct platform_device *pdev)
+{
+	return 0;
+}
+
+static const struct of_device_id max96717_of_table[] = {
+	{
+		.compatible = "maxim4c,link0,max96717",
+		.data = (const void *)(MAXIM4C_LINK_ID_MAX + MAXIM4C_LINK_ID_A)
+	}, {
+		.compatible = "maxim4c,link1,max96717",
+		.data = (const void *)(MAXIM4C_LINK_ID_MAX + MAXIM4C_LINK_ID_B)
+	}, {
+		.compatible = "maxim4c,link2,max96717",
+		.data = (const void *)(MAXIM4C_LINK_ID_MAX + MAXIM4C_LINK_ID_C)
+	}, {
+		.compatible = "maxim4c,link3,max96717",
+		.data = (const void *)(MAXIM4C_LINK_ID_MAX + MAXIM4C_LINK_ID_D)
+	},
+	{ /* Sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, max96717_of_table);
+
+static struct platform_driver max96717_driver = {
+	.probe		= max96717_probe,
+	.remove		= max96717_remove,
+	.driver		= {
+		.name	= "max96717",
+		.of_match_table = max96717_of_table,
+	},
+};
+
+module_platform_driver(max96717_driver);
+
+MODULE_AUTHOR("Cai Wenzhong <cwz@rock-chips.com>");
+MODULE_DESCRIPTION("Maxim MAX96717 Serializer Driver");
+MODULE_LICENSE("GPL");
diff --git a/kernel/drivers/media/i2c/nvp6158_drv/nvp6158_v4l2.c b/kernel/drivers/media/i2c/nvp6158_drv/nvp6158_v4l2.c
index 0f163d0..a89a836 100644
--- a/kernel/drivers/media/i2c/nvp6158_drv/nvp6158_v4l2.c
+++ b/kernel/drivers/media/i2c/nvp6158_drv/nvp6158_v4l2.c
@@ -924,6 +924,17 @@
 	return ret;
 }
 
+static int nvp6158_g_frame_interval(struct v4l2_subdev *sd,
+				    struct v4l2_subdev_frame_interval *fi)
+{
+	struct nvp6158 *nvp6158 = to_nvp6158(sd);
+	const struct nvp6158_framesize *size = nvp6158->frame_size;
+
+	fi->interval = size->max_fps;
+
+	return 0;
+}
+
 static void nvp6158_get_module_inf(struct nvp6158 *nvp6158,
 				   struct rkmodule_inf *inf)
 {
@@ -1174,6 +1185,7 @@
 static const struct v4l2_subdev_video_ops nvp6158_video_ops = {
 	.s_stream = nvp6158_stream,
 	.querystd = nvp6158_querystd,
+	.g_frame_interval = nvp6158_g_frame_interval,
 };
 
 static const struct v4l2_subdev_pad_ops nvp6158_subdev_pad_ops = {
diff --git a/kernel/drivers/media/i2c/os02k10.c b/kernel/drivers/media/i2c/os02k10.c
new file mode 100644
index 0000000..3988053
--- /dev/null
+++ b/kernel/drivers/media/i2c/os02k10.c
@@ -0,0 +1,2262 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * os02k10 driver
+ *
+ * Copyright (C) 2022 Rockchip Electronics Co., Ltd.
+ *
+ */
+//#define DEBUG
+
+#include <linux/clk.h>
+#include <linux/device.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 <linux/sysfs.h>
+#include <linux/slab.h>
+#include <linux/version.h>
+#include <linux/rk-camera-module.h>
+#include <linux/rk-preisp.h>
+#include <media/media-entity.h>
+#include <media/v4l2-async.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-subdev.h>
+#include <linux/pinctrl/consumer.h>
+#include "../platform/rockchip/isp/rkisp_tb_helper.h"
+#include <linux/rk-preisp.h>
+
+#define DRIVER_VERSION			KERNEL_VERSION(0, 0x01, 0x00)
+
+#ifndef V4L2_CID_DIGITAL_GAIN
+#define V4L2_CID_DIGITAL_GAIN		V4L2_CID_GAIN
+#endif
+
+#define OS02K10_LANES			2
+#define OS02K10_BITS_PER_SAMPLE		10
+#define OS02K10_LINK_FREQ_360M		360000000
+#define OS02K10_LINK_FREQ_480M		480000000
+
+#define PIXEL_RATE_WITH_360M_10BIT		(OS02K10_LINK_FREQ_360M * 2 * \
+					OS02K10_LANES / OS02K10_BITS_PER_SAMPLE)
+
+#define PIXEL_RATE_WITH_480M_10BIT		(OS02K10_LINK_FREQ_480M * 2 * \
+					OS02K10_LANES / OS02K10_BITS_PER_SAMPLE)
+
+#define OS02K10_XVCLK_FREQ		24000000
+
+#define CHIP_ID				0x530243
+#define OS02K10_REG_CHIP_ID		0x300a
+
+#define OS02K10_REG_CTRL_MODE		0x0100
+#define OS02K10_MODE_SW_STANDBY		0x0
+#define OS02K10_MODE_STREAMING		BIT(0)
+
+#define OS02K10_REG_DCG_CONVER_H	0x376c
+#define OS02K10_REG_DCG_CONVER_L	0x3c55
+#define OS02K10_REG_DCG_RATIO		0x73fe
+
+#define OS02K10_AEC_LONG_EXP_REG_H           0x3501 //bit[7:0] to long exposure[15:8]
+#define OS02K10_AEC_LONG_EXP_REG_L           0x3502 //bit[7:0] to long exposure[7:0]
+#define OS02K10_AEC_LONG_REL_GAIN_REG_H      0x3508 //bit[3:0] to long real gain[7:4]
+#define OS02K10_AEC_LONG_REL_GAIN_REG_L      0x3509 //bit[7:4] to long real gain[3:0]
+#define OS02K10_AEC_LONG_DIG_GAIN_REG_H      0x350a //bit[3:0] to long digital gain[13:10]
+#define OS02K10_AEC_LONG_DIG_GAIN_REG_M      0x350b //bit[7:0] to long digital gain[9:2]
+#define OS02K10_AEC_LONG_DIG_GAIN_REG_L      0x350c //bit[7:6] to long digital gain[1:0]
+
+//#define OS02K10_AEC_SHORT_EXP_REG_H        0x3541 //bit[7:0] to short exposure[15:8]
+//#define OS02K10_AEC_SHORT_EXP_REG_L        0x3542 //bit[7:0] to short exposure[7:0]
+#define OS02K10_AEC_SHORT_REL_GAIN_REG_H     0x3548 //bit[3:0] to short real gain[7:4]
+#define OS02K10_AEC_SHORT_REL_GAIN_REG_L     0x3549 //bit[7:4] to short real gain[3:0]
+#define OS02K10_AEC_SHORT_DIG_GAIN_REG_H     0x354a //bit[3:0] to short digital gain[13:10]
+#define OS02K10_AEC_SHORT_DIG_GAIN_REG_M     0x354b //bit[7:0] to short digital gain[9:2]
+#define OS02K10_AEC_SHORT_DIG_GAIN_REG_L     0x354c //bit[7:6] to short digital gain[1:0]
+
+#define OS02K10_AEC_VERY_SHORT_EXP_REG_H         0x3581 //bit[7:0] to very short exposure[15:8]
+#define OS02K10_AEC_VERY_SHORT_EXP_REG_L         0x3582 //bit[7:0] to very short exposure[7:0]
+#define OS02K10_AEC_VERY_SHORT_REL_GAIN_REG_H    0x3588 //bit[3:0] to very short real gain[7:4]
+#define OS02K10_AEC_VERY_SHORT_REL_GAIN_REG_L    0x3589 //bit[7:4] to very short real gain[3:0]
+#define OS02K10_AEC_VERY_SHORT_DIG_GAIN_REG_H    0x358a //bit[3:0] to very short digital gain[13:10]
+#define OS02K10_AEC_VERY_SHORT_DIG_GAIN_REG_M    0x358b //bit[7:0] to very short digital gain[9:2]
+#define OS02K10_AEC_VERY_SHORT_DIG_GAIN_REG_L    0x358c //bit[7:6] to very short digital gain[1:0]
+
+#define OS02K10_AEC_TIMING_HTS_REG_H         0x380c //bit[7:0] to horizontal timing[15:8] for log and short exposure
+#define OS02K10_AEC_TIMING_HTS_REG_L         0x380d //bit[7:0] to horizontal timing[7:0] for log and short exposure
+#define OS02K10_AEC_TIMING_VTS_REG_H         0x380e //bit[7:0] to vertical timing[15:8] for log and short exposure
+#define OS02K10_AEC_TIMING_VTS_REG_L         0x380f //bit[7:0] to vertical timing[7:0] for log and short exposure
+#define OS02K10_AEC_TIMING_VERY_HTS_REG_H    0x384c //bit[7:0] to horizontal timing[15:8] for very short exposure
+#define OS02K10_AEC_TIMING_VERY_HTS_REG_L    0x384d //bit[7:0] to horizontal timing[7:0] for very short exposure
+
+#define OS02K10_HORIZONTAL_START_REG_H           0x3800 //bit[7:0] to array horizontal start[15:8]
+#define OS02K10_HORIZONTAL_START_REG_L           0x3801 //bit[7:0] to array horizontal start[7:0]
+#define OS02K10_VERTICAL_START_REG_H             0x3802 //bit[7:0] to array vertical start[15:8]
+#define OS02K10_VERTICAL_START_REG_L             0x3803 //bit[7:0] to array vertical start[15:8]
+#define OS02K10_HORIZONTAL_END_REG_H             0x3804 //bit[7:0] to array horizontal end[15:8]
+#define OS02K10_HORIZONTAL_END_REG_L             0x3805 //bit[7:0] to array horizontal end[15:8]
+#define OS02K10_VERTICAL_END_REG_H               0x3806 //bit[7:0] to array vertical end[15:8]
+#define OS02K10_VERTICAL_END_REG_L               0x3807 //bit[7:0] to array vertical end[15:8]
+#define OS02K10_HORIZONTAL_OUTPUT_SIZE_REG_H     0x3808 //bit[7:0] to array horizontal output size for final image[15:8]
+#define OS02K10_HORIZONTAL_OUTPUT_SIZE_REG_L     0x3809 //bit[7:0] to array horizontal output size for final image[7:0]
+#define OS02K10_VERTICAL_OUTPUT_SIZE_REG_H       0x380a //bit[7:0] to array vertical output size for final image[15:8]
+#define OS02K10_VERTICAL_OUTPUT_SIZE_REG_L       0x380b //bit[7:0] to array vertical output size for final image[7:0]
+
+#define OS02K10_WINDOW_HORIZONTAL_TRUNC_SIZE_REG_H      0x3810 //bit[7:0] to isp window horizontal truncation size[15:8]
+#define OS02K10_WINDOW_HORIZONTAL_TRUNC_SIZE_REG_L      0x3811 //bit[7:0] to isp window horizontal truncation size[7:0]
+#define OS02K10_WINDOW_VERTICAL_TRUNC_SIZE_REG_H        0x3812 //bit[7:0] to isp window vertical truncation size[15:8]
+#define OS02K10_WINDOW_VERTICAL_TRUNC_SIZE_REG_L        0x3813 //bit[7:0] to isp window vertical truncation size[15:8]
+
+#define OS02K10_FLIPMIRROR_REG                   0x3820
+#define OS02K10_FLIPMIRROR_REGBIT_MIRROR         0x02   //bit[1] = 2'b1, mirror
+#define OS02K10_FLIPMIRROR_REGBIT_FLIP           0x0c   //bit[3:2] = 2'b11, flip
+#define OS02K10_FLIPMIRROR_REGBIT_MIRROR_FLIP    0x0e   //bit[3:1] = 2'b111, mirror and flip
+#define OS02K10_FLIPMIRROR_STATE_NORMAL          0
+#define OS02K10_FLIPMIRROR_STATE_FLIPMIRROR      1
+#define OS02K10_FLIPMIRROR_STATE_MIRROR          2
+#define OS02K10_FLIPMIRROR_STATE_FLIP            3
+
+#define OS02K10_FINE_INTG_TIME_MIN               1
+#define OS02K10_FINE_INTG_TIME_MAX_MARGIN        0
+#define OS02K10_COARSE_INTG_TIME_MIN             16
+#define OS02K10_COARSE_INTG_TIME_MAX_MARGIN      8
+
+#define OS02K10_GAIN_MIN            0x10
+#define OS02K10_GAIN_MAX	    15863
+#define OS02K10_GAIN_STEP           1
+#define OS02K10_GAIN_DEFAULT        0x10
+#define OS02K10_REAL_GAIN           1
+#define OS02K10_SENSOR_GAIN         0
+#define OS02K10_VTS_MAX				0xffff
+#define	OS02K10_EXPOSURE_MIN		1
+#define	OS02K10_EXPOSURE_STEP		1
+
+#define OS02K10_GROUP_UPDATE_ADDRESS	0x3208
+#define OS02K10_GROUP_UPDATE_START_DATA	0x00
+#define OS02K10_GROUP_UPDATE_END_DATA	0x10
+#define OS02K10_GROUP_UPDATE_LAUNCH		0xA0
+
+#define OS02K10_REG_TEST_PATTERN				 0x50C0
+#define OS02K10_TEST_PATTERN_BIT_MASK			 BIT(7)	// bit[7] test pattern enable
+
+#define OS02K10_FETCH_MSB_BYTE_EXP(VAL)	(((VAL) >> 8) & 0xFF)	/* 8 Bits */
+#define OS02K10_FETCH_LSB_BYTE_EXP(VAL)	((VAL) & 0xFF)	/* 8 Bits */
+
+#define OS02K10_FETCH_LSB_GAIN(VAL)	(((VAL) << 4) & 0xf0)
+#define OS02K10_FETCH_MSB_GAIN(VAL)	(((VAL) >> 4) & 0x1f)
+
+#define OS02K10_FETCH_MIRROR(VAL, ENABLE)        (ENABLE ? VAL | 0x02 : VAL & 0xfd)
+#define OS02K10_FETCH_FLIP(VAL, ENABLE)          (ENABLE ? VAL | 0x0c : VAL & 0xf3)
+
+#define REG_DELAY			0xFFFE
+#define REG_NULL			0xFFFF
+
+#define OS02K10_REG_VALUE_08BIT		1
+#define OS02K10_REG_VALUE_16BIT		2
+#define OS02K10_REG_VALUE_24BIT		3
+
+#define OF_CAMERA_PINCTRL_STATE_DEFAULT	"rockchip,camera_default"
+#define OF_CAMERA_PINCTRL_STATE_SLEEP	"rockchip,camera_sleep"
+#define OF_CAMERA_HDR_MODE		"rockchip,camera-hdr-mode"
+#define OS02K10_NAME			"os02k10"
+
+static const char * const os02k10_supply_names[] = {
+	"avdd",		/* Analog power */
+	"dovdd",	/* Digital I/O power */
+	"dvdd",		/* Digital core power */
+};
+
+#define OS02K10_NUM_SUPPLIES ARRAY_SIZE(os02k10_supply_names)
+
+struct regval {
+	u16 addr;
+	u8 val;
+};
+
+struct os02k10_mode {
+	u32 bus_fmt;
+	u32 width;
+	u32 height;
+	struct v4l2_fract max_fps;
+	u32 hts_def;
+	u32 vts_def;
+	u32 exp_def;
+	u32 mipi_freq_idx;
+	u32 bpp;
+	const struct regval *reg_list;
+	u32 hdr_mode;
+	u32 vc[PAD_MAX];
+};
+
+struct os02k10 {
+	struct i2c_client	*client;
+	struct clk		*xvclk;
+	struct gpio_desc	*reset_gpio;
+	struct gpio_desc	*pwdn_gpio;
+	struct regulator_bulk_data supplies[OS02K10_NUM_SUPPLIES];
+
+	struct pinctrl		*pinctrl;
+	struct pinctrl_state	*pins_default;
+	struct pinctrl_state	*pins_sleep;
+
+	struct v4l2_subdev	subdev;
+	struct media_pad	pad;
+	struct v4l2_ctrl_handler ctrl_handler;
+	struct v4l2_ctrl	*exposure;
+	struct v4l2_ctrl	*anal_gain;
+	struct v4l2_ctrl	*digi_gain;
+	struct v4l2_ctrl	*hblank;
+	struct v4l2_ctrl	*vblank;
+	struct v4l2_ctrl	*pixel_rate;
+	struct v4l2_ctrl	*link_freq;
+	struct v4l2_ctrl	*test_pattern;
+	struct mutex		mutex;
+	struct v4l2_fract	cur_fps;
+	bool			long_hcg;
+	bool			middle_hcg;
+	bool			short_hcg;
+	bool			streaming;
+	bool			power_on;
+	const struct os02k10_mode *cur_mode;
+	u32			module_index;
+	const char		*module_facing;
+	const char		*module_name;
+	const char		*len_name;
+	u32			cur_vts;
+	u32			dcg_ratio;
+	bool			has_init_exp;
+	bool			is_thunderboot;
+	bool			is_first_streamoff;
+	struct preisp_hdrae_exp_s init_hdrae_exp;
+};
+
+#define to_os02k10(sd) container_of(sd, struct os02k10, subdev)
+
+/*
+ * Xclk 24Mhz
+ */
+static const struct regval os02k10_global_regs[] = {
+	{REG_NULL, 0x00},
+};
+
+static const struct regval os02k10_linear_10_640x480_regs[] = {
+	{0x0103,0x01},
+	{REG_DELAY, 0x0a},
+	{0x0100,0x00},
+	{0x0109,0x01},
+	{0x0104,0x02},
+	{0x0102,0x00},
+	{0x0305,0x5c},
+	{0x0306,0x00},
+	{0x0307,0x00},
+	{0x030a,0x01},
+	{0x0317,0x09},
+	{0x0323,0x07},
+	{0x0324,0x01},
+	{0x0325,0xb0},
+	{0x0327,0x07},
+	{0x032c,0x02},
+	{0x032d,0x02},
+	{0x032e,0x05},
+	{0x300f,0x11},
+	{0x3012,0x21},
+	{0x3026,0x10},
+	{0x3027,0x08},
+	{0x302d,0x24},
+	{0x3103,0x29},
+	{0x3106,0x10},
+	{0x3400,0x00},
+	{0x3406,0x08},
+	{0x3408,0x05},
+	{0x340c,0x05},
+	{0x3425,0x51},
+	{0x3426,0x10},
+	{0x3427,0x14},
+	{0x3428,0x10},
+	{0x3429,0x10},
+	{0x342a,0x10},
+	{0x342b,0x04},
+	{0x3504,0x08},
+	{0x3508,0x01},
+	{0x3509,0x00},
+	{0x3544,0x08},
+	{0x3548,0x01},
+	{0x3549,0x00},
+	{0x3584,0x08},
+	{0x3588,0x01},
+	{0x3589,0x00},
+	{0x3601,0x70},
+	{0x3604,0xe3},
+	{0x3605,0x7f},
+	{0x3606,0x00},
+	{0x3608,0xa8},
+	{0x360a,0xd0},
+	{0x360b,0x08},
+	{0x360e,0xc8},
+	{0x360f,0x66},
+	{0x3610,0x81},
+	{0x3611,0x89},
+	{0x3612,0x4e},
+	{0x3613,0xbd},
+	{0x362a,0x0e},
+	{0x362b,0x0e},
+	{0x362c,0x0e},
+	{0x362d,0x0e},
+	{0x362e,0x0c},
+	{0x362f,0x1a},
+	{0x3630,0x32},
+	{0x3631,0x64},
+	{0x3638,0x00},
+	{0x3643,0x00},
+	{0x3644,0x00},
+	{0x3645,0x00},
+	{0x3646,0x00},
+	{0x3647,0x00},
+	{0x3648,0x00},
+	{0x3649,0x00},
+	{0x364a,0x04},
+	{0x364c,0x0e},
+	{0x364d,0x0e},
+	{0x364e,0x0e},
+	{0x364f,0x0e},
+	{0x3650,0xff},
+	{0x3651,0xff},
+	{0x3661,0x07},
+	{0x3662,0x02},
+	{0x3663,0x20},
+	{0x3665,0x12},
+	{0x3667,0xd4},
+	{0x3668,0x80},
+	{0x366f,0x00},
+	{0x3670,0xc7},
+	{0x3671,0x08},
+	{0x3673,0x2a},
+	{0x3681,0x80},
+	{0x3700,0x26},
+	{0x3701,0x1e},
+	{0x3702,0x25},
+	{0x3703,0x28},
+	{0x3706,0x3e},
+	{0x3707,0x0a},
+	{0x3708,0x36},
+	{0x3709,0x55},
+	{0x370a,0x00},
+	{0x370b,0xa3},
+	{0x3714,0x03},
+	{0x371b,0x16},
+	{0x371c,0x00},
+	{0x371d,0x08},
+	{0x3756,0x9b},
+	{0x3757,0x9b},
+	{0x3762,0x1d},
+	{0x376c,0x10},
+	{0x3776,0x05},
+	{0x3777,0x22},
+	{0x3779,0x60},
+	{0x377c,0x48},
+	{0x3783,0x02},
+	{0x3784,0x06},
+	{0x3785,0x0a},
+	{0x3790,0x10},
+	{0x3793,0x04},
+	{0x3794,0x07},
+	{0x3796,0x00},
+	{0x3797,0x02},
+	{0x379c,0x4d},
+	{0x37a1,0x80},
+	{0x37bb,0x88},
+	{0x37bd,0x01},
+	{0x37be,0x01},
+	{0x37bf,0x00},
+	{0x37c0,0x01},
+	{0x37c7,0x56},
+	{0x37ca,0x21},
+	{0x37cc,0x13},
+	{0x37cd,0x90},
+	{0x37cf,0x04},
+	{0x37d1,0x3e},
+	{0x37d2,0x00},
+	{0x37d3,0xa3},
+	{0x37d5,0x3e},
+	{0x37d6,0x00},
+	{0x37d7,0xa3},
+	{0x37d8,0x01},
+	{0x37da,0x00},
+	{0x37db,0x00},
+	{0x37dc,0x00},
+	{0x37dd,0x00},
+	{0x3800,0x00},
+	{0x3801,0x00},
+	{0x3802,0x00},
+	{0x3803,0x04},
+	{0x3804,0x07},
+	{0x3805,0x8f},
+	{0x3806,0x04},
+	{0x3807,0x43},
+	{0x3808,0x07},
+	{0x3809,0x80},
+	{0x380a,0x04},
+	{0x380b,0x38},
+	{0x380c,0x02},
+	{0x380d,0xd0},
+	{0x380e,0x04},
+	{0x380f,0xe2},
+	{0x3811,0x08},
+	{0x3813,0x04},
+	{0x3814,0x03},
+	{0x3815,0x01},
+	{0x3816,0x03},
+	{0x3817,0x01},
+	{0x381c,0x00},
+	{0x3820,0x02},
+	{0x3821,0x09},
+	{0x3822,0x14},
+	{0x3833,0x41},
+	{0x384c,0x02},
+	{0x384d,0xd0},
+	{0x3858,0x0d},
+	{0x3865,0x00},
+	{0x3866,0xc0},
+	{0x3867,0x00},
+	{0x3868,0xc0},
+	{0x3900,0x13},
+	{0x3940,0x13},
+	{0x3980,0x13},
+	{0x390c,0x03},
+	{0x390d,0x02},
+	{0x390e,0x01},
+	{0x390f,0x03},
+	{0x3910,0x02},
+	{0x3911,0x01},
+	{0x394c,0x02},
+	{0x394d,0x02},
+	{0x394e,0x01},
+	{0x394f,0x02},
+	{0x3950,0x02},
+	{0x3951,0x01},
+	{0x398c,0x02},
+	{0x398d,0x01},
+	{0x398e,0x01},
+	{0x398f,0x02},
+	{0x3990,0x01},
+	{0x3991,0x01},
+	{0x5395,0x38},
+	{0x5392,0x14},
+	{0x5396,0x02},
+	{0x5397,0x01},
+	{0x5398,0x01},
+	{0x5399,0x02},
+	{0x539a,0x01},
+	{0x539b,0x01},
+	{0x5415,0x38},
+	{0x5412,0x14},
+	{0x5416,0x01},
+	{0x5417,0x01},
+	{0x5418,0x01},
+	{0x5419,0x01},
+	{0x541a,0x01},
+	{0x541b,0x01},
+	{0x5495,0x38},
+	{0x5492,0x14},
+	{0x5496,0x01},
+	{0x5497,0x01},
+	{0x5498,0x01},
+	{0x5499,0x01},
+	{0x549a,0x01},
+	{0x549b,0x01},
+	{0x3c01,0x11},
+	{0x3c05,0x00},
+	{0x3c0f,0x1c},
+	{0x3c12,0x0d},
+	{0x3c14,0x21},
+	{0x3c19,0x01},
+	{0x3c21,0x40},
+	{0x3c3b,0x18},
+	{0x3c3d,0xc9},
+	{0x3c55,0x08},
+	{0x3c5d,0xcf},
+	{0x3c5e,0xcf},
+	{0x3ce0,0x00},
+	{0x3ce1,0x00},
+	{0x3ce2,0x00},
+	{0x3ce3,0x00},
+	{0x3d8c,0x70},
+	{0x3d8d,0x10},
+	{0x4001,0x2f},
+	{0x4033,0x80},
+	{0x4008,0x02},
+	{0x4009,0x07},
+	{0x4004,0x00},
+	{0x4005,0x40},
+	{0x400a,0x01},
+	{0x400b,0x3c},
+	{0x400e,0x40},
+	{0x4011,0xbb},
+	{0x410f,0x01},
+	{0x4028,0x6f},
+	{0x4029,0x0f},
+	{0x402a,0x3f},
+	{0x402b,0x01},
+	{0x402e,0x00},
+	{0x402f,0x40},
+	{0x4030,0x00},
+	{0x4031,0x40},
+	{0x4032,0x2f},
+	{0x4050,0x00},
+	{0x4051,0x03},
+	{0x4288,0xcf},
+	{0x4289,0x03},
+	{0x428a,0x46},
+	{0x430b,0x0f},
+	{0x430c,0xfc},
+	{0x430d,0x00},
+	{0x430e,0x00},
+	{0x4314,0x04},
+	{0x4500,0x1a},
+	{0x4501,0x18},
+	{0x4504,0x00},
+	{0x4507,0x02},
+	{0x4508,0x1a},
+	{0x4603,0x00},
+	{0x4640,0x62},
+	{0x4646,0xaa},
+	{0x4647,0x55},
+	{0x4648,0x99},
+	{0x4649,0x66},
+	{0x464d,0x00},
+	{0x4654,0x11},
+	{0x4655,0x22},
+	{0x4800,0x04},
+	{0x4810,0xff},
+	{0x4811,0xff},
+	{0x480e,0x00},
+	{0x4813,0x00},
+	{0x4837,0x0e},
+	{0x484b,0x27},
+	{0x4d00,0x4e},
+	{0x4d01,0x0c},
+	{0x4d02,0xb8},
+	{0x4d03,0xea},
+	{0x4d04,0x74},
+	{0x4d05,0xb7},
+	{0x4d09,0x4f},
+	{0x5000,0x1f},
+	{0x5080,0x00},
+	{0x50c0,0x00},
+	{0x5100,0x00},
+	{0x5200,0x00},
+	{0x5201,0x70},
+	{0x5202,0x03},
+	{0x5203,0x7f},
+	{0x5780,0x53},
+	{0x5786,0x01},
+	{0x3501,0x02},
+	{0x3800,0x00},
+	{0x3801,0x00},
+	{0x3802,0x00},
+	{0x3803,0x00},
+	{0x3804,0x07},
+	{0x3805,0x8f},
+	{0x3806,0x04},
+	{0x3807,0x47},
+	{0x3808,0x02},
+	{0x3809,0x80},
+	{0x380a,0x01},
+	{0x380b,0xe0},
+	{0x3811,0xa0},
+	{0x3813,0x1e},
+	{0x0305,0x50},
+	{0x4837,0x10},
+	{0x380c,0x05},
+	{0x380d,0xa0},
+	{0x380e,0x02},
+	{0x380f,0x71},
+	{0x3501,0x02},
+	{0x3502,0x69},
+	{REG_NULL, 0x00},
+};
+
+/*
+ * Xclk 24Mhz
+ * max_framerate 30fps
+ * mipi_datarate per lane 360Mbps, 2lane
+ */
+static const struct regval os02k10_linear_10_1920x1080_regs[] = {
+	{0x0103,0x01},
+	{REG_DELAY, 0x0a},
+	{0x0100,0x00},
+	{0x0109,0x01},
+	{0x0104,0x02},
+	{0x0102,0x00},
+	{0x0305,0x5c},
+	{0x0306,0x00},
+	{0x0307,0x00},
+	{0x030a,0x01},
+	{0x0317,0x09},
+	{0x0323,0x07},
+	{0x0324,0x01},
+	{0x0325,0xb0},
+	{0x0327,0x07},
+	{0x032c,0x02},
+	{0x032d,0x02},
+	{0x032e,0x05},
+	{0x300f,0x11},
+	{0x3012,0x21},
+	{0x3026,0x10},
+	{0x3027,0x08},
+	{0x302d,0x24},
+	{0x3103,0x29},
+	{0x3106,0x10},
+	{0x3400,0x00},
+	{0x3406,0x08},
+	{0x3408,0x05},
+	{0x340c,0x05},
+	{0x3425,0x51},
+	{0x3426,0x10},
+	{0x3427,0x14},
+	{0x3428,0x10},
+	{0x3429,0x10},
+	{0x342a,0x10},
+	{0x342b,0x04},
+	{0x3504,0x08},
+	{0x3508,0x01},
+	{0x3509,0x00},
+	{0x3544,0x08},
+	{0x3548,0x01},
+	{0x3549,0x00},
+	{0x3584,0x08},
+	{0x3588,0x01},
+	{0x3589,0x00},
+	{0x3601,0x70},
+	{0x3604,0xe3},
+	{0x3605,0x7f},
+	{0x3606,0x00},
+	{0x3608,0xa8},
+	{0x360a,0xd0},
+	{0x360b,0x08},
+	{0x360e,0xc8},
+	{0x360f,0x66},
+	{0x3610,0x81},
+	{0x3611,0x89},
+	{0x3612,0x4e},
+	{0x3613,0xbd},
+	{0x362a,0x0e},
+	{0x362b,0x0e},
+	{0x362c,0x0e},
+	{0x362d,0x0e},
+	{0x362e,0x0c},
+	{0x362f,0x1a},
+	{0x3630,0x32},
+	{0x3631,0x64},
+	{0x3638,0x00},
+	{0x3643,0x00},
+	{0x3644,0x00},
+	{0x3645,0x00},
+	{0x3646,0x00},
+	{0x3647,0x00},
+	{0x3648,0x00},
+	{0x3649,0x00},
+	{0x364a,0x04},
+	{0x364c,0x0e},
+	{0x364d,0x0e},
+	{0x364e,0x0e},
+	{0x364f,0x0e},
+	{0x3650,0xff},
+	{0x3651,0xff},
+	{0x3661,0x07},
+	{0x3662,0x02},
+	{0x3663,0x20},
+	{0x3665,0x12},
+	{0x3667,0xd4},
+	{0x3668,0x80},
+	{0x366f,0x00},
+	{0x3670,0xc7},
+	{0x3671,0x08},
+	{0x3673,0x2a},
+	{0x3681,0x80},
+	{0x3700,0x26},
+	{0x3701,0x1e},
+	{0x3702,0x25},
+	{0x3703,0x28},
+	{0x3706,0x3e},
+	{0x3707,0x0a},
+	{0x3708,0x36},
+	{0x3709,0x55},
+	{0x370a,0x00},
+	{0x370b,0xa3},
+	{0x3714,0x01},
+	{0x371b,0x16},
+	{0x371c,0x00},
+	{0x371d,0x08},
+	{0x3756,0x9b},
+	{0x3757,0x9b},
+	{0x3762,0x1d},
+	{0x376c,0x00},
+	{0x3776,0x05},
+	{0x3777,0x22},
+	{0x3779,0x60},
+	{0x377c,0x48},
+	{0x3783,0x02},
+	{0x3784,0x06},
+	{0x3785,0x0a},
+	{0x3790,0x10},
+	{0x3793,0x04},
+	{0x3794,0x07},
+	{0x3796,0x00},
+	{0x3797,0x02},
+	{0x379c,0x4d},
+	{0x37a1,0x80},
+	{0x37bb,0x88},
+	{0x37bd,0x01},
+	{0x37be,0x01},
+	{0x37bf,0x00},
+	{0x37c0,0x01},
+	{0x37c7,0x56},
+	{0x37ca,0x21},
+	{0x37cc,0x13},
+	{0x37cd,0x90},
+	{0x37cf,0x02},
+	{0x37d1,0x3e},
+	{0x37d2,0x00},
+	{0x37d3,0xa3},
+	{0x37d5,0x3e},
+	{0x37d6,0x00},
+	{0x37d7,0xa3},
+	{0x37d8,0x01},
+	{0x37da,0x00},
+	{0x37db,0x00},
+	{0x37dc,0x00},
+	{0x37dd,0x00},
+	{0x3800,0x00},
+	{0x3801,0x00},
+	{0x3802,0x00},
+	{0x3803,0x04},
+	{0x3804,0x07},
+	{0x3805,0x8f},
+	{0x3806,0x04},
+	{0x3807,0x43},
+	{0x3808,0x07},
+	{0x3809,0x80},
+	{0x380a,0x04},
+	{0x380b,0x38},
+	{0x380c,0x05},
+	{0x380d,0xa0},
+	{0x380e,0x09},
+	{0x380f,0xc0},
+	{0x3811,0x08},
+	{0x3813,0x04},
+	{0x3814,0x01},
+	{0x3815,0x01},
+	{0x3816,0x01},
+	{0x3817,0x01},
+	{0x381c,0x00},
+	{0x3820,0x02},
+	{0x3821,0x00},
+	{0x3822,0x14},
+	{0x3833,0x41},
+	{0x384c,0x02},
+	{0x384d,0xd0},
+	{0x3858,0x0d},
+	{0x3865,0x00},
+	{0x3866,0xc0},
+	{0x3867,0x00},
+	{0x3868,0xc0},
+	{0x3900,0x13},
+	{0x3940,0x13},
+	{0x3980,0x13},
+	{0x390c,0x03},
+	{0x390d,0x02},
+	{0x390e,0x01},
+	{0x390f,0x03},
+	{0x3910,0x02},
+	{0x3911,0x01},
+	{0x394c,0x02},
+	{0x394d,0x02},
+	{0x394e,0x01},
+	{0x394f,0x02},
+	{0x3950,0x02},
+	{0x3951,0x01},
+	{0x398c,0x02},
+	{0x398d,0x01},
+	{0x398e,0x01},
+	{0x398f,0x02},
+	{0x3990,0x01},
+	{0x3991,0x01},
+	{0x5395,0x38},
+	{0x5392,0x14},
+	{0x5396,0x02},
+	{0x5397,0x01},
+	{0x5398,0x01},
+	{0x5399,0x02},
+	{0x539a,0x01},
+	{0x539b,0x01},
+	{0x5415,0x38},
+	{0x5412,0x14},
+	{0x5416,0x01},
+	{0x5417,0x01},
+	{0x5418,0x01},
+	{0x5419,0x01},
+	{0x541a,0x01},
+	{0x541b,0x01},
+	{0x5495,0x38},
+	{0x5492,0x14},
+	{0x5496,0x01},
+	{0x5497,0x01},
+	{0x5498,0x01},
+	{0x5499,0x01},
+	{0x549a,0x01},
+	{0x549b,0x01},
+	{0x3c01,0x11},
+	{0x3c05,0x00},
+	{0x3c0f,0x1c},
+	{0x3c12,0x0d},
+	{0x3c14,0x21},
+	{0x3c19,0x01},
+	{0x3c21,0x40},
+	{0x3c3b,0x18},
+	{0x3c3d,0xc9},
+	{0x3c55,0xcb},
+	{0x3c5d,0xcf},
+	{0x3c5e,0xcf},
+	{0x3ce0,0x00},
+	{0x3ce1,0x00},
+	{0x3ce2,0x00},
+	{0x3ce3,0x00},
+	{0x3d8c,0x70},
+	{0x3d8d,0x10},
+	{0x4001,0x2f},
+	{0x4033,0x80},
+	{0x4008,0x02},
+	{0x4009,0x11},
+	{0x4004,0x00},
+	{0x4005,0x40},
+	{0x400a,0x01},
+	{0x400b,0x3c},
+	{0x400e,0x40},
+	{0x4011,0xbb},
+	{0x410f,0x01},
+	{0x4028,0x6f},
+	{0x4029,0x0f},
+	{0x402a,0x3f},
+	{0x402b,0x01},
+	{0x402e,0x00},
+	{0x402f,0x40},
+	{0x4030,0x00},
+	{0x4031,0x40},
+	{0x4032,0x2f},
+	{0x4050,0x00},
+	{0x4051,0x07},
+	{0x4288,0xcf},
+	{0x4289,0x03},
+	{0x428a,0x46},
+	{0x430b,0x0f},
+	{0x430c,0xfc},
+	{0x430d,0x00},
+	{0x430e,0x00},
+	{0x4314,0x04},
+	{0x4500,0x18},
+	{0x4501,0x18},
+	{0x4504,0x00},
+	{0x4507,0x02},
+	{0x4508,0x1a},
+	{0x4603,0x00},
+	{0x4640,0x62},
+	{0x4646,0xaa},
+	{0x4647,0x55},
+	{0x4648,0x99},
+	{0x4649,0x66},
+	{0x464d,0x00},
+	{0x4654,0x11},
+	{0x4655,0x22},
+	{0x4800,0x04},
+	{0x4810,0xff},
+	{0x4811,0xff},
+	{0x480e,0x00},
+	{0x4813,0x00},
+	{0x4837,0x0e},
+	{0x484b,0x27},
+	{0x4d00,0x4e},
+	{0x4d01,0x0c},
+	{0x4d02,0xb8},
+	{0x4d03,0xea},
+	{0x4d04,0x74},
+	{0x4d05,0xb7},
+	{0x4d09,0x4f},
+	{0x5000,0x1f},
+	{0x5080,0x00},
+	{0x50c0,0x00},
+	{0x5100,0x00},
+	{0x5200,0x00},
+	{0x5201,0x70},
+	{0x5202,0x03},
+	{0x5203,0x7f},
+	{0x5780,0x53},
+	{0x5786,0x01},
+	{0x3501,0x02},
+	{0x0305,0x50},
+	{0x4837,0x10},
+	{0x380c,0x05},
+	{0x380d,0xa0},
+	{0x380e,0x09},
+	{0x380f,0xc0},
+	{0x3501,0x04},
+	{0x3502,0xda},
+	{REG_NULL, 0x00},
+};
+
+static const struct os02k10_mode supported_modes[] = {
+	{
+		.width = 1920,
+		.height = 1080,
+		.max_fps = {
+			.numerator = 10000,
+			.denominator = 300000,
+		},
+		.exp_def = 0x04da,
+		.hts_def = 0x05a0,
+		.vts_def = 0x09c0,
+		.bus_fmt = MEDIA_BUS_FMT_SBGGR10_1X10,
+		.reg_list = os02k10_linear_10_1920x1080_regs,
+		.hdr_mode = NO_HDR,
+		.bpp = 10,
+		.mipi_freq_idx = 1,
+		.vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_0,
+	}, {
+		.width = 640,
+		.height = 480,
+		.max_fps = {
+			.numerator = 10000,
+			.denominator = 1200000,
+		},
+		.exp_def = 0x0269,
+		.hts_def = 0x05a0,
+		.vts_def = 0x0271,
+		.bus_fmt = MEDIA_BUS_FMT_SBGGR10_1X10,
+		.reg_list = os02k10_linear_10_640x480_regs,
+		.hdr_mode = NO_HDR,
+		.bpp = 10,
+		.mipi_freq_idx = 1,
+		.vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_0,
+	},
+};
+
+static const s64 link_freq_menu_items[] = {
+	OS02K10_LINK_FREQ_360M,
+	OS02K10_LINK_FREQ_480M
+};
+
+static const char * const os02k10_test_pattern_menu[] = {
+	"Disabled",
+	"Vertical Color Bar Type 1",
+	"Vertical Color Bar Type 2",
+	"Vertical Color Bar Type 3",
+	"Vertical Color Bar Type 4"
+};
+
+/* Write registers up to 4 at a time */
+static int os02k10_write_reg(struct i2c_client *client, u16 reg,
+			    u32 len, u32 val)
+{
+	u32 buf_i, val_i;
+	u8 buf[6];
+	u8 *val_p;
+	__be32 val_be;
+
+	if (len > 4)
+		return -EINVAL;
+
+	buf[0] = reg >> 8;
+	buf[1] = reg & 0xff;
+
+	val_be = cpu_to_be32(val);
+	val_p = (u8 *)&val_be;
+	buf_i = 2;
+	val_i = 4 - len;
+
+	while (val_i < 4)
+		buf[buf_i++] = val_p[val_i++];
+
+	if (i2c_master_send(client, buf, len + 2) != len + 2)
+		return -EIO;
+
+	return 0;
+}
+
+static int os02k10_write_array(struct i2c_client *client,
+			       const struct regval *regs)
+{
+	u32 i;
+	int ret = 0;
+
+	for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++)
+		if(regs[i].addr == REG_DELAY){
+			usleep_range(regs[i].val * 1000, (regs[i].val + 1)  * 1000);
+		}
+		else{
+			ret = os02k10_write_reg(client, regs[i].addr,
+					OS02K10_REG_VALUE_08BIT, regs[i].val);
+		}
+	return ret;
+}
+
+/* Read registers up to 4 at a time */
+static int os02k10_read_reg(struct i2c_client *client, u16 reg, unsigned int len,
+			    u32 *val)
+{
+	struct i2c_msg msgs[2];
+	u8 *data_be_p;
+	__be32 data_be = 0;
+	__be16 reg_addr_be = cpu_to_be16(reg);
+	int ret;
+
+	if (len > 4 || !len)
+		return -EINVAL;
+
+	data_be_p = (u8 *)&data_be;
+	/* Write register address */
+	msgs[0].addr = client->addr;
+	msgs[0].flags = 0;
+	msgs[0].len = 2;
+	msgs[0].buf = (u8 *)&reg_addr_be;
+
+	/* Read data from register */
+	msgs[1].addr = client->addr;
+	msgs[1].flags = I2C_M_RD;
+	msgs[1].len = len;
+	msgs[1].buf = &data_be_p[4 - len];
+
+	ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+	if (ret != ARRAY_SIZE(msgs))
+		return -EIO;
+
+	*val = be32_to_cpu(data_be);
+
+	return 0;
+}
+
+static int os02k10_set_hdrae(struct os02k10 *os02k10,
+			    struct preisp_hdrae_exp_s *ae)
+{
+	int ret = 0;
+
+	return ret;
+}
+
+static int os02k10_get_reso_dist(const struct os02k10_mode *mode,
+				 struct v4l2_mbus_framefmt *framefmt)
+{
+	return abs(mode->width - framefmt->width) +
+	       abs(mode->height - framefmt->height);
+}
+
+static const struct os02k10_mode *
+os02k10_find_best_fit(struct v4l2_subdev_format *fmt)
+{
+	struct v4l2_mbus_framefmt *framefmt = &fmt->format;
+	int dist;
+	int cur_best_fit = 0;
+	int cur_best_fit_dist = -1;
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(supported_modes); i++) {
+		dist = os02k10_get_reso_dist(&supported_modes[i], framefmt);
+		if (cur_best_fit_dist == -1 || dist < cur_best_fit_dist) {
+			cur_best_fit_dist = dist;
+			cur_best_fit = i;
+		}
+	}
+
+	return &supported_modes[cur_best_fit];
+}
+
+static int os02k10_set_fmt(struct v4l2_subdev *sd,
+			   struct v4l2_subdev_pad_config *cfg,
+			   struct v4l2_subdev_format *fmt)
+{
+	struct os02k10 *os02k10 = to_os02k10(sd);
+	const struct os02k10_mode *mode;
+	s64 h_blank, vblank_def;
+	u64 pixel_rate = 0;
+
+	mutex_lock(&os02k10->mutex);
+
+	mode = os02k10_find_best_fit(fmt);
+	fmt->format.code = mode->bus_fmt;
+	fmt->format.width = mode->width;
+	fmt->format.height = mode->height;
+	fmt->format.field = V4L2_FIELD_NONE;
+	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+		*v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = fmt->format;
+#else
+		mutex_unlock(&os02k10->mutex);
+		return -ENOTTY;
+#endif
+	} else {
+		os02k10->cur_mode = mode;
+		h_blank = mode->hts_def - mode->width;
+		__v4l2_ctrl_modify_range(os02k10->hblank, h_blank,
+					 h_blank, 1, h_blank);
+		vblank_def = mode->vts_def - mode->height;
+		__v4l2_ctrl_modify_range(os02k10->vblank, vblank_def,
+					 OS02K10_VTS_MAX - mode->height,
+					 1, vblank_def);
+
+		__v4l2_ctrl_s_ctrl(os02k10->link_freq, mode->mipi_freq_idx);
+		pixel_rate = (u32)link_freq_menu_items[mode->mipi_freq_idx] /
+			     mode->bpp * 2 * OS02K10_LANES;
+		__v4l2_ctrl_s_ctrl_int64(os02k10->pixel_rate, pixel_rate);
+		os02k10->cur_fps = mode->max_fps;
+	}
+
+	mutex_unlock(&os02k10->mutex);
+
+	return 0;
+}
+
+static int os02k10_get_fmt(struct v4l2_subdev *sd,
+			   struct v4l2_subdev_pad_config *cfg,
+			   struct v4l2_subdev_format *fmt)
+{
+	struct os02k10 *os02k10 = to_os02k10(sd);
+	const struct os02k10_mode *mode = os02k10->cur_mode;
+
+	mutex_lock(&os02k10->mutex);
+	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+		fmt->format = *v4l2_subdev_get_try_format(sd, cfg, fmt->pad);
+#else
+		mutex_unlock(&os02k10->mutex);
+		return -ENOTTY;
+#endif
+	} else {
+		fmt->format.width = mode->width;
+		fmt->format.height = mode->height;
+		fmt->format.code = mode->bus_fmt;
+		fmt->format.field = V4L2_FIELD_NONE;
+		/* format info: width/height/data type/virctual channel */
+		if (fmt->pad < PAD_MAX && mode->hdr_mode != NO_HDR)
+			fmt->reserved[0] = mode->vc[fmt->pad];
+		else
+			fmt->reserved[0] = mode->vc[PAD0];
+	}
+	mutex_unlock(&os02k10->mutex);
+
+	return 0;
+}
+
+static int os02k10_enum_mbus_code(struct v4l2_subdev *sd,
+				  struct v4l2_subdev_pad_config *cfg,
+				  struct v4l2_subdev_mbus_code_enum *code)
+{
+	struct os02k10 *os02k10 = to_os02k10(sd);
+
+	if (code->index != 0)
+		return -EINVAL;
+	code->code = os02k10->cur_mode->bus_fmt;
+
+	return 0;
+}
+
+static int os02k10_enum_frame_sizes(struct v4l2_subdev *sd,
+				    struct v4l2_subdev_pad_config *cfg,
+				    struct v4l2_subdev_frame_size_enum *fse)
+{
+	if (fse->index >= ARRAY_SIZE(supported_modes))
+		return -EINVAL;
+
+	if (fse->code != supported_modes[fse->index].bus_fmt)
+		return -EINVAL;
+
+	fse->min_width  = supported_modes[fse->index].width;
+	fse->max_width  = supported_modes[fse->index].width;
+	fse->max_height = supported_modes[fse->index].height;
+	fse->min_height = supported_modes[fse->index].height;
+
+	return 0;
+}
+
+static int os02k10_enable_test_pattern(struct os02k10 *os02k10, u32 pattern)
+{
+	u32 val = 0;
+	int ret = 0;
+
+	ret = os02k10_read_reg(os02k10->client, OS02K10_REG_TEST_PATTERN,
+			       OS02K10_REG_VALUE_08BIT, &val);
+	if (pattern)
+		val |= OS02K10_TEST_PATTERN_BIT_MASK;
+	else
+		val &= ~OS02K10_TEST_PATTERN_BIT_MASK;
+
+	ret |= os02k10_write_reg(os02k10->client, OS02K10_REG_TEST_PATTERN,
+				 OS02K10_REG_VALUE_08BIT, val);
+	return ret;
+}
+
+static int os02k10_g_frame_interval(struct v4l2_subdev *sd,
+				    struct v4l2_subdev_frame_interval *fi)
+{
+	struct os02k10 *os02k10 = to_os02k10(sd);
+	const struct os02k10_mode *mode = os02k10->cur_mode;
+
+	if (os02k10->streaming)
+		fi->interval = os02k10->cur_fps;
+	else
+		fi->interval = mode->max_fps;
+
+	return 0;
+}
+
+static int os02k10_g_mbus_config(struct v4l2_subdev *sd, unsigned int pad_id,
+				 struct v4l2_mbus_config *config)
+{
+	struct os02k10 *os02k10 = to_os02k10(sd);
+	const struct os02k10_mode *mode = os02k10->cur_mode;
+	u32 val = 1 << (OS02K10_LANES - 1) |
+		V4L2_MBUS_CSI2_CHANNEL_0 |
+		V4L2_MBUS_CSI2_CONTINUOUS_CLOCK;
+
+	if (mode->hdr_mode != NO_HDR)
+		val |= V4L2_MBUS_CSI2_CHANNEL_1;
+	if (mode->hdr_mode == HDR_X3)
+		val |= V4L2_MBUS_CSI2_CHANNEL_2;
+
+	config->type = V4L2_MBUS_CSI2_DPHY;
+	config->flags = val;
+
+	return 0;
+}
+
+static void os02k10_get_module_inf(struct os02k10 *os02k10,
+				   struct rkmodule_inf *inf)
+{
+	memset(inf, 0, sizeof(*inf));
+	strscpy(inf->base.sensor, OS02K10_NAME, sizeof(inf->base.sensor));
+	strscpy(inf->base.module, os02k10->module_name,
+		sizeof(inf->base.module));
+	strscpy(inf->base.lens, os02k10->len_name, sizeof(inf->base.lens));
+}
+
+static int os02k10_set_conversion_gain(struct os02k10 *os02k10, u32 *cg)
+{
+	int ret = 0;
+	struct i2c_client *client = os02k10->client;
+	u32 cur_cg = *cg;
+	u32 val_h = 0, val_l = 0;
+
+	mutex_lock(&os02k10->mutex);
+	dev_dbg(&os02k10->client->dev, "set conversion gain %d, long_hcg: %d\n", cur_cg, os02k10->long_hcg);
+	if (cur_cg == GAIN_MODE_LCG) {
+		val_l = 0x08;
+		val_h = 0x10;
+		os02k10->long_hcg = false;
+	} else if (cur_cg == GAIN_MODE_HCG) {
+		val_l = 0xcb;
+		val_h = 0x00;
+		os02k10->long_hcg = true;
+	}
+
+	if (!pm_runtime_get_if_in_use(&client->dev))
+		return 0;
+
+	ret |= os02k10_write_reg(client,
+				 OS02K10_REG_DCG_CONVER_H,
+				 OS02K10_REG_VALUE_08BIT,
+				 val_h);
+	ret |= os02k10_write_reg(client,
+				 OS02K10_REG_DCG_CONVER_L,
+				 OS02K10_REG_VALUE_08BIT,
+				 val_l);
+	dev_dbg(&client->dev, "set conversion gain %d, (reg_h reg_l, val_h val_l)=(0x%x 0x%x, 0x%x 0x%x)\n",
+		cur_cg, OS02K10_REG_DCG_CONVER_H, OS02K10_REG_DCG_CONVER_L,val_h, val_l);
+	pm_runtime_put(&client->dev);
+	mutex_unlock(&os02k10->mutex);
+	return ret;
+}
+
+static long os02k10_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
+{
+	struct os02k10 *os02k10 = to_os02k10(sd);
+	struct rkmodule_hdr_cfg *hdr;
+	struct rkmodule_dcg_ratio *dcg;
+	u32 i, h, w;
+	long ret = 0;
+	u32 stream = 0;
+
+	switch (cmd) {
+	case RKMODULE_GET_MODULE_INFO:
+		os02k10_get_module_inf(os02k10, (struct rkmodule_inf *)arg);
+		break;
+	case RKMODULE_GET_HDR_CFG:
+		hdr = (struct rkmodule_hdr_cfg *)arg;
+		hdr->esp.mode = HDR_NORMAL_VC;
+		hdr->hdr_mode = os02k10->cur_mode->hdr_mode;
+		break;
+	case RKMODULE_SET_HDR_CFG:
+		hdr = (struct rkmodule_hdr_cfg *)arg;
+		w = os02k10->cur_mode->width;
+		h = os02k10->cur_mode->height;
+		for (i = 0; i < ARRAY_SIZE(supported_modes); i++) {
+			if (w == supported_modes[i].width &&
+			    h == supported_modes[i].height &&
+			    supported_modes[i].hdr_mode == hdr->hdr_mode) {
+				os02k10->cur_mode = &supported_modes[i];
+				break;
+			}
+		}
+		if (i == ARRAY_SIZE(supported_modes)) {
+			dev_err(&os02k10->client->dev,
+				"not find hdr mode:%d %dx%d config\n",
+				hdr->hdr_mode, w, h);
+			ret = -EINVAL;
+		} else {
+			w = os02k10->cur_mode->hts_def - os02k10->cur_mode->width;
+			h = os02k10->cur_mode->vts_def - os02k10->cur_mode->height;
+			__v4l2_ctrl_modify_range(os02k10->hblank, w, w, 1, w);
+			__v4l2_ctrl_modify_range(os02k10->vblank, h,
+						 OS02K10_VTS_MAX - os02k10->cur_mode->height, 1, h);
+			os02k10->cur_fps = os02k10->cur_mode->max_fps;
+		}
+		break;
+	case PREISP_CMD_SET_HDRAE_EXP:
+		os02k10_set_hdrae(os02k10, arg);
+		break;
+	case RKMODULE_SET_CONVERSION_GAIN:
+		ret = os02k10_set_conversion_gain(os02k10, (u32 *)arg);
+		break;
+	case RKMODULE_SET_QUICK_STREAM:
+
+		stream = *((u32 *)arg);
+
+		if (stream)
+			ret = os02k10_write_reg(os02k10->client, OS02K10_REG_CTRL_MODE,
+				 OS02K10_REG_VALUE_08BIT, OS02K10_MODE_STREAMING);
+		else
+			ret = os02k10_write_reg(os02k10->client, OS02K10_REG_CTRL_MODE,
+				 OS02K10_REG_VALUE_08BIT, OS02K10_MODE_SW_STANDBY);
+		break;
+	case RKMODULE_GET_DCG_RATIO:
+		if (os02k10->dcg_ratio == 0)
+			return -EINVAL;
+		dcg = (struct rkmodule_dcg_ratio *)arg;
+		dcg->integer = (os02k10->dcg_ratio >> 8) & 0xff;
+		dcg->decimal = os02k10->dcg_ratio & 0xff;
+		dcg->div_coeff = 256;
+		dev_info(&os02k10->client->dev,
+			 "get dcg ratio integer %d, decimal %d div_coeff %d\n",
+			 dcg->integer, dcg->decimal, dcg->div_coeff);
+		break;
+	default:
+		ret = -ENOIOCTLCMD;
+		break;
+	}
+
+	return ret;
+}
+
+#ifdef CONFIG_COMPAT
+static long os02k10_compat_ioctl32(struct v4l2_subdev *sd,
+				   unsigned int cmd, unsigned long arg)
+{
+	void __user *up = compat_ptr(arg);
+	struct rkmodule_inf *inf;
+	struct rkmodule_hdr_cfg *hdr;
+	struct rkmodule_dcg_ratio *dcg;
+	struct preisp_hdrae_exp_s *hdrae;
+	long ret;
+	u32 stream = 0;
+
+	switch (cmd) {
+	case RKMODULE_GET_MODULE_INFO:
+		inf = kzalloc(sizeof(*inf), GFP_KERNEL);
+		if (!inf) {
+			ret = -ENOMEM;
+			return ret;
+		}
+
+		ret = os02k10_ioctl(sd, cmd, inf);
+		if (!ret) {
+			ret = copy_to_user(up, inf, sizeof(*inf));
+			if (ret)
+				return -EFAULT;
+		}
+		kfree(inf);
+		break;
+	case RKMODULE_GET_HDR_CFG:
+		hdr = kzalloc(sizeof(*hdr), GFP_KERNEL);
+		if (!hdr) {
+			ret = -ENOMEM;
+			return ret;
+		}
+
+		ret = os02k10_ioctl(sd, cmd, hdr);
+		if (!ret) {
+			ret = copy_to_user(up, hdr, sizeof(*hdr));
+			if (ret)
+				return -EFAULT;
+		}
+		kfree(hdr);
+		break;
+	case RKMODULE_SET_HDR_CFG:
+		hdr = kzalloc(sizeof(*hdr), GFP_KERNEL);
+		if (!hdr) {
+			ret = -ENOMEM;
+			return ret;
+		}
+
+		if (copy_from_user(hdr, up, sizeof(*hdr))) {
+			kfree(hdr);
+			return -EFAULT;
+		}
+
+		ret = os02k10_ioctl(sd, cmd, hdr);
+		kfree(hdr);
+		break;
+	case PREISP_CMD_SET_HDRAE_EXP:
+		hdrae = kzalloc(sizeof(*hdrae), GFP_KERNEL);
+		if (!hdrae) {
+			ret = -ENOMEM;
+			return ret;
+		}
+
+		if (copy_from_user(hdrae, up, sizeof(*hdrae))) {
+			kfree(hdrae);
+			return -EFAULT;
+		}
+
+		ret = os02k10_ioctl(sd, cmd, hdrae);
+		kfree(hdrae);
+		break;
+	case RKMODULE_SET_QUICK_STREAM:
+		if (copy_from_user(&stream, up, sizeof(u32)))
+			return -EFAULT;
+
+		ret = os02k10_ioctl(sd, cmd, &stream);
+		break;
+	case RKMODULE_GET_DCG_RATIO:
+		dcg = kzalloc(sizeof(*dcg), GFP_KERNEL);
+		if (!dcg) {
+			ret = -ENOMEM;
+			return ret;
+		}
+
+		ret = os02k10_ioctl(sd, cmd, dcg);
+		if (!ret) {
+			ret = copy_to_user(up, dcg, sizeof(*dcg));
+			if (ret)
+				return -EFAULT;
+		}
+		kfree(dcg);
+		break;
+	default:
+		ret = -ENOIOCTLCMD;
+		break;
+	}
+
+	return ret;
+}
+#endif
+
+static int os02k10_init_conversion_gain(struct os02k10 *os02k10, u32 cur_cg)
+{
+	int ret = 0;
+	struct i2c_client *client = os02k10->client;
+	u32 val_h = 0, val_l = 0;
+
+	if (cur_cg == GAIN_MODE_LCG) {
+		val_l = 0x08;
+		val_h = 0x10;
+	} else if (cur_cg == GAIN_MODE_HCG) {
+		val_l = 0xcb;
+		val_h = 0x00;
+	}
+	ret |= os02k10_write_reg(client,
+				 OS02K10_REG_DCG_CONVER_H,
+				 OS02K10_REG_VALUE_08BIT,
+				 val_h);
+	ret |= os02k10_write_reg(client,
+				 OS02K10_REG_DCG_CONVER_L,
+				 OS02K10_REG_VALUE_08BIT,
+				 val_l);
+	dev_dbg(&client->dev, "init conversion gain %d, (reg_h reg_l, val_h val_l)=(0x%x 0x%x, 0x%x 0x%x)\n",
+		cur_cg, OS02K10_REG_DCG_CONVER_H, OS02K10_REG_DCG_CONVER_L, val_h, val_l);
+	return ret;
+}
+
+static int __os02k10_start_stream(struct os02k10 *os02k10)
+{
+	int ret;
+
+	if (!os02k10->is_thunderboot) {
+		ret = os02k10_write_array(os02k10->client, os02k10->cur_mode->reg_list);
+		if (ret)
+			return ret;
+		/* In case these controls are set before streaming */
+		ret = __v4l2_ctrl_handler_setup(&os02k10->ctrl_handler);
+		if (ret)
+			return ret;
+		if (os02k10->has_init_exp && os02k10->cur_mode->hdr_mode != NO_HDR) {
+			ret = os02k10_ioctl(&os02k10->subdev, PREISP_CMD_SET_HDRAE_EXP,
+				&os02k10->init_hdrae_exp);
+			if (ret) {
+				dev_err(&os02k10->client->dev,
+					"init exp fail in hdr mode\n");
+				return ret;
+			}
+		}
+		os02k10_init_conversion_gain(os02k10, os02k10->long_hcg);
+	}
+	return os02k10_write_reg(os02k10->client, OS02K10_REG_CTRL_MODE,
+				 OS02K10_REG_VALUE_08BIT, OS02K10_MODE_STREAMING);
+}
+
+static int __os02k10_stop_stream(struct os02k10 *os02k10)
+{
+	os02k10->has_init_exp = false;
+	if (os02k10->is_thunderboot) {
+		os02k10->is_first_streamoff = true;
+		pm_runtime_put(&os02k10->client->dev);
+	}
+	return os02k10_write_reg(os02k10->client, OS02K10_REG_CTRL_MODE,
+				 OS02K10_REG_VALUE_08BIT, OS02K10_MODE_SW_STANDBY);
+}
+
+static int __os02k10_power_on(struct os02k10 *os02k10);
+static int os02k10_s_stream(struct v4l2_subdev *sd, int on)
+{
+	struct os02k10 *os02k10 = to_os02k10(sd);
+	struct i2c_client *client = os02k10->client;
+	int ret = 0;
+
+	mutex_lock(&os02k10->mutex);
+	on = !!on;
+	if (on == os02k10->streaming)
+		goto unlock_and_return;
+
+	if (on) {
+		if (os02k10->is_thunderboot && rkisp_tb_get_state() == RKISP_TB_NG) {
+			os02k10->is_thunderboot = false;
+			__os02k10_power_on(os02k10);
+		}
+		ret = pm_runtime_get_sync(&client->dev);
+		if (ret < 0) {
+			pm_runtime_put_noidle(&client->dev);
+			goto unlock_and_return;
+		}
+
+		ret = __os02k10_start_stream(os02k10);
+		if (ret) {
+			v4l2_err(sd, "start stream failed while write regs\n");
+			pm_runtime_put(&client->dev);
+			goto unlock_and_return;
+		}
+	} else {
+		__os02k10_stop_stream(os02k10);
+		pm_runtime_put(&client->dev);
+	}
+
+	os02k10->streaming = on;
+
+unlock_and_return:
+	mutex_unlock(&os02k10->mutex);
+
+	return ret;
+}
+
+static int os02k10_s_power(struct v4l2_subdev *sd, int on)
+{
+	struct os02k10 *os02k10 = to_os02k10(sd);
+	struct i2c_client *client = os02k10->client;
+	int ret = 0;
+
+	mutex_lock(&os02k10->mutex);
+
+	/* If the power state is not modified - no work to do. */
+	if (os02k10->power_on == !!on)
+		goto unlock_and_return;
+
+	if (on) {
+		ret = pm_runtime_get_sync(&client->dev);
+		if (ret < 0) {
+			pm_runtime_put_noidle(&client->dev);
+			goto unlock_and_return;
+		}
+		if (!os02k10->is_thunderboot) {
+			ret = os02k10_write_array(os02k10->client, os02k10_global_regs);
+			if (ret) {
+				v4l2_err(sd, "could not set init registers\n");
+				pm_runtime_put_noidle(&client->dev);
+				goto unlock_and_return;
+			}
+		}
+
+		os02k10->power_on = true;
+	} else {
+		pm_runtime_put(&client->dev);
+		os02k10->power_on = false;
+	}
+
+unlock_and_return:
+	mutex_unlock(&os02k10->mutex);
+
+	return ret;
+}
+
+/* Calculate the delay in us by clock rate and clock cycles */
+static inline u32 os02k10_cal_delay(u32 cycles)
+{
+	return DIV_ROUND_UP(cycles, OS02K10_XVCLK_FREQ / 1000 / 1000);
+}
+
+static int __os02k10_power_on(struct os02k10 *os02k10)
+{
+	int ret;
+	u32 delay_us;
+	struct device *dev = &os02k10->client->dev;
+
+	if (!IS_ERR_OR_NULL(os02k10->pins_default)) {
+		ret = pinctrl_select_state(os02k10->pinctrl,
+					   os02k10->pins_default);
+		if (ret < 0)
+			dev_err(dev, "could not set pins\n");
+	}
+	ret = clk_set_rate(os02k10->xvclk, OS02K10_XVCLK_FREQ);
+	if (ret < 0)
+		dev_warn(dev, "Failed to set xvclk rate (24MHz)\n");
+	if (clk_get_rate(os02k10->xvclk) != OS02K10_XVCLK_FREQ)
+		dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n");
+	ret = clk_prepare_enable(os02k10->xvclk);
+	if (ret < 0) {
+		dev_err(dev, "Failed to enable xvclk\n");
+		return ret;
+	}
+	if (os02k10->is_thunderboot)
+		return 0;
+
+	if (!IS_ERR(os02k10->reset_gpio))
+		gpiod_set_value_cansleep(os02k10->reset_gpio, 0);
+
+	ret = regulator_bulk_enable(OS02K10_NUM_SUPPLIES, os02k10->supplies);
+	if (ret < 0) {
+		dev_err(dev, "Failed to enable regulators\n");
+		goto disable_clk;
+	}
+
+	if (!IS_ERR(os02k10->reset_gpio))
+		gpiod_set_value_cansleep(os02k10->reset_gpio, 1);
+
+	usleep_range(500, 1000);
+	if (!IS_ERR(os02k10->pwdn_gpio))
+		gpiod_set_value_cansleep(os02k10->pwdn_gpio, 1);
+
+	if (!IS_ERR(os02k10->reset_gpio))
+		usleep_range(6000, 8000);
+	else
+		usleep_range(12000, 16000);
+
+	/* 8192 cycles prior to first SCCB transaction */
+	delay_us = os02k10_cal_delay(8192);
+	usleep_range(delay_us, delay_us * 2);
+
+	return 0;
+
+disable_clk:
+	clk_disable_unprepare(os02k10->xvclk);
+
+	return ret;
+}
+
+static void __os02k10_power_off(struct os02k10 *os02k10)
+{
+	int ret;
+	struct device *dev = &os02k10->client->dev;
+
+	clk_disable_unprepare(os02k10->xvclk);
+	if (os02k10->is_thunderboot) {
+		if (os02k10->is_first_streamoff) {
+			os02k10->is_thunderboot = false;
+			os02k10->is_first_streamoff = false;
+		} else {
+			return;
+		}
+	}
+	if (!IS_ERR(os02k10->pwdn_gpio))
+		gpiod_set_value_cansleep(os02k10->pwdn_gpio, 0);
+	if (!IS_ERR(os02k10->reset_gpio))
+		gpiod_set_value_cansleep(os02k10->reset_gpio, 0);
+	if (!IS_ERR_OR_NULL(os02k10->pins_sleep)) {
+		ret = pinctrl_select_state(os02k10->pinctrl,
+					   os02k10->pins_sleep);
+		if (ret < 0)
+			dev_dbg(dev, "could not set pins\n");
+	}
+	regulator_bulk_disable(OS02K10_NUM_SUPPLIES, os02k10->supplies);
+}
+
+static int os02k10_runtime_resume(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct os02k10 *os02k10 = to_os02k10(sd);
+
+	return __os02k10_power_on(os02k10);
+}
+
+static int os02k10_runtime_suspend(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct os02k10 *os02k10 = to_os02k10(sd);
+
+	__os02k10_power_off(os02k10);
+
+	return 0;
+}
+
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+static int os02k10_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+	struct os02k10 *os02k10 = to_os02k10(sd);
+	struct v4l2_mbus_framefmt *try_fmt =
+				v4l2_subdev_get_try_format(sd, fh->pad, 0);
+	const struct os02k10_mode *def_mode = &supported_modes[0];
+
+	mutex_lock(&os02k10->mutex);
+	/* Initialize try_fmt */
+	try_fmt->width = def_mode->width;
+	try_fmt->height = def_mode->height;
+	try_fmt->code = def_mode->bus_fmt;
+	try_fmt->field = V4L2_FIELD_NONE;
+
+	mutex_unlock(&os02k10->mutex);
+	/* No crop or compose */
+
+	return 0;
+}
+#endif
+
+static int os02k10_enum_frame_interval(struct v4l2_subdev *sd,
+				       struct v4l2_subdev_pad_config *cfg,
+				       struct v4l2_subdev_frame_interval_enum *fie)
+{
+	if (fie->index >= ARRAY_SIZE(supported_modes))
+		return -EINVAL;
+
+	fie->code = supported_modes[fie->index].bus_fmt;
+	fie->width = supported_modes[fie->index].width;
+	fie->height = supported_modes[fie->index].height;
+	fie->interval = supported_modes[fie->index].max_fps;
+	fie->reserved[0] = supported_modes[fie->index].hdr_mode;
+	return 0;
+}
+
+static const struct dev_pm_ops os02k10_pm_ops = {
+	SET_RUNTIME_PM_OPS(os02k10_runtime_suspend,
+			   os02k10_runtime_resume, NULL)
+};
+
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+static const struct v4l2_subdev_internal_ops os02k10_internal_ops = {
+	.open = os02k10_open,
+};
+#endif
+
+static const struct v4l2_subdev_core_ops os02k10_core_ops = {
+	.s_power = os02k10_s_power,
+	.ioctl = os02k10_ioctl,
+#ifdef CONFIG_COMPAT
+	.compat_ioctl32 = os02k10_compat_ioctl32,
+#endif
+};
+
+static const struct v4l2_subdev_video_ops os02k10_video_ops = {
+	.s_stream = os02k10_s_stream,
+	.g_frame_interval = os02k10_g_frame_interval,
+};
+
+static const struct v4l2_subdev_pad_ops os02k10_pad_ops = {
+	.enum_mbus_code = os02k10_enum_mbus_code,
+	.enum_frame_size = os02k10_enum_frame_sizes,
+	.enum_frame_interval = os02k10_enum_frame_interval,
+	.get_fmt = os02k10_get_fmt,
+	.set_fmt = os02k10_set_fmt,
+	.get_mbus_config = os02k10_g_mbus_config,
+};
+
+static const struct v4l2_subdev_ops os02k10_subdev_ops = {
+	.core	= &os02k10_core_ops,
+	.video	= &os02k10_video_ops,
+	.pad	= &os02k10_pad_ops,
+};
+
+static void os02k10_modify_fps_info(struct os02k10 *os02k10)
+{
+	const struct os02k10_mode *mode = os02k10->cur_mode;
+
+	os02k10->cur_fps.denominator = mode->max_fps.denominator * mode->vts_def /
+				       os02k10->cur_vts;
+}
+
+static int os02k10_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct os02k10 *os02k10 = container_of(ctrl->handler,
+					       struct os02k10, ctrl_handler);
+	struct i2c_client *client = os02k10->client;
+	u32 again = 0, dgain = 0;
+	s64 max;
+	int ret = 0;
+	u32 val = 0;
+
+	/* Propagate change of current control to all related controls */
+	switch (ctrl->id) {
+	case V4L2_CID_VBLANK:
+		/* Update max exposure while meeting expected vblanking */
+		max = os02k10->cur_mode->height + ctrl->val - 8;
+		__v4l2_ctrl_modify_range(os02k10->exposure,
+					 os02k10->exposure->minimum, max,
+					 os02k10->exposure->step,
+					 os02k10->exposure->default_value);
+		break;
+	}
+
+	if (!pm_runtime_get_if_in_use(&client->dev))
+		return 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_EXPOSURE:
+		dev_dbg(&client->dev, "set exposure value 0x%x\n", ctrl->val);
+		if (os02k10->cur_mode->hdr_mode == NO_HDR) {
+			/* 4 least significant bits of expsoure are fractional part */
+			ret = os02k10_write_reg(os02k10->client,
+						OS02K10_AEC_LONG_EXP_REG_H,
+						OS02K10_REG_VALUE_16BIT,
+						ctrl->val);
+			dev_dbg(&client->dev, "set exposure 0x%x\n", ctrl->val);
+		}
+		break;
+	case V4L2_CID_ANALOGUE_GAIN:
+		if (ctrl->val > 992) {
+			dgain = ctrl->val * 1024 / 992;
+			again = 992;
+		} else {
+			dgain = 1024;
+			again = ctrl->val;
+		}
+		dev_dbg(&client->dev, "gain %d, ag 0x%x, dg 0x%x\n",
+			ctrl->val, again, dgain);
+		ret = os02k10_write_reg(os02k10->client,
+					OS02K10_AEC_LONG_REL_GAIN_REG_H,
+					OS02K10_REG_VALUE_16BIT,
+					(again << 2) & 0xff0);
+		ret |= os02k10_write_reg(os02k10->client,
+					 OS02K10_AEC_LONG_DIG_GAIN_REG_H,
+					 OS02K10_REG_VALUE_24BIT,
+					 (dgain << 6) & 0xfffc0);
+		break;
+	case V4L2_CID_VBLANK:
+		dev_dbg(&client->dev, "set blank value 0x%x\n", ctrl->val);
+		ret = os02k10_write_reg(os02k10->client,
+					OS02K10_AEC_TIMING_VTS_REG_H,
+					OS02K10_REG_VALUE_16BIT,
+					ctrl->val + os02k10->cur_mode->height);
+		os02k10->cur_vts = ctrl->val + os02k10->cur_mode->height;
+		if (os02k10->cur_vts != os02k10->cur_mode->vts_def)
+			os02k10_modify_fps_info(os02k10);
+		break;
+	case V4L2_CID_TEST_PATTERN:
+		ret = os02k10_enable_test_pattern(os02k10, ctrl->val);
+		break;
+	case V4L2_CID_HFLIP:
+		ret = os02k10_read_reg(os02k10->client, OS02K10_FLIPMIRROR_REG,
+				       OS02K10_REG_VALUE_08BIT, &val);
+		ret |= os02k10_write_reg(os02k10->client, OS02K10_FLIPMIRROR_REG,
+					 OS02K10_REG_VALUE_08BIT,
+					 OS02K10_FETCH_MIRROR(val, ctrl->val));
+		break;
+	case V4L2_CID_VFLIP:
+		ret = os02k10_read_reg(os02k10->client, OS02K10_FLIPMIRROR_REG,
+				       OS02K10_REG_VALUE_08BIT, &val);
+		ret |= os02k10_write_reg(os02k10->client, OS02K10_FLIPMIRROR_REG,
+					 OS02K10_REG_VALUE_08BIT,
+					 OS02K10_FETCH_FLIP(val, ctrl->val));
+		break;
+	default:
+		dev_warn(&client->dev, "%s Unhandled id:0x%x, val:0x%x\n",
+			 __func__, ctrl->id, ctrl->val);
+		break;
+	}
+
+	pm_runtime_put(&client->dev);
+
+	return ret;
+}
+
+static const struct v4l2_ctrl_ops os02k10_ctrl_ops = {
+	.s_ctrl = os02k10_set_ctrl,
+};
+
+static int os02k10_initialize_controls(struct os02k10 *os02k10)
+{
+	const struct os02k10_mode *mode;
+	struct v4l2_ctrl_handler *handler;
+	s64 exposure_max, vblank_def;
+	u64 dst_pixel_rate = 0;
+	u32 h_blank;
+	int ret;
+
+	handler = &os02k10->ctrl_handler;
+	mode = os02k10->cur_mode;
+	ret = v4l2_ctrl_handler_init(handler, 9);
+	if (ret)
+		return ret;
+	handler->lock = &os02k10->mutex;
+
+	os02k10->link_freq = v4l2_ctrl_new_int_menu(handler, NULL,
+				V4L2_CID_LINK_FREQ,
+				ARRAY_SIZE(link_freq_menu_items) - 1, 0,
+				link_freq_menu_items);
+	__v4l2_ctrl_s_ctrl(os02k10->link_freq, mode->mipi_freq_idx);
+
+	if (mode->mipi_freq_idx == 0)
+		dst_pixel_rate = OS02K10_LINK_FREQ_360M;
+	else if (mode->mipi_freq_idx == 1)
+		dst_pixel_rate = OS02K10_LINK_FREQ_480M;
+
+	os02k10->pixel_rate = v4l2_ctrl_new_std(handler, NULL,
+						V4L2_CID_PIXEL_RATE, 0,
+						OS02K10_LINK_FREQ_480M,
+						1, dst_pixel_rate);
+
+	h_blank = mode->hts_def - mode->width;
+	os02k10->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK,
+					    h_blank, h_blank, 1, h_blank);
+	if (os02k10->hblank)
+		os02k10->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+	vblank_def = mode->vts_def - mode->height;
+	os02k10->vblank = v4l2_ctrl_new_std(handler, &os02k10_ctrl_ops,
+					    V4L2_CID_VBLANK, vblank_def,
+					    OS02K10_VTS_MAX - mode->height,
+					    1, vblank_def);
+	exposure_max = mode->vts_def - 8;
+	os02k10->exposure = v4l2_ctrl_new_std(handler, &os02k10_ctrl_ops,
+					      V4L2_CID_EXPOSURE, OS02K10_EXPOSURE_MIN,
+					      exposure_max, OS02K10_EXPOSURE_STEP,
+					      mode->exp_def);
+	os02k10->anal_gain = v4l2_ctrl_new_std(handler, &os02k10_ctrl_ops,
+					       V4L2_CID_ANALOGUE_GAIN, OS02K10_GAIN_MIN,
+					       OS02K10_GAIN_MAX, OS02K10_GAIN_STEP,
+					       OS02K10_GAIN_DEFAULT);
+	os02k10->test_pattern = v4l2_ctrl_new_std_menu_items(handler,
+							    &os02k10_ctrl_ops,
+					V4L2_CID_TEST_PATTERN,
+					ARRAY_SIZE(os02k10_test_pattern_menu) - 1,
+					0, 0, os02k10_test_pattern_menu);
+	v4l2_ctrl_new_std(handler, &os02k10_ctrl_ops,
+				V4L2_CID_HFLIP, 0, 1, 1, 0);
+
+	v4l2_ctrl_new_std(handler, &os02k10_ctrl_ops,
+				V4L2_CID_VFLIP, 0, 1, 1, 0);
+
+	if (handler->error) {
+		ret = handler->error;
+		dev_err(&os02k10->client->dev,
+			"Failed to init controls(%d)\n", ret);
+		goto err_free_handler;
+	}
+
+	os02k10->subdev.ctrl_handler = handler;
+	os02k10->has_init_exp = false;
+	os02k10->cur_fps = mode->max_fps;
+	os02k10->long_hcg = false;
+	os02k10->middle_hcg = false;
+	os02k10->short_hcg = false;
+	return 0;
+
+err_free_handler:
+	v4l2_ctrl_handler_free(handler);
+
+	return ret;
+}
+
+static int os02k10_check_sensor_id(struct os02k10 *os02k10,
+				   struct i2c_client *client)
+{
+	struct device *dev = &os02k10->client->dev;
+	u32 id = 0;
+	int ret;
+
+	if (os02k10->is_thunderboot) {
+		dev_info(dev, "Enable thunderboot mode, skip sensor id check\n");
+		return 0;
+	}
+
+	ret = os02k10_read_reg(client, OS02K10_REG_CHIP_ID,
+			       OS02K10_REG_VALUE_16BIT, &id);
+	if (id != CHIP_ID) {
+		dev_err(dev, "Unexpected sensor id(%06x), ret(%d)\n", id, ret);
+		return -ENODEV;
+	}
+
+	dev_info(dev, "Detected OS%06x sensor\n", CHIP_ID);
+
+	return 0;
+}
+
+static int os02k10_configure_regulators(struct os02k10 *os02k10)
+{
+	unsigned int i;
+
+	for (i = 0; i < OS02K10_NUM_SUPPLIES; i++)
+		os02k10->supplies[i].supply = os02k10_supply_names[i];
+
+	return devm_regulator_bulk_get(&os02k10->client->dev,
+				       OS02K10_NUM_SUPPLIES,
+				       os02k10->supplies);
+}
+
+static int os02k10_get_dcg_ratio(struct os02k10 *os02k10)
+{
+	struct device *dev = &os02k10->client->dev;
+	u32 val = 0;
+	int ret = 0;
+
+	if (os02k10->is_thunderboot) {
+		ret = os02k10_read_reg(os02k10->client, OS02K10_REG_DCG_RATIO,
+					OS02K10_REG_VALUE_16BIT, &val);
+	} else {
+		ret = os02k10_write_reg(os02k10->client, OS02K10_REG_CTRL_MODE,
+					OS02K10_REG_VALUE_08BIT, OS02K10_MODE_STREAMING);
+		usleep_range(5000, 6000);
+		ret |= os02k10_read_reg(os02k10->client, OS02K10_REG_DCG_RATIO,
+					OS02K10_REG_VALUE_16BIT, &val);
+		ret |= os02k10_write_reg(os02k10->client, OS02K10_REG_CTRL_MODE,
+					OS02K10_REG_VALUE_08BIT, OS02K10_MODE_SW_STANDBY);
+	}
+
+	if (ret != 0 || val == 0) {
+		os02k10->dcg_ratio = 0;
+		dev_err(dev, "get dcg ratio fail, ret %d, dcg ratio %d\n", ret, val);
+	} else {
+		os02k10->dcg_ratio = val;
+		dev_info(dev, "get dcg ratio reg val 0x%04x\n", val);
+	}
+
+	return ret;
+}
+
+static int os02k10_probe(struct i2c_client *client,
+			 const struct i2c_device_id *id)
+{
+	struct device *dev = &client->dev;
+	struct device_node *node = dev->of_node;
+	struct os02k10 *os02k10;
+	struct v4l2_subdev *sd;
+	char facing[2];
+	int ret;
+	u32 i, hdr_mode = 0;
+
+	dev_info(dev, "driver version: %02x.%02x.%02x",
+		 DRIVER_VERSION >> 16,
+		 (DRIVER_VERSION & 0xff00) >> 8,
+		 DRIVER_VERSION & 0x00ff);
+
+	os02k10 = devm_kzalloc(dev, sizeof(*os02k10), GFP_KERNEL);
+	if (!os02k10)
+		return -ENOMEM;
+
+	of_property_read_u32(node, OF_CAMERA_HDR_MODE, &hdr_mode);
+	ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX,
+				   &os02k10->module_index);
+	ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING,
+				       &os02k10->module_facing);
+	ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME,
+				       &os02k10->module_name);
+	ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME,
+				       &os02k10->len_name);
+	if (ret) {
+		dev_err(dev, "could not get module information!\n");
+		return -EINVAL;
+	}
+	os02k10->is_thunderboot = IS_ENABLED(CONFIG_VIDEO_ROCKCHIP_THUNDER_BOOT_ISP);
+	dev_info(dev, "is_thunderboot: %d\n", os02k10->is_thunderboot);
+	os02k10->client = client;
+	for (i = 0; i < ARRAY_SIZE(supported_modes); i++) {
+		if (hdr_mode == supported_modes[i].hdr_mode) {
+			os02k10->cur_mode = &supported_modes[i];
+			break;
+		}
+	}
+	if (i == ARRAY_SIZE(supported_modes))
+		os02k10->cur_mode = &supported_modes[0];
+
+	os02k10->xvclk = devm_clk_get(dev, "xvclk");
+	if (IS_ERR(os02k10->xvclk)) {
+		dev_err(dev, "Failed to get xvclk\n");
+		return -EINVAL;
+	}
+
+	os02k10->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_ASIS);
+	if (IS_ERR(os02k10->reset_gpio))
+		dev_warn(dev, "Failed to get reset-gpios\n");
+
+	os02k10->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_ASIS);
+	if (IS_ERR(os02k10->pwdn_gpio))
+		dev_warn(dev, "Failed to get pwdn-gpios\n");
+
+	os02k10->pinctrl = devm_pinctrl_get(dev);
+	if (!IS_ERR(os02k10->pinctrl)) {
+		os02k10->pins_default =
+			pinctrl_lookup_state(os02k10->pinctrl,
+					     OF_CAMERA_PINCTRL_STATE_DEFAULT);
+		if (IS_ERR(os02k10->pins_default))
+			dev_err(dev, "could not get default pinstate\n");
+
+		os02k10->pins_sleep =
+			pinctrl_lookup_state(os02k10->pinctrl,
+					     OF_CAMERA_PINCTRL_STATE_SLEEP);
+		if (IS_ERR(os02k10->pins_sleep))
+			dev_err(dev, "could not get sleep pinstate\n");
+	} else {
+		dev_err(dev, "no pinctrl\n");
+	}
+
+	ret = os02k10_configure_regulators(os02k10);
+	if (ret) {
+		dev_err(dev, "Failed to get power regulators\n");
+		return ret;
+	}
+
+	mutex_init(&os02k10->mutex);
+
+	sd = &os02k10->subdev;
+	v4l2_i2c_subdev_init(sd, client, &os02k10_subdev_ops);
+	ret = os02k10_initialize_controls(os02k10);
+	if (ret)
+		goto err_destroy_mutex;
+
+	ret = __os02k10_power_on(os02k10);
+	if (ret)
+		goto err_free_handler;
+
+	ret = os02k10_check_sensor_id(os02k10, client);
+	if (ret)
+		goto err_power_off;
+
+	ret = os02k10_get_dcg_ratio(os02k10);
+	if (ret)
+		dev_warn(dev, "get dcg ratio failed\n");
+
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+	sd->internal_ops = &os02k10_internal_ops;
+	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
+		     V4L2_SUBDEV_FL_HAS_EVENTS;
+#endif
+#if defined(CONFIG_MEDIA_CONTROLLER)
+	os02k10->pad.flags = MEDIA_PAD_FL_SOURCE;
+	sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
+	ret = media_entity_pads_init(&sd->entity, 1, &os02k10->pad);
+	if (ret < 0)
+		goto err_power_off;
+#endif
+
+	memset(facing, 0, sizeof(facing));
+	if (strcmp(os02k10->module_facing, "back") == 0)
+		facing[0] = 'b';
+	else
+		facing[0] = 'f';
+
+	snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s",
+		 os02k10->module_index, facing,
+		 OS02K10_NAME, dev_name(sd->dev));
+	ret = v4l2_async_register_subdev_sensor_common(sd);
+	if (ret) {
+		dev_err(dev, "v4l2 async register subdev failed\n");
+		goto err_clean_entity;
+	}
+
+	pm_runtime_set_active(dev);
+	pm_runtime_enable(dev);
+	if (os02k10->is_thunderboot)
+		pm_runtime_get_sync(dev);
+	else
+		pm_runtime_idle(dev);
+
+	return 0;
+
+err_clean_entity:
+#if defined(CONFIG_MEDIA_CONTROLLER)
+	media_entity_cleanup(&sd->entity);
+#endif
+err_power_off:
+	__os02k10_power_off(os02k10);
+err_free_handler:
+	v4l2_ctrl_handler_free(&os02k10->ctrl_handler);
+err_destroy_mutex:
+	mutex_destroy(&os02k10->mutex);
+
+	return ret;
+}
+
+static int os02k10_remove(struct i2c_client *client)
+{
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct os02k10 *os02k10 = to_os02k10(sd);
+
+	v4l2_async_unregister_subdev(sd);
+#if defined(CONFIG_MEDIA_CONTROLLER)
+	media_entity_cleanup(&sd->entity);
+#endif
+	v4l2_ctrl_handler_free(&os02k10->ctrl_handler);
+	mutex_destroy(&os02k10->mutex);
+
+	pm_runtime_disable(&client->dev);
+	if (!pm_runtime_status_suspended(&client->dev))
+		__os02k10_power_off(os02k10);
+	pm_runtime_set_suspended(&client->dev);
+
+	return 0;
+}
+
+#if IS_ENABLED(CONFIG_OF)
+static const struct of_device_id os02k10_of_match[] = {
+	{ .compatible = "ovti,os02k10" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, os02k10_of_match);
+#endif
+
+static const struct i2c_device_id os02k10_match_id[] = {
+	{ "ovti,os02k10", 0 },
+	{ },
+};
+
+static struct i2c_driver os02k10_i2c_driver = {
+	.driver = {
+		.name = OS02K10_NAME,
+		.pm = &os02k10_pm_ops,
+		.of_match_table = of_match_ptr(os02k10_of_match),
+	},
+	.probe		= &os02k10_probe,
+	.remove		= &os02k10_remove,
+	.id_table	= os02k10_match_id,
+};
+
+static int __init sensor_mod_init(void)
+{
+	return i2c_add_driver(&os02k10_i2c_driver);
+}
+
+static void __exit sensor_mod_exit(void)
+{
+	i2c_del_driver(&os02k10_i2c_driver);
+}
+
+#if defined(CONFIG_VIDEO_ROCKCHIP_THUNDER_BOOT_ISP) && !defined(CONFIG_INITCALL_ASYNC)
+subsys_initcall(sensor_mod_init);
+#else
+device_initcall_sync(sensor_mod_init);
+#endif
+module_exit(sensor_mod_exit);
+
+MODULE_DESCRIPTION("ovti os02k10 sensor driver");
+MODULE_LICENSE("GPL");
diff --git a/kernel/drivers/media/i2c/otp_eeprom.c b/kernel/drivers/media/i2c/otp_eeprom.c
index 2fb123c..315220e 100644
--- a/kernel/drivers/media/i2c/otp_eeprom.c
+++ b/kernel/drivers/media/i2c/otp_eeprom.c
@@ -649,6 +649,11 @@
 	checksum += otp_ptr->pdaf_data.dccmap_checksum;
 	base_addr += 1;
 
+	ret |= read_reg_otp(client, base_addr,
+		2, &otp_ptr->pdaf_data.pd_offset);
+	checksum += otp_ptr->pdaf_data.pd_offset;
+	base_addr += 2;
+
 	memset(pdaf_buf, 0, RK_PDAF_RESERVED_SIZE);
 	ret |= read_reg_otp_buf(client, base_addr,
 	       RK_PDAF_RESERVED_SIZE, pdaf_buf);
@@ -931,7 +936,7 @@
 			seq_printf(p, "flag=%d;\n", dev->otp->pdaf_data.flag);
 			seq_printf(p, "gainmap_width=%d;\n", gainmap_w);
 			seq_printf(p, "gainmap_height=%d;\n", gainmap_h);
-
+			seq_printf(p, "pd_offset=%d\n", dev->otp->pdaf_data.pd_offset);
 			seq_printf(p, "gainmap_table=\n");
 			for (i = 0; i < gainmap_h; i++) {
 				for (j = 0; j < gainmap_w; j++) {
diff --git a/kernel/drivers/media/i2c/otp_eeprom.h b/kernel/drivers/media/i2c/otp_eeprom.h
index 503d277..f284e6d 100644
--- a/kernel/drivers/media/i2c/otp_eeprom.h
+++ b/kernel/drivers/media/i2c/otp_eeprom.h
@@ -56,7 +56,7 @@
 #define RK_LSC_RESERVED_SIZE	0x0020
 #define RK_GAINMAP_SIZE		0x0800
 #define RK_DCCMAP_SIZE		0x0200
-#define RK_PDAF_RESERVED_SIZE	0x0020
+#define RK_PDAF_RESERVED_SIZE	0x001e
 #define RK_AF_RESERVED_SIZE	0x0014
 #define RKOTP_MAX_MODULE	0x0008
 
@@ -151,6 +151,7 @@
 	u32 dccmap_height;
 	u32 dccmap[RK_DCCMAP_SIZE];
 	u32 dccmap_checksum;
+	u32 pd_offset;
 	u32 checksum;
 	u32 size;
 };
diff --git a/kernel/drivers/media/i2c/ov50c40.c b/kernel/drivers/media/i2c/ov50c40.c
index 55f5b82..040dc7f 100644
--- a/kernel/drivers/media/i2c/ov50c40.c
+++ b/kernel/drivers/media/i2c/ov50c40.c
@@ -6203,6 +6203,7 @@
 		inf->pdaf.flag = 1;
 		inf->pdaf.gainmap_width = otp->pdaf_data.gainmap_width;
 		inf->pdaf.gainmap_height = otp->pdaf_data.gainmap_height;
+		inf->pdaf.pd_offset = otp->pdaf_data.pd_offset;
 		inf->pdaf.dcc_mode = otp->pdaf_data.dcc_mode;
 		inf->pdaf.dcc_dir = otp->pdaf_data.dcc_dir;
 		inf->pdaf.dccmap_width = otp->pdaf_data.dccmap_width;
diff --git a/kernel/drivers/media/i2c/rk628/Kconfig b/kernel/drivers/media/i2c/rk628/Kconfig
index 98894a0..b433e50 100644
--- a/kernel/drivers/media/i2c/rk628/Kconfig
+++ b/kernel/drivers/media/i2c/rk628/Kconfig
@@ -13,6 +13,7 @@
 	select HDMI
 	select V4L2_FWNODE
 	select VIDEO_RK628
+	select VIDEO_ROCKCHIP_HDMIRX_CLASS
 	help
 	  Support for the Rockchip RK628 HDMI to MIPI CSI-2 bridge.
 
diff --git a/kernel/drivers/media/i2c/rk628/rk628_bt1120_v4l2.c b/kernel/drivers/media/i2c/rk628/rk628_bt1120_v4l2.c
index e427176..96cc08f 100644
--- a/kernel/drivers/media/i2c/rk628/rk628_bt1120_v4l2.c
+++ b/kernel/drivers/media/i2c/rk628/rk628_bt1120_v4l2.c
@@ -491,6 +491,7 @@
 	mutex_lock(&bt1120->confctl_mutex);
 	bt1120->avi_rcv_rdy = false;
 	plugin = tx_5v_power_present(sd);
+	v4l2_ctrl_s_ctrl(bt1120->detect_tx_5v_ctrl, plugin);
 	v4l2_dbg(1, debug, sd, "%s: 5v_det:%d\n", __func__, plugin);
 	if (plugin) {
 		rk628_set_io_func_to_vop(bt1120->rk628);
@@ -1250,21 +1251,6 @@
 	return 0;
 }
 
-static int rk628_bt1120_get_ctrl(struct v4l2_ctrl *ctrl)
-{
-	int ret = -1;
-	struct rk628_bt1120 *bt1120 = container_of(ctrl->handler, struct rk628_bt1120,
-			hdl);
-	struct v4l2_subdev *sd = &(bt1120->sd);
-
-	if (ctrl->id == V4L2_CID_DV_RX_POWER_PRESENT) {
-		ret = tx_5v_power_present(sd);
-		*ctrl->p_new.p_s32 = ret;
-	}
-
-	return ret;
-}
-
 static int rk628_bt1120_enum_frame_sizes(struct v4l2_subdev *sd,
 				   struct v4l2_subdev_pad_config *cfg,
 				   struct v4l2_subdev_frame_size_enum *fse)
@@ -1542,6 +1528,9 @@
 	case RKMODULE_GET_MODULE_INFO:
 		rk628_bt1120_get_module_inf(bt1120, (struct rkmodule_inf *)arg);
 		break;
+	case RKMODULE_GET_HDMI_MODE:
+		*(int *)arg = RKMODULE_HDMIIN_MODE;
+		break;
 	default:
 		ret = -ENOIOCTLCMD;
 		break;
@@ -1557,6 +1546,7 @@
 	void __user *up = compat_ptr(arg);
 	struct rkmodule_inf *inf;
 	long ret;
+	int *seq;
 
 	switch (cmd) {
 	case RKMODULE_GET_MODULE_INFO:
@@ -1574,7 +1564,21 @@
 		}
 		kfree(inf);
 		break;
+	case RKMODULE_GET_HDMI_MODE:
+		seq = kzalloc(sizeof(*seq), GFP_KERNEL);
+		if (!seq) {
+			ret = -ENOMEM;
+			return ret;
+		}
 
+		ret = rk628_bt1120_ioctl(sd, cmd, seq);
+		if (!ret) {
+			ret = copy_to_user(up, seq, sizeof(*seq));
+			if (ret)
+				ret = -EFAULT;
+		}
+		kfree(seq);
+		break;
 	default:
 		ret = -ENOIOCTLCMD;
 		break;
@@ -1611,10 +1615,6 @@
 	.open = bt1120_open,
 };
 #endif
-
-static const struct v4l2_ctrl_ops rk628_bt1120_ctrl_ops = {
-	.g_volatile_ctrl = rk628_bt1120_get_ctrl,
-};
 
 static const struct v4l2_subdev_core_ops rk628_bt1120_core_ops = {
 	.interrupt_service_routine = rk628_bt1120_isr,
@@ -1712,21 +1712,21 @@
 	if (IS_ERR(bt1120->enable_gpio)) {
 		ret = PTR_ERR(bt1120->enable_gpio);
 		dev_err(dev, "failed to request enable GPIO: %d\n", ret);
-		return ret;
+		goto clk_put;
 	}
 
 	bt1120->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
 	if (IS_ERR(bt1120->reset_gpio)) {
 		ret = PTR_ERR(bt1120->reset_gpio);
 		dev_err(dev, "failed to request reset GPIO: %d\n", ret);
-		return ret;
+		goto clk_put;
 	}
 
 	bt1120->power_gpio = devm_gpiod_get_optional(dev, "power", GPIOD_OUT_HIGH);
 	if (IS_ERR(bt1120->power_gpio)) {
 		dev_err(dev, "failed to get power gpio\n");
 		ret = PTR_ERR(bt1120->power_gpio);
-		return ret;
+		goto clk_put;
 	}
 
 	bt1120->plugin_det_gpio = devm_gpiod_get_optional(dev, "plugin-det",
@@ -1734,7 +1734,7 @@
 	if (IS_ERR(bt1120->plugin_det_gpio)) {
 		dev_err(dev, "failed to get hdmirx det gpio\n");
 		ret = PTR_ERR(bt1120->plugin_det_gpio);
-		return ret;
+		goto clk_put;
 	}
 
 	if (bt1120->enable_gpio) {
@@ -1768,14 +1768,15 @@
 	ep = of_graph_get_next_endpoint(dev->of_node, NULL);
 	if (!ep) {
 		dev_err(dev, "missing endpoint node\n");
-		return -EINVAL;
+		ret = -EINVAL;
+		goto clk_put;
 	}
 
 	ret = v4l2_fwnode_endpoint_alloc_parse(of_fwnode_handle(ep), &endpoint);
+	of_node_put(ep);
 	if (ret) {
 		dev_err(dev, "failed to parse endpoint\n");
-		of_node_put(ep);
-		return ret;
+		goto clk_put;
 	}
 
 	bt1120->enable_hdcp = hdcp1x_enable;
@@ -1791,6 +1792,9 @@
 	ret = 0;
 
 	v4l2_fwnode_endpoint_free(&endpoint);
+
+clk_put:
+	clk_disable_unprepare(bt1120->soc_24M);
 
 	return ret;
 }
@@ -1872,10 +1876,8 @@
 			V4L2_CID_PIXEL_RATE, 0, RK628_CSI_PIXEL_RATE_HIGH, 1,
 			RK628_CSI_PIXEL_RATE_HIGH);
 	bt1120->detect_tx_5v_ctrl = v4l2_ctrl_new_std(&bt1120->hdl,
-			&rk628_bt1120_ctrl_ops, V4L2_CID_DV_RX_POWER_PRESENT,
+			NULL, V4L2_CID_DV_RX_POWER_PRESENT,
 			0, 1, 0, 0);
-	if (bt1120->detect_tx_5v_ctrl)
-		bt1120->detect_tx_5v_ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
 
 	/* custom controls */
 	bt1120->audio_sampling_rate_ctrl = v4l2_ctrl_new_custom(&bt1120->hdl,
diff --git a/kernel/drivers/media/i2c/rk628/rk628_csi_v4l2.c b/kernel/drivers/media/i2c/rk628/rk628_csi_v4l2.c
index d8a39ca..7cb20b7 100644
--- a/kernel/drivers/media/i2c/rk628/rk628_csi_v4l2.c
+++ b/kernel/drivers/media/i2c/rk628/rk628_csi_v4l2.c
@@ -26,6 +26,7 @@
 #include <linux/version.h>
 #include <linux/videodev2.h>
 #include <linux/workqueue.h>
+#include <linux/rk_hdmirx_class.h>
 #include <media/v4l2-controls_rockchip.h>
 #include <media/v4l2-ctrls.h>
 #include <media/v4l2-device.h>
@@ -54,10 +55,10 @@
 #define EDID_BLOCK_SIZE			128
 
 #define RK628_CSI_LINK_FREQ_LOW		350000000
-#define RK628_CSI_LINK_FREQ_HIGH	400000000
+#define RK628_CSI_LINK_FREQ_HIGH	600000000
 #define RK628_CSI_PIXEL_RATE_LOW	400000000
 #define RK628_CSI_PIXEL_RATE_HIGH	600000000
-#define MIPI_DATARATE_MBPS_LOW		750
+#define MIPI_DATARATE_MBPS_LOW		700
 #define MIPI_DATARATE_MBPS_HIGH		1250
 
 #define POLL_INTERVAL_MS		1000
@@ -126,12 +127,14 @@
 	bool hpd_output_inverted;
 	bool avi_rcv_rdy;
 	bool vid_ints_en;
+	bool continues_clk;
 	struct rk628_hdcp hdcp;
 	bool i2s_enable_default;
 	HAUDINFO audio_info;
 	struct rk628_combtxphy *txphy;
 	struct rk628_dsi dsi;
 	const struct rk628_plat_data *plat_data;
+	struct device *classdev;
 };
 
 struct rk628_csi_mode {
@@ -152,7 +155,7 @@
 	.type = V4L2_DV_BT_656_1120,
 	/* keep this initialization for compatibility with GCC < 4.4.6 */
 	.reserved = { 0 },
-	V4L2_INIT_BT_TIMINGS(1, 10000, 1, 10000, 0, 400000000,
+	V4L2_INIT_BT_TIMINGS(1, 10000, 1, 10000, 0, 600000000,
 			V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT |
 			V4L2_DV_BT_STD_GTF | V4L2_DV_BT_STD_CVT,
 			V4L2_DV_BT_CAP_PROGRESSIVE | V4L2_DV_BT_CAP_INTERLACED |
@@ -194,6 +197,17 @@
 	0x16, 0x20, 0x58, 0x2C, 0x25, 0x00, 0xC0, 0x6C,
 	0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00,
 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC1,
+};
+
+static struct rkmodule_csi_dphy_param rk3588_dcphy_param = {
+	.vendor = PHY_VENDOR_SAMSUNG,
+	.lp_vol_ref = 0,
+	.lp_hys_sw = {3, 0, 0, 0},
+	.lp_escclk_pol_sel = {1, 0, 0, 0},
+	.skew_data_cal_clk = {0, 3, 3, 3},
+	.clk_hs_term_sel = 2,
+	.data_hs_term_sel = {2, 2, 2, 2},
+	.reserved = {0},
 };
 
 static const struct rk628_csi_mode supported_modes[] = {
@@ -492,6 +506,7 @@
 	mutex_lock(&csi->confctl_mutex);
 	csi->avi_rcv_rdy = false;
 	plugin = tx_5v_power_present(sd);
+	v4l2_ctrl_s_ctrl(csi->detect_tx_5v_ctrl, plugin);
 	v4l2_dbg(1, debug, sd, "%s: 5v_det:%d\n", __func__, plugin);
 	if (plugin) {
 		rk628_csi_enable_interrupts(sd, false);
@@ -872,7 +887,16 @@
 			BYPASS_SELECT(1));
 	rk628_i2c_write(csi->rk628, CSITX_CONFIG_DONE, CONFIG_DONE_IMD);
 	rk628_i2c_write(csi->rk628, CSITX_SYS_CTRL2, VOP_WHOLE_FRM_EN | VSYNC_ENABLE);
-	rk628_i2c_update_bits(csi->rk628, CSITX_SYS_CTRL3_IMD,
+	if (csi->continues_clk)
+		rk628_i2c_update_bits(csi->rk628, CSITX_SYS_CTRL3_IMD,
+			CONT_MODE_CLK_CLR_MASK |
+			CONT_MODE_CLK_SET_MASK |
+			NON_CONTINUOUS_MODE_MASK,
+			CONT_MODE_CLK_CLR(0) |
+			CONT_MODE_CLK_SET(1) |
+			NON_CONTINUOUS_MODE(0));
+	else
+		rk628_i2c_update_bits(csi->rk628, CSITX_SYS_CTRL3_IMD,
 			CONT_MODE_CLK_CLR_MASK |
 			CONT_MODE_CLK_SET_MASK |
 			NON_CONTINUOUS_MODE_MASK,
@@ -1476,21 +1500,6 @@
 	return 0;
 }
 
-static int rk628_csi_get_ctrl(struct v4l2_ctrl *ctrl)
-{
-	int ret = -1;
-	struct rk628_csi *csi = container_of(ctrl->handler, struct rk628_csi,
-			hdl);
-	struct v4l2_subdev *sd = &(csi->sd);
-
-	if (ctrl->id == V4L2_CID_DV_RX_POWER_PRESENT) {
-		ret = tx_5v_power_present(sd);
-		*ctrl->p_new.p_s32 = ret;
-	}
-
-	return ret;
-}
-
 static int rk628_csi_enum_frame_sizes(struct v4l2_subdev *sd,
 				   struct v4l2_subdev_pad_config *cfg,
 				   struct v4l2_subdev_frame_size_enum *fse)
@@ -1541,6 +1550,25 @@
 	format->format.field = csi->timings.bt.interlaced ?
 		V4L2_FIELD_INTERLACED : V4L2_FIELD_NONE;
 	mutex_unlock(&csi->confctl_mutex);
+
+	if (((csi->timings.bt.width == 3840) && (csi->timings.bt.height == 2160)) ||
+		csi->csi_lanes_in_use <= 2) {
+		v4l2_dbg(1, debug, sd,
+			"%s res wxh:%dx%d, link freq:%llu, pixrate:%u\n",
+			__func__, csi->timings.bt.width, csi->timings.bt.height,
+			link_freq_menu_items[1], RK628_CSI_PIXEL_RATE_HIGH);
+		__v4l2_ctrl_s_ctrl(csi->link_freq, 1);
+		__v4l2_ctrl_s_ctrl_int64(csi->pixel_rate,
+			RK628_CSI_PIXEL_RATE_HIGH);
+	} else {
+		v4l2_dbg(1, debug, sd,
+			"%s res wxh:%dx%d, link freq:%llu, pixrate:%u\n",
+			__func__, csi->timings.bt.width, csi->timings.bt.height,
+			link_freq_menu_items[0], RK628_CSI_PIXEL_RATE_LOW);
+		__v4l2_ctrl_s_ctrl(csi->link_freq, 0);
+		__v4l2_ctrl_s_ctrl_int64(csi->pixel_rate,
+			RK628_CSI_PIXEL_RATE_LOW);
+	}
 
 	v4l2_dbg(1, debug, sd, "%s: fmt code:%d, w:%d, h:%d, field code:%d\n",
 			__func__, format->format.code, format->format.width,
@@ -1614,24 +1642,6 @@
 	csi->mbus_fmt_code = format->format.code;
 	mode = rk628_csi_find_best_fit(format);
 	csi->cur_mode = mode;
-
-	if ((mode->width == 3840) && (mode->height == 2160)) {
-		v4l2_dbg(1, debug, sd,
-			"%s res wxh:%dx%d, link freq:%llu, pixrate:%u\n",
-			__func__, mode->width, mode->height,
-			link_freq_menu_items[1], RK628_CSI_PIXEL_RATE_HIGH);
-		__v4l2_ctrl_s_ctrl(csi->link_freq, 1);
-		__v4l2_ctrl_s_ctrl_int64(csi->pixel_rate,
-			RK628_CSI_PIXEL_RATE_HIGH);
-	} else {
-		v4l2_dbg(1, debug, sd,
-			"%s res wxh:%dx%d, link freq:%llu, pixrate:%u\n",
-			__func__, mode->width, mode->height,
-			link_freq_menu_items[0], RK628_CSI_PIXEL_RATE_LOW);
-		__v4l2_ctrl_s_ctrl(csi->link_freq, 0);
-		__v4l2_ctrl_s_ctrl_int64(csi->pixel_rate,
-			RK628_CSI_PIXEL_RATE_LOW);
-	}
 
 	enable_stream(sd, false);
 
@@ -1778,10 +1788,28 @@
 {
 	struct rk628_csi *csi = to_csi(sd);
 	long ret = 0;
+	struct rkmodule_csi_dphy_param *dphy_param;
 
 	switch (cmd) {
 	case RKMODULE_GET_MODULE_INFO:
 		rk628_csi_get_module_inf(csi, (struct rkmodule_inf *)arg);
+		break;
+	case RKMODULE_GET_HDMI_MODE:
+		*(int *)arg = RKMODULE_HDMIIN_MODE;
+		break;
+	case RKMODULE_SET_CSI_DPHY_PARAM:
+		dphy_param = (struct rkmodule_csi_dphy_param *)arg;
+		if (dphy_param->vendor == PHY_VENDOR_SAMSUNG)
+			rk3588_dcphy_param = *dphy_param;
+		v4l2_dbg(1, debug, sd,
+			"sensor set dphy param\n");
+		break;
+	case RKMODULE_GET_CSI_DPHY_PARAM:
+		dphy_param = (struct rkmodule_csi_dphy_param *)arg;
+		*dphy_param = rk3588_dcphy_param;
+
+		v4l2_dbg(1, debug, sd,
+			"sensor get dphy param\n");
 		break;
 	default:
 		ret = -ENOIOCTLCMD;
@@ -1811,6 +1839,10 @@
 	rk628_txphy_set_bus_width(csi->rk628, bus_width);
 	rk628_txphy_set_mode(csi->rk628, PHY_MODE_VIDEO_MIPI);
 
+	if (csi->lane_mbps == MIPI_DATARATE_MBPS_HIGH)
+		mipi_dphy_init_hsmanual(csi->rk628, true);
+	else
+		mipi_dphy_init_hsmanual(csi->rk628, false);
 	mipi_dphy_init_hsfreqrange(csi->rk628, csi->lane_mbps);
 	usleep_range(1500, 2000);
 	rk628_txphy_power_on(csi->rk628);
@@ -1840,6 +1872,8 @@
 	void __user *up = compat_ptr(arg);
 	struct rkmodule_inf *inf;
 	long ret;
+	int *seq;
+	struct rkmodule_csi_dphy_param *dphy_param;
 
 	switch (cmd) {
 	case RKMODULE_GET_MODULE_INFO:
@@ -1857,7 +1891,50 @@
 		}
 		kfree(inf);
 		break;
+	case RKMODULE_GET_HDMI_MODE:
+		seq = kzalloc(sizeof(*seq), GFP_KERNEL);
+		if (!seq) {
+			ret = -ENOMEM;
+			return ret;
+		}
 
+		ret = rk628_csi_ioctl(sd, cmd, seq);
+		if (!ret) {
+			ret = copy_to_user(up, seq, sizeof(*seq));
+			if (ret)
+				ret = -EFAULT;
+		}
+		kfree(seq);
+		break;
+	case RKMODULE_SET_CSI_DPHY_PARAM:
+		dphy_param = kzalloc(sizeof(*dphy_param), GFP_KERNEL);
+		if (!dphy_param) {
+			ret = -ENOMEM;
+			return ret;
+		}
+
+		ret = copy_from_user(dphy_param, up, sizeof(*dphy_param));
+		if (!ret)
+			ret = rk628_csi_ioctl(sd, cmd, dphy_param);
+		else
+			ret = -EFAULT;
+		kfree(dphy_param);
+		break;
+	case RKMODULE_GET_CSI_DPHY_PARAM:
+		dphy_param = kzalloc(sizeof(*dphy_param), GFP_KERNEL);
+		if (!dphy_param) {
+			ret = -ENOMEM;
+			return ret;
+		}
+
+		ret = rk628_csi_ioctl(sd, cmd, dphy_param);
+		if (!ret) {
+			ret = copy_to_user(up, dphy_param, sizeof(*dphy_param));
+			if (ret)
+				ret = -EFAULT;
+		}
+		kfree(dphy_param);
+		break;
 	default:
 		ret = -ENOIOCTLCMD;
 		break;
@@ -1866,10 +1943,6 @@
 	return ret;
 }
 #endif
-
-static const struct v4l2_ctrl_ops rk628_csi_ctrl_ops = {
-	.g_volatile_ctrl = rk628_csi_get_ctrl,
-};
 
 static const struct v4l2_subdev_core_ops rk628_csi_core_ops = {
 	.interrupt_service_routine = rk628_csi_isr,
@@ -1971,6 +2044,7 @@
 	int ret = -EINVAL;
 	bool hdcp1x_enable = false, i2s_enable_default = false;
 	bool scaler_en = false;
+	bool continues_clk = false;
 
 	csi->soc_24M = devm_clk_get(dev, "soc_24M");
 	if (csi->soc_24M == ERR_PTR(-ENOENT))
@@ -1986,21 +2060,21 @@
 	if (IS_ERR(csi->enable_gpio)) {
 		ret = PTR_ERR(csi->enable_gpio);
 		dev_err(dev, "failed to request enable GPIO: %d\n", ret);
-		return ret;
+		goto clk_put;
 	}
 
 	csi->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
 	if (IS_ERR(csi->reset_gpio)) {
 		ret = PTR_ERR(csi->reset_gpio);
 		dev_err(dev, "failed to request reset GPIO: %d\n", ret);
-		return ret;
+		goto clk_put;
 	}
 
 	csi->power_gpio = devm_gpiod_get_optional(dev, "power", GPIOD_OUT_HIGH);
 	if (IS_ERR(csi->power_gpio)) {
 		dev_err(dev, "failed to get power gpio\n");
 		ret = PTR_ERR(csi->power_gpio);
-		return ret;
+		goto clk_put;
 	}
 
 	csi->plugin_det_gpio = devm_gpiod_get_optional(dev, "plugin-det",
@@ -2008,7 +2082,7 @@
 	if (IS_ERR(csi->plugin_det_gpio)) {
 		dev_err(dev, "failed to get hdmirx det gpio\n");
 		ret = PTR_ERR(csi->plugin_det_gpio);
-		return ret;
+		goto clk_put;
 	}
 
 	if (csi->enable_gpio) {
@@ -2043,10 +2117,14 @@
 	if (of_property_read_bool(dev->of_node, "scaler-en"))
 		scaler_en = true;
 
+	if (of_property_read_bool(dev->of_node, "continues-clk"))
+		continues_clk = true;
+
 	ep = of_graph_get_next_endpoint(dev->of_node, NULL);
 	if (!ep) {
 		dev_err(dev, "missing endpoint node\n");
-		return -EINVAL;
+		ret = -EINVAL;
+		goto clk_put;
 	}
 
 	ret = v4l2_fwnode_endpoint_alloc_parse(of_fwnode_handle(ep), &endpoint);
@@ -2067,6 +2145,7 @@
 	csi->scaler_en = scaler_en;
 	if (csi->scaler_en)
 		csi->timings = dst_timing;
+	csi->continues_clk = continues_clk;
 
 	csi->rxphy_pwron = false;
 	csi->txphy_pwron = false;
@@ -2080,6 +2159,8 @@
 	v4l2_fwnode_endpoint_free(&endpoint);
 put_node:
 	of_node_put(ep);
+clk_put:
+	clk_disable_unprepare(csi->soc_24M);
 
 	return ret;
 }
@@ -2108,6 +2189,36 @@
 	{}
 };
 MODULE_DEVICE_TABLE(of, rk628_csi_of_match);
+
+static bool tx_5v_power_present(struct v4l2_subdev *sd);
+
+static ssize_t audio_rate_show(struct device *dev,
+			       struct device_attribute *attr, char *buf)
+{
+	struct rk628_csi *csi = dev_get_drvdata(dev);
+
+	return snprintf(buf, PAGE_SIZE, "%d", rk628_hdmirx_audio_fs(csi->audio_info));
+}
+
+static ssize_t audio_present_show(struct device *dev,
+				  struct device_attribute *attr, char *buf)
+{
+	struct rk628_csi *csi = dev_get_drvdata(dev);
+
+	return snprintf(buf, PAGE_SIZE, "%d",
+			tx_5v_power_present(&csi->sd) ?
+			rk628_hdmirx_audio_present(csi->audio_info) : 0);
+}
+
+static DEVICE_ATTR_RO(audio_rate);
+static DEVICE_ATTR_RO(audio_present);
+
+static struct attribute *rk628_attrs[] = {
+	&dev_attr_audio_rate.attr,
+	&dev_attr_audio_present.attr,
+	NULL
+};
+ATTRIBUTE_GROUPS(rk628);
 
 static int rk628_csi_probe(struct i2c_client *client,
 			  const struct i2c_device_id *id)
@@ -2203,10 +2314,8 @@
 			V4L2_CID_PIXEL_RATE, 0, RK628_CSI_PIXEL_RATE_HIGH, 1,
 			RK628_CSI_PIXEL_RATE_HIGH);
 	csi->detect_tx_5v_ctrl = v4l2_ctrl_new_std(&csi->hdl,
-			&rk628_csi_ctrl_ops, V4L2_CID_DV_RX_POWER_PRESENT,
+			NULL, V4L2_CID_DV_RX_POWER_PRESENT,
 			0, 1, 0, 0);
-	if (csi->detect_tx_5v_ctrl)
-		csi->detect_tx_5v_ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
 
 	/* custom controls */
 	csi->audio_sampling_rate_ctrl = v4l2_ctrl_new_custom(&csi->hdl,
@@ -2255,6 +2364,14 @@
 		goto err_hdl;
 	}
 
+	csi->classdev = device_create_with_groups(rk_hdmirx_class(),
+						  dev, MKDEV(0, 0),
+						  csi,
+						  rk628_groups,
+						  "rk628");
+	if (IS_ERR(csi->classdev))
+		goto err_hdl;
+
 	INIT_DELAYED_WORK(&csi->delayed_work_enable_hotplug,
 			rk628_csi_delayed_work_enable_hotplug);
 	INIT_DELAYED_WORK(&csi->delayed_work_res_change,
diff --git a/kernel/drivers/media/i2c/rk628/rk628_mipi_dphy.h b/kernel/drivers/media/i2c/rk628/rk628_mipi_dphy.h
index 1e13bc0..31331f4 100644
--- a/kernel/drivers/media/i2c/rk628/rk628_mipi_dphy.h
+++ b/kernel/drivers/media/i2c/rk628/rk628_mipi_dphy.h
@@ -18,6 +18,9 @@
 
 /* Test Code: 0x44 (HS RX Control of Lane 0) */
 #define HSFREQRANGE(x)			UPDATE(x, 6, 1)
+#define HSTX(x)				UPDATE(x, 6, 0)
+#define HSZERO(x)			UPDATE(x, 5, 0)
+#define HSPOST(x)			UPDATE(x, 4, 0)
 
 static inline void testif_testclk_assert(struct rk628 *rk628)
 {
@@ -189,6 +192,40 @@
 	testif_write(rk628, 0x44, HSFREQRANGE(hsfreqrange));
 }
 
+static void __maybe_unused mipi_dphy_init_hsmanual(struct rk628 *rk628, bool manual)
+{
+	if (manual) {
+		//config mipi timing when mipi freq is 1250Mbps
+		testif_write(rk628, 0x71, HSTX(0x4a) | BIT(7));
+		usleep_range(1500, 2000);
+		testif_write(rk628, 0x72, HSZERO(0xf) | BIT(6));
+		usleep_range(1500, 2000);
+		testif_write(rk628, 0x73, HSTX(0x5d) | BIT(7));
+		usleep_range(1500, 2000);
+		testif_write(rk628, 0x61, HSTX(0x3a) | BIT(7));
+		usleep_range(1500, 2000);
+		testif_write(rk628, 0x62, HSZERO(0x3a) | BIT(6));
+		usleep_range(1500, 2000);
+		testif_write(rk628, 0x63, HSTX(0x5a) | BIT(7));
+		usleep_range(1500, 2000);
+		testif_write(rk628, 0x65, HSPOST(0x1f) | BIT(5));
+	} else {
+		testif_write(rk628, 0x71, 0);
+		usleep_range(1500, 2000);
+		testif_write(rk628, 0x72, 0);
+		usleep_range(1500, 2000);
+		testif_write(rk628, 0x73, 0);
+		usleep_range(1500, 2000);
+		testif_write(rk628, 0x61, 0);
+		usleep_range(1500, 2000);
+		testif_write(rk628, 0x62, 0);
+		usleep_range(1500, 2000);
+		testif_write(rk628, 0x63, 0);
+		usleep_range(1500, 2000);
+		testif_write(rk628, 0x65, 0);
+	}
+}
+
 static inline int mipi_dphy_reset(struct rk628 *rk628)
 {
 	u32 val, mask;
diff --git a/kernel/drivers/media/i2c/s5kjn1.c b/kernel/drivers/media/i2c/s5kjn1.c
index 29621d3..c291c1a 100644
--- a/kernel/drivers/media/i2c/s5kjn1.c
+++ b/kernel/drivers/media/i2c/s5kjn1.c
@@ -1265,6 +1265,7 @@
 		inf->pdaf.flag = 1;
 		inf->pdaf.gainmap_width = otp->pdaf_data.gainmap_width;
 		inf->pdaf.gainmap_height = otp->pdaf_data.gainmap_height;
+		inf->pdaf.pd_offset = otp->pdaf_data.pd_offset;
 		inf->pdaf.dcc_mode = otp->pdaf_data.dcc_mode;
 		inf->pdaf.dcc_dir = otp->pdaf_data.dcc_dir;
 		inf->pdaf.dccmap_width = otp->pdaf_data.dccmap_width;
diff --git a/kernel/drivers/media/i2c/sc031gs.c b/kernel/drivers/media/i2c/sc031gs.c
index 1901787..7415d82 100644
--- a/kernel/drivers/media/i2c/sc031gs.c
+++ b/kernel/drivers/media/i2c/sc031gs.c
@@ -1040,8 +1040,7 @@
 				       ctrl->val + sc031gs->cur_mode->height);
 		if (!ret)
 			sc031gs->cur_vts = ctrl->val + sc031gs->cur_mode->height;
-		if (sc031gs->cur_vts != sc031gs->cur_mode->vts_def)
-			sc031gs_modify_fps_info(sc031gs);
+		sc031gs_modify_fps_info(sc031gs);
 		break;
 	case V4L2_CID_TEST_PATTERN:
 		ret = sc031gs_enable_test_pattern(sc031gs, ctrl->val);
diff --git a/kernel/drivers/media/i2c/sc035gs.c b/kernel/drivers/media/i2c/sc035gs.c
index 0001f37..201160d 100644
--- a/kernel/drivers/media/i2c/sc035gs.c
+++ b/kernel/drivers/media/i2c/sc035gs.c
@@ -1025,8 +1025,7 @@
 					ctrl->val + sc035gs->cur_mode->height);
 		if (!ret)
 			sc035gs->cur_vts = ctrl->val + sc035gs->cur_mode->height;
-		if (sc035gs->cur_vts != sc035gs->cur_mode->vts_def)
-			sc035gs_modify_fps_info(sc035gs);
+		sc035gs_modify_fps_info(sc035gs);
 		break;
 	case V4L2_CID_TEST_PATTERN:
 		ret = sc035gs_enable_test_pattern(sc035gs, ctrl->val);
diff --git a/kernel/drivers/media/i2c/sc132gs.c b/kernel/drivers/media/i2c/sc132gs.c
index dd6b63e..a8f0a50 100644
--- a/kernel/drivers/media/i2c/sc132gs.c
+++ b/kernel/drivers/media/i2c/sc132gs.c
@@ -1140,8 +1140,7 @@
 					ctrl->val + sc132gs->cur_mode->height);
 		if (!ret)
 			sc132gs->cur_vts = ctrl->val + sc132gs->cur_mode->height;
-		if (sc132gs->cur_vts != sc132gs->cur_mode->vts_def)
-			sc132gs_modify_fps_info(sc132gs);
+		sc132gs_modify_fps_info(sc132gs);
 		break;
 		break;
 	case V4L2_CID_TEST_PATTERN:
diff --git a/kernel/drivers/media/i2c/sc1346.c b/kernel/drivers/media/i2c/sc1346.c
new file mode 100644
index 0000000..e57de10
--- /dev/null
+++ b/kernel/drivers/media/i2c/sc1346.c
@@ -0,0 +1,1505 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * sc1346 driver
+ *
+ * Copyright (C) 2023 Rockchip Electronics Co., Ltd.
+ *
+ * V0.0X01.0X01 first version
+ */
+
+//#define DEBUG
+#include <linux/clk.h>
+#include <linux/device.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 <linux/sysfs.h>
+#include <linux/slab.h>
+#include <linux/version.h>
+#include <linux/rk-camera-module.h>
+#include <linux/rk-preisp.h>
+#include <media/media-entity.h>
+#include <media/v4l2-async.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-subdev.h>
+#include <linux/pinctrl/consumer.h>
+#include "../platform/rockchip/isp/rkisp_tb_helper.h"
+
+#define DRIVER_VERSION			KERNEL_VERSION(0, 0x01, 0x01)
+
+#ifndef V4L2_CID_DIGITAL_GAIN
+#define V4L2_CID_DIGITAL_GAIN		V4L2_CID_GAIN
+#endif
+
+#define SC1346_LANES			1
+#define SC1346_BITS_PER_SAMPLE		10
+#define SC1346_LINK_FREQ_375		371250000
+
+#define PIXEL_RATE_WITH_375M_10BIT	(SC1346_LINK_FREQ_375 * 2 * \
+					SC1346_LANES / SC1346_BITS_PER_SAMPLE)
+#define SC1346_XVCLK_FREQ		27000000
+
+#define CHIP_ID				0xda4d
+#define SC1346_REG_CHIP_ID		0x3107
+
+#define SC1346_REG_CTRL_MODE		0x0100
+#define SC1346_MODE_SW_STANDBY		0x0
+#define SC1346_MODE_STREAMING		BIT(0)
+
+#define SC1346_REG_EXPOSURE_H		0x3e00
+#define SC1346_REG_EXPOSURE_M		0x3e01
+#define SC1346_REG_EXPOSURE_L		0x3e02
+#define	SC1346_EXPOSURE_MIN		2
+#define	SC1346_EXPOSURE_STEP		1
+#define SC1346_VTS_MAX			0x7fff
+
+#define SC1346_REG_DIG_GAIN		0x3e06
+#define SC1346_REG_DIG_FINE_GAIN	0x3e07
+#define SC1346_REG_ANA_GAIN		0x3e09
+#define SC1346_GAIN_MIN			0x0020
+#define SC1346_GAIN_MAX			(32 * 15 * 32)    //32*15*32
+#define SC1346_GAIN_STEP		1
+#define SC1346_GAIN_DEFAULT		0x200
+
+#define SC1346_REG_GROUP_HOLD		0x3812
+#define SC1346_GROUP_HOLD_START		0x00
+#define SC1346_GROUP_HOLD_END		0x30
+
+#define SC1346_REG_TEST_PATTERN		0x4501
+#define SC1346_TEST_PATTERN_BIT_MASK	BIT(3)
+
+#define SC1346_REG_VTS_H		0x320e
+#define SC1346_REG_VTS_L		0x320f
+
+#define SC1346_FLIP_MIRROR_REG		0x3221
+
+#define SC1346_FETCH_EXP_H(VAL)		(((VAL) >> 12) & 0xF)
+#define SC1346_FETCH_EXP_M(VAL)		(((VAL) >> 4) & 0xFF)
+#define SC1346_FETCH_EXP_L(VAL)		(((VAL) & 0xF) << 4)
+
+#define SC1346_FETCH_AGAIN_H(VAL)	(((VAL) >> 8) & 0x03)
+#define SC1346_FETCH_AGAIN_L(VAL)	((VAL) & 0xFF)
+
+#define SC1346_FETCH_MIRROR(VAL, ENABLE)	(ENABLE ? VAL | 0x06 : VAL & 0xf9)
+#define SC1346_FETCH_FLIP(VAL, ENABLE)		(ENABLE ? VAL | 0x60 : VAL & 0x9f)
+
+#define REG_DELAY			0xFFFE
+#define REG_NULL			0xFFFF
+
+#define SC1346_REG_VALUE_08BIT		1
+#define SC1346_REG_VALUE_16BIT		2
+#define SC1346_REG_VALUE_24BIT		3
+
+#define OF_CAMERA_PINCTRL_STATE_DEFAULT	"rockchip,camera_default"
+#define OF_CAMERA_PINCTRL_STATE_SLEEP	"rockchip,camera_sleep"
+#define SC1346_NAME			"sc1346"
+
+static const char * const sc1346_supply_names[] = {
+	"avdd",		/* Analog power */
+	"dovdd",	/* Digital I/O power */
+	"dvdd",		/* Digital core power */
+};
+
+#define SC1346_NUM_SUPPLIES ARRAY_SIZE(sc1346_supply_names)
+
+struct regval {
+	u16 addr;
+	u8 val;
+};
+
+struct sc1346_mode {
+	u32 bus_fmt;
+	u32 width;
+	u32 height;
+	struct v4l2_fract max_fps;
+	u32 hts_def;
+	u32 vts_def;
+	u32 exp_def;
+	const struct regval *reg_list;
+	u32 hdr_mode;
+	u32 vc[PAD_MAX];
+};
+
+struct sc1346 {
+	struct i2c_client	*client;
+	struct clk		*xvclk;
+	struct gpio_desc	*reset_gpio;
+	struct gpio_desc	*pwdn_gpio;
+	struct regulator_bulk_data supplies[SC1346_NUM_SUPPLIES];
+
+	struct pinctrl		*pinctrl;
+	struct pinctrl_state	*pins_default;
+	struct pinctrl_state	*pins_sleep;
+
+	struct v4l2_subdev	subdev;
+	struct media_pad	pad;
+	struct v4l2_ctrl_handler ctrl_handler;
+	struct v4l2_ctrl	*exposure;
+	struct v4l2_ctrl	*anal_gain;
+	struct v4l2_ctrl	*digi_gain;
+	struct v4l2_ctrl	*hblank;
+	struct v4l2_ctrl	*vblank;
+	struct v4l2_ctrl	*test_pattern;
+	struct mutex		mutex;
+	bool			streaming;
+	bool			power_on;
+	const struct sc1346_mode *cur_mode;
+	struct v4l2_fract	cur_fps;
+	u32			module_index;
+	const char		*module_facing;
+	const char		*module_name;
+	const char		*len_name;
+	u32			cur_vts;
+	bool			is_thunderboot;
+	bool			is_first_streamoff;
+};
+
+#define to_sc1346(sd) container_of(sd, struct sc1346, subdev)
+
+/*
+ * Xclk 27Mhz
+ */
+static const struct regval sc1346_global_regs[] = {
+	{REG_NULL, 0x00},
+};
+
+/*
+ * Xclk 27Mhz
+ * max_framerate 30fps
+ * mipi_datarate per lane 371.25Mbps, 1lane
+ */
+static const struct regval sc1346_linear_10_1280x720_regs[] = {
+	{0x0103, 0x01},
+	{0x0100, 0x00},
+	{0x36e9, 0x80},
+	{0x37f9, 0x80},
+	{0x301f, 0x01},
+	{0x3106, 0x05},
+	{0x320e, 0x02},
+	{0x320f, 0xee},
+	{0x3301, 0x06},
+	{0x3306, 0x50},
+	{0x3308, 0x0a},
+	{0x330a, 0x00},
+	{0x330b, 0xda},
+	{0x330e, 0x0a},
+	{0x331e, 0x61},
+	{0x331f, 0xa1},
+	{0x3364, 0x1f},
+	{0x3390, 0x09},
+	{0x3391, 0x0f},
+	{0x3392, 0x1f},
+	{0x3393, 0x30},
+	{0x3394, 0x30},
+	{0x3395, 0x30},
+	{0x33ad, 0x10},
+	{0x33b3, 0x40},
+	{0x33f9, 0x50},
+	{0x33fb, 0x80},
+	{0x33fc, 0x09},
+	{0x33fd, 0x0f},
+	{0x349f, 0x03},
+	{0x34a6, 0x09},
+	{0x34a7, 0x0f},
+	{0x34a8, 0x40},
+	{0x34a9, 0x30},
+	{0x34aa, 0x00},
+	{0x34ab, 0xe8},
+	{0x34ac, 0x01},
+	{0x34ad, 0x0c},
+	{0x3630, 0xe2},
+	{0x3632, 0x76},
+	{0x3633, 0x33},
+	{0x3639, 0xf4},
+	{0x3641, 0x00},
+	{0x3670, 0x09},
+	{0x3674, 0xe2},
+	{0x3675, 0xea},
+	{0x3676, 0xea},
+	{0x367c, 0x09},
+	{0x367d, 0x0f},
+	{0x3690, 0x22},
+	{0x3691, 0x22},
+	{0x3692, 0x22},
+	{0x3698, 0x88},
+	{0x3699, 0x90},
+	{0x369a, 0xa1},
+	{0x369b, 0xc3},
+	{0x369c, 0x09},
+	{0x369d, 0x0f},
+	{0x36a2, 0x09},
+	{0x36a3, 0x0b},
+	{0x36a4, 0x0f},
+	{0x36d0, 0x01},
+	{0x370f, 0x01},
+	{0x3722, 0x41},
+	{0x3724, 0x41},
+	{0x3725, 0xc1},
+	{0x3728, 0x00},
+	{0x37b0, 0x41},
+	{0x37b1, 0x41},
+	{0x37b2, 0x47},
+	{0x37b3, 0x09},
+	{0x37b4, 0x0f},
+	{0x3903, 0x40},
+	{0x3904, 0x04},
+	{0x3905, 0x8d},
+	{0x3907, 0x00},
+	{0x3908, 0x41},
+	{0x391f, 0x41},
+	{0x3933, 0x80},
+	{0x3934, 0x02},
+	{0x3937, 0x74},
+	{0x3939, 0x0f},
+	{0x393a, 0xd4},
+	{0x3e01, 0x2e},
+	{0x3e02, 0xa0},
+	{0x440e, 0x02},
+	{0x4509, 0x20},
+	{0x450d, 0x28},
+	{0x5780, 0x66},
+	{0x578d, 0x40},
+	{0x36e9, 0x20},
+	{0x37f9, 0x20},
+	{REG_NULL, 0x00},
+};
+
+static const struct sc1346_mode supported_modes[] = {
+	{
+		.width = 1280,
+		.height = 720,
+		.max_fps = {
+			.numerator = 10000,
+			.denominator = 300000,
+		},
+		.exp_def = 0x0080,
+		.hts_def = 0x0250 * 2,
+		.vts_def = 0x02ee,
+		.bus_fmt = MEDIA_BUS_FMT_SBGGR10_1X10,
+		.reg_list = sc1346_linear_10_1280x720_regs,
+		.hdr_mode = NO_HDR,
+		.vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_0,
+	}
+};
+
+static const s64 link_freq_menu_items[] = {
+	SC1346_LINK_FREQ_375
+};
+
+static const char * const sc1346_test_pattern_menu[] = {
+	"Disabled",
+	"Vertical Color Bar Type 1",
+	"Vertical Color Bar Type 2",
+	"Vertical Color Bar Type 3",
+	"Vertical Color Bar Type 4"
+};
+
+/* Write registers up to 4 at a time */
+static int sc1346_write_reg(struct i2c_client *client, u16 reg,
+			    u32 len, u32 val)
+{
+	u32 buf_i, val_i;
+	u8 buf[6];
+	u8 *val_p;
+	__be32 val_be;
+
+	if (len > 4)
+		return -EINVAL;
+
+	buf[0] = reg >> 8;
+	buf[1] = reg & 0xff;
+
+	val_be = cpu_to_be32(val);
+	val_p = (u8 *)&val_be;
+	buf_i = 2;
+	val_i = 4 - len;
+
+	while (val_i < 4)
+		buf[buf_i++] = val_p[val_i++];
+
+	if (i2c_master_send(client, buf, len + 2) != len + 2)
+		return -EIO;
+	return 0;
+}
+
+static int sc1346_write_array(struct i2c_client *client,
+			       const struct regval *regs)
+{
+	u32 i;
+	int ret = 0;
+
+	for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++)
+		ret = sc1346_write_reg(client, regs[i].addr,
+					SC1346_REG_VALUE_08BIT, regs[i].val);
+
+	return ret;
+}
+
+/* Read registers up to 4 at a time */
+static int sc1346_read_reg(struct i2c_client *client, u16 reg, unsigned int len,
+			    u32 *val)
+{
+	struct i2c_msg msgs[2];
+	u8 *data_be_p;
+	__be32 data_be = 0;
+	__be16 reg_addr_be = cpu_to_be16(reg);
+	int ret;
+
+	if (len > 4 || !len)
+		return -EINVAL;
+
+	data_be_p = (u8 *)&data_be;
+	/* Write register address */
+	msgs[0].addr = client->addr;
+	msgs[0].flags = 0;
+	msgs[0].len = 2;
+	msgs[0].buf = (u8 *)&reg_addr_be;
+
+	/* Read data from register */
+	msgs[1].addr = client->addr;
+	msgs[1].flags = I2C_M_RD;
+	msgs[1].len = len;
+	msgs[1].buf = &data_be_p[4 - len];
+
+	ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+	if (ret != ARRAY_SIZE(msgs))
+		return -EIO;
+
+	*val = be32_to_cpu(data_be);
+
+	return 0;
+}
+
+static int sc1346_set_gain_reg(struct sc1346 *sc1346, u32 gain)
+{
+	u32 coarse_again = 0, coarse_dgian = 0, fine_dgian = 0;
+	u32 gain_factor;
+	int ret = 0;
+
+	if (gain < 32)
+		gain = 32;
+	else if (gain > SC1346_GAIN_MAX)
+		gain = SC1346_GAIN_MAX;
+
+	gain_factor = gain * 1000 / 32;
+	if (gain_factor < 2000) {
+		coarse_again = 0x00;
+		coarse_dgian = 0x00;
+		fine_dgian = gain_factor * 128 / 1000;
+	} else if (gain_factor < 4000) {
+		coarse_again = 0x08;
+		coarse_dgian = 0x00;
+		fine_dgian = gain_factor * 128 / 2000;
+	} else if (gain_factor < 8000) {
+		coarse_again = 0x09;
+		coarse_dgian = 0x00;
+		fine_dgian = gain_factor * 128 / 4000;
+	} else if (gain_factor < 16000) {
+		coarse_again = 0x0b;
+		coarse_dgian = 0x00;
+		fine_dgian = gain_factor * 128 / 8000;
+	} else if (gain_factor < 32000) {
+		coarse_again = 0x0f;
+		coarse_dgian = 0x00;
+		fine_dgian = gain_factor * 128 / 16000;
+	} else if (gain_factor < 32000 * 2) {
+		coarse_again = 0x1f;
+		coarse_dgian = 0x00;
+		fine_dgian = gain_factor * 128 / 32000;
+	} else if (gain_factor < 32000 * 4) {
+		//open dgain begin  max digital gain 4X
+		coarse_again = 0x1f;
+		coarse_dgian = 0x01;
+		fine_dgian = gain_factor * 128 / 32000 / 2;
+	} else if (gain_factor < 32000 * 8) {
+		coarse_again = 0x1f;
+		coarse_dgian = 0x03;
+		fine_dgian = gain_factor * 128 / 32000 / 4;
+	} else if (gain_factor < 32000 * 15) {
+		coarse_again = 0x1f;
+		coarse_dgian = 0x07;
+		fine_dgian = gain_factor * 128 / 32000 / 8;
+	} else {
+		coarse_again = 0x1f;
+		coarse_dgian = 0x07;
+		fine_dgian = 0xf0;
+	}
+
+	ret = sc1346_write_reg(sc1346->client,
+				SC1346_REG_DIG_GAIN,
+				SC1346_REG_VALUE_08BIT,
+				coarse_dgian);
+	ret |= sc1346_write_reg(sc1346->client,
+				 SC1346_REG_DIG_FINE_GAIN,
+				 SC1346_REG_VALUE_08BIT,
+				 fine_dgian);
+	ret |= sc1346_write_reg(sc1346->client,
+				 SC1346_REG_ANA_GAIN,
+				 SC1346_REG_VALUE_08BIT,
+				 coarse_again);
+
+	return ret;
+}
+
+static int sc1346_get_reso_dist(const struct sc1346_mode *mode,
+				 struct v4l2_mbus_framefmt *framefmt)
+{
+	return abs(mode->width - framefmt->width) +
+	       abs(mode->height - framefmt->height);
+}
+
+static const struct sc1346_mode *
+sc1346_find_best_fit(struct v4l2_subdev_format *fmt)
+{
+	struct v4l2_mbus_framefmt *framefmt = &fmt->format;
+	int dist;
+	int cur_best_fit = 0;
+	int cur_best_fit_dist = -1;
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(supported_modes); i++) {
+		dist = sc1346_get_reso_dist(&supported_modes[i], framefmt);
+		if (cur_best_fit_dist == -1 || dist < cur_best_fit_dist) {
+			cur_best_fit_dist = dist;
+			cur_best_fit = i;
+		}
+	}
+
+	return &supported_modes[cur_best_fit];
+}
+
+static int sc1346_set_fmt(struct v4l2_subdev *sd,
+			   struct v4l2_subdev_pad_config *cfg,
+			   struct v4l2_subdev_format *fmt)
+{
+	struct sc1346 *sc1346 = to_sc1346(sd);
+	const struct sc1346_mode *mode;
+	s64 h_blank, vblank_def;
+
+	mutex_lock(&sc1346->mutex);
+
+	mode = sc1346_find_best_fit(fmt);
+	fmt->format.code = mode->bus_fmt;
+	fmt->format.width = mode->width;
+	fmt->format.height = mode->height;
+	fmt->format.field = V4L2_FIELD_NONE;
+	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+		*v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = fmt->format;
+#else
+		mutex_unlock(&sc1346->mutex);
+		return -ENOTTY;
+#endif
+	} else {
+		sc1346->cur_mode = mode;
+		h_blank = mode->hts_def - mode->width;
+		__v4l2_ctrl_modify_range(sc1346->hblank, h_blank,
+					 h_blank, 1, h_blank);
+		vblank_def = mode->vts_def - mode->height;
+		__v4l2_ctrl_modify_range(sc1346->vblank, vblank_def,
+					 SC1346_VTS_MAX - mode->height,
+					 1, vblank_def);
+		sc1346->cur_fps = mode->max_fps;
+	}
+
+	mutex_unlock(&sc1346->mutex);
+
+	return 0;
+}
+
+static int sc1346_get_fmt(struct v4l2_subdev *sd,
+			   struct v4l2_subdev_pad_config *cfg,
+			   struct v4l2_subdev_format *fmt)
+{
+	struct sc1346 *sc1346 = to_sc1346(sd);
+	const struct sc1346_mode *mode = sc1346->cur_mode;
+
+	mutex_lock(&sc1346->mutex);
+	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+		fmt->format = *v4l2_subdev_get_try_format(sd, cfg, fmt->pad);
+#else
+		mutex_unlock(&sc1346->mutex);
+		return -ENOTTY;
+#endif
+	} else {
+		fmt->format.width = mode->width;
+		fmt->format.height = mode->height;
+		fmt->format.code = mode->bus_fmt;
+		fmt->format.field = V4L2_FIELD_NONE;
+		/* format info: width/height/data type/virctual channel */
+		if (fmt->pad < PAD_MAX && mode->hdr_mode != NO_HDR)
+			fmt->reserved[0] = mode->vc[fmt->pad];
+		else
+			fmt->reserved[0] = mode->vc[PAD0];
+	}
+	mutex_unlock(&sc1346->mutex);
+
+	return 0;
+}
+
+static int sc1346_enum_mbus_code(struct v4l2_subdev *sd,
+				  struct v4l2_subdev_pad_config *cfg,
+				  struct v4l2_subdev_mbus_code_enum *code)
+{
+	struct sc1346 *sc1346 = to_sc1346(sd);
+
+	if (code->index != 0)
+		return -EINVAL;
+	code->code = sc1346->cur_mode->bus_fmt;
+
+	return 0;
+}
+
+static int sc1346_enum_frame_sizes(struct v4l2_subdev *sd,
+				    struct v4l2_subdev_pad_config *cfg,
+				    struct v4l2_subdev_frame_size_enum *fse)
+{
+	if (fse->index >= ARRAY_SIZE(supported_modes))
+		return -EINVAL;
+
+	if (fse->code != supported_modes[0].bus_fmt)
+		return -EINVAL;
+
+	fse->min_width  = supported_modes[fse->index].width;
+	fse->max_width  = supported_modes[fse->index].width;
+	fse->max_height = supported_modes[fse->index].height;
+	fse->min_height = supported_modes[fse->index].height;
+
+	return 0;
+}
+
+static int sc1346_enable_test_pattern(struct sc1346 *sc1346, u32 pattern)
+{
+	u32 val = 0;
+	int ret = 0;
+
+	ret = sc1346_read_reg(sc1346->client, SC1346_REG_TEST_PATTERN,
+			       SC1346_REG_VALUE_08BIT, &val);
+	if (pattern)
+		val |= SC1346_TEST_PATTERN_BIT_MASK;
+	else
+		val &= ~SC1346_TEST_PATTERN_BIT_MASK;
+
+	ret |= sc1346_write_reg(sc1346->client, SC1346_REG_TEST_PATTERN,
+				 SC1346_REG_VALUE_08BIT, val);
+	return ret;
+}
+
+static int sc1346_g_frame_interval(struct v4l2_subdev *sd,
+				    struct v4l2_subdev_frame_interval *fi)
+{
+	struct sc1346 *sc1346 = to_sc1346(sd);
+	const struct sc1346_mode *mode = sc1346->cur_mode;
+
+	if (sc1346->streaming)
+		fi->interval = sc1346->cur_fps;
+	else
+		fi->interval = mode->max_fps;
+
+	return 0;
+}
+
+static int sc1346_g_mbus_config(struct v4l2_subdev *sd,
+				unsigned int pad_id,
+				struct v4l2_mbus_config *config)
+{
+	struct sc1346 *sc1346 = to_sc1346(sd);
+	const struct sc1346_mode *mode = sc1346->cur_mode;
+	u32 val = 1 << (SC1346_LANES - 1) |
+		V4L2_MBUS_CSI2_CHANNEL_0 |
+		V4L2_MBUS_CSI2_CONTINUOUS_CLOCK;
+
+	if (mode->hdr_mode != NO_HDR)
+		val |= V4L2_MBUS_CSI2_CHANNEL_1;
+	if (mode->hdr_mode == HDR_X3)
+		val |= V4L2_MBUS_CSI2_CHANNEL_2;
+
+	config->type = V4L2_MBUS_CSI2_DPHY;
+	config->flags = val;
+
+	return 0;
+}
+
+static void sc1346_get_module_inf(struct sc1346 *sc1346,
+				   struct rkmodule_inf *inf)
+{
+	memset(inf, 0, sizeof(*inf));
+	strscpy(inf->base.sensor, SC1346_NAME, sizeof(inf->base.sensor));
+	strscpy(inf->base.module, sc1346->module_name,
+		sizeof(inf->base.module));
+	strscpy(inf->base.lens, sc1346->len_name, sizeof(inf->base.lens));
+}
+
+static long sc1346_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
+{
+	struct sc1346 *sc1346 = to_sc1346(sd);
+	struct rkmodule_hdr_cfg *hdr;
+	u32 i, h, w;
+	long ret = 0;
+	u32 stream = 0;
+
+	switch (cmd) {
+	case RKMODULE_GET_MODULE_INFO:
+		sc1346_get_module_inf(sc1346, (struct rkmodule_inf *)arg);
+		break;
+	case RKMODULE_GET_HDR_CFG:
+		hdr = (struct rkmodule_hdr_cfg *)arg;
+		hdr->esp.mode = HDR_NORMAL_VC;
+		hdr->hdr_mode = sc1346->cur_mode->hdr_mode;
+		break;
+	case RKMODULE_SET_HDR_CFG:
+		hdr = (struct rkmodule_hdr_cfg *)arg;
+		w = sc1346->cur_mode->width;
+		h = sc1346->cur_mode->height;
+		for (i = 0; i < ARRAY_SIZE(supported_modes); i++) {
+			if (w == supported_modes[i].width &&
+			    h == supported_modes[i].height &&
+			    supported_modes[i].hdr_mode == hdr->hdr_mode) {
+				sc1346->cur_mode = &supported_modes[i];
+				break;
+			}
+		}
+		if (i == ARRAY_SIZE(supported_modes)) {
+			dev_err(&sc1346->client->dev,
+				"not find hdr mode:%d %dx%d config\n",
+				hdr->hdr_mode, w, h);
+			ret = -EINVAL;
+		} else {
+			w = sc1346->cur_mode->hts_def - sc1346->cur_mode->width;
+			h = sc1346->cur_mode->vts_def - sc1346->cur_mode->height;
+			__v4l2_ctrl_modify_range(sc1346->hblank, w, w, 1, w);
+			__v4l2_ctrl_modify_range(sc1346->vblank, h,
+						 SC1346_VTS_MAX - sc1346->cur_mode->height, 1, h);
+		}
+		break;
+	case PREISP_CMD_SET_HDRAE_EXP:
+		break;
+	case RKMODULE_SET_QUICK_STREAM:
+
+		stream = *((u32 *)arg);
+
+		if (stream)
+			ret = sc1346_write_reg(sc1346->client, SC1346_REG_CTRL_MODE,
+				 SC1346_REG_VALUE_08BIT, SC1346_MODE_STREAMING);
+		else
+			ret = sc1346_write_reg(sc1346->client, SC1346_REG_CTRL_MODE,
+				 SC1346_REG_VALUE_08BIT, SC1346_MODE_SW_STANDBY);
+		break;
+	default:
+		ret = -ENOIOCTLCMD;
+		break;
+	}
+
+	return ret;
+}
+
+#ifdef CONFIG_COMPAT
+static long sc1346_compat_ioctl32(struct v4l2_subdev *sd,
+				   unsigned int cmd, unsigned long arg)
+{
+	void __user *up = compat_ptr(arg);
+	struct rkmodule_inf *inf;
+	struct rkmodule_hdr_cfg *hdr;
+	struct preisp_hdrae_exp_s *hdrae;
+	long ret;
+	u32 stream = 0;
+
+	switch (cmd) {
+	case RKMODULE_GET_MODULE_INFO:
+		inf = kzalloc(sizeof(*inf), GFP_KERNEL);
+		if (!inf) {
+			ret = -ENOMEM;
+			return ret;
+		}
+
+		ret = sc1346_ioctl(sd, cmd, inf);
+		if (!ret) {
+			if (copy_to_user(up, inf, sizeof(*inf)))
+				ret = -EFAULT;
+		}
+		kfree(inf);
+		break;
+	case RKMODULE_GET_HDR_CFG:
+		hdr = kzalloc(sizeof(*hdr), GFP_KERNEL);
+		if (!hdr) {
+			ret = -ENOMEM;
+			return ret;
+		}
+
+		ret = sc1346_ioctl(sd, cmd, hdr);
+		if (!ret) {
+			if (copy_to_user(up, hdr, sizeof(*hdr)))
+				ret = -EFAULT;
+		}
+		kfree(hdr);
+		break;
+	case RKMODULE_SET_HDR_CFG:
+		hdr = kzalloc(sizeof(*hdr), GFP_KERNEL);
+		if (!hdr) {
+			ret = -ENOMEM;
+			return ret;
+		}
+
+		ret = copy_from_user(hdr, up, sizeof(*hdr));
+		if (!ret)
+			ret = sc1346_ioctl(sd, cmd, hdr);
+		else
+			ret = -EFAULT;
+		kfree(hdr);
+		break;
+	case PREISP_CMD_SET_HDRAE_EXP:
+		hdrae = kzalloc(sizeof(*hdrae), GFP_KERNEL);
+		if (!hdrae) {
+			ret = -ENOMEM;
+			return ret;
+		}
+
+		ret = copy_from_user(hdrae, up, sizeof(*hdrae));
+		if (!ret)
+			ret = sc1346_ioctl(sd, cmd, hdrae);
+		else
+			ret = -EFAULT;
+		kfree(hdrae);
+		break;
+	case RKMODULE_SET_QUICK_STREAM:
+		ret = copy_from_user(&stream, up, sizeof(u32));
+		if (!ret)
+			ret = sc1346_ioctl(sd, cmd, &stream);
+		else
+			ret = -EFAULT;
+		break;
+	default:
+		ret = -ENOIOCTLCMD;
+		break;
+	}
+
+	return ret;
+}
+#endif
+
+static int __sc1346_start_stream(struct sc1346 *sc1346)
+{
+	int ret;
+
+	if (!sc1346->is_thunderboot) {
+		ret = sc1346_write_array(sc1346->client, sc1346->cur_mode->reg_list);
+		if (ret)
+			return ret;
+
+		/* In case these controls are set before streaming */
+		ret = __v4l2_ctrl_handler_setup(&sc1346->ctrl_handler);
+		if (ret)
+			return ret;
+	}
+
+	return sc1346_write_reg(sc1346->client, SC1346_REG_CTRL_MODE,
+				 SC1346_REG_VALUE_08BIT, SC1346_MODE_STREAMING);
+}
+
+static int __sc1346_stop_stream(struct sc1346 *sc1346)
+{
+	if (sc1346->is_thunderboot) {
+		sc1346->is_first_streamoff = true;
+		pm_runtime_put(&sc1346->client->dev);
+	}
+	return sc1346_write_reg(sc1346->client, SC1346_REG_CTRL_MODE,
+				 SC1346_REG_VALUE_08BIT, SC1346_MODE_SW_STANDBY);
+}
+
+static int __sc1346_power_on(struct sc1346 *sc1346);
+static int sc1346_s_stream(struct v4l2_subdev *sd, int on)
+{
+	struct sc1346 *sc1346 = to_sc1346(sd);
+	struct i2c_client *client = sc1346->client;
+	int ret = 0;
+
+	mutex_lock(&sc1346->mutex);
+	on = !!on;
+	if (on == sc1346->streaming)
+		goto unlock_and_return;
+
+	if (on) {
+		if (sc1346->is_thunderboot && rkisp_tb_get_state() == RKISP_TB_NG) {
+			sc1346->is_thunderboot = false;
+			__sc1346_power_on(sc1346);
+		}
+
+		ret = pm_runtime_get_sync(&client->dev);
+		if (ret < 0) {
+			pm_runtime_put_noidle(&client->dev);
+			goto unlock_and_return;
+		}
+
+		ret = __sc1346_start_stream(sc1346);
+		if (ret) {
+			v4l2_err(sd, "start stream failed while write regs\n");
+			pm_runtime_put(&client->dev);
+			goto unlock_and_return;
+		}
+	} else {
+		__sc1346_stop_stream(sc1346);
+		pm_runtime_put(&client->dev);
+	}
+
+	sc1346->streaming = on;
+
+unlock_and_return:
+	mutex_unlock(&sc1346->mutex);
+
+	return ret;
+}
+
+static int sc1346_s_power(struct v4l2_subdev *sd, int on)
+{
+	struct sc1346 *sc1346 = to_sc1346(sd);
+	struct i2c_client *client = sc1346->client;
+	int ret = 0;
+
+	mutex_lock(&sc1346->mutex);
+
+	/* If the power state is not modified - no work to do. */
+	if (sc1346->power_on == !!on)
+		goto unlock_and_return;
+
+	if (on) {
+		ret = pm_runtime_get_sync(&client->dev);
+		if (ret < 0) {
+			pm_runtime_put_noidle(&client->dev);
+			goto unlock_and_return;
+		}
+
+		if (!sc1346->is_thunderboot) {
+			ret = sc1346_write_array(sc1346->client, sc1346_global_regs);
+			if (ret) {
+				v4l2_err(sd, "could not set init registers\n");
+				pm_runtime_put_noidle(&client->dev);
+				goto unlock_and_return;
+			}
+		}
+
+		sc1346->power_on = true;
+	} else {
+		pm_runtime_put(&client->dev);
+		sc1346->power_on = false;
+	}
+
+unlock_and_return:
+	mutex_unlock(&sc1346->mutex);
+
+	return ret;
+}
+
+/* Calculate the delay in us by clock rate and clock cycles */
+static inline u32 sc1346_cal_delay(u32 cycles)
+{
+	return DIV_ROUND_UP(cycles, SC1346_XVCLK_FREQ / 1000 / 1000);
+}
+
+static int __sc1346_power_on(struct sc1346 *sc1346)
+{
+	int ret;
+	u32 delay_us;
+	struct device *dev = &sc1346->client->dev;
+
+	if (!IS_ERR_OR_NULL(sc1346->pins_default)) {
+		ret = pinctrl_select_state(sc1346->pinctrl,
+					   sc1346->pins_default);
+		if (ret < 0)
+			dev_err(dev, "could not set pins\n");
+	}
+	ret = clk_set_rate(sc1346->xvclk, SC1346_XVCLK_FREQ);
+	if (ret < 0)
+		dev_warn(dev, "Failed to set xvclk rate (27MHz)\n");
+	if (clk_get_rate(sc1346->xvclk) != SC1346_XVCLK_FREQ)
+		dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n");
+	ret = clk_prepare_enable(sc1346->xvclk);
+	if (ret < 0) {
+		dev_err(dev, "Failed to enable xvclk\n");
+		return ret;
+	}
+	if (sc1346->is_thunderboot)
+		return 0;
+
+	if (!IS_ERR(sc1346->reset_gpio))
+		gpiod_set_value_cansleep(sc1346->reset_gpio, 0);
+
+	ret = regulator_bulk_enable(SC1346_NUM_SUPPLIES, sc1346->supplies);
+	if (ret < 0) {
+		dev_err(dev, "Failed to enable regulators\n");
+		goto disable_clk;
+	}
+
+	if (!IS_ERR(sc1346->reset_gpio))
+		gpiod_set_value_cansleep(sc1346->reset_gpio, 1);
+
+	usleep_range(500, 1000);
+	if (!IS_ERR(sc1346->pwdn_gpio))
+		gpiod_set_value_cansleep(sc1346->pwdn_gpio, 1);
+
+	if (!IS_ERR(sc1346->reset_gpio))
+		usleep_range(6000, 8000);
+	else
+		usleep_range(12000, 16000);
+
+	/* 8192 cycles prior to first SCCB transaction */
+	delay_us = sc1346_cal_delay(8192);
+	usleep_range(delay_us, delay_us * 2);
+
+	return 0;
+
+disable_clk:
+	clk_disable_unprepare(sc1346->xvclk);
+
+	return ret;
+}
+
+static void __sc1346_power_off(struct sc1346 *sc1346)
+{
+	int ret;
+	struct device *dev = &sc1346->client->dev;
+
+	clk_disable_unprepare(sc1346->xvclk);
+	if (sc1346->is_thunderboot) {
+		if (sc1346->is_first_streamoff) {
+			sc1346->is_thunderboot = false;
+			sc1346->is_first_streamoff = false;
+		} else {
+			return;
+		}
+	}
+
+	if (!IS_ERR(sc1346->pwdn_gpio))
+		gpiod_set_value_cansleep(sc1346->pwdn_gpio, 0);
+	if (!IS_ERR(sc1346->reset_gpio))
+		gpiod_set_value_cansleep(sc1346->reset_gpio, 0);
+	if (!IS_ERR_OR_NULL(sc1346->pins_sleep)) {
+		ret = pinctrl_select_state(sc1346->pinctrl,
+					   sc1346->pins_sleep);
+		if (ret < 0)
+			dev_dbg(dev, "could not set pins\n");
+	}
+	regulator_bulk_disable(SC1346_NUM_SUPPLIES, sc1346->supplies);
+}
+
+static int __maybe_unused sc1346_runtime_resume(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct sc1346 *sc1346 = to_sc1346(sd);
+
+	return __sc1346_power_on(sc1346);
+}
+
+static int __maybe_unused sc1346_runtime_suspend(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct sc1346 *sc1346 = to_sc1346(sd);
+
+	__sc1346_power_off(sc1346);
+
+	return 0;
+}
+
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+static int sc1346_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+	struct sc1346 *sc1346 = to_sc1346(sd);
+	struct v4l2_mbus_framefmt *try_fmt =
+				v4l2_subdev_get_try_format(sd, fh->pad, 0);
+	const struct sc1346_mode *def_mode = &supported_modes[0];
+
+	mutex_lock(&sc1346->mutex);
+	/* Initialize try_fmt */
+	try_fmt->width = def_mode->width;
+	try_fmt->height = def_mode->height;
+	try_fmt->code = def_mode->bus_fmt;
+	try_fmt->field = V4L2_FIELD_NONE;
+
+	mutex_unlock(&sc1346->mutex);
+	/* No crop or compose */
+
+	return 0;
+}
+#endif
+
+static int sc1346_enum_frame_interval(struct v4l2_subdev *sd,
+				       struct v4l2_subdev_pad_config *cfg,
+				       struct v4l2_subdev_frame_interval_enum *fie)
+{
+	if (fie->index >= ARRAY_SIZE(supported_modes))
+		return -EINVAL;
+
+	fie->code = supported_modes[fie->index].bus_fmt;
+	fie->width = supported_modes[fie->index].width;
+	fie->height = supported_modes[fie->index].height;
+	fie->interval = supported_modes[fie->index].max_fps;
+	fie->reserved[0] = supported_modes[fie->index].hdr_mode;
+	return 0;
+}
+
+static const struct dev_pm_ops sc1346_pm_ops = {
+	SET_RUNTIME_PM_OPS(sc1346_runtime_suspend,
+			   sc1346_runtime_resume, NULL)
+};
+
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+static const struct v4l2_subdev_internal_ops sc1346_internal_ops = {
+	.open = sc1346_open,
+};
+#endif
+
+static const struct v4l2_subdev_core_ops sc1346_core_ops = {
+	.s_power = sc1346_s_power,
+	.ioctl = sc1346_ioctl,
+#ifdef CONFIG_COMPAT
+	.compat_ioctl32 = sc1346_compat_ioctl32,
+#endif
+};
+
+static const struct v4l2_subdev_video_ops sc1346_video_ops = {
+	.s_stream = sc1346_s_stream,
+	.g_frame_interval = sc1346_g_frame_interval,
+};
+
+static const struct v4l2_subdev_pad_ops sc1346_pad_ops = {
+	.enum_mbus_code = sc1346_enum_mbus_code,
+	.enum_frame_size = sc1346_enum_frame_sizes,
+	.enum_frame_interval = sc1346_enum_frame_interval,
+	.get_fmt = sc1346_get_fmt,
+	.set_fmt = sc1346_set_fmt,
+	.get_mbus_config = sc1346_g_mbus_config,
+};
+
+static const struct v4l2_subdev_ops sc1346_subdev_ops = {
+	.core	= &sc1346_core_ops,
+	.video	= &sc1346_video_ops,
+	.pad	= &sc1346_pad_ops,
+};
+
+static void sc1346_modify_fps_info(struct sc1346 *sc1346)
+{
+	const struct sc1346_mode *mode = sc1346->cur_mode;
+
+	sc1346->cur_fps.denominator = mode->max_fps.denominator * mode->vts_def /
+				      sc1346->cur_vts;
+}
+
+static int sc1346_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct sc1346 *sc1346 = container_of(ctrl->handler,
+					       struct sc1346, ctrl_handler);
+	struct i2c_client *client = sc1346->client;
+	s64 max;
+	int ret = 0;
+	u32 val = 0;
+
+	/* Propagate change of current control to all related controls */
+	switch (ctrl->id) {
+	case V4L2_CID_VBLANK:
+		/* Update max exposure while meeting expected vblanking */
+		max = sc1346->cur_mode->height + ctrl->val - 8;
+		__v4l2_ctrl_modify_range(sc1346->exposure,
+					 sc1346->exposure->minimum, max,
+					 sc1346->exposure->step,
+					 sc1346->exposure->default_value);
+		break;
+	}
+
+	if (!pm_runtime_get_if_in_use(&client->dev))
+		return 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_EXPOSURE:
+		dev_dbg(&client->dev, "set exposure 0x%x\n", ctrl->val);
+		if (sc1346->cur_mode->hdr_mode == NO_HDR) {
+			val = ctrl->val;
+			/* 4 least significant bits of expsoure are fractional part */
+			ret = sc1346_write_reg(sc1346->client,
+						SC1346_REG_EXPOSURE_H,
+						SC1346_REG_VALUE_08BIT,
+						SC1346_FETCH_EXP_H(val));
+			ret |= sc1346_write_reg(sc1346->client,
+						 SC1346_REG_EXPOSURE_M,
+						 SC1346_REG_VALUE_08BIT,
+						 SC1346_FETCH_EXP_M(val));
+			ret |= sc1346_write_reg(sc1346->client,
+						 SC1346_REG_EXPOSURE_L,
+						 SC1346_REG_VALUE_08BIT,
+						 SC1346_FETCH_EXP_L(val));
+		}
+		break;
+	case V4L2_CID_ANALOGUE_GAIN:
+		dev_dbg(&client->dev, "set gain 0x%x\n", ctrl->val);
+		if (sc1346->cur_mode->hdr_mode == NO_HDR)
+			ret = sc1346_set_gain_reg(sc1346, ctrl->val);
+		break;
+	case V4L2_CID_VBLANK:
+		dev_dbg(&client->dev, "set vblank 0x%x\n", ctrl->val);
+		ret = sc1346_write_reg(sc1346->client,
+					SC1346_REG_VTS_H,
+					SC1346_REG_VALUE_08BIT,
+					(ctrl->val + sc1346->cur_mode->height)
+					>> 8);
+		ret |= sc1346_write_reg(sc1346->client,
+					 SC1346_REG_VTS_L,
+					 SC1346_REG_VALUE_08BIT,
+					 (ctrl->val + sc1346->cur_mode->height)
+					 & 0xff);
+		sc1346->cur_vts = ctrl->val + sc1346->cur_mode->height;
+		sc1346_modify_fps_info(sc1346);
+		break;
+	case V4L2_CID_TEST_PATTERN:
+		ret = sc1346_enable_test_pattern(sc1346, ctrl->val);
+		break;
+	case V4L2_CID_HFLIP:
+		ret = sc1346_read_reg(sc1346->client, SC1346_FLIP_MIRROR_REG,
+				       SC1346_REG_VALUE_08BIT, &val);
+		ret |= sc1346_write_reg(sc1346->client, SC1346_FLIP_MIRROR_REG,
+					 SC1346_REG_VALUE_08BIT,
+					 SC1346_FETCH_MIRROR(val, ctrl->val));
+		break;
+	case V4L2_CID_VFLIP:
+		ret = sc1346_read_reg(sc1346->client, SC1346_FLIP_MIRROR_REG,
+				       SC1346_REG_VALUE_08BIT, &val);
+		ret |= sc1346_write_reg(sc1346->client, SC1346_FLIP_MIRROR_REG,
+					 SC1346_REG_VALUE_08BIT,
+					 SC1346_FETCH_FLIP(val, ctrl->val));
+		break;
+	default:
+		dev_warn(&client->dev, "%s Unhandled id:0x%x, val:0x%x\n",
+			 __func__, ctrl->id, ctrl->val);
+		break;
+	}
+
+	pm_runtime_put(&client->dev);
+
+	return ret;
+}
+
+static const struct v4l2_ctrl_ops sc1346_ctrl_ops = {
+	.s_ctrl = sc1346_set_ctrl,
+};
+
+static int sc1346_initialize_controls(struct sc1346 *sc1346)
+{
+	const struct sc1346_mode *mode;
+	struct v4l2_ctrl_handler *handler;
+	struct v4l2_ctrl *ctrl;
+	s64 exposure_max, vblank_def;
+	u32 h_blank;
+	int ret;
+
+	handler = &sc1346->ctrl_handler;
+	mode = sc1346->cur_mode;
+	ret = v4l2_ctrl_handler_init(handler, 9);
+	if (ret)
+		return ret;
+	handler->lock = &sc1346->mutex;
+
+	ctrl = v4l2_ctrl_new_int_menu(handler, NULL, V4L2_CID_LINK_FREQ,
+				      0, 0, link_freq_menu_items);
+	if (ctrl)
+		ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+	v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE,
+			  0, PIXEL_RATE_WITH_375M_10BIT, 1, PIXEL_RATE_WITH_375M_10BIT);
+
+	h_blank = mode->hts_def - mode->width;
+	sc1346->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK,
+					    h_blank, h_blank, 1, h_blank);
+	if (sc1346->hblank)
+		sc1346->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+	vblank_def = mode->vts_def - mode->height;
+	sc1346->vblank = v4l2_ctrl_new_std(handler, &sc1346_ctrl_ops,
+					    V4L2_CID_VBLANK, vblank_def,
+					    SC1346_VTS_MAX - mode->height,
+					    1, vblank_def);
+	sc1346->cur_fps = mode->max_fps;
+	exposure_max = mode->vts_def - 8;
+	sc1346->exposure = v4l2_ctrl_new_std(handler, &sc1346_ctrl_ops,
+					      V4L2_CID_EXPOSURE, SC1346_EXPOSURE_MIN,
+					      exposure_max, SC1346_EXPOSURE_STEP,
+					      mode->exp_def);
+	sc1346->anal_gain = v4l2_ctrl_new_std(handler, &sc1346_ctrl_ops,
+					       V4L2_CID_ANALOGUE_GAIN, SC1346_GAIN_MIN,
+					       SC1346_GAIN_MAX, SC1346_GAIN_STEP,
+					       SC1346_GAIN_DEFAULT);
+	sc1346->test_pattern = v4l2_ctrl_new_std_menu_items(handler,
+							    &sc1346_ctrl_ops,
+					V4L2_CID_TEST_PATTERN,
+					ARRAY_SIZE(sc1346_test_pattern_menu) - 1,
+					0, 0, sc1346_test_pattern_menu);
+	v4l2_ctrl_new_std(handler, &sc1346_ctrl_ops,
+				V4L2_CID_HFLIP, 0, 1, 1, 0);
+	v4l2_ctrl_new_std(handler, &sc1346_ctrl_ops,
+				V4L2_CID_VFLIP, 0, 1, 1, 0);
+	if (handler->error) {
+		ret = handler->error;
+		dev_err(&sc1346->client->dev,
+			"Failed to init controls(%d)\n", ret);
+		goto err_free_handler;
+	}
+
+	sc1346->subdev.ctrl_handler = handler;
+
+	return 0;
+
+err_free_handler:
+	v4l2_ctrl_handler_free(handler);
+
+	return ret;
+}
+
+static int sc1346_check_sensor_id(struct sc1346 *sc1346,
+				   struct i2c_client *client)
+{
+	struct device *dev = &sc1346->client->dev;
+	u32 id = 0;
+	int ret;
+
+	if (sc1346->is_thunderboot) {
+		dev_info(dev, "Enable thunderboot mode, skip sensor id check\n");
+		return 0;
+	}
+
+	ret = sc1346_read_reg(client, SC1346_REG_CHIP_ID,
+			       SC1346_REG_VALUE_16BIT, &id);
+	if (id != CHIP_ID) {
+		dev_err(dev, "Unexpected sensor id(%06x), ret(%d)\n", id, ret);
+		return -ENODEV;
+	}
+
+	dev_info(dev, "Detected OV%06x sensor\n", CHIP_ID);
+
+	return 0;
+}
+
+static int sc1346_configure_regulators(struct sc1346 *sc1346)
+{
+	unsigned int i;
+
+	for (i = 0; i < SC1346_NUM_SUPPLIES; i++)
+		sc1346->supplies[i].supply = sc1346_supply_names[i];
+
+	return devm_regulator_bulk_get(&sc1346->client->dev,
+				       SC1346_NUM_SUPPLIES,
+				       sc1346->supplies);
+}
+
+static int sc1346_probe(struct i2c_client *client,
+			 const struct i2c_device_id *id)
+{
+	struct device *dev = &client->dev;
+	struct device_node *node = dev->of_node;
+	struct sc1346 *sc1346;
+	struct v4l2_subdev *sd;
+	char facing[2];
+	int ret;
+
+	dev_info(dev, "driver version: %02x.%02x.%02x",
+		 DRIVER_VERSION >> 16,
+		 (DRIVER_VERSION & 0xff00) >> 8,
+		 DRIVER_VERSION & 0x00ff);
+
+	sc1346 = devm_kzalloc(dev, sizeof(*sc1346), GFP_KERNEL);
+	if (!sc1346)
+		return -ENOMEM;
+
+	ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX,
+				   &sc1346->module_index);
+	ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING,
+				       &sc1346->module_facing);
+	ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME,
+				       &sc1346->module_name);
+	ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME,
+				       &sc1346->len_name);
+	if (ret) {
+		dev_err(dev, "could not get module information!\n");
+		return -EINVAL;
+	}
+
+	sc1346->is_thunderboot = IS_ENABLED(CONFIG_VIDEO_ROCKCHIP_THUNDER_BOOT_ISP);
+	sc1346->client = client;
+	sc1346->cur_mode = &supported_modes[0];
+
+	sc1346->xvclk = devm_clk_get(dev, "xvclk");
+	if (IS_ERR(sc1346->xvclk)) {
+		dev_err(dev, "Failed to get xvclk\n");
+		return -EINVAL;
+	}
+
+	if (sc1346->is_thunderboot) {
+		sc1346->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_ASIS);
+		if (IS_ERR(sc1346->reset_gpio))
+			dev_warn(dev, "Failed to get reset-gpios\n");
+
+		sc1346->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_ASIS);
+		if (IS_ERR(sc1346->pwdn_gpio))
+			dev_warn(dev, "Failed to get pwdn-gpios\n");
+	} else {
+		sc1346->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
+		if (IS_ERR(sc1346->reset_gpio))
+			dev_warn(dev, "Failed to get reset-gpios\n");
+
+		sc1346->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_OUT_LOW);
+		if (IS_ERR(sc1346->pwdn_gpio))
+			dev_warn(dev, "Failed to get pwdn-gpios\n");
+	}
+	sc1346->pinctrl = devm_pinctrl_get(dev);
+	if (!IS_ERR(sc1346->pinctrl)) {
+		sc1346->pins_default =
+			pinctrl_lookup_state(sc1346->pinctrl,
+					     OF_CAMERA_PINCTRL_STATE_DEFAULT);
+		if (IS_ERR(sc1346->pins_default))
+			dev_err(dev, "could not get default pinstate\n");
+
+		sc1346->pins_sleep =
+			pinctrl_lookup_state(sc1346->pinctrl,
+					     OF_CAMERA_PINCTRL_STATE_SLEEP);
+		if (IS_ERR(sc1346->pins_sleep))
+			dev_err(dev, "could not get sleep pinstate\n");
+	} else {
+		dev_err(dev, "no pinctrl\n");
+	}
+
+	ret = sc1346_configure_regulators(sc1346);
+	if (ret) {
+		dev_err(dev, "Failed to get power regulators\n");
+		return ret;
+	}
+
+	mutex_init(&sc1346->mutex);
+
+	sd = &sc1346->subdev;
+	v4l2_i2c_subdev_init(sd, client, &sc1346_subdev_ops);
+	ret = sc1346_initialize_controls(sc1346);
+	if (ret)
+		goto err_destroy_mutex;
+
+	ret = __sc1346_power_on(sc1346);
+	if (ret)
+		goto err_free_handler;
+
+	ret = sc1346_check_sensor_id(sc1346, client);
+	if (ret)
+		goto err_power_off;
+
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+	sd->internal_ops = &sc1346_internal_ops;
+	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
+		     V4L2_SUBDEV_FL_HAS_EVENTS;
+#endif
+#if defined(CONFIG_MEDIA_CONTROLLER)
+	sc1346->pad.flags = MEDIA_PAD_FL_SOURCE;
+	sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
+	ret = media_entity_pads_init(&sd->entity, 1, &sc1346->pad);
+	if (ret < 0)
+		goto err_power_off;
+#endif
+
+	memset(facing, 0, sizeof(facing));
+	if (strcmp(sc1346->module_facing, "back") == 0)
+		facing[0] = 'b';
+	else
+		facing[0] = 'f';
+
+	snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s",
+		 sc1346->module_index, facing,
+		 SC1346_NAME, dev_name(sd->dev));
+	ret = v4l2_async_register_subdev_sensor_common(sd);
+	if (ret) {
+		dev_err(dev, "v4l2 async register subdev failed\n");
+		goto err_clean_entity;
+	}
+
+	pm_runtime_set_active(dev);
+	pm_runtime_enable(dev);
+	if (sc1346->is_thunderboot)
+		pm_runtime_get_sync(dev);
+	else
+		pm_runtime_idle(dev);
+
+	return 0;
+
+err_clean_entity:
+#if defined(CONFIG_MEDIA_CONTROLLER)
+	media_entity_cleanup(&sd->entity);
+#endif
+err_power_off:
+	__sc1346_power_off(sc1346);
+err_free_handler:
+	v4l2_ctrl_handler_free(&sc1346->ctrl_handler);
+err_destroy_mutex:
+	mutex_destroy(&sc1346->mutex);
+
+	return ret;
+}
+
+static int sc1346_remove(struct i2c_client *client)
+{
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct sc1346 *sc1346 = to_sc1346(sd);
+
+	v4l2_async_unregister_subdev(sd);
+#if defined(CONFIG_MEDIA_CONTROLLER)
+	media_entity_cleanup(&sd->entity);
+#endif
+	v4l2_ctrl_handler_free(&sc1346->ctrl_handler);
+	mutex_destroy(&sc1346->mutex);
+
+	pm_runtime_disable(&client->dev);
+	if (!pm_runtime_status_suspended(&client->dev))
+		__sc1346_power_off(sc1346);
+	pm_runtime_set_suspended(&client->dev);
+
+	return 0;
+}
+
+#if IS_ENABLED(CONFIG_OF)
+static const struct of_device_id sc1346_of_match[] = {
+	{ .compatible = "smartsens,sc1346" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, sc1346_of_match);
+#endif
+
+static const struct i2c_device_id sc1346_match_id[] = {
+	{ "smartsens,sc1346", 0 },
+	{ },
+};
+
+static struct i2c_driver sc1346_i2c_driver = {
+	.driver = {
+		.name = SC1346_NAME,
+		.pm = &sc1346_pm_ops,
+		.of_match_table = of_match_ptr(sc1346_of_match),
+	},
+	.probe		= &sc1346_probe,
+	.remove		= &sc1346_remove,
+	.id_table	= sc1346_match_id,
+};
+
+static int __init sensor_mod_init(void)
+{
+	return i2c_add_driver(&sc1346_i2c_driver);
+}
+
+static void __exit sensor_mod_exit(void)
+{
+	i2c_del_driver(&sc1346_i2c_driver);
+}
+
+#if defined(CONFIG_VIDEO_ROCKCHIP_THUNDER_BOOT_ISP) && !defined(CONFIG_INITCALL_ASYNC)
+subsys_initcall(sensor_mod_init);
+#else
+device_initcall_sync(sensor_mod_init);
+#endif
+module_exit(sensor_mod_exit);
+
+MODULE_DESCRIPTION("smartsens sc1346 sensor driver");
+MODULE_LICENSE("GPL");
diff --git a/kernel/drivers/media/i2c/sc200ai.c b/kernel/drivers/media/i2c/sc200ai.c
index e47fa1a..daa7e4e 100644
--- a/kernel/drivers/media/i2c/sc200ai.c
+++ b/kernel/drivers/media/i2c/sc200ai.c
@@ -1744,8 +1744,7 @@
 					 & 0xff);
 		if (!ret)
 			sc200ai->cur_vts = ctrl->val + sc200ai->cur_mode->height;
-		if (sc200ai->cur_vts != sc200ai->cur_mode->vts_def)
-			sc200ai_modify_fps_info(sc200ai);
+		sc200ai_modify_fps_info(sc200ai);
 		break;
 	case V4L2_CID_TEST_PATTERN:
 		ret = sc200ai_enable_test_pattern(sc200ai, ctrl->val);
diff --git a/kernel/drivers/media/i2c/sc210iot.c b/kernel/drivers/media/i2c/sc210iot.c
index d5a2f67..40d1e04 100644
--- a/kernel/drivers/media/i2c/sc210iot.c
+++ b/kernel/drivers/media/i2c/sc210iot.c
@@ -408,8 +408,7 @@
 					(ctrl->val + sc210iot->cur_mode->height) & 0xff);
 		if (!ret)
 			sc210iot->cur_vts = ctrl->val + sc210iot->cur_mode->height;
-		if (sc210iot->cur_vts != sc210iot->cur_mode->vts_def)
-			sc210iot_modify_fps_info(sc210iot);
+		sc210iot_modify_fps_info(sc210iot);
 		break;
 	case V4L2_CID_HFLIP:
 		regmap_update_bits(sc210iot->regmap, SC210IOT_REG_MIRROR_FLIP,
diff --git a/kernel/drivers/media/i2c/sc2232.c b/kernel/drivers/media/i2c/sc2232.c
index 59156ad..9186997 100644
--- a/kernel/drivers/media/i2c/sc2232.c
+++ b/kernel/drivers/media/i2c/sc2232.c
@@ -1219,8 +1219,7 @@
 					ctrl->val + sc2232->cur_mode->height);
 		if (!ret)
 			sc2232->cur_vts = ctrl->val + sc2232->cur_mode->height;
-		if (sc2232->cur_vts != sc2232->cur_mode->vts_def)
-			sc2232_modify_fps_info(sc2232);
+		sc2232_modify_fps_info(sc2232);
 		dev_dbg(&client->dev, "set vblank 0x%x\n",
 			ctrl->val);
 		break;
diff --git a/kernel/drivers/media/i2c/sc2239.c b/kernel/drivers/media/i2c/sc2239.c
index 2e49ad9..f56fe45 100644
--- a/kernel/drivers/media/i2c/sc2239.c
+++ b/kernel/drivers/media/i2c/sc2239.c
@@ -974,8 +974,7 @@
 				       ctrl->val + sc2239->cur_mode->height);
 		if (!ret)
 			sc2239->cur_vts = ctrl->val + sc2239->cur_mode->height;
-		if (sc2239->cur_vts != sc2239->cur_mode->vts_def)
-			sc2239_modify_fps_info(sc2239);
+		sc2239_modify_fps_info(sc2239);
 		break;
 	case V4L2_CID_TEST_PATTERN:
 		ret = sc2239_enable_test_pattern(sc2239, ctrl->val);
diff --git a/kernel/drivers/media/i2c/sc223a.c b/kernel/drivers/media/i2c/sc223a.c
new file mode 100644
index 0000000..e578ac9
--- /dev/null
+++ b/kernel/drivers/media/i2c/sc223a.c
@@ -0,0 +1,1608 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * sc223a driver
+ *
+ * Copyright (C) 2023 Rockchip Electronics Co., Ltd.
+ *
+ * V0.0X01.0X01 first version
+ */
+
+//#define DEBUG
+#include <linux/clk.h>
+#include <linux/device.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 <linux/sysfs.h>
+#include <linux/slab.h>
+#include <linux/version.h>
+#include <linux/rk-camera-module.h>
+#include <linux/rk-preisp.h>
+#include <media/media-entity.h>
+#include <media/v4l2-async.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-subdev.h>
+#include <linux/pinctrl/consumer.h>
+#include "../platform/rockchip/isp/rkisp_tb_helper.h"
+
+#define DRIVER_VERSION			KERNEL_VERSION(0, 0x01, 0x01)
+
+#ifndef V4L2_CID_DIGITAL_GAIN
+#define V4L2_CID_DIGITAL_GAIN		V4L2_CID_GAIN
+#endif
+
+#define SC223A_LANES			2
+#define SC223A_BITS_PER_SAMPLE		10
+#define SC223A_LINK_FREQ_405		202500000
+
+#define PIXEL_RATE_WITH_405M_10BIT	(SC223A_LINK_FREQ_405 * 2 * \
+					SC223A_LANES / SC223A_BITS_PER_SAMPLE)
+#define SC223A_XVCLK_FREQ		24000000
+
+#define CHIP_ID				0xcb3e
+#define SC223A_REG_CHIP_ID		0x3107
+
+#define SC223A_REG_CTRL_MODE		0x0100
+#define SC223A_MODE_SW_STANDBY		0x0
+#define SC223A_MODE_STREAMING		BIT(0)
+
+#define SC223A_REG_EXPOSURE_H		0x3e00
+#define SC223A_REG_EXPOSURE_M		0x3e01
+#define SC223A_REG_EXPOSURE_L		0x3e02
+#define	SC223A_EXPOSURE_MIN		3
+#define	SC223A_EXPOSURE_STEP		1
+#define SC223A_VTS_MAX			0x7fff
+
+#define SC223A_REG_DIG_GAIN		0x3e06
+#define SC223A_REG_DIG_FINE_GAIN	0x3e07
+#define SC223A_REG_ANA_GAIN		0x3e09
+#define SC223A_GAIN_MIN			0x0080
+#define SC223A_GAIN_MAX			(29656)	//57.92*4*128
+#define SC223A_GAIN_STEP		1
+#define SC223A_GAIN_DEFAULT		0x80
+
+#define SC223A_REG_GROUP_HOLD		0x3812
+#define SC223A_GROUP_HOLD_START		0x00
+#define SC223A_GROUP_HOLD_END		0x30
+
+#define SC223A_REG_TEST_PATTERN		0x4501
+#define SC223A_TEST_PATTERN_BIT_MASK	BIT(3)
+
+#define SC223A_REG_VTS_H		0x320e
+#define SC223A_REG_VTS_L		0x320f
+
+#define SC223A_FLIP_MIRROR_REG		0x3221
+
+#define SC223A_FETCH_EXP_H(VAL)		(((VAL) >> 12) & 0xF)
+#define SC223A_FETCH_EXP_M(VAL)		(((VAL) >> 4) & 0xFF)
+#define SC223A_FETCH_EXP_L(VAL)		(((VAL) & 0xF) << 4)
+
+#define SC223A_FETCH_AGAIN_H(VAL)	(((VAL) >> 8) & 0x03)
+#define SC223A_FETCH_AGAIN_L(VAL)	((VAL) & 0xFF)
+
+#define SC223A_FETCH_MIRROR(VAL, ENABLE)	(ENABLE ? VAL | 0x06 : VAL & 0xf9)
+#define SC223A_FETCH_FLIP(VAL, ENABLE)		(ENABLE ? VAL | 0x60 : VAL & 0x9f)
+
+#define REG_DELAY			0xFFFE
+#define REG_NULL			0xFFFF
+
+#define SC223A_REG_VALUE_08BIT		1
+#define SC223A_REG_VALUE_16BIT		2
+#define SC223A_REG_VALUE_24BIT		3
+
+#define OF_CAMERA_PINCTRL_STATE_DEFAULT	"rockchip,camera_default"
+#define OF_CAMERA_PINCTRL_STATE_SLEEP	"rockchip,camera_sleep"
+#define SC223A_NAME			"sc223a"
+
+static const char * const sc223a_supply_names[] = {
+	"avdd",		/* Analog power */
+	"dovdd",	/* Digital I/O power */
+	"dvdd",		/* Digital core power */
+};
+
+#define SC223A_NUM_SUPPLIES ARRAY_SIZE(sc223a_supply_names)
+
+struct regval {
+	u16 addr;
+	u8 val;
+};
+
+struct sc223a_mode {
+	u32 bus_fmt;
+	u32 width;
+	u32 height;
+	struct v4l2_fract max_fps;
+	u32 hts_def;
+	u32 vts_def;
+	u32 exp_def;
+	const struct regval *reg_list;
+	u32 hdr_mode;
+	u32 vc[PAD_MAX];
+};
+
+struct sc223a {
+	struct i2c_client	*client;
+	struct clk		*xvclk;
+	struct gpio_desc	*reset_gpio;
+	struct gpio_desc	*pwdn_gpio;
+	struct regulator_bulk_data supplies[SC223A_NUM_SUPPLIES];
+
+	struct pinctrl		*pinctrl;
+	struct pinctrl_state	*pins_default;
+	struct pinctrl_state	*pins_sleep;
+
+	struct v4l2_subdev	subdev;
+	struct media_pad	pad;
+	struct v4l2_ctrl_handler ctrl_handler;
+	struct v4l2_ctrl	*exposure;
+	struct v4l2_ctrl	*anal_gain;
+	struct v4l2_ctrl	*digi_gain;
+	struct v4l2_ctrl	*hblank;
+	struct v4l2_ctrl	*vblank;
+	struct v4l2_ctrl	*test_pattern;
+	struct mutex		mutex;
+	struct v4l2_fract	cur_fps;
+	bool			streaming;
+	bool			power_on;
+	const struct sc223a_mode *cur_mode;
+	u32			module_index;
+	const char		*module_facing;
+	const char		*module_name;
+	const char		*len_name;
+	u32			cur_vts;
+	bool			has_init_exp;
+	bool			is_thunderboot;
+	bool			is_first_streamoff;
+	struct preisp_hdrae_exp_s init_hdrae_exp;
+};
+
+#define to_sc223a(sd) container_of(sd, struct sc223a, subdev)
+
+/*
+ * Xclk 24Mhz
+ */
+static const struct regval sc223a_global_regs[] = {
+	{REG_NULL, 0x00},
+};
+
+/*
+ * Xclk 24Mhz
+ * max_framerate 30fps
+ * mipi_datarate per lane 405Mbps, 2lane
+ */
+static const struct regval sc223a_linear_10_1920x1080_30fps_regs[] = {
+	{0x0100, 0x00},
+	{0x36e9, 0x80},
+	{0x37f9, 0x80},
+	{0x301f, 0x08},
+	{0x30b8, 0x44},
+	{0x320c, 0x08},
+	{0x320d, 0xca},
+	{0x320e, 0x04},
+	{0x320f, 0xb0},
+	{0x3253, 0x0c},
+	{0x3281, 0x80},
+	{0x3301, 0x06},
+	{0x3302, 0x12},
+	{0x3306, 0x84},
+	{0x3309, 0x60},
+	{0x330a, 0x00},
+	{0x330b, 0xe0},
+	{0x330d, 0x20},
+	{0x3314, 0x15},
+	{0x331e, 0x41},
+	{0x331f, 0x51},
+	{0x3320, 0x0a},
+	{0x3326, 0x0e},
+	{0x3333, 0x10},
+	{0x3334, 0x40},
+	{0x335d, 0x60},
+	{0x335e, 0x06},
+	{0x335f, 0x08},
+	{0x3364, 0x56},
+	{0x337a, 0x06},
+	{0x337b, 0x0e},
+	{0x337c, 0x02},
+	{0x337d, 0x0a},
+	{0x3390, 0x03},
+	{0x3391, 0x0f},
+	{0x3392, 0x1f},
+	{0x3393, 0x06},
+	{0x3394, 0x06},
+	{0x3395, 0x06},
+	{0x3396, 0x48},
+	{0x3397, 0x4b},
+	{0x3398, 0x5f},
+	{0x3399, 0x06},
+	{0x339a, 0x06},
+	{0x339b, 0x9c},
+	{0x339c, 0x9c},
+	{0x33a2, 0x04},
+	{0x33a3, 0x0a},
+	{0x33ad, 0x1c},
+	{0x33af, 0x40},
+	{0x33b1, 0x80},
+	{0x33b3, 0x20},
+	{0x349f, 0x02},
+	{0x34a6, 0x48},
+	{0x34a7, 0x4b},
+	{0x34a8, 0x20},
+	{0x34a9, 0x20},
+	{0x34f8, 0x5f},
+	{0x34f9, 0x10},
+	{0x3616, 0xac},
+	{0x3630, 0xc0},
+	{0x3631, 0x86},
+	{0x3632, 0x26},
+	{0x3633, 0x32},
+	{0x3637, 0x29},
+	{0x363a, 0x84},
+	{0x363b, 0x04},
+	{0x363c, 0x08},
+	{0x3641, 0x3a},
+	{0x364f, 0x39},
+	{0x3670, 0xce},
+	{0x3674, 0xc0},
+	{0x3675, 0xc0},
+	{0x3676, 0xc0},
+	{0x3677, 0x86},
+	{0x3678, 0x8b},
+	{0x3679, 0x8c},
+	{0x367c, 0x4b},
+	{0x367d, 0x5f},
+	{0x367e, 0x4b},
+	{0x367f, 0x5f},
+	{0x3690, 0x62},
+	{0x3691, 0x63},
+	{0x3692, 0x63},
+	{0x3699, 0x86},
+	{0x369a, 0x92},
+	{0x369b, 0xa4},
+	{0x369c, 0x48},
+	{0x369d, 0x4b},
+	{0x36a2, 0x4b},
+	{0x36a3, 0x4f},
+	{0x36ea, 0x09},
+	{0x36eb, 0x0c},
+	{0x36ec, 0x1c},
+	{0x36ed, 0x28},
+	{0x370f, 0x01},
+	{0x3721, 0x6c},
+	{0x3722, 0x09},
+	{0x3724, 0x41},
+	{0x3725, 0xc4},
+	{0x37b0, 0x09},
+	{0x37b1, 0x09},
+	{0x37b2, 0x09},
+	{0x37b3, 0x48},
+	{0x37b4, 0x5f},
+	{0x37fa, 0x09},
+	{0x37fb, 0x32},
+	{0x37fc, 0x10},
+	{0x37fd, 0x37},
+	{0x3900, 0x19},
+	{0x3901, 0x02},
+	{0x3905, 0xb8},
+	{0x391b, 0x82},
+	{0x391c, 0x00},
+	{0x391f, 0x04},
+	{0x3933, 0x81},
+	{0x3934, 0x4c},
+	{0x393f, 0xff},
+	{0x3940, 0x73},
+	{0x3942, 0x01},
+	{0x3943, 0x4d},
+	{0x3946, 0x20},
+	{0x3957, 0x86},
+	{0x3e01, 0x95},
+	{0x3e02, 0x60},
+	{0x3e28, 0xc4},
+	{0x440e, 0x02},
+	{0x4501, 0xc0},
+	{0x4509, 0x14},
+	{0x450d, 0x11},
+	{0x4518, 0x00},
+	{0x451b, 0x0a},
+	{0x4819, 0x07},
+	{0x481b, 0x04},
+	{0x481d, 0x0e},
+	{0x481f, 0x03},
+	{0x4821, 0x09},
+	{0x4823, 0x04},
+	{0x4825, 0x03},
+	{0x4827, 0x03},
+	{0x4829, 0x06},
+	{0x501c, 0x00},
+	{0x501d, 0x60},
+	{0x501e, 0x00},
+	{0x501f, 0x40},
+	{0x5799, 0x06},
+	{0x5ae0, 0xfe},
+	{0x5ae1, 0x40},
+	{0x5ae2, 0x38},
+	{0x5ae3, 0x30},
+	{0x5ae4, 0x28},
+	{0x5ae5, 0x38},
+	{0x5ae6, 0x30},
+	{0x5ae7, 0x28},
+	{0x5ae8, 0x3f},
+	{0x5ae9, 0x34},
+	{0x5aea, 0x2c},
+	{0x5aeb, 0x3f},
+	{0x5aec, 0x34},
+	{0x5aed, 0x2c},
+	{0x5aee, 0xfe},
+	{0x5aef, 0x40},
+	{0x5af4, 0x38},
+	{0x5af5, 0x30},
+	{0x5af6, 0x28},
+	{0x5af7, 0x38},
+	{0x5af8, 0x30},
+	{0x5af9, 0x28},
+	{0x5afa, 0x3f},
+	{0x5afb, 0x34},
+	{0x5afc, 0x2c},
+	{0x5afd, 0x3f},
+	{0x5afe, 0x34},
+	{0x5aff, 0x2c},
+	{0x36e9, 0x53},
+	{0x37f9, 0x53},
+	{REG_NULL, 0x00},
+};
+
+static const struct sc223a_mode supported_modes[] = {
+	{
+		.width = 1920,
+		.height = 1080,
+		.max_fps = {
+			.numerator = 10000,
+			.denominator = 300000,
+		},
+		.exp_def = 0x0080,
+		.hts_def = 0x08ca,
+		.vts_def = 0x04b0,
+		.bus_fmt = MEDIA_BUS_FMT_SBGGR10_1X10,
+		.reg_list = sc223a_linear_10_1920x1080_30fps_regs,
+		.hdr_mode = NO_HDR,
+		.vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_0,
+	}
+};
+
+static const s64 link_freq_menu_items[] = {
+	SC223A_LINK_FREQ_405
+};
+
+static const char * const sc223a_test_pattern_menu[] = {
+	"Disabled",
+	"Vertical Color Bar Type 1",
+	"Vertical Color Bar Type 2",
+	"Vertical Color Bar Type 3",
+	"Vertical Color Bar Type 4"
+};
+
+/* Write registers up to 4 at a time */
+static int sc223a_write_reg(struct i2c_client *client, u16 reg,
+			    u32 len, u32 val)
+{
+	u32 buf_i, val_i;
+	u8 buf[6];
+	u8 *val_p;
+	__be32 val_be;
+
+	if (len > 4)
+		return -EINVAL;
+
+	buf[0] = reg >> 8;
+	buf[1] = reg & 0xff;
+
+	val_be = cpu_to_be32(val);
+	val_p = (u8 *)&val_be;
+	buf_i = 2;
+	val_i = 4 - len;
+
+	while (val_i < 4)
+		buf[buf_i++] = val_p[val_i++];
+
+	if (i2c_master_send(client, buf, len + 2) != len + 2)
+		return -EIO;
+	return 0;
+}
+
+static int sc223a_write_array(struct i2c_client *client,
+			       const struct regval *regs)
+{
+	u32 i;
+	int ret = 0;
+
+	for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++)
+		ret = sc223a_write_reg(client, regs[i].addr,
+					SC223A_REG_VALUE_08BIT, regs[i].val);
+
+	return ret;
+}
+
+/* Read registers up to 4 at a time */
+static int sc223a_read_reg(struct i2c_client *client, u16 reg, unsigned int len,
+			    u32 *val)
+{
+	struct i2c_msg msgs[2];
+	u8 *data_be_p;
+	__be32 data_be = 0;
+	__be16 reg_addr_be = cpu_to_be16(reg);
+	int ret;
+
+	if (len > 4 || !len)
+		return -EINVAL;
+
+	data_be_p = (u8 *)&data_be;
+	/* Write register address */
+	msgs[0].addr = client->addr;
+	msgs[0].flags = 0;
+	msgs[0].len = 2;
+	msgs[0].buf = (u8 *)&reg_addr_be;
+
+	/* Read data from register */
+	msgs[1].addr = client->addr;
+	msgs[1].flags = I2C_M_RD;
+	msgs[1].len = len;
+	msgs[1].buf = &data_be_p[4 - len];
+
+	ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+	if (ret != ARRAY_SIZE(msgs))
+		return -EIO;
+
+	*val = be32_to_cpu(data_be);
+
+	return 0;
+}
+
+static int sc223a_set_gain_reg(struct sc223a *sc223a, u32 gain)
+{
+	struct i2c_client *client = sc223a->client;
+	u32 coarse_again = 0, coarse_dgain = 0, fine_dgain = 0;
+	int ret = 0, gain_factor;
+
+	if (gain < 128)
+		gain = 128;
+	else if (gain > SC223A_GAIN_MAX)
+		gain = SC223A_GAIN_MAX;
+
+	gain_factor = gain * 1000 / 128;
+	if (gain_factor < 1810) {
+		coarse_again = 0x00;
+		coarse_dgain = 0x00;
+		fine_dgain = gain_factor * 128 / 1000;
+	} else if (gain_factor < 1810 * 2) {
+		coarse_again = 0x40;
+		coarse_dgain = 0x00;
+		fine_dgain = gain_factor * 128 / 1810;
+	} else if (gain_factor < 1810 * 4) {
+		coarse_again = 0x48;
+		coarse_dgain = 0x00;
+		fine_dgain = gain_factor * 128 / 1810 / 2;
+	} else if (gain_factor < 1810 * 8) {
+		coarse_again = 0x49;
+		coarse_dgain = 0x00;
+		fine_dgain = gain_factor * 128 / 1810 / 4;
+	} else if (gain_factor < 1810 * 16) {
+		coarse_again = 0x4b;
+		coarse_dgain = 0x00;
+		fine_dgain = gain_factor * 128 / 1810 / 8;
+	} else if (gain_factor < 1810 * 32) {
+		coarse_again = 0x4f;
+		coarse_dgain = 0x00;
+		fine_dgain = gain_factor * 128 / 1810 / 16;
+	} else if (gain_factor < 1810 * 64) {
+		//open dgain begin  max digital gain 4X
+		coarse_again = 0x5f;
+		coarse_dgain = 0x00;
+		fine_dgain = gain_factor * 128 / 1810 / 32;
+	} else if (gain_factor < 1810 * 128) {
+		coarse_again = 0x5f;
+		coarse_dgain = 0x01;
+		fine_dgain = gain_factor * 128 / 1810 / 64;
+	} else {
+		coarse_again = 0x5f;
+		coarse_dgain = 0x03;
+		fine_dgain = 0x80;
+	}
+	dev_dbg(&client->dev, "c_again: 0x%x, c_dgain: 0x%x, f_dgain: 0x%0x\n",
+		    coarse_again, coarse_dgain, fine_dgain);
+
+	ret = sc223a_write_reg(sc223a->client,
+				SC223A_REG_DIG_GAIN,
+				SC223A_REG_VALUE_08BIT,
+				coarse_dgain);
+	ret |= sc223a_write_reg(sc223a->client,
+				 SC223A_REG_DIG_FINE_GAIN,
+				 SC223A_REG_VALUE_08BIT,
+				 fine_dgain);
+	ret |= sc223a_write_reg(sc223a->client,
+				 SC223A_REG_ANA_GAIN,
+				 SC223A_REG_VALUE_08BIT,
+				 coarse_again);
+
+	return ret;
+}
+
+static int sc223a_get_reso_dist(const struct sc223a_mode *mode,
+				 struct v4l2_mbus_framefmt *framefmt)
+{
+	return abs(mode->width - framefmt->width) +
+	       abs(mode->height - framefmt->height);
+}
+
+static const struct sc223a_mode *
+sc223a_find_best_fit(struct v4l2_subdev_format *fmt)
+{
+	struct v4l2_mbus_framefmt *framefmt = &fmt->format;
+	int dist;
+	int cur_best_fit = 0;
+	int cur_best_fit_dist = -1;
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(supported_modes); i++) {
+		dist = sc223a_get_reso_dist(&supported_modes[i], framefmt);
+		if (cur_best_fit_dist == -1 || dist < cur_best_fit_dist) {
+			cur_best_fit_dist = dist;
+			cur_best_fit = i;
+		}
+	}
+
+	return &supported_modes[cur_best_fit];
+}
+
+static int sc223a_set_fmt(struct v4l2_subdev *sd,
+			   struct v4l2_subdev_pad_config *cfg,
+			   struct v4l2_subdev_format *fmt)
+{
+	struct sc223a *sc223a = to_sc223a(sd);
+	const struct sc223a_mode *mode;
+	s64 h_blank, vblank_def;
+
+	mutex_lock(&sc223a->mutex);
+
+	mode = sc223a_find_best_fit(fmt);
+	fmt->format.code = mode->bus_fmt;
+	fmt->format.width = mode->width;
+	fmt->format.height = mode->height;
+	fmt->format.field = V4L2_FIELD_NONE;
+	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+		*v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = fmt->format;
+#else
+		mutex_unlock(&sc223a->mutex);
+		return -ENOTTY;
+#endif
+	} else {
+		sc223a->cur_mode = mode;
+		h_blank = mode->hts_def - mode->width;
+		__v4l2_ctrl_modify_range(sc223a->hblank, h_blank,
+					 h_blank, 1, h_blank);
+		vblank_def = mode->vts_def - mode->height;
+		__v4l2_ctrl_modify_range(sc223a->vblank, vblank_def,
+					 SC223A_VTS_MAX - mode->height,
+					 1, vblank_def);
+		sc223a->cur_fps = mode->max_fps;
+	}
+
+	mutex_unlock(&sc223a->mutex);
+
+	return 0;
+}
+
+static int sc223a_get_fmt(struct v4l2_subdev *sd,
+			   struct v4l2_subdev_pad_config *cfg,
+			   struct v4l2_subdev_format *fmt)
+{
+	struct sc223a *sc223a = to_sc223a(sd);
+	const struct sc223a_mode *mode = sc223a->cur_mode;
+
+	mutex_lock(&sc223a->mutex);
+	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+		fmt->format = *v4l2_subdev_get_try_format(sd, cfg, fmt->pad);
+#else
+		mutex_unlock(&sc223a->mutex);
+		return -ENOTTY;
+#endif
+	} else {
+		fmt->format.width = mode->width;
+		fmt->format.height = mode->height;
+		fmt->format.code = mode->bus_fmt;
+		fmt->format.field = V4L2_FIELD_NONE;
+		/* format info: width/height/data type/virctual channel */
+		if (fmt->pad < PAD_MAX && mode->hdr_mode != NO_HDR)
+			fmt->reserved[0] = mode->vc[fmt->pad];
+		else
+			fmt->reserved[0] = mode->vc[PAD0];
+	}
+	mutex_unlock(&sc223a->mutex);
+
+	return 0;
+}
+
+static int sc223a_enum_mbus_code(struct v4l2_subdev *sd,
+				  struct v4l2_subdev_pad_config *cfg,
+				  struct v4l2_subdev_mbus_code_enum *code)
+{
+	struct sc223a *sc223a = to_sc223a(sd);
+
+	if (code->index != 0)
+		return -EINVAL;
+	code->code = sc223a->cur_mode->bus_fmt;
+
+	return 0;
+}
+
+static int sc223a_enum_frame_sizes(struct v4l2_subdev *sd,
+				    struct v4l2_subdev_pad_config *cfg,
+				    struct v4l2_subdev_frame_size_enum *fse)
+{
+	if (fse->index >= ARRAY_SIZE(supported_modes))
+		return -EINVAL;
+
+	if (fse->code != supported_modes[0].bus_fmt)
+		return -EINVAL;
+
+	fse->min_width  = supported_modes[fse->index].width;
+	fse->max_width  = supported_modes[fse->index].width;
+	fse->max_height = supported_modes[fse->index].height;
+	fse->min_height = supported_modes[fse->index].height;
+
+	return 0;
+}
+
+static int sc223a_enable_test_pattern(struct sc223a *sc223a, u32 pattern)
+{
+	u32 val = 0;
+	int ret = 0;
+
+	ret = sc223a_read_reg(sc223a->client, SC223A_REG_TEST_PATTERN,
+			       SC223A_REG_VALUE_08BIT, &val);
+	if (pattern)
+		val |= SC223A_TEST_PATTERN_BIT_MASK;
+	else
+		val &= ~SC223A_TEST_PATTERN_BIT_MASK;
+
+	ret |= sc223a_write_reg(sc223a->client, SC223A_REG_TEST_PATTERN,
+				 SC223A_REG_VALUE_08BIT, val);
+	return ret;
+}
+
+static int sc223a_g_frame_interval(struct v4l2_subdev *sd,
+				    struct v4l2_subdev_frame_interval *fi)
+{
+	struct sc223a *sc223a = to_sc223a(sd);
+	const struct sc223a_mode *mode = sc223a->cur_mode;
+
+	if (sc223a->streaming)
+		fi->interval = sc223a->cur_fps;
+	else
+		fi->interval = mode->max_fps;
+
+	return 0;
+}
+
+static int sc223a_g_mbus_config(struct v4l2_subdev *sd,
+				unsigned int pad_id,
+				struct v4l2_mbus_config *config)
+{
+	struct sc223a *sc223a = to_sc223a(sd);
+	const struct sc223a_mode *mode = sc223a->cur_mode;
+
+	u32 val = 1 << (SC223A_LANES - 1) |
+		V4L2_MBUS_CSI2_CHANNEL_0 |
+		V4L2_MBUS_CSI2_CONTINUOUS_CLOCK;
+
+	if (mode->hdr_mode != NO_HDR)
+		val |= V4L2_MBUS_CSI2_CHANNEL_1;
+	if (mode->hdr_mode == HDR_X3)
+		val |= V4L2_MBUS_CSI2_CHANNEL_2;
+
+	config->type = V4L2_MBUS_CSI2_DPHY;
+	config->flags = val;
+
+	return 0;
+}
+
+static void sc223a_get_module_inf(struct sc223a *sc223a,
+				   struct rkmodule_inf *inf)
+{
+	memset(inf, 0, sizeof(*inf));
+	strscpy(inf->base.sensor, SC223A_NAME, sizeof(inf->base.sensor));
+	strscpy(inf->base.module, sc223a->module_name,
+		sizeof(inf->base.module));
+	strscpy(inf->base.lens, sc223a->len_name, sizeof(inf->base.lens));
+}
+
+static long sc223a_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
+{
+	struct sc223a *sc223a = to_sc223a(sd);
+	struct rkmodule_hdr_cfg *hdr;
+	u32 i, h, w;
+	long ret = 0;
+	u32 stream = 0;
+
+	switch (cmd) {
+	case RKMODULE_GET_MODULE_INFO:
+		sc223a_get_module_inf(sc223a, (struct rkmodule_inf *)arg);
+		break;
+	case RKMODULE_GET_HDR_CFG:
+		hdr = (struct rkmodule_hdr_cfg *)arg;
+		hdr->esp.mode = HDR_NORMAL_VC;
+		hdr->hdr_mode = sc223a->cur_mode->hdr_mode;
+		break;
+	case RKMODULE_SET_HDR_CFG:
+		hdr = (struct rkmodule_hdr_cfg *)arg;
+		w = sc223a->cur_mode->width;
+		h = sc223a->cur_mode->height;
+		for (i = 0; i < ARRAY_SIZE(supported_modes); i++) {
+			if (w == supported_modes[i].width &&
+			    h == supported_modes[i].height &&
+			    supported_modes[i].hdr_mode == hdr->hdr_mode) {
+				sc223a->cur_mode = &supported_modes[i];
+				break;
+			}
+		}
+		if (i == ARRAY_SIZE(supported_modes)) {
+			dev_err(&sc223a->client->dev,
+				"not find hdr mode:%d %dx%d config\n",
+				hdr->hdr_mode, w, h);
+			ret = -EINVAL;
+		} else {
+			w = sc223a->cur_mode->hts_def - sc223a->cur_mode->width;
+			h = sc223a->cur_mode->vts_def - sc223a->cur_mode->height;
+			__v4l2_ctrl_modify_range(sc223a->hblank, w, w, 1, w);
+			__v4l2_ctrl_modify_range(sc223a->vblank, h,
+						 SC223A_VTS_MAX - sc223a->cur_mode->height, 1, h);
+			sc223a->cur_fps = sc223a->cur_mode->max_fps;
+		}
+		break;
+	case PREISP_CMD_SET_HDRAE_EXP:
+		break;
+	case RKMODULE_SET_QUICK_STREAM:
+
+		stream = *((u32 *)arg);
+
+		if (stream)
+			ret = sc223a_write_reg(sc223a->client, SC223A_REG_CTRL_MODE,
+				 SC223A_REG_VALUE_08BIT, SC223A_MODE_STREAMING);
+		else
+			ret = sc223a_write_reg(sc223a->client, SC223A_REG_CTRL_MODE,
+				 SC223A_REG_VALUE_08BIT, SC223A_MODE_SW_STANDBY);
+		break;
+	default:
+		ret = -ENOIOCTLCMD;
+		break;
+	}
+
+	return ret;
+}
+
+#ifdef CONFIG_COMPAT
+static long sc223a_compat_ioctl32(struct v4l2_subdev *sd,
+				   unsigned int cmd, unsigned long arg)
+{
+	void __user *up = compat_ptr(arg);
+	struct rkmodule_inf *inf;
+	struct rkmodule_hdr_cfg *hdr;
+	struct preisp_hdrae_exp_s *hdrae;
+	long ret;
+	u32 stream = 0;
+
+	switch (cmd) {
+	case RKMODULE_GET_MODULE_INFO:
+		inf = kzalloc(sizeof(*inf), GFP_KERNEL);
+		if (!inf) {
+			ret = -ENOMEM;
+			return ret;
+		}
+
+		ret = sc223a_ioctl(sd, cmd, inf);
+		if (!ret) {
+			if (copy_to_user(up, inf, sizeof(*inf)))
+				ret = -EFAULT;
+		}
+		kfree(inf);
+		break;
+	case RKMODULE_GET_HDR_CFG:
+		hdr = kzalloc(sizeof(*hdr), GFP_KERNEL);
+		if (!hdr) {
+			ret = -ENOMEM;
+			return ret;
+		}
+
+		ret = sc223a_ioctl(sd, cmd, hdr);
+		if (!ret) {
+			if (copy_to_user(up, hdr, sizeof(*hdr)))
+				ret = -EFAULT;
+		}
+		kfree(hdr);
+		break;
+	case RKMODULE_SET_HDR_CFG:
+		hdr = kzalloc(sizeof(*hdr), GFP_KERNEL);
+		if (!hdr) {
+			ret = -ENOMEM;
+			return ret;
+		}
+
+		ret = copy_from_user(hdr, up, sizeof(*hdr));
+		if (!ret)
+			ret = sc223a_ioctl(sd, cmd, hdr);
+		else
+			ret = -EFAULT;
+		kfree(hdr);
+		break;
+	case PREISP_CMD_SET_HDRAE_EXP:
+		hdrae = kzalloc(sizeof(*hdrae), GFP_KERNEL);
+		if (!hdrae) {
+			ret = -ENOMEM;
+			return ret;
+		}
+
+		ret = copy_from_user(hdrae, up, sizeof(*hdrae));
+		if (!ret)
+			ret = sc223a_ioctl(sd, cmd, hdrae);
+		else
+			ret = -EFAULT;
+		kfree(hdrae);
+		break;
+	case RKMODULE_SET_QUICK_STREAM:
+		ret = copy_from_user(&stream, up, sizeof(u32));
+		if (!ret)
+			ret = sc223a_ioctl(sd, cmd, &stream);
+		else
+			ret = -EFAULT;
+		break;
+	default:
+		ret = -ENOIOCTLCMD;
+		break;
+	}
+
+	return ret;
+}
+#endif
+
+static int __sc223a_start_stream(struct sc223a *sc223a)
+{
+	int ret;
+
+	if (!sc223a->is_thunderboot) {
+		ret = sc223a_write_array(sc223a->client, sc223a->cur_mode->reg_list);
+		if (ret)
+			return ret;
+		/* In case these controls are set before streaming */
+		ret = __v4l2_ctrl_handler_setup(&sc223a->ctrl_handler);
+		if (ret)
+			return ret;
+		if (sc223a->has_init_exp && sc223a->cur_mode->hdr_mode != NO_HDR) {
+			ret = sc223a_ioctl(&sc223a->subdev, PREISP_CMD_SET_HDRAE_EXP,
+				&sc223a->init_hdrae_exp);
+			if (ret) {
+				dev_err(&sc223a->client->dev,
+					"init exp fail in hdr mode\n");
+				return ret;
+			}
+		}
+	}
+	return sc223a_write_reg(sc223a->client, SC223A_REG_CTRL_MODE,
+				SC223A_REG_VALUE_08BIT, SC223A_MODE_STREAMING);
+}
+
+static int __sc223a_stop_stream(struct sc223a *sc223a)
+{
+	sc223a->has_init_exp = false;
+	if (sc223a->is_thunderboot) {
+		sc223a->is_first_streamoff = true;
+		pm_runtime_put(&sc223a->client->dev);
+	}
+	return sc223a_write_reg(sc223a->client, SC223A_REG_CTRL_MODE,
+				 SC223A_REG_VALUE_08BIT, SC223A_MODE_SW_STANDBY);
+}
+
+static int __sc223a_power_on(struct sc223a *sc223a);
+static int sc223a_s_stream(struct v4l2_subdev *sd, int on)
+{
+	struct sc223a *sc223a = to_sc223a(sd);
+	struct i2c_client *client = sc223a->client;
+	int ret = 0;
+
+	mutex_lock(&sc223a->mutex);
+	on = !!on;
+	if (on == sc223a->streaming)
+		goto unlock_and_return;
+	if (on) {
+		if (sc223a->is_thunderboot && rkisp_tb_get_state() == RKISP_TB_NG) {
+			sc223a->is_thunderboot = false;
+			__sc223a_power_on(sc223a);
+		}
+		ret = pm_runtime_get_sync(&client->dev);
+		if (ret < 0) {
+			pm_runtime_put_noidle(&client->dev);
+			goto unlock_and_return;
+		}
+		ret = __sc223a_start_stream(sc223a);
+		if (ret) {
+			v4l2_err(sd, "start stream failed while write regs\n");
+			pm_runtime_put(&client->dev);
+			goto unlock_and_return;
+		}
+	} else {
+		__sc223a_stop_stream(sc223a);
+		pm_runtime_put(&client->dev);
+	}
+
+	sc223a->streaming = on;
+unlock_and_return:
+	mutex_unlock(&sc223a->mutex);
+	return ret;
+}
+
+static int sc223a_s_power(struct v4l2_subdev *sd, int on)
+{
+	struct sc223a *sc223a = to_sc223a(sd);
+	struct i2c_client *client = sc223a->client;
+	int ret = 0;
+
+	mutex_lock(&sc223a->mutex);
+
+	/* If the power state is not modified - no work to do. */
+	if (sc223a->power_on == !!on)
+		goto unlock_and_return;
+
+	if (on) {
+		ret = pm_runtime_get_sync(&client->dev);
+		if (ret < 0) {
+			pm_runtime_put_noidle(&client->dev);
+			goto unlock_and_return;
+		}
+
+		if (!sc223a->is_thunderboot) {
+			ret = sc223a_write_array(sc223a->client, sc223a_global_regs);
+			if (ret) {
+				v4l2_err(sd, "could not set init registers\n");
+				pm_runtime_put_noidle(&client->dev);
+				goto unlock_and_return;
+			}
+		}
+
+		sc223a->power_on = true;
+	} else {
+		pm_runtime_put(&client->dev);
+		sc223a->power_on = false;
+	}
+
+unlock_and_return:
+	mutex_unlock(&sc223a->mutex);
+
+	return ret;
+}
+
+/* Calculate the delay in us by clock rate and clock cycles */
+static inline u32 sc223a_cal_delay(u32 cycles)
+{
+	return DIV_ROUND_UP(cycles, SC223A_XVCLK_FREQ / 1000 / 1000);
+}
+
+static int __sc223a_power_on(struct sc223a *sc223a)
+{
+	int ret;
+	u32 delay_us;
+	struct device *dev = &sc223a->client->dev;
+
+	if (!IS_ERR_OR_NULL(sc223a->pins_default)) {
+		ret = pinctrl_select_state(sc223a->pinctrl,
+					   sc223a->pins_default);
+		if (ret < 0)
+			dev_err(dev, "could not set pins\n");
+	}
+	ret = clk_set_rate(sc223a->xvclk, SC223A_XVCLK_FREQ);
+	if (ret < 0)
+		dev_warn(dev, "Failed to set xvclk rate (24MHz)\n");
+	if (clk_get_rate(sc223a->xvclk) != SC223A_XVCLK_FREQ)
+		dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n");
+	ret = clk_prepare_enable(sc223a->xvclk);
+	if (ret < 0) {
+		dev_err(dev, "Failed to enable xvclk\n");
+		return ret;
+	}
+
+	if (sc223a->is_thunderboot)
+		return 0;
+
+	if (!IS_ERR(sc223a->reset_gpio))
+		gpiod_set_value_cansleep(sc223a->reset_gpio, 0);
+
+	ret = regulator_bulk_enable(SC223A_NUM_SUPPLIES, sc223a->supplies);
+	if (ret < 0) {
+		dev_err(dev, "Failed to enable regulators\n");
+		goto disable_clk;
+	}
+
+	if (!IS_ERR(sc223a->reset_gpio))
+		gpiod_set_value_cansleep(sc223a->reset_gpio, 1);
+
+	usleep_range(500, 1000);
+
+	if (!IS_ERR(sc223a->pwdn_gpio))
+		gpiod_set_value_cansleep(sc223a->pwdn_gpio, 1);
+
+	if (!IS_ERR(sc223a->reset_gpio))
+		usleep_range(6000, 8000);
+	else
+		usleep_range(12000, 16000);
+
+	/* 8192 cycles prior to first SCCB transaction */
+	delay_us = sc223a_cal_delay(8192);
+	usleep_range(delay_us, delay_us * 2);
+
+	return 0;
+
+disable_clk:
+	clk_disable_unprepare(sc223a->xvclk);
+
+	return ret;
+}
+
+static void __sc223a_power_off(struct sc223a *sc223a)
+{
+	int ret;
+	struct device *dev = &sc223a->client->dev;
+
+	clk_disable_unprepare(sc223a->xvclk);
+	if (sc223a->is_thunderboot) {
+		if (sc223a->is_first_streamoff) {
+			sc223a->is_thunderboot = false;
+			sc223a->is_first_streamoff = false;
+		} else {
+			return;
+		}
+	}
+
+	if (!IS_ERR(sc223a->pwdn_gpio))
+		gpiod_set_value_cansleep(sc223a->pwdn_gpio, 0);
+	clk_disable_unprepare(sc223a->xvclk);
+	if (!IS_ERR(sc223a->reset_gpio))
+		gpiod_set_value_cansleep(sc223a->reset_gpio, 0);
+	if (!IS_ERR_OR_NULL(sc223a->pins_sleep)) {
+		ret = pinctrl_select_state(sc223a->pinctrl,
+					   sc223a->pins_sleep);
+		if (ret < 0)
+			dev_dbg(dev, "could not set pins\n");
+	}
+	regulator_bulk_disable(SC223A_NUM_SUPPLIES, sc223a->supplies);
+}
+
+static int sc223a_runtime_resume(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct sc223a *sc223a = to_sc223a(sd);
+
+	return __sc223a_power_on(sc223a);
+}
+
+static int sc223a_runtime_suspend(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct sc223a *sc223a = to_sc223a(sd);
+
+	__sc223a_power_off(sc223a);
+
+	return 0;
+}
+
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+static int sc223a_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+	struct sc223a *sc223a = to_sc223a(sd);
+	struct v4l2_mbus_framefmt *try_fmt =
+				v4l2_subdev_get_try_format(sd, fh->pad, 0);
+	const struct sc223a_mode *def_mode = &supported_modes[0];
+
+	mutex_lock(&sc223a->mutex);
+	/* Initialize try_fmt */
+	try_fmt->width = def_mode->width;
+	try_fmt->height = def_mode->height;
+	try_fmt->code = def_mode->bus_fmt;
+	try_fmt->field = V4L2_FIELD_NONE;
+
+	mutex_unlock(&sc223a->mutex);
+	/* No crop or compose */
+
+	return 0;
+}
+#endif
+
+static int sc223a_enum_frame_interval(struct v4l2_subdev *sd,
+				       struct v4l2_subdev_pad_config *cfg,
+				       struct v4l2_subdev_frame_interval_enum *fie)
+{
+	if (fie->index >= ARRAY_SIZE(supported_modes))
+		return -EINVAL;
+
+	fie->code = supported_modes[fie->index].bus_fmt;
+	fie->width = supported_modes[fie->index].width;
+	fie->height = supported_modes[fie->index].height;
+	fie->interval = supported_modes[fie->index].max_fps;
+	fie->reserved[0] = supported_modes[fie->index].hdr_mode;
+	return 0;
+}
+
+static const struct dev_pm_ops sc223a_pm_ops = {
+	SET_RUNTIME_PM_OPS(sc223a_runtime_suspend,
+			   sc223a_runtime_resume, NULL)
+};
+
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+static const struct v4l2_subdev_internal_ops sc223a_internal_ops = {
+	.open = sc223a_open,
+};
+#endif
+
+static const struct v4l2_subdev_core_ops sc223a_core_ops = {
+	.s_power = sc223a_s_power,
+	.ioctl = sc223a_ioctl,
+#ifdef CONFIG_COMPAT
+	.compat_ioctl32 = sc223a_compat_ioctl32,
+#endif
+};
+
+static const struct v4l2_subdev_video_ops sc223a_video_ops = {
+	.s_stream = sc223a_s_stream,
+	.g_frame_interval = sc223a_g_frame_interval,
+};
+
+static const struct v4l2_subdev_pad_ops sc223a_pad_ops = {
+	.enum_mbus_code = sc223a_enum_mbus_code,
+	.enum_frame_size = sc223a_enum_frame_sizes,
+	.enum_frame_interval = sc223a_enum_frame_interval,
+	.get_fmt = sc223a_get_fmt,
+	.set_fmt = sc223a_set_fmt,
+	.get_mbus_config = sc223a_g_mbus_config,
+};
+
+static const struct v4l2_subdev_ops sc223a_subdev_ops = {
+	.core	= &sc223a_core_ops,
+	.video	= &sc223a_video_ops,
+	.pad	= &sc223a_pad_ops,
+};
+
+static void sc223a_modify_fps_info(struct sc223a *sc223a)
+{
+	const struct sc223a_mode *mode = sc223a->cur_mode;
+
+	sc223a->cur_fps.denominator = mode->max_fps.denominator * mode->vts_def /
+				      sc223a->cur_vts;
+}
+
+static int sc223a_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct sc223a *sc223a = container_of(ctrl->handler,
+					       struct sc223a, ctrl_handler);
+	struct i2c_client *client = sc223a->client;
+	s64 max;
+	int ret = 0;
+	u32 val = 0;
+
+	/* Propagate change of current control to all related controls */
+	switch (ctrl->id) {
+	case V4L2_CID_VBLANK:
+		/* Update max exposure while meeting expected vblanking */
+		max = sc223a->cur_mode->height + ctrl->val - 10;
+		__v4l2_ctrl_modify_range(sc223a->exposure,
+					 sc223a->exposure->minimum, max,
+					 sc223a->exposure->step,
+					 sc223a->exposure->default_value);
+		break;
+	}
+
+	if (!pm_runtime_get_if_in_use(&client->dev))
+		return 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_EXPOSURE:
+		dev_dbg(&client->dev, "set exposure 0x%x\n", ctrl->val);
+		if (sc223a->cur_mode->hdr_mode == NO_HDR) {
+			val = ctrl->val * 2;
+			/* 4 least significant bits of expsoure are fractional part */
+			ret = sc223a_write_reg(sc223a->client,
+						SC223A_REG_EXPOSURE_H,
+						SC223A_REG_VALUE_08BIT,
+						SC223A_FETCH_EXP_H(val));
+			ret |= sc223a_write_reg(sc223a->client,
+						 SC223A_REG_EXPOSURE_M,
+						 SC223A_REG_VALUE_08BIT,
+						 SC223A_FETCH_EXP_M(val));
+			ret |= sc223a_write_reg(sc223a->client,
+						 SC223A_REG_EXPOSURE_L,
+						 SC223A_REG_VALUE_08BIT,
+						 SC223A_FETCH_EXP_L(val));
+		}
+		break;
+	case V4L2_CID_ANALOGUE_GAIN:
+		dev_dbg(&client->dev, "set gain 0x%x\n", ctrl->val);
+		if (sc223a->cur_mode->hdr_mode == NO_HDR)
+			ret = sc223a_set_gain_reg(sc223a, ctrl->val);
+		break;
+	case V4L2_CID_VBLANK:
+		dev_dbg(&client->dev, "set vblank 0x%x\n", ctrl->val);
+		ret = sc223a_write_reg(sc223a->client,
+					SC223A_REG_VTS_H,
+					SC223A_REG_VALUE_08BIT,
+					(ctrl->val + sc223a->cur_mode->height)
+					>> 8);
+		ret |= sc223a_write_reg(sc223a->client,
+					 SC223A_REG_VTS_L,
+					 SC223A_REG_VALUE_08BIT,
+					 (ctrl->val + sc223a->cur_mode->height)
+					 & 0xff);
+		sc223a->cur_vts = ctrl->val + sc223a->cur_mode->height;
+		sc223a_modify_fps_info(sc223a);
+		break;
+	case V4L2_CID_TEST_PATTERN:
+		ret = sc223a_enable_test_pattern(sc223a, ctrl->val);
+		break;
+	case V4L2_CID_HFLIP:
+		ret = sc223a_read_reg(sc223a->client, SC223A_FLIP_MIRROR_REG,
+				       SC223A_REG_VALUE_08BIT, &val);
+		ret |= sc223a_write_reg(sc223a->client, SC223A_FLIP_MIRROR_REG,
+					 SC223A_REG_VALUE_08BIT,
+					 SC223A_FETCH_MIRROR(val, ctrl->val));
+		break;
+	case V4L2_CID_VFLIP:
+		ret = sc223a_read_reg(sc223a->client, SC223A_FLIP_MIRROR_REG,
+				       SC223A_REG_VALUE_08BIT, &val);
+		ret |= sc223a_write_reg(sc223a->client, SC223A_FLIP_MIRROR_REG,
+					 SC223A_REG_VALUE_08BIT,
+					 SC223A_FETCH_FLIP(val, ctrl->val));
+		break;
+	default:
+		dev_warn(&client->dev, "%s Unhandled id:0x%x, val:0x%x\n",
+			 __func__, ctrl->id, ctrl->val);
+		break;
+	}
+
+	pm_runtime_put(&client->dev);
+
+	return ret;
+}
+
+static const struct v4l2_ctrl_ops sc223a_ctrl_ops = {
+	.s_ctrl = sc223a_set_ctrl,
+};
+
+static int sc223a_initialize_controls(struct sc223a *sc223a)
+{
+	const struct sc223a_mode *mode;
+	struct v4l2_ctrl_handler *handler;
+	struct v4l2_ctrl *ctrl;
+	s64 exposure_max, vblank_def;
+	u32 h_blank;
+	int ret;
+
+	handler = &sc223a->ctrl_handler;
+	mode = sc223a->cur_mode;
+	ret = v4l2_ctrl_handler_init(handler, 9);
+	if (ret)
+		return ret;
+	handler->lock = &sc223a->mutex;
+
+	ctrl = v4l2_ctrl_new_int_menu(handler, NULL, V4L2_CID_LINK_FREQ,
+				      0, 0, link_freq_menu_items);
+	if (ctrl)
+		ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+	v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE,
+			  0, PIXEL_RATE_WITH_405M_10BIT, 1, PIXEL_RATE_WITH_405M_10BIT);
+
+	h_blank = mode->hts_def - mode->width;
+	sc223a->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK,
+					    h_blank, h_blank, 1, h_blank);
+	if (sc223a->hblank)
+		sc223a->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+	vblank_def = mode->vts_def - mode->height;
+	sc223a->vblank = v4l2_ctrl_new_std(handler, &sc223a_ctrl_ops,
+					    V4L2_CID_VBLANK, vblank_def,
+					    SC223A_VTS_MAX - mode->height,
+					    1, vblank_def);
+	exposure_max = mode->vts_def - 10;
+	sc223a->exposure = v4l2_ctrl_new_std(handler, &sc223a_ctrl_ops,
+					      V4L2_CID_EXPOSURE, SC223A_EXPOSURE_MIN,
+					      exposure_max, SC223A_EXPOSURE_STEP,
+					      mode->exp_def);
+	sc223a->anal_gain = v4l2_ctrl_new_std(handler, &sc223a_ctrl_ops,
+					       V4L2_CID_ANALOGUE_GAIN, SC223A_GAIN_MIN,
+					       SC223A_GAIN_MAX, SC223A_GAIN_STEP,
+					       SC223A_GAIN_DEFAULT);
+	sc223a->test_pattern = v4l2_ctrl_new_std_menu_items(handler,
+							    &sc223a_ctrl_ops,
+					V4L2_CID_TEST_PATTERN,
+					ARRAY_SIZE(sc223a_test_pattern_menu) - 1,
+					0, 0, sc223a_test_pattern_menu);
+	v4l2_ctrl_new_std(handler, &sc223a_ctrl_ops,
+				V4L2_CID_HFLIP, 0, 1, 1, 0);
+	v4l2_ctrl_new_std(handler, &sc223a_ctrl_ops,
+				V4L2_CID_VFLIP, 0, 1, 1, 0);
+	if (handler->error) {
+		ret = handler->error;
+		dev_err(&sc223a->client->dev,
+			"Failed to init controls(%d)\n", ret);
+		goto err_free_handler;
+	}
+
+	sc223a->subdev.ctrl_handler = handler;
+	sc223a->has_init_exp = false;
+	sc223a->cur_fps = mode->max_fps;
+
+	return 0;
+
+err_free_handler:
+	v4l2_ctrl_handler_free(handler);
+
+	return ret;
+}
+
+static int sc223a_check_sensor_id(struct sc223a *sc223a,
+				   struct i2c_client *client)
+{
+	struct device *dev = &sc223a->client->dev;
+	u32 id = 0;
+	int ret;
+
+	if (sc223a->is_thunderboot) {
+		dev_info(dev, "Enable thunderboot mode, skip sensor id check\n");
+		return 0;
+	}
+
+	ret = sc223a_read_reg(client, SC223A_REG_CHIP_ID,
+			       SC223A_REG_VALUE_16BIT, &id);
+	if (id != CHIP_ID) {
+		dev_err(dev, "Unexpected sensor id(%06x), ret(%d)\n", id, ret);
+		return -ENODEV;
+	}
+
+	dev_info(dev, "Detected OV%06x sensor\n", CHIP_ID);
+
+	return 0;
+}
+
+static int sc223a_configure_regulators(struct sc223a *sc223a)
+{
+	unsigned int i;
+
+	for (i = 0; i < SC223A_NUM_SUPPLIES; i++)
+		sc223a->supplies[i].supply = sc223a_supply_names[i];
+
+	return devm_regulator_bulk_get(&sc223a->client->dev,
+				       SC223A_NUM_SUPPLIES,
+				       sc223a->supplies);
+}
+
+static int sc223a_probe(struct i2c_client *client,
+			 const struct i2c_device_id *id)
+{
+	struct device *dev = &client->dev;
+	struct device_node *node = dev->of_node;
+	struct sc223a *sc223a;
+	struct v4l2_subdev *sd;
+	char facing[2];
+	int ret;
+	int i, hdr_mode = 0;
+
+	dev_info(dev, "driver version: %02x.%02x.%02x",
+		 DRIVER_VERSION >> 16,
+		 (DRIVER_VERSION & 0xff00) >> 8,
+		 DRIVER_VERSION & 0x00ff);
+
+	sc223a = devm_kzalloc(dev, sizeof(*sc223a), GFP_KERNEL);
+	if (!sc223a)
+		return -ENOMEM;
+
+	ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX,
+				   &sc223a->module_index);
+	ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING,
+				       &sc223a->module_facing);
+	ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME,
+				       &sc223a->module_name);
+	ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME,
+				       &sc223a->len_name);
+	if (ret) {
+		dev_err(dev, "could not get module information!\n");
+		return -EINVAL;
+	}
+
+	sc223a->is_thunderboot = IS_ENABLED(CONFIG_VIDEO_ROCKCHIP_THUNDER_BOOT_ISP);
+
+	sc223a->client = client;
+	for (i = 0; i < ARRAY_SIZE(supported_modes); i++) {
+		if (hdr_mode == supported_modes[i].hdr_mode) {
+			sc223a->cur_mode = &supported_modes[i];
+			break;
+		}
+	}
+	if (i == ARRAY_SIZE(supported_modes))
+		sc223a->cur_mode = &supported_modes[0];
+
+	sc223a->xvclk = devm_clk_get(dev, "xvclk");
+	if (IS_ERR(sc223a->xvclk)) {
+		dev_err(dev, "Failed to get xvclk\n");
+		return -EINVAL;
+	}
+
+	if (sc223a->is_thunderboot) {
+		sc223a->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_ASIS);
+		if (IS_ERR(sc223a->reset_gpio))
+			dev_warn(dev, "Failed to get reset-gpios\n");
+
+		sc223a->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_ASIS);
+		if (IS_ERR(sc223a->pwdn_gpio))
+			dev_warn(dev, "Failed to get pwdn-gpios\n");
+	} else {
+		sc223a->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
+		if (IS_ERR(sc223a->reset_gpio))
+			dev_warn(dev, "Failed to get reset-gpios\n");
+
+		sc223a->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_OUT_LOW);
+		if (IS_ERR(sc223a->pwdn_gpio))
+			dev_warn(dev, "Failed to get pwdn-gpios\n");
+	}
+
+	sc223a->pinctrl = devm_pinctrl_get(dev);
+	if (!IS_ERR(sc223a->pinctrl)) {
+		sc223a->pins_default =
+			pinctrl_lookup_state(sc223a->pinctrl,
+					     OF_CAMERA_PINCTRL_STATE_DEFAULT);
+		if (IS_ERR(sc223a->pins_default))
+			dev_err(dev, "could not get default pinstate\n");
+
+		sc223a->pins_sleep =
+			pinctrl_lookup_state(sc223a->pinctrl,
+					     OF_CAMERA_PINCTRL_STATE_SLEEP);
+		if (IS_ERR(sc223a->pins_sleep))
+			dev_err(dev, "could not get sleep pinstate\n");
+	} else {
+		dev_err(dev, "no pinctrl\n");
+	}
+
+	ret = sc223a_configure_regulators(sc223a);
+	if (ret) {
+		dev_err(dev, "Failed to get power regulators\n");
+		return ret;
+	}
+
+	mutex_init(&sc223a->mutex);
+
+	sd = &sc223a->subdev;
+	v4l2_i2c_subdev_init(sd, client, &sc223a_subdev_ops);
+	ret = sc223a_initialize_controls(sc223a);
+	if (ret)
+		goto err_destroy_mutex;
+
+	ret = __sc223a_power_on(sc223a);
+	if (ret)
+		goto err_free_handler;
+
+	ret = sc223a_check_sensor_id(sc223a, client);
+	if (ret)
+		goto err_power_off;
+
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+	sd->internal_ops = &sc223a_internal_ops;
+	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
+		     V4L2_SUBDEV_FL_HAS_EVENTS;
+#endif
+#if defined(CONFIG_MEDIA_CONTROLLER)
+	sc223a->pad.flags = MEDIA_PAD_FL_SOURCE;
+	sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
+	ret = media_entity_pads_init(&sd->entity, 1, &sc223a->pad);
+	if (ret < 0)
+		goto err_power_off;
+#endif
+
+	memset(facing, 0, sizeof(facing));
+	if (strcmp(sc223a->module_facing, "back") == 0)
+		facing[0] = 'b';
+	else
+		facing[0] = 'f';
+
+	snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s",
+		 sc223a->module_index, facing,
+		 SC223A_NAME, dev_name(sd->dev));
+	ret = v4l2_async_register_subdev_sensor_common(sd);
+	if (ret) {
+		dev_err(dev, "v4l2 async register subdev failed\n");
+		goto err_clean_entity;
+	}
+
+	pm_runtime_set_active(dev);
+	pm_runtime_enable(dev);
+	if (sc223a->is_thunderboot)
+		pm_runtime_get_sync(dev);
+	else
+		pm_runtime_idle(dev);
+
+	return 0;
+
+err_clean_entity:
+#if defined(CONFIG_MEDIA_CONTROLLER)
+	media_entity_cleanup(&sd->entity);
+#endif
+err_power_off:
+	__sc223a_power_off(sc223a);
+err_free_handler:
+	v4l2_ctrl_handler_free(&sc223a->ctrl_handler);
+err_destroy_mutex:
+	mutex_destroy(&sc223a->mutex);
+
+	return ret;
+}
+
+static int sc223a_remove(struct i2c_client *client)
+{
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct sc223a *sc223a = to_sc223a(sd);
+
+	v4l2_async_unregister_subdev(sd);
+#if defined(CONFIG_MEDIA_CONTROLLER)
+	media_entity_cleanup(&sd->entity);
+#endif
+	v4l2_ctrl_handler_free(&sc223a->ctrl_handler);
+	mutex_destroy(&sc223a->mutex);
+
+	pm_runtime_disable(&client->dev);
+	if (!pm_runtime_status_suspended(&client->dev))
+		__sc223a_power_off(sc223a);
+	pm_runtime_set_suspended(&client->dev);
+
+	return 0;
+}
+
+#if IS_ENABLED(CONFIG_OF)
+static const struct of_device_id sc223a_of_match[] = {
+	{ .compatible = "smartsens,sc223a" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, sc223a_of_match);
+#endif
+
+static const struct i2c_device_id sc223a_match_id[] = {
+	{ "smartsens,sc223a", 0 },
+	{ },
+};
+
+static struct i2c_driver sc223a_i2c_driver = {
+	.driver = {
+		.name = SC223A_NAME,
+		.pm = &sc223a_pm_ops,
+		.of_match_table = of_match_ptr(sc223a_of_match),
+	},
+	.probe		= &sc223a_probe,
+	.remove		= &sc223a_remove,
+	.id_table	= sc223a_match_id,
+};
+
+static int __init sensor_mod_init(void)
+{
+	return i2c_add_driver(&sc223a_i2c_driver);
+}
+
+static void __exit sensor_mod_exit(void)
+{
+	i2c_del_driver(&sc223a_i2c_driver);
+}
+
+#if defined(CONFIG_VIDEO_ROCKCHIP_THUNDER_BOOT_ISP) && !defined(CONFIG_INITCALL_ASYNC)
+subsys_initcall(sensor_mod_init);
+#else
+device_initcall_sync(sensor_mod_init);
+#endif
+module_exit(sensor_mod_exit);
+
+MODULE_DESCRIPTION("smartsens sc223a sensor driver");
+MODULE_LICENSE("GPL");
diff --git a/kernel/drivers/media/i2c/sc230ai.c b/kernel/drivers/media/i2c/sc230ai.c
index bfd76fc..084f5bf 100644
--- a/kernel/drivers/media/i2c/sc230ai.c
+++ b/kernel/drivers/media/i2c/sc230ai.c
@@ -61,9 +61,6 @@
 #define SC230AI_REG_SEXPOSURE_L		0x3e05
 #define	SC230AI_EXPOSURE_MIN		1
 #define	SC230AI_EXPOSURE_STEP		1
-#define	SC230AI_EXPOSURE_LIN_MAX	(2 * 0x465 - 9)
-#define	SC230AI_EXPOSURE_HDR_MAX_S	(2 * 0x465 - 9)
-#define	SC230AI_EXPOSURE_HDR_MAX_L	(2 * 0x465 - 9)
 #define SC230AI_VTS_MAX			0x7fff
 
 #define SC230AI_REG_DIG_GAIN		0x3e06
@@ -74,7 +71,7 @@
 #define SC230AI_REG_SANA_GAIN		0x3e12
 #define SC230AI_REG_SANA_FINE_GAIN	0x3e13
 #define SC230AI_GAIN_MIN		1000
-#define SC230AI_GAIN_MAX		1722628       //108.512*15.875*1000
+#define SC230AI_GAIN_MAX		1574800       // 99.2*15.875*1000
 #define SC230AI_GAIN_STEP		1
 #define SC230AI_GAIN_DEFAULT		1000
 #define SC230AI_LGAIN			0
@@ -678,46 +675,46 @@
 		*again = 0x00;
 		*dgain = 0x00;
 		*dgain_fine = total_gain * 128 / 1000;
-	} else if (total_gain < 3391) {	/* 2 ~ 3.391 gain*/
+	} else if (total_gain < 3100) {	/* 2 ~ 3.1 gain*/
 		*again = 0x01;
 		*dgain = 0x00;
 		*dgain_fine = total_gain * 128 / 1000 / 2;
-	} else if (total_gain < 3391 * 2) {	/* 3.391 ~ 6.782 gain*/
+	} else if (total_gain < 3100 * 2) {	/* 3.100 ~ 6.200 gain*/
 		*again = 0x40;
 		*dgain = 0x00;
-		*dgain_fine = total_gain * 128 / 3391;
-	} else if (total_gain < 3391 * 4) {	/* 6.782 ~ 13.564 gain*/
+		*dgain_fine = total_gain * 128 / 3100;
+	} else if (total_gain < 3100 * 4) {	/* 6.200 ~ 12.400 gain*/
 		*again = 0x48;
 		*dgain = 0x00;
-		*dgain_fine = total_gain * 128 / 3391 / 2;
-	} else if (total_gain < 3391 * 8) {	/* 13.564 ~ 27.128 gain*/
+		*dgain_fine = total_gain * 128 / 3100 / 2;
+	} else if (total_gain < 3100 * 8) {	/* 12.400 ~ 24.800 gain*/
 		*again = 0x49;
 		*dgain = 0x00;
-		*dgain_fine = total_gain * 128 / 3391 / 4;
-	} else if (total_gain < 3391 * 16) {	/* 27.128 ~ 54.256 gain*/
+		*dgain_fine = total_gain * 128 / 3100 / 4;
+	} else if (total_gain < 3100 * 16) {	/* 24.800 ~ 49.600 gain*/
 		*again = 0x4b;
 		*dgain = 0x00;
-		*dgain_fine = total_gain * 128 / 3391 / 8;
-	} else if (total_gain < 3391 * 32) {	/* 54.256 ~ 108.512 gain*/
+		*dgain_fine = total_gain * 128 / 3100 / 8;
+	} else if (total_gain < 3100 * 32) {	/* 49.600 ~ 99.200 gain*/
 		*again = 0x4f;
 		*dgain = 0x00;
-		*dgain_fine = total_gain * 128 / 3391 / 16;
-	} else if (total_gain < 3391 * 64) {	/* 108.512 ~ 217.024 gain*/
+		*dgain_fine = total_gain * 128 / 3100 / 16;
+	} else if (total_gain < 3100 * 64) {	/* 99.200 ~ 198.400 gain*/
 		*again = 0x5f;
 		*dgain = 0x00;
-		*dgain_fine = total_gain * 128 / 3391 / 32;
-	} else if (total_gain < 3391 * 128) {	/* 217.024 ~ 434.048 gain*/
+		*dgain_fine = total_gain * 128 / 3100 / 32;
+	} else if (total_gain < 3100 * 128) {	/* 198.400 ~ 396.800 gain*/
 		*again = 0x5f;
 		*dgain = 0x01;
-		*dgain_fine = total_gain * 128 / 3391 / 64;
-	} else if (total_gain < 3391 * 256) {	/* 434.048 ~ 868.096 gain*/
+		*dgain_fine = total_gain * 128 / 3100 / 64;
+	} else if (total_gain < 3100 * 256) {	/* 396.800 ~ 793.600 gain*/
 		*again = 0x5f;
 		*dgain = 0x03;
-		*dgain_fine = total_gain * 128 / 3391 / 128;
-	} else if (total_gain < 3391 * 512) {	/* 868.096 ~ 1736.192 gain*/
+		*dgain_fine = total_gain * 128 / 3100 / 128;
+	} else {				/* 793.600 ~ 1587.200 gain*/
 		*again = 0x5f;
 		*dgain = 0x07;
-		*dgain_fine = total_gain * 128 / 3391 / 128;
+		*dgain_fine = total_gain * 128 / 3100 / 128;
 	}
 
 	return ret;
@@ -1406,7 +1403,7 @@
 	switch (ctrl->id) {
 	case V4L2_CID_VBLANK:
 		/* Update max exposure while meeting expected vblanking */
-		max = sc230ai->cur_mode->height + ctrl->val - 4;
+		max = sc230ai->cur_mode->height + ctrl->val - 5;
 		__v4l2_ctrl_modify_range(sc230ai->exposure,
 					 sc230ai->exposure->minimum, max,
 					 sc230ai->exposure->step,
@@ -1469,8 +1466,7 @@
 					 (ctrl->val + sc230ai->cur_mode->height)
 					 & 0xff);
 		sc230ai->cur_vts = ctrl->val + sc230ai->cur_mode->height;
-		if (sc230ai->cur_vts != sc230ai->cur_mode->vts_def)
-			sc230ai_modify_fps_info(sc230ai);
+		sc230ai_modify_fps_info(sc230ai);
 		break;
 	case V4L2_CID_TEST_PATTERN:
 		ret = sc230ai_enable_test_pattern(sc230ai, ctrl->val);
@@ -1546,7 +1542,7 @@
 					    V4L2_CID_VBLANK, vblank_def,
 					    SC230AI_VTS_MAX - mode->height,
 					    1, vblank_def);
-	exposure_max = SC230AI_EXPOSURE_LIN_MAX;
+	exposure_max = mode->vts_def - 5;
 	sc230ai->exposure = v4l2_ctrl_new_std(handler, &sc230ai_ctrl_ops,
 					      V4L2_CID_EXPOSURE, SC230AI_EXPOSURE_MIN,
 					      exposure_max, SC230AI_EXPOSURE_STEP,
diff --git a/kernel/drivers/media/i2c/sc2310.c b/kernel/drivers/media/i2c/sc2310.c
index ac76a27..574836d 100644
--- a/kernel/drivers/media/i2c/sc2310.c
+++ b/kernel/drivers/media/i2c/sc2310.c
@@ -1653,8 +1653,7 @@
 					ctrl->val + sc2310->cur_mode->height);
 		if (!ret)
 			sc2310->cur_vts = ctrl->val + sc2310->cur_mode->height;
-		if (sc2310->cur_vts != sc2310->cur_mode->vts_def)
-			sc2310_modify_fps_info(sc2310);
+		sc2310_modify_fps_info(sc2310);
 		dev_dbg(&client->dev, "set vblank 0x%x\n",
 			ctrl->val);
 		break;
diff --git a/kernel/drivers/media/i2c/sc301iot.c b/kernel/drivers/media/i2c/sc301iot.c
index e5d09e0..668d8b2 100644
--- a/kernel/drivers/media/i2c/sc301iot.c
+++ b/kernel/drivers/media/i2c/sc301iot.c
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
- * SC301IOT driver
+ * sc301iot driver
  *
  * Copyright (C) 2022 Fuzhou Rockchip Electronics Co., Ltd.
  *
@@ -110,7 +110,7 @@
 #define OF_CAMERA_PINCTRL_STATE_DEFAULT	"rockchip,camera_default"
 #define OF_CAMERA_PINCTRL_STATE_SLEEP	"rockchip,camera_sleep"
 #define OF_CAMERA_HDR_MODE		"rockchip,camera-hdr-mode"
-#define SC301IOT_NAME			"SC301IOT"
+#define SC301IOT_NAME			"sc301iot"
 
 static const char * const SC301IOT_supply_names[] = {
 	"avdd",		/* Analog power */
@@ -125,7 +125,7 @@
 	u8 val;
 };
 
-struct SC301IOT_mode {
+struct sc301iot_mode {
 	u32 bus_fmt;
 	u32 width;
 	u32 height;
@@ -138,7 +138,7 @@
 	u32 vc[PAD_MAX];
 };
 
-struct SC301IOT {
+struct sc301iot {
 	struct i2c_client	*client;
 	struct clk		*xvclk;
 	struct gpio_desc	*reset_gpio;
@@ -162,7 +162,7 @@
 	struct v4l2_fract	cur_fps;
 	bool			streaming;
 	bool			power_on;
-	const struct SC301IOT_mode *cur_mode;
+	const struct sc301iot_mode *cur_mode;
 	u32			module_index;
 	const char		*module_facing;
 	const char		*module_name;
@@ -175,12 +175,12 @@
 	u32         sync_mode;
 };
 
-#define to_SC301IOT(sd) container_of(sd, struct SC301IOT, subdev)
+#define to_sc301iot(sd) container_of(sd, struct sc301iot, subdev)
 
 /*
  * Xclk 24Mhz
  */
-static const struct regval SC301IOT_global_regs[] = {
+static const struct regval sc301iot_global_regs[] = {
 	{REG_NULL, 0x00},
 };
 
@@ -189,7 +189,7 @@
  * max_framerate 30fps
  * mipi_datarate per lane 540Mbps, 2lane
  */
-static const struct regval SC301IOT_linear_10_2048x1536_regs[] = {
+static const struct regval sc301iot_linear_10_2048x1536_regs[] = {
 	{0x0103, 0x01},
 	{0x0100, 0x00},
 	{0x36e9, 0x80},
@@ -344,7 +344,7 @@
  * max_framerate 30fps
  * mipi_datarate per lane 1080Mbps, HDR 2lane
  */
-static const struct regval SC301IOT_hdr_10_2048x1536_regs[] = {
+static const struct regval sc301iot_hdr_10_2048x1536_regs[] = {
 	{0x0103, 0x01},
 	{0x0100, 0x00},
 	{0x36e9, 0x80},
@@ -508,7 +508,7 @@
  * max_framerate 30fps
  * mipi_datarate per lane 540Mbps, 2lane
  */
-static const struct regval SC301IOT_linear_10_1536x1536_regs[] = {
+static const struct regval sc301iot_linear_10_1536x1536_regs[] = {
 	{0x0103, 0x01},
 	{0x0100, 0x00},
 	{0x36e9, 0x80},
@@ -664,7 +664,7 @@
  * max_framerate 30fps
  * mipi_datarate per lane 1080Mbps, HDR 2lane
  */
-static const struct regval SC301IOT_hdr_10_1536x1536_regs[] = {
+static const struct regval sc301iot_hdr_10_1536x1536_regs[] = {
 	{0x0103,  0x01},
 	{0x0100, 0x00},
 	{0x36e9,  0x80},
@@ -824,7 +824,7 @@
 	{REG_NULL, 0x00},
 };
 
-static const struct SC301IOT_mode supported_modes[] = {
+static const struct sc301iot_mode supported_modes[] = {
 	{
 		.width = 2048,
 		.height = 1536,
@@ -836,7 +836,7 @@
 		.hts_def = 0x8ca,
 		.vts_def = 0x640,
 		.bus_fmt = MEDIA_BUS_FMT_SBGGR10_1X10,
-		.reg_list = SC301IOT_linear_10_2048x1536_regs,
+		.reg_list = sc301iot_linear_10_2048x1536_regs,
 		.hdr_mode = NO_HDR,
 		.vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_0,
 	}, {
@@ -850,7 +850,7 @@
 		.hts_def = 0x8ca,
 		.vts_def = 0xc80,
 		.bus_fmt = MEDIA_BUS_FMT_SBGGR10_1X10,
-		.reg_list = SC301IOT_hdr_10_2048x1536_regs,
+		.reg_list = sc301iot_hdr_10_2048x1536_regs,
 		.hdr_mode = HDR_X2,
 		.vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_1,
 		.vc[PAD1] = V4L2_MBUS_CSI2_CHANNEL_0,//L->csi wr0
@@ -867,7 +867,7 @@
 		.hts_def = 0x8ca,
 		.vts_def = 0x640,
 		.bus_fmt = MEDIA_BUS_FMT_SBGGR10_1X10,
-		.reg_list = SC301IOT_linear_10_1536x1536_regs,
+		.reg_list = sc301iot_linear_10_1536x1536_regs,
 		.hdr_mode = NO_HDR,
 		.vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_0,
 	}, {
@@ -881,7 +881,7 @@
 		.hts_def = 0x8ca,
 		.vts_def = 0xc80,
 		.bus_fmt = MEDIA_BUS_FMT_SBGGR10_1X10,
-		.reg_list = SC301IOT_hdr_10_1536x1536_regs,
+		.reg_list = sc301iot_hdr_10_1536x1536_regs,
 		.hdr_mode = HDR_X2,
 		.vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_1,
 		.vc[PAD1] = V4L2_MBUS_CSI2_CHANNEL_0,//L->csi wr0
@@ -894,7 +894,7 @@
 	SC301IOT_LINK_FREQ_594
 };
 
-static const char * const SC301IOT_test_pattern_menu[] = {
+static const char * const sc301iot_test_pattern_menu[] = {
 	"Disabled",
 	"Vertical Color Bar Type 1",
 	"Vertical Color Bar Type 2",
@@ -903,7 +903,7 @@
 };
 
 /* Write registers up to 4 at a time */
-static int SC301IOT_write_reg(struct i2c_client *client, u16 reg,
+static int sc301iot_write_reg(struct i2c_client *client, u16 reg,
 			    u32 len, u32 val)
 {
 	u32 buf_i, val_i;
@@ -931,21 +931,21 @@
 	return 0;
 }
 
-static int SC301IOT_write_array(struct i2c_client *client,
+static int sc301iot_write_array(struct i2c_client *client,
 			       const struct regval *regs)
 {
 	u32 i;
 	int ret = 0;
 
 	for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++)
-		ret = SC301IOT_write_reg(client, regs[i].addr,
+		ret = sc301iot_write_reg(client, regs[i].addr,
 					SC301IOT_REG_VALUE_08BIT, regs[i].val);
 
 	return ret;
 }
 
 /* Read registers up to 4 at a time */
-static int SC301IOT_read_reg(struct i2c_client *client, u16 reg, unsigned int len,
+static int sc301iot_read_reg(struct i2c_client *client, u16 reg, unsigned int len,
 			    u32 *val)
 {
 	struct i2c_msg msgs[2];
@@ -983,7 +983,7 @@
 
 
 /* mode: 0 = lgain  1 = sgain */
-static int SC301IOT_set_gain_reg(struct SC301IOT *SC301IOT, u32 gain, int mode)
+static int sc301iot_set_gain_reg(struct sc301iot *sc301iot, u32 gain, int mode)
 {
 	u8 ANA_Coarse_gain_reg = 0x00, DIG_Fine_gain_reg = 0x80;
 	u32 ANA_Coarse_gain = 1024, DIG_gain_reg = 0x00;
@@ -1026,28 +1026,28 @@
 	DIG_Fine_gain_reg = gain/8;
 
 	if (mode == SC301IOT_LGAIN) {
-		ret = SC301IOT_write_reg(SC301IOT->client,
+		ret = sc301iot_write_reg(sc301iot->client,
 					SC301IOT_REG_DIG_GAIN,
 					SC301IOT_REG_VALUE_08BIT,
 					DIG_gain_reg & 0xF);
-		ret |= SC301IOT_write_reg(SC301IOT->client,
+		ret |= sc301iot_write_reg(sc301iot->client,
 					 SC301IOT_REG_DIG_FINE_GAIN,
 					 SC301IOT_REG_VALUE_08BIT,
 					 DIG_Fine_gain_reg);
-		ret |= SC301IOT_write_reg(SC301IOT->client,
+		ret |= sc301iot_write_reg(sc301iot->client,
 					 SC301IOT_REG_ANA_GAIN,
 					 SC301IOT_REG_VALUE_08BIT,
 					 ANA_Coarse_gain_reg);
 	} else {
-		ret = SC301IOT_write_reg(SC301IOT->client,
+		ret = sc301iot_write_reg(sc301iot->client,
 					SC301IOT_REG_SDIG_GAIN,
 					SC301IOT_REG_VALUE_08BIT,
 					DIG_gain_reg & 0xF);
-		ret |= SC301IOT_write_reg(SC301IOT->client,
+		ret |= sc301iot_write_reg(sc301iot->client,
 					 SC301IOT_REG_SDIG_FINE_GAIN,
 					 SC301IOT_REG_VALUE_08BIT,
 					 DIG_Fine_gain_reg);
-		ret |= SC301IOT_write_reg(SC301IOT->client,
+		ret |= sc301iot_write_reg(sc301iot->client,
 					 SC301IOT_REG_SANA_GAIN,
 					 SC301IOT_REG_VALUE_08BIT,
 					 ANA_Coarse_gain_reg);
@@ -1055,17 +1055,17 @@
 	return ret;
 }
 
-static int SC301IOT_set_hdrae(struct SC301IOT *SC301IOT,
+static int sc301iot_set_hdrae(struct sc301iot *sc301iot,
 			    struct preisp_hdrae_exp_s *ae)
 {
 	int ret = 0;
 	u32 l_exp_time, m_exp_time, s_exp_time;
 	u32 l_a_gain, m_a_gain, s_a_gain;
 
-	if (!SC301IOT->has_init_exp && !SC301IOT->streaming) {
-		SC301IOT->init_hdrae_exp = *ae;
-		SC301IOT->has_init_exp = true;
-		dev_dbg(&SC301IOT->client->dev, "SC301IOT don't stream, record exp for hdr!\n");
+	if (!sc301iot->has_init_exp && !sc301iot->streaming) {
+		sc301iot->init_hdrae_exp = *ae;
+		sc301iot->has_init_exp = true;
+		dev_dbg(&sc301iot->client->dev, "sc301iot don't stream, record exp for hdr!\n");
 		return ret;
 	}
 	l_exp_time = ae->long_exp_reg;
@@ -1075,12 +1075,12 @@
 	m_a_gain = ae->middle_gain_reg;
 	s_a_gain = ae->short_gain_reg;
 
-	dev_dbg(&SC301IOT->client->dev,
+	dev_dbg(&sc301iot->client->dev,
 		"rev exp req: L_exp: 0x%x, 0x%x, M_exp: 0x%x, 0x%x S_exp: 0x%x, 0x%x\n",
 		l_exp_time, m_exp_time, s_exp_time,
 		l_a_gain, m_a_gain, s_a_gain);
 
-	if (SC301IOT->cur_mode->hdr_mode == HDR_X2) {
+	if (sc301iot->cur_mode->hdr_mode == HDR_X2) {
 		//2 stagger
 		l_a_gain = m_a_gain;
 		l_exp_time = m_exp_time;
@@ -1097,42 +1097,42 @@
 	if (s_exp_time > 182)                //(191 - 9)
 		s_exp_time = 182;
 
-	ret = SC301IOT_write_reg(SC301IOT->client,
+	ret = sc301iot_write_reg(sc301iot->client,
 				SC301IOT_REG_EXPOSURE_H,
 				SC301IOT_REG_VALUE_08BIT,
 				SC301IOT_FETCH_EXP_H(l_exp_time));
-	ret |= SC301IOT_write_reg(SC301IOT->client,
+	ret |= sc301iot_write_reg(sc301iot->client,
 				 SC301IOT_REG_EXPOSURE_M,
 				 SC301IOT_REG_VALUE_08BIT,
 				 SC301IOT_FETCH_EXP_M(l_exp_time));
-	ret |= SC301IOT_write_reg(SC301IOT->client,
+	ret |= sc301iot_write_reg(sc301iot->client,
 				 SC301IOT_REG_EXPOSURE_L,
 				 SC301IOT_REG_VALUE_08BIT,
 				 SC301IOT_FETCH_EXP_L(l_exp_time));
-	ret |= SC301IOT_write_reg(SC301IOT->client,
+	ret |= sc301iot_write_reg(sc301iot->client,
 				 SC301IOT_REG_SEXPOSURE_M,
 				 SC301IOT_REG_VALUE_08BIT,
 				 SC301IOT_FETCH_EXP_M(s_exp_time));
-	ret |= SC301IOT_write_reg(SC301IOT->client,
+	ret |= sc301iot_write_reg(sc301iot->client,
 				 SC301IOT_REG_SEXPOSURE_L,
 				 SC301IOT_REG_VALUE_08BIT,
 				 SC301IOT_FETCH_EXP_L(s_exp_time));
 
 
-	ret |= SC301IOT_set_gain_reg(SC301IOT, l_a_gain, SC301IOT_LGAIN);
-	ret |= SC301IOT_set_gain_reg(SC301IOT, s_a_gain, SC301IOT_SGAIN);
+	ret |= sc301iot_set_gain_reg(sc301iot, l_a_gain, SC301IOT_LGAIN);
+	ret |= sc301iot_set_gain_reg(sc301iot, s_a_gain, SC301IOT_SGAIN);
 	return ret;
 }
 
-static int SC301IOT_get_reso_dist(const struct SC301IOT_mode *mode,
+static int sc301iot_get_reso_dist(const struct sc301iot_mode *mode,
 				 struct v4l2_mbus_framefmt *framefmt)
 {
 	return abs(mode->width - framefmt->width) +
 	       abs(mode->height - framefmt->height);
 }
 
-static const struct SC301IOT_mode *
-SC301IOT_find_best_fit(struct v4l2_subdev_format *fmt)
+static const struct sc301iot_mode *
+sc301iot_find_best_fit(struct v4l2_subdev_format *fmt)
 {
 	struct v4l2_mbus_framefmt *framefmt = &fmt->format;
 	int dist;
@@ -1141,7 +1141,7 @@
 	unsigned int i;
 
 	for (i = 0; i < ARRAY_SIZE(supported_modes); i++) {
-		dist = SC301IOT_get_reso_dist(&supported_modes[i], framefmt);
+		dist = sc301iot_get_reso_dist(&supported_modes[i], framefmt);
 		if (cur_best_fit_dist == -1 || dist < cur_best_fit_dist) {
 			cur_best_fit_dist = dist;
 			cur_best_fit = i;
@@ -1151,17 +1151,17 @@
 	return &supported_modes[cur_best_fit];
 }
 
-static int SC301IOT_set_fmt(struct v4l2_subdev *sd,
+static int sc301iot_set_fmt(struct v4l2_subdev *sd,
 			   struct v4l2_subdev_pad_config *cfg,
 			   struct v4l2_subdev_format *fmt)
 {
-	struct SC301IOT *SC301IOT = to_SC301IOT(sd);
-	const struct SC301IOT_mode *mode;
+	struct sc301iot *sc301iot = to_sc301iot(sd);
+	const struct sc301iot_mode *mode;
 	s64 h_blank, vblank_def;
 
-	mutex_lock(&SC301IOT->mutex);
+	mutex_lock(&sc301iot->mutex);
 
-	mode = SC301IOT_find_best_fit(fmt);
+	mode = sc301iot_find_best_fit(fmt);
 	fmt->format.code = mode->bus_fmt;
 	fmt->format.width = mode->width;
 	fmt->format.height = mode->height;
@@ -1170,40 +1170,40 @@
 #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
 		*v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = fmt->format;
 #else
-		mutex_unlock(&SC301IOT->mutex);
+		mutex_unlock(&sc301iot->mutex);
 		return -ENOTTY;
 #endif
 	} else {
-		SC301IOT->cur_mode = mode;
+		sc301iot->cur_mode = mode;
 		h_blank = mode->hts_def - mode->width;
-		__v4l2_ctrl_modify_range(SC301IOT->hblank, h_blank,
+		__v4l2_ctrl_modify_range(sc301iot->hblank, h_blank,
 					 h_blank, 1, h_blank);
 		vblank_def = mode->vts_def - mode->height;
-		__v4l2_ctrl_modify_range(SC301IOT->vblank, vblank_def,
+		__v4l2_ctrl_modify_range(sc301iot->vblank, vblank_def,
 					 SC301IOT_VTS_MAX - mode->height,
 					 1, vblank_def);
-		SC301IOT->cur_fps = mode->max_fps;
-		SC301IOT->cur_vts = mode->vts_def;
+		sc301iot->cur_fps = mode->max_fps;
+		sc301iot->cur_vts = mode->vts_def;
 	}
 
-	mutex_unlock(&SC301IOT->mutex);
+	mutex_unlock(&sc301iot->mutex);
 
 	return 0;
 }
 
-static int SC301IOT_get_fmt(struct v4l2_subdev *sd,
+static int sc301iot_get_fmt(struct v4l2_subdev *sd,
 			   struct v4l2_subdev_pad_config *cfg,
 			   struct v4l2_subdev_format *fmt)
 {
-	struct SC301IOT *SC301IOT = to_SC301IOT(sd);
-	const struct SC301IOT_mode *mode = SC301IOT->cur_mode;
+	struct sc301iot *sc301iot = to_sc301iot(sd);
+	const struct sc301iot_mode *mode = sc301iot->cur_mode;
 
-	mutex_lock(&SC301IOT->mutex);
+	mutex_lock(&sc301iot->mutex);
 	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
 #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
 		fmt->format = *v4l2_subdev_get_try_format(sd, cfg, fmt->pad);
 #else
-		mutex_unlock(&SC301IOT->mutex);
+		mutex_unlock(&sc301iot->mutex);
 		return -ENOTTY;
 #endif
 	} else {
@@ -1217,24 +1217,24 @@
 		else
 			fmt->reserved[0] = mode->vc[PAD0];
 	}
-	mutex_unlock(&SC301IOT->mutex);
+	mutex_unlock(&sc301iot->mutex);
 	return 0;
 }
 
-static int SC301IOT_enum_mbus_code(struct v4l2_subdev *sd,
+static int sc301iot_enum_mbus_code(struct v4l2_subdev *sd,
 				  struct v4l2_subdev_pad_config *cfg,
 				  struct v4l2_subdev_mbus_code_enum *code)
 {
-	struct SC301IOT *SC301IOT = to_SC301IOT(sd);
+	struct sc301iot *sc301iot = to_sc301iot(sd);
 
 	if (code->index != 0)
 		return -EINVAL;
-	code->code = SC301IOT->cur_mode->bus_fmt;
+	code->code = sc301iot->cur_mode->bus_fmt;
 
 	return 0;
 }
 
-static int SC301IOT_enum_frame_sizes(struct v4l2_subdev *sd,
+static int sc301iot_enum_frame_sizes(struct v4l2_subdev *sd,
 				    struct v4l2_subdev_pad_config *cfg,
 				    struct v4l2_subdev_frame_size_enum *fse)
 {
@@ -1252,42 +1252,42 @@
 	return 0;
 }
 
-static int SC301IOT_enable_test_pattern(struct SC301IOT *SC301IOT, u32 pattern)
+static int sc301iot_enable_test_pattern(struct sc301iot *sc301iot, u32 pattern)
 {
 	u32 val = 0;
 	int ret = 0;
 
-	ret = SC301IOT_read_reg(SC301IOT->client, SC301IOT_REG_TEST_PATTERN,
+	ret = sc301iot_read_reg(sc301iot->client, SC301IOT_REG_TEST_PATTERN,
 			       SC301IOT_REG_VALUE_08BIT, &val);
 	if (pattern)
 		val |= SC301IOT_TEST_PATTERN_BIT_MASK;
 	else
 		val &= ~SC301IOT_TEST_PATTERN_BIT_MASK;
 
-	ret |= SC301IOT_write_reg(SC301IOT->client, SC301IOT_REG_TEST_PATTERN,
+	ret |= sc301iot_write_reg(sc301iot->client, SC301IOT_REG_TEST_PATTERN,
 				 SC301IOT_REG_VALUE_08BIT, val);
 	return ret;
 }
 
-static int SC301IOT_g_frame_interval(struct v4l2_subdev *sd,
+static int sc301iot_g_frame_interval(struct v4l2_subdev *sd,
 				    struct v4l2_subdev_frame_interval *fi)
 {
-	struct SC301IOT *SC301IOT = to_SC301IOT(sd);
-	const struct SC301IOT_mode *mode = SC301IOT->cur_mode;
+	struct sc301iot *sc301iot = to_sc301iot(sd);
+	const struct sc301iot_mode *mode = sc301iot->cur_mode;
 
-	if (SC301IOT->streaming)
-		fi->interval = SC301IOT->cur_fps;
+	if (sc301iot->streaming)
+		fi->interval = sc301iot->cur_fps;
 	else
 		fi->interval = mode->max_fps;
 
 	return 0;
 }
 
-static int SC301IOT_g_mbus_config(struct v4l2_subdev *sd, unsigned int pad_id,
+static int sc301iot_g_mbus_config(struct v4l2_subdev *sd, unsigned int pad_id,
 				 struct v4l2_mbus_config *config)
 {
-	struct SC301IOT *SC301IOT = to_SC301IOT(sd);
-	const struct SC301IOT_mode *mode = SC301IOT->cur_mode;
+	struct sc301iot *sc301iot = to_sc301iot(sd);
+	const struct sc301iot_mode *mode = sc301iot->cur_mode;
 	u32 val = 1 << (SC301IOT_LANES - 1) |
 		V4L2_MBUS_CSI2_CHANNEL_0 |
 		V4L2_MBUS_CSI2_CONTINUOUS_CLOCK;
@@ -1303,31 +1303,31 @@
 	return 0;
 }
 
-static void SC301IOT_get_module_inf(struct SC301IOT *SC301IOT,
+static void sc301iot_get_module_inf(struct sc301iot *sc301iot,
 				   struct rkmodule_inf *inf)
 {
 	memset(inf, 0, sizeof(*inf));
 	strscpy(inf->base.sensor, SC301IOT_NAME, sizeof(inf->base.sensor));
-	strscpy(inf->base.module, SC301IOT->module_name,
+	strscpy(inf->base.module, sc301iot->module_name,
 		sizeof(inf->base.module));
-	strscpy(inf->base.lens, SC301IOT->len_name, sizeof(inf->base.lens));
+	strscpy(inf->base.lens, sc301iot->len_name, sizeof(inf->base.lens));
 }
 
-static int SC301IOT_get_channel_info(struct SC301IOT *SC301IOT,
+static int sc301iot_get_channel_info(struct sc301iot *sc301iot,
 					    struct rkmodule_channel_info *ch_info)
 {
 	if (ch_info->index < PAD0 || ch_info->index >= PAD_MAX)
 		return -EINVAL;
-	ch_info->vc = SC301IOT->cur_mode->vc[ch_info->index];
-	ch_info->width = SC301IOT->cur_mode->width;
-	ch_info->height = SC301IOT->cur_mode->height;
-	ch_info->bus_fmt = SC301IOT->cur_mode->bus_fmt;
+	ch_info->vc = sc301iot->cur_mode->vc[ch_info->index];
+	ch_info->width = sc301iot->cur_mode->width;
+	ch_info->height = sc301iot->cur_mode->height;
+	ch_info->bus_fmt = sc301iot->cur_mode->bus_fmt;
 	return 0;
 }
 
-static long SC301IOT_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
+static long sc301iot_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
 {
-	struct SC301IOT *SC301IOT = to_SC301IOT(sd);
+	struct sc301iot *sc301iot = to_sc301iot(sd);
 	struct rkmodule_hdr_cfg *hdr;
 	struct rkmodule_channel_info *ch_info;
 	u32 i, h, w;
@@ -1337,68 +1337,68 @@
 
 	switch (cmd) {
 	case RKMODULE_GET_MODULE_INFO:
-		SC301IOT_get_module_inf(SC301IOT, (struct rkmodule_inf *)arg);
+		sc301iot_get_module_inf(sc301iot, (struct rkmodule_inf *)arg);
 		break;
 	case RKMODULE_GET_HDR_CFG:
 		hdr = (struct rkmodule_hdr_cfg *)arg;
 		hdr->esp.mode = HDR_NORMAL_VC;
-		hdr->hdr_mode = SC301IOT->cur_mode->hdr_mode;
+		hdr->hdr_mode = sc301iot->cur_mode->hdr_mode;
 		break;
 	case RKMODULE_SET_HDR_CFG:
 		hdr = (struct rkmodule_hdr_cfg *)arg;
-		w = SC301IOT->cur_mode->width;
-		h = SC301IOT->cur_mode->height;
+		w = sc301iot->cur_mode->width;
+		h = sc301iot->cur_mode->height;
 		for (i = 0; i < ARRAY_SIZE(supported_modes); i++) {
 			if (w == supported_modes[i].width &&
 			    h == supported_modes[i].height &&
 			    supported_modes[i].hdr_mode == hdr->hdr_mode) {
-				SC301IOT->cur_mode = &supported_modes[i];
+				sc301iot->cur_mode = &supported_modes[i];
 				break;
 			}
 		}
 		if (i == ARRAY_SIZE(supported_modes)) {
-			dev_err(&SC301IOT->client->dev,
+			dev_err(&sc301iot->client->dev,
 				"not find hdr mode:%d %dx%d config\n",
 				hdr->hdr_mode, w, h);
 			ret = -EINVAL;
 		} else {
-			w = SC301IOT->cur_mode->hts_def - SC301IOT->cur_mode->width;
-			h = SC301IOT->cur_mode->vts_def - SC301IOT->cur_mode->height;
-			__v4l2_ctrl_modify_range(SC301IOT->hblank, w, w, 1, w);
-			__v4l2_ctrl_modify_range(SC301IOT->vblank,
+			w = sc301iot->cur_mode->hts_def - sc301iot->cur_mode->width;
+			h = sc301iot->cur_mode->vts_def - sc301iot->cur_mode->height;
+			__v4l2_ctrl_modify_range(sc301iot->hblank, w, w, 1, w);
+			__v4l2_ctrl_modify_range(sc301iot->vblank,
 				h,
-				SC301IOT_VTS_MAX - SC301IOT->cur_mode->height, 1, h);
-			SC301IOT->cur_fps = SC301IOT->cur_mode->max_fps;
-			SC301IOT->cur_vts = SC301IOT->cur_mode->vts_def;
+				SC301IOT_VTS_MAX - sc301iot->cur_mode->height, 1, h);
+			sc301iot->cur_fps = sc301iot->cur_mode->max_fps;
+			sc301iot->cur_vts = sc301iot->cur_mode->vts_def;
 		}
 		break;
 	case PREISP_CMD_SET_HDRAE_EXP:
-		SC301IOT_set_hdrae(SC301IOT, arg);
+		sc301iot_set_hdrae(sc301iot, arg);
 		break;
 	case RKMODULE_SET_QUICK_STREAM:
 
 		stream = *((u32 *)arg);
 
 		if (stream)
-			ret = SC301IOT_write_reg(SC301IOT->client, SC301IOT_REG_CTRL_MODE,
+			ret = sc301iot_write_reg(sc301iot->client, SC301IOT_REG_CTRL_MODE,
 				 SC301IOT_REG_VALUE_08BIT, SC301IOT_MODE_STREAMING);
 		else
-			ret = SC301IOT_write_reg(SC301IOT->client, SC301IOT_REG_CTRL_MODE,
+			ret = sc301iot_write_reg(sc301iot->client, SC301IOT_REG_CTRL_MODE,
 				 SC301IOT_REG_VALUE_08BIT, SC301IOT_MODE_SW_STANDBY);
 		break;
 	case RKMODULE_GET_SYNC_MODE:
-		*((u32 *)arg) = SC301IOT->sync_mode;
+		*((u32 *)arg) = sc301iot->sync_mode;
 		break;
 	case RKMODULE_SET_SYNC_MODE:
 		sync_mode = *((u32 *)arg);
 		if (sync_mode > 3)
 			break;
-		SC301IOT->sync_mode = sync_mode;
-		dev_info(&SC301IOT->client->dev, "sync_mode = [%u]\n", SC301IOT->sync_mode);
+		sc301iot->sync_mode = sync_mode;
+		dev_info(&sc301iot->client->dev, "sync_mode = [%u]\n", sc301iot->sync_mode);
 		break;
 	case RKMODULE_GET_CHANNEL_INFO:
 		ch_info = (struct rkmodule_channel_info *)arg;
-		ret = SC301IOT_get_channel_info(SC301IOT, ch_info);
+		ret = sc301iot_get_channel_info(sc301iot, ch_info);
 		break;
 	default:
 		ret = -ENOIOCTLCMD;
@@ -1409,7 +1409,7 @@
 }
 
 #ifdef CONFIG_COMPAT
-static long SC301IOT_compat_ioctl32(struct v4l2_subdev *sd,
+static long sc301iot_compat_ioctl32(struct v4l2_subdev *sd,
 				   unsigned int cmd, unsigned long arg)
 {
 	void __user *up = compat_ptr(arg);
@@ -1429,7 +1429,7 @@
 			return ret;
 		}
 
-		ret = SC301IOT_ioctl(sd, cmd, inf);
+		ret = sc301iot_ioctl(sd, cmd, inf);
 		if (!ret) {
 			if (copy_to_user(up, inf, sizeof(*inf))) {
 				kfree(inf);
@@ -1448,7 +1448,7 @@
 			kfree(cfg);
 			return -EFAULT;
 		}
-		ret = SC301IOT_ioctl(sd, cmd, cfg);
+		ret = sc301iot_ioctl(sd, cmd, cfg);
 		kfree(cfg);
 		break;
 	case RKMODULE_GET_HDR_CFG:
@@ -1458,7 +1458,7 @@
 			return ret;
 		}
 
-		ret = SC301IOT_ioctl(sd, cmd, hdr);
+		ret = sc301iot_ioctl(sd, cmd, hdr);
 		if (!ret) {
 			if (copy_to_user(up, hdr, sizeof(*hdr))) {
 				kfree(hdr);
@@ -1477,7 +1477,7 @@
 			kfree(hdr);
 			return -EFAULT;
 		}
-		ret = SC301IOT_ioctl(sd, cmd, hdr);
+		ret = sc301iot_ioctl(sd, cmd, hdr);
 		kfree(hdr);
 		break;
 	case PREISP_CMD_SET_HDRAE_EXP:
@@ -1490,13 +1490,13 @@
 			kfree(hdrae);
 			return -EFAULT;
 		}
-		ret = SC301IOT_ioctl(sd, cmd, hdrae);
+		ret = sc301iot_ioctl(sd, cmd, hdrae);
 		kfree(hdrae);
 		break;
 	case RKMODULE_SET_QUICK_STREAM:
 		if (copy_from_user(&stream, up, sizeof(u32)))
 			return -EFAULT;
-		ret = SC301IOT_ioctl(sd, cmd, &stream);
+		ret = sc301iot_ioctl(sd, cmd, &stream);
 		break;
 	case RKMODULE_GET_CHANNEL_INFO:
 		ch_info = kzalloc(sizeof(*ch_info), GFP_KERNEL);
@@ -1505,7 +1505,7 @@
 			return ret;
 		}
 
-		ret = SC301IOT_ioctl(sd, cmd, ch_info);
+		ret = sc301iot_ioctl(sd, cmd, ch_info);
 		if (!ret) {
 			ret = copy_to_user(up, ch_info, sizeof(*ch_info));
 			if (ret)
@@ -1522,10 +1522,10 @@
 }
 #endif
 
-static int SC301IOT_s_frame_interval(struct v4l2_subdev *sd,
+static int sc301iot_s_frame_interval(struct v4l2_subdev *sd,
 				   struct v4l2_subdev_frame_interval *fi)
 {
-	struct SC301IOT *SC301IOT = to_SC301IOT(sd);
+	struct sc301iot *sc301iot = to_sc301iot(sd);
 	struct device *dev = sd->dev;
 	int ret = -1;
 	s64 vblank_def;
@@ -1534,100 +1534,102 @@
 	fps_set = DIV_ROUND_CLOSEST(fi->interval.denominator, fi->interval.numerator);
 	dev_info(dev, "%s set fps = %u\n", __func__, fps_set);
 
-	mutex_lock(&SC301IOT->mutex);
+	mutex_lock(&sc301iot->mutex);
 
-	current_fps = DIV_ROUND_CLOSEST(SC301IOT->cur_mode->max_fps.denominator,
-			SC301IOT->cur_mode->max_fps.numerator);
-	vblank_def = SC301IOT->cur_mode->vts_def * current_fps / fps_set -
-						SC301IOT->cur_mode->height;
-	if (SC301IOT->sync_mode == SLAVE_MODE)
+	current_fps = DIV_ROUND_CLOSEST(sc301iot->cur_mode->max_fps.denominator,
+			sc301iot->cur_mode->max_fps.numerator);
+	vblank_def = sc301iot->cur_mode->vts_def * current_fps / fps_set -
+						sc301iot->cur_mode->height;
+	if (sc301iot->sync_mode == SLAVE_MODE)
 		vblank_def -= 3;  // adjust vts
-	ret = __v4l2_ctrl_s_ctrl(SC301IOT->vblank, vblank_def);
-	mutex_unlock(&SC301IOT->mutex);
+	ret = __v4l2_ctrl_s_ctrl(sc301iot->vblank, vblank_def);
+	mutex_unlock(&sc301iot->mutex);
 	if (ret < 0)
 		dev_err(dev, "%s __v4l2_ctrl_s_ctrl error - %d\n", __func__, ret);
 	return ret;
 }
 
-static int __SC301IOT_start_stream(struct SC301IOT *SC301IOT)
+static int __sc301iot_start_stream(struct sc301iot *sc301iot)
 {
 	int ret;
 
-	if (!SC301IOT->is_thunderboot) {
-		ret = SC301IOT_write_array(SC301IOT->client, SC301IOT->cur_mode->reg_list);
+	if (!sc301iot->is_thunderboot) {
+		ret = sc301iot_write_array(sc301iot->client, sc301iot->cur_mode->reg_list);
 		if (ret)
 			return ret;
 
 		/* In case these controls are set before streaming */
-		ret = __v4l2_ctrl_handler_setup(&SC301IOT->ctrl_handler);
+		ret = __v4l2_ctrl_handler_setup(&sc301iot->ctrl_handler);
 		if (ret)
 			return ret;
-		if (SC301IOT->has_init_exp && SC301IOT->cur_mode->hdr_mode != NO_HDR) {
-			ret = SC301IOT_ioctl(&SC301IOT->subdev, PREISP_CMD_SET_HDRAE_EXP,
-					&SC301IOT->init_hdrae_exp);
+		if (sc301iot->has_init_exp && sc301iot->cur_mode->hdr_mode != NO_HDR) {
+			ret = sc301iot_ioctl(&sc301iot->subdev, PREISP_CMD_SET_HDRAE_EXP,
+					&sc301iot->init_hdrae_exp);
 			if (ret) {
-				dev_err(&SC301IOT->client->dev,
+				dev_err(&sc301iot->client->dev,
 					"init exp fail in hdr mode\n");
 				return ret;
 			}
 		}
 
-		if (SC301IOT->sync_mode == SLAVE_MODE) {
-			SC301IOT_write_reg(SC301IOT->client, 0x3222,
+		if (sc301iot->sync_mode == SLAVE_MODE) {
+			sc301iot_write_reg(sc301iot->client, 0x3222,
 					SC301IOT_REG_VALUE_08BIT, 0x01);
-			SC301IOT_write_reg(SC301IOT->client, 0x3223,
+			sc301iot_write_reg(sc301iot->client, 0x3223,
 					SC301IOT_REG_VALUE_08BIT, 0xc8);
-			SC301IOT_write_reg(SC301IOT->client, 0x3225,
+			sc301iot_write_reg(sc301iot->client, 0x3225,
 					SC301IOT_REG_VALUE_08BIT, 0x10);
-			SC301IOT_write_reg(SC301IOT->client, 0x322e,
-					SC301IOT_REG_VALUE_08BIT, (SC301IOT->cur_vts - 4) >> 8);
-			SC301IOT_write_reg(SC301IOT->client, 0x322f,
-					SC301IOT_REG_VALUE_08BIT, (SC301IOT->cur_vts - 4) & 0xff);
-		} else if (SC301IOT->sync_mode == NO_SYNC_MODE) {
-			SC301IOT_write_reg(SC301IOT->client, 0x3222,
+			sc301iot_write_reg(sc301iot->client, 0x322e,
+					SC301IOT_REG_VALUE_08BIT, (sc301iot->cur_vts - 4) >> 8);
+			sc301iot_write_reg(sc301iot->client, 0x322f,
+					SC301IOT_REG_VALUE_08BIT, (sc301iot->cur_vts - 4) & 0xff);
+		} else if (sc301iot->sync_mode == NO_SYNC_MODE) {
+			sc301iot_write_reg(sc301iot->client, 0x3222,
 						SC301IOT_REG_VALUE_08BIT, 0x00);
-			SC301IOT_write_reg(SC301IOT->client, 0x3223,
+			sc301iot_write_reg(sc301iot->client, 0x3223,
 						SC301IOT_REG_VALUE_08BIT, 0xd0);
-			SC301IOT_write_reg(SC301IOT->client, 0x3225,
+			sc301iot_write_reg(sc301iot->client, 0x3225,
 						SC301IOT_REG_VALUE_08BIT, 0x00);
-			SC301IOT_write_reg(SC301IOT->client, 0x322e,
+			sc301iot_write_reg(sc301iot->client, 0x322e,
 						SC301IOT_REG_VALUE_08BIT, 0x00);
-			SC301IOT_write_reg(SC301IOT->client, 0x322f,
+			sc301iot_write_reg(sc301iot->client, 0x322f,
 						SC301IOT_REG_VALUE_08BIT, 0x02);
 		}
 	}
 
-	dev_dbg(&SC301IOT->client->dev, "start stream\n");
-	return SC301IOT_write_reg(SC301IOT->client, SC301IOT_REG_CTRL_MODE,
+	dev_dbg(&sc301iot->client->dev, "start stream\n");
+	return sc301iot_write_reg(sc301iot->client, SC301IOT_REG_CTRL_MODE,
 				 SC301IOT_REG_VALUE_08BIT, SC301IOT_MODE_STREAMING);
 }
 
-static int __SC301IOT_stop_stream(struct SC301IOT *SC301IOT)
+static int __sc301iot_stop_stream(struct sc301iot *sc301iot)
 {
-	SC301IOT->has_init_exp = false;
-	dev_dbg(&SC301IOT->client->dev, "stop stream\n");
-	if (SC301IOT->is_thunderboot)
-		SC301IOT->is_first_streamoff = true;
-	return SC301IOT_write_reg(SC301IOT->client, SC301IOT_REG_CTRL_MODE,
+	sc301iot->has_init_exp = false;
+	dev_dbg(&sc301iot->client->dev, "stop stream\n");
+	if (sc301iot->is_thunderboot) {
+		sc301iot->is_first_streamoff = true;
+		pm_runtime_put(&sc301iot->client->dev);
+	}
+	return sc301iot_write_reg(sc301iot->client, SC301IOT_REG_CTRL_MODE,
 				 SC301IOT_REG_VALUE_08BIT, SC301IOT_MODE_SW_STANDBY);
 }
 
-static int __SC301IOT_power_on(struct SC301IOT *SC301IOT);
-static int SC301IOT_s_stream(struct v4l2_subdev *sd, int on)
+static int __sc301iot_power_on(struct sc301iot *sc301iot);
+static int sc301iot_s_stream(struct v4l2_subdev *sd, int on)
 {
-	struct SC301IOT *SC301IOT = to_SC301IOT(sd);
-	struct i2c_client *client = SC301IOT->client;
+	struct sc301iot *sc301iot = to_sc301iot(sd);
+	struct i2c_client *client = sc301iot->client;
 	int ret = 0;
 
-	mutex_lock(&SC301IOT->mutex);
+	mutex_lock(&sc301iot->mutex);
 	on = !!on;
-	if (on == SC301IOT->streaming)
+	if (on == sc301iot->streaming)
 		goto unlock_and_return;
 
 	if (on) {
-		if (SC301IOT->is_thunderboot && rkisp_tb_get_state() == RKISP_TB_NG) {
-			SC301IOT->is_thunderboot = false;
-			__SC301IOT_power_on(SC301IOT);
+		if (sc301iot->is_thunderboot && rkisp_tb_get_state() == RKISP_TB_NG) {
+			sc301iot->is_thunderboot = false;
+			__sc301iot_power_on(sc301iot);
 		}
 
 		ret = pm_runtime_get_sync(&client->dev);
@@ -1636,35 +1638,35 @@
 			goto unlock_and_return;
 		}
 
-		ret = __SC301IOT_start_stream(SC301IOT);
+		ret = __sc301iot_start_stream(sc301iot);
 		if (ret) {
 			v4l2_err(sd, "start stream failed while write regs\n");
 			pm_runtime_put(&client->dev);
 			goto unlock_and_return;
 		}
 	} else {
-		__SC301IOT_stop_stream(SC301IOT);
+		__sc301iot_stop_stream(sc301iot);
 		pm_runtime_put(&client->dev);
 	}
 
-	SC301IOT->streaming = on;
+	sc301iot->streaming = on;
 
 unlock_and_return:
-	mutex_unlock(&SC301IOT->mutex);
+	mutex_unlock(&sc301iot->mutex);
 
 	return ret;
 }
 
-static int SC301IOT_s_power(struct v4l2_subdev *sd, int on)
+static int sc301iot_s_power(struct v4l2_subdev *sd, int on)
 {
-	struct SC301IOT *SC301IOT = to_SC301IOT(sd);
-	struct i2c_client *client = SC301IOT->client;
+	struct sc301iot *sc301iot = to_sc301iot(sd);
+	struct i2c_client *client = sc301iot->client;
 	int ret = 0;
 
-	mutex_lock(&SC301IOT->mutex);
+	mutex_lock(&sc301iot->mutex);
 
 	/* If the power state is not modified - no work to do. */
-	if (SC301IOT->power_on == !!on)
+	if (sc301iot->power_on == !!on)
 		goto unlock_and_return;
 
 	if (on) {
@@ -1674,8 +1676,8 @@
 			goto unlock_and_return;
 		}
 
-		if (!SC301IOT->is_thunderboot) {
-			ret = SC301IOT_write_array(SC301IOT->client, SC301IOT_global_regs);
+		if (!sc301iot->is_thunderboot) {
+			ret = sc301iot_write_array(sc301iot->client, sc301iot_global_regs);
 			if (ret) {
 				v4l2_err(sd, "could not set init registers\n");
 				pm_runtime_put_noidle(&client->dev);
@@ -1683,152 +1685,152 @@
 			}
 		}
 
-		SC301IOT->power_on = true;
+		sc301iot->power_on = true;
 	} else {
 		pm_runtime_put(&client->dev);
-		SC301IOT->power_on = false;
+		sc301iot->power_on = false;
 	}
 
 unlock_and_return:
-	mutex_unlock(&SC301IOT->mutex);
+	mutex_unlock(&sc301iot->mutex);
 
 	return ret;
 }
 
 /* Calculate the delay in us by clock rate and clock cycles */
-static inline u32 SC301IOT_cal_delay(u32 cycles)
+static inline u32 sc301iot_cal_delay(u32 cycles)
 {
 	return DIV_ROUND_UP(cycles, SC301IOT_XVCLK_FREQ / 1000 / 1000);
 }
 
-static int __SC301IOT_power_on(struct SC301IOT *SC301IOT)
+static int __sc301iot_power_on(struct sc301iot *sc301iot)
 {
 	int ret;
 	u32 delay_us;
-	struct device *dev = &SC301IOT->client->dev;
+	struct device *dev = &sc301iot->client->dev;
 
-	if (!IS_ERR_OR_NULL(SC301IOT->pins_default)) {
-		ret = pinctrl_select_state(SC301IOT->pinctrl,
-					   SC301IOT->pins_default);
+	if (!IS_ERR_OR_NULL(sc301iot->pins_default)) {
+		ret = pinctrl_select_state(sc301iot->pinctrl,
+					   sc301iot->pins_default);
 		if (ret < 0)
 			dev_err(dev, "could not set pins\n");
 	}
-	ret = clk_set_rate(SC301IOT->xvclk, SC301IOT_XVCLK_FREQ);
+	ret = clk_set_rate(sc301iot->xvclk, SC301IOT_XVCLK_FREQ);
 	if (ret < 0)
 		dev_warn(dev, "Failed to set xvclk rate (24MHz)\n");
-	if (clk_get_rate(SC301IOT->xvclk) != SC301IOT_XVCLK_FREQ)
+	if (clk_get_rate(sc301iot->xvclk) != SC301IOT_XVCLK_FREQ)
 		dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n");
-	ret = clk_prepare_enable(SC301IOT->xvclk);
+	ret = clk_prepare_enable(sc301iot->xvclk);
 	if (ret < 0) {
 		dev_err(dev, "Failed to enable xvclk\n");
 		goto disable_clk;
 	}
-	if (SC301IOT->is_thunderboot)
+	if (sc301iot->is_thunderboot)
 		return 0;
-	if (!IS_ERR(SC301IOT->reset_gpio))
-		gpiod_set_value_cansleep(SC301IOT->reset_gpio, 0);
+	if (!IS_ERR(sc301iot->reset_gpio))
+		gpiod_set_value_cansleep(sc301iot->reset_gpio, 0);
 
-	ret = regulator_bulk_enable(SC301IOT_NUM_SUPPLIES, SC301IOT->supplies);
+	ret = regulator_bulk_enable(SC301IOT_NUM_SUPPLIES, sc301iot->supplies);
 	if (ret < 0) {
 		dev_err(dev, "Failed to enable regulators\n");
 		goto disable_clk;
 	}
-	if (!IS_ERR(SC301IOT->reset_gpio))
-		gpiod_set_value_cansleep(SC301IOT->reset_gpio, 1);
+	if (!IS_ERR(sc301iot->reset_gpio))
+		gpiod_set_value_cansleep(sc301iot->reset_gpio, 1);
 
 	usleep_range(500, 1000);
-	if (!IS_ERR(SC301IOT->pwdn_gpio))
-		gpiod_set_value_cansleep(SC301IOT->pwdn_gpio, 1);
+	if (!IS_ERR(sc301iot->pwdn_gpio))
+		gpiod_set_value_cansleep(sc301iot->pwdn_gpio, 1);
 	usleep_range(4500, 5000);
 
-	if (!IS_ERR(SC301IOT->reset_gpio))
+	if (!IS_ERR(sc301iot->reset_gpio))
 		usleep_range(6000, 8000);
 	else
 		usleep_range(12000, 16000);
 
 	/* 8192 cycles prior to first SCCB transaction */
-	delay_us = SC301IOT_cal_delay(8192);
+	delay_us = sc301iot_cal_delay(8192);
 	usleep_range(delay_us, delay_us * 2);
 
 	return 0;
 
 disable_clk:
-	clk_disable_unprepare(SC301IOT->xvclk);
+	clk_disable_unprepare(sc301iot->xvclk);
 
 	return ret;
 }
 
-static void __SC301IOT_power_off(struct SC301IOT *SC301IOT)
+static void __sc301iot_power_off(struct sc301iot *sc301iot)
 {
 	int ret;
-	struct device *dev = &SC301IOT->client->dev;
+	struct device *dev = &sc301iot->client->dev;
 
-	clk_disable_unprepare(SC301IOT->xvclk);
-	if (SC301IOT->is_thunderboot) {
-		if (SC301IOT->is_first_streamoff) {
-			SC301IOT->is_thunderboot = false;
-			SC301IOT->is_first_streamoff = false;
+	clk_disable_unprepare(sc301iot->xvclk);
+	if (sc301iot->is_thunderboot) {
+		if (sc301iot->is_first_streamoff) {
+			sc301iot->is_thunderboot = false;
+			sc301iot->is_first_streamoff = false;
 		} else {
 			return;
 		}
 	}
 
-	if (!IS_ERR(SC301IOT->pwdn_gpio))
-		gpiod_set_value_cansleep(SC301IOT->pwdn_gpio, 0);
-	if (!IS_ERR(SC301IOT->reset_gpio))
-		gpiod_set_value_cansleep(SC301IOT->reset_gpio, 0);
-	if (!IS_ERR_OR_NULL(SC301IOT->pins_sleep)) {
-		ret = pinctrl_select_state(SC301IOT->pinctrl,
-					   SC301IOT->pins_sleep);
+	if (!IS_ERR(sc301iot->pwdn_gpio))
+		gpiod_set_value_cansleep(sc301iot->pwdn_gpio, 0);
+	if (!IS_ERR(sc301iot->reset_gpio))
+		gpiod_set_value_cansleep(sc301iot->reset_gpio, 0);
+	if (!IS_ERR_OR_NULL(sc301iot->pins_sleep)) {
+		ret = pinctrl_select_state(sc301iot->pinctrl,
+					   sc301iot->pins_sleep);
 		if (ret < 0)
 			dev_dbg(dev, "could not set pins\n");
 	}
-	regulator_bulk_disable(SC301IOT_NUM_SUPPLIES, SC301IOT->supplies);
+	regulator_bulk_disable(SC301IOT_NUM_SUPPLIES, sc301iot->supplies);
 }
 
-static int SC301IOT_runtime_resume(struct device *dev)
+static int sc301iot_runtime_resume(struct device *dev)
 {
 	struct i2c_client *client = to_i2c_client(dev);
 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
-	struct SC301IOT *SC301IOT = to_SC301IOT(sd);
+	struct sc301iot *sc301iot = to_sc301iot(sd);
 
-	return __SC301IOT_power_on(SC301IOT);
+	return __sc301iot_power_on(sc301iot);
 }
 
-static int SC301IOT_runtime_suspend(struct device *dev)
+static int sc301iot_runtime_suspend(struct device *dev)
 {
 	struct i2c_client *client = to_i2c_client(dev);
 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
-	struct SC301IOT *SC301IOT = to_SC301IOT(sd);
+	struct sc301iot *sc301iot = to_sc301iot(sd);
 
-	__SC301IOT_power_off(SC301IOT);
+	__sc301iot_power_off(sc301iot);
 
 	return 0;
 }
 
 #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
-static int SC301IOT_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+static int sc301iot_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
 {
-	struct SC301IOT *SC301IOT = to_SC301IOT(sd);
+	struct sc301iot *sc301iot = to_sc301iot(sd);
 	struct v4l2_mbus_framefmt *try_fmt =
 				v4l2_subdev_get_try_format(sd, fh->pad, 0);
-	const struct SC301IOT_mode *def_mode = &supported_modes[0];
+	const struct sc301iot_mode *def_mode = &supported_modes[0];
 
-	mutex_lock(&SC301IOT->mutex);
+	mutex_lock(&sc301iot->mutex);
 	/* Initialize try_fmt */
 	try_fmt->width = def_mode->width;
 	try_fmt->height = def_mode->height;
 	try_fmt->code = def_mode->bus_fmt;
 	try_fmt->field = V4L2_FIELD_NONE;
 
-	mutex_unlock(&SC301IOT->mutex);
+	mutex_unlock(&sc301iot->mutex);
 	/* No crop or compose */
 
 	return 0;
 }
 #endif
 
-static int SC301IOT_enum_frame_interval(struct v4l2_subdev *sd,
+static int sc301iot_enum_frame_interval(struct v4l2_subdev *sd,
 				       struct v4l2_subdev_pad_config *cfg,
 				       struct v4l2_subdev_frame_interval_enum *fie)
 {
@@ -1843,59 +1845,59 @@
 	return 0;
 }
 
-static const struct dev_pm_ops SC301IOT_pm_ops = {
-	SET_RUNTIME_PM_OPS(SC301IOT_runtime_suspend,
-			   SC301IOT_runtime_resume, NULL)
+static const struct dev_pm_ops sc301iot_pm_ops = {
+	SET_RUNTIME_PM_OPS(sc301iot_runtime_suspend,
+			   sc301iot_runtime_resume, NULL)
 };
 
 #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
-static const struct v4l2_subdev_internal_ops SC301IOT_internal_ops = {
-	.open = SC301IOT_open,
+static const struct v4l2_subdev_internal_ops sc301iot_internal_ops = {
+	.open = sc301iot_open,
 };
 #endif
 
-static const struct v4l2_subdev_core_ops SC301IOT_core_ops = {
-	.s_power = SC301IOT_s_power,
-	.ioctl = SC301IOT_ioctl,
+static const struct v4l2_subdev_core_ops sc301iot_core_ops = {
+	.s_power = sc301iot_s_power,
+	.ioctl = sc301iot_ioctl,
 #ifdef CONFIG_COMPAT
-	.compat_ioctl32 = SC301IOT_compat_ioctl32,
+	.compat_ioctl32 = sc301iot_compat_ioctl32,
 #endif
 };
 
-static const struct v4l2_subdev_video_ops SC301IOT_video_ops = {
-	.s_stream = SC301IOT_s_stream,
-	.g_frame_interval = SC301IOT_g_frame_interval,
-	.s_frame_interval = SC301IOT_s_frame_interval,
+static const struct v4l2_subdev_video_ops sc301iot_video_ops = {
+	.s_stream = sc301iot_s_stream,
+	.g_frame_interval = sc301iot_g_frame_interval,
+	.s_frame_interval = sc301iot_s_frame_interval,
 };
 
-static const struct v4l2_subdev_pad_ops SC301IOT_pad_ops = {
-	.enum_mbus_code = SC301IOT_enum_mbus_code,
-	.enum_frame_size = SC301IOT_enum_frame_sizes,
-	.enum_frame_interval = SC301IOT_enum_frame_interval,
-	.get_fmt = SC301IOT_get_fmt,
-	.set_fmt = SC301IOT_set_fmt,
-	.get_mbus_config = SC301IOT_g_mbus_config,
+static const struct v4l2_subdev_pad_ops sc301iot_pad_ops = {
+	.enum_mbus_code = sc301iot_enum_mbus_code,
+	.enum_frame_size = sc301iot_enum_frame_sizes,
+	.enum_frame_interval = sc301iot_enum_frame_interval,
+	.get_fmt = sc301iot_get_fmt,
+	.set_fmt = sc301iot_set_fmt,
+	.get_mbus_config = sc301iot_g_mbus_config,
 };
 
-static const struct v4l2_subdev_ops SC301IOT_subdev_ops = {
-	.core	= &SC301IOT_core_ops,
-	.video	= &SC301IOT_video_ops,
-	.pad	= &SC301IOT_pad_ops,
+static const struct v4l2_subdev_ops sc301iot_subdev_ops = {
+	.core	= &sc301iot_core_ops,
+	.video	= &sc301iot_video_ops,
+	.pad	= &sc301iot_pad_ops,
 };
 
-static void SC301IOT_modify_fps_info(struct SC301IOT *SC301IOT)
+static void sc301iot_modify_fps_info(struct sc301iot *sc301iot)
 {
-	const struct SC301IOT_mode *mode = SC301IOT->cur_mode;
+	const struct sc301iot_mode *mode = sc301iot->cur_mode;
 
-	SC301IOT->cur_fps.denominator = mode->max_fps.denominator * mode->vts_def /
-					SC301IOT->cur_vts;
+	sc301iot->cur_fps.denominator = mode->max_fps.denominator * mode->vts_def /
+					sc301iot->cur_vts;
 }
 
-static int SC301IOT_set_ctrl(struct v4l2_ctrl *ctrl)
+static int sc301iot_set_ctrl(struct v4l2_ctrl *ctrl)
 {
-	struct SC301IOT *SC301IOT = container_of(ctrl->handler,
-					       struct SC301IOT, ctrl_handler);
-	struct i2c_client *client = SC301IOT->client;
+	struct sc301iot *sc301iot = container_of(ctrl->handler,
+					       struct sc301iot, ctrl_handler);
+	struct i2c_client *client = sc301iot->client;
 	s64 max;
 	int ret = 0;
 	u32 val = 0;
@@ -1904,11 +1906,11 @@
 	switch (ctrl->id) {
 	case V4L2_CID_VBLANK:
 		/* Update max exposure while meeting expected vblanking */
-		max = SC301IOT->cur_mode->height + ctrl->val - 4;
-		__v4l2_ctrl_modify_range(SC301IOT->exposure,
-					 SC301IOT->exposure->minimum, max,
-					 SC301IOT->exposure->step,
-					 SC301IOT->exposure->default_value);
+		max = sc301iot->cur_mode->height + ctrl->val - 4;
+		__v4l2_ctrl_modify_range(sc301iot->exposure,
+					 sc301iot->exposure->minimum, max,
+					 sc301iot->exposure->step,
+					 sc301iot->exposure->default_value);
 		break;
 	}
 
@@ -1917,57 +1919,56 @@
 
 	switch (ctrl->id) {
 	case V4L2_CID_EXPOSURE:
-		if (SC301IOT->cur_mode->hdr_mode == NO_HDR) {
+		if (sc301iot->cur_mode->hdr_mode == NO_HDR) {
 			ctrl->val = ctrl->val;
 			/* 4 least significant bits of expsoure are fractional part */
-			ret = SC301IOT_write_reg(SC301IOT->client,
+			ret = sc301iot_write_reg(sc301iot->client,
 						SC301IOT_REG_EXPOSURE_H,
 						SC301IOT_REG_VALUE_08BIT,
 						SC301IOT_FETCH_EXP_H(ctrl->val));
-			ret |= SC301IOT_write_reg(SC301IOT->client,
+			ret |= sc301iot_write_reg(sc301iot->client,
 						 SC301IOT_REG_EXPOSURE_M,
 						 SC301IOT_REG_VALUE_08BIT,
 						 SC301IOT_FETCH_EXP_M(ctrl->val));
-			ret |= SC301IOT_write_reg(SC301IOT->client,
+			ret |= sc301iot_write_reg(sc301iot->client,
 						 SC301IOT_REG_EXPOSURE_L,
 						 SC301IOT_REG_VALUE_08BIT,
 						 SC301IOT_FETCH_EXP_L(ctrl->val));
 		}
 		break;
 	case V4L2_CID_ANALOGUE_GAIN:
-		if (SC301IOT->cur_mode->hdr_mode == NO_HDR)
-			ret = SC301IOT_set_gain_reg(SC301IOT, ctrl->val, SC301IOT_LGAIN);
+		if (sc301iot->cur_mode->hdr_mode == NO_HDR)
+			ret = sc301iot_set_gain_reg(sc301iot, ctrl->val, SC301IOT_LGAIN);
 		break;
 	case V4L2_CID_VBLANK:
-		ret = SC301IOT_write_reg(SC301IOT->client,
+		ret = sc301iot_write_reg(sc301iot->client,
 					SC301IOT_REG_VTS_H,
 					SC301IOT_REG_VALUE_08BIT,
-					(ctrl->val + SC301IOT->cur_mode->height)
+					(ctrl->val + sc301iot->cur_mode->height)
 					>> 8);
-		ret |= SC301IOT_write_reg(SC301IOT->client,
+		ret |= sc301iot_write_reg(sc301iot->client,
 					 SC301IOT_REG_VTS_L,
 					 SC301IOT_REG_VALUE_08BIT,
-					 (ctrl->val + SC301IOT->cur_mode->height)
+					 (ctrl->val + sc301iot->cur_mode->height)
 					 & 0xff);
 		if (!ret)
-			SC301IOT->cur_vts = ctrl->val + SC301IOT->cur_mode->height;
-		if (SC301IOT->cur_vts != SC301IOT->cur_mode->vts_def)
-			SC301IOT_modify_fps_info(SC301IOT);
+			sc301iot->cur_vts = ctrl->val + sc301iot->cur_mode->height;
+		sc301iot_modify_fps_info(sc301iot);
 		break;
 	case V4L2_CID_TEST_PATTERN:
-		ret = SC301IOT_enable_test_pattern(SC301IOT, ctrl->val);
+		ret = sc301iot_enable_test_pattern(sc301iot, ctrl->val);
 		break;
 	case V4L2_CID_HFLIP:
-		ret = SC301IOT_read_reg(SC301IOT->client, SC301IOT_FLIP_MIRROR_REG,
+		ret = sc301iot_read_reg(sc301iot->client, SC301IOT_FLIP_MIRROR_REG,
 				       SC301IOT_REG_VALUE_08BIT, &val);
-		ret |= SC301IOT_write_reg(SC301IOT->client, SC301IOT_FLIP_MIRROR_REG,
+		ret |= sc301iot_write_reg(sc301iot->client, SC301IOT_FLIP_MIRROR_REG,
 					 SC301IOT_REG_VALUE_08BIT,
 					 SC301IOT_FETCH_MIRROR(val, ctrl->val));
 		break;
 	case V4L2_CID_VFLIP:
-		ret = SC301IOT_read_reg(SC301IOT->client, SC301IOT_FLIP_MIRROR_REG,
+		ret = sc301iot_read_reg(sc301iot->client, SC301IOT_FLIP_MIRROR_REG,
 				       SC301IOT_REG_VALUE_08BIT, &val);
-		ret |= SC301IOT_write_reg(SC301IOT->client, SC301IOT_FLIP_MIRROR_REG,
+		ret |= sc301iot_write_reg(sc301iot->client, SC301IOT_FLIP_MIRROR_REG,
 					 SC301IOT_REG_VALUE_08BIT,
 					 SC301IOT_FETCH_FLIP(val, ctrl->val));
 		break;
@@ -1982,25 +1983,25 @@
 	return ret;
 }
 
-static const struct v4l2_ctrl_ops SC301IOT_ctrl_ops = {
-	.s_ctrl = SC301IOT_set_ctrl,
+static const struct v4l2_ctrl_ops sc301iot_ctrl_ops = {
+	.s_ctrl = sc301iot_set_ctrl,
 };
 
-static int SC301IOT_initialize_controls(struct SC301IOT *SC301IOT)
+static int sc301iot_initialize_controls(struct sc301iot *sc301iot)
 {
-	const struct SC301IOT_mode *mode;
+	const struct sc301iot_mode *mode;
 	struct v4l2_ctrl_handler *handler;
 	struct v4l2_ctrl *ctrl;
 	s64 exposure_max, vblank_def;
 	u32 h_blank;
 	int ret;
 
-	handler = &SC301IOT->ctrl_handler;
-	mode = SC301IOT->cur_mode;
+	handler = &sc301iot->ctrl_handler;
+	mode = sc301iot->cur_mode;
 	ret = v4l2_ctrl_handler_init(handler, 9);
 	if (ret)
 		return ret;
-	handler->lock = &SC301IOT->mutex;
+	handler->lock = &sc301iot->mutex;
 
 	ctrl = v4l2_ctrl_new_int_menu(handler, NULL, V4L2_CID_LINK_FREQ,
 				      0, 0, link_freq_menu_items);
@@ -2011,46 +2012,46 @@
 			  0, PIXEL_RATE_WITH_594M_10BIT, 1, PIXEL_RATE_WITH_594M_10BIT);
 
 	h_blank = mode->hts_def - mode->width;
-	SC301IOT->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK,
+	sc301iot->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK,
 					    h_blank, h_blank, 1, h_blank);
-	if (SC301IOT->hblank)
-		SC301IOT->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+	if (sc301iot->hblank)
+		sc301iot->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
 	vblank_def = mode->vts_def - mode->height;
-	SC301IOT->vblank = v4l2_ctrl_new_std(handler, &SC301IOT_ctrl_ops,
+	sc301iot->vblank = v4l2_ctrl_new_std(handler, &sc301iot_ctrl_ops,
 					    V4L2_CID_VBLANK, vblank_def,
 					    SC301IOT_VTS_MAX - mode->height,
 					    1, vblank_def);
 	exposure_max = mode->vts_def - 8;
-	SC301IOT->exposure = v4l2_ctrl_new_std(handler, &SC301IOT_ctrl_ops,
+	sc301iot->exposure = v4l2_ctrl_new_std(handler, &sc301iot_ctrl_ops,
 					      V4L2_CID_EXPOSURE, SC301IOT_EXPOSURE_MIN,
 					      exposure_max, SC301IOT_EXPOSURE_STEP,
 					      mode->exp_def);
-	SC301IOT->anal_gain = v4l2_ctrl_new_std(handler, &SC301IOT_ctrl_ops,
+	sc301iot->anal_gain = v4l2_ctrl_new_std(handler, &sc301iot_ctrl_ops,
 					       V4L2_CID_ANALOGUE_GAIN, SC301IOT_GAIN_MIN,
 					       SC301IOT_GAIN_MAX, SC301IOT_GAIN_STEP,
 					       SC301IOT_GAIN_DEFAULT);
-	SC301IOT->test_pattern = v4l2_ctrl_new_std_menu_items(handler,
-							    &SC301IOT_ctrl_ops,
+	sc301iot->test_pattern = v4l2_ctrl_new_std_menu_items(handler,
+							    &sc301iot_ctrl_ops,
 					V4L2_CID_TEST_PATTERN,
-					ARRAY_SIZE(SC301IOT_test_pattern_menu) - 1,
-					0, 0, SC301IOT_test_pattern_menu);
-	v4l2_ctrl_new_std(handler, &SC301IOT_ctrl_ops,
+					ARRAY_SIZE(sc301iot_test_pattern_menu) - 1,
+					0, 0, sc301iot_test_pattern_menu);
+	v4l2_ctrl_new_std(handler, &sc301iot_ctrl_ops,
 				V4L2_CID_HFLIP, 0, 1, 1, 0);
 
-	v4l2_ctrl_new_std(handler, &SC301IOT_ctrl_ops,
+	v4l2_ctrl_new_std(handler, &sc301iot_ctrl_ops,
 				V4L2_CID_VFLIP, 0, 1, 1, 0);
 
 	if (handler->error) {
 		ret = handler->error;
-		dev_err(&SC301IOT->client->dev,
+		dev_err(&sc301iot->client->dev,
 			"Failed to init controls(%d)\n", ret);
 		goto err_free_handler;
 	}
 
-	SC301IOT->subdev.ctrl_handler = handler;
-	SC301IOT->has_init_exp = false;
-	SC301IOT->cur_fps = mode->max_fps;
-	SC301IOT->cur_vts = mode->vts_def;
+	sc301iot->subdev.ctrl_handler = handler;
+	sc301iot->has_init_exp = false;
+	sc301iot->cur_fps = mode->max_fps;
+	sc301iot->cur_vts = mode->vts_def;
 
 	return 0;
 
@@ -2060,19 +2061,19 @@
 	return ret;
 }
 
-static int SC301IOT_check_sensor_id(struct SC301IOT *SC301IOT,
+static int sc301iot_check_sensor_id(struct sc301iot *sc301iot,
 				   struct i2c_client *client)
 {
-	struct device *dev = &SC301IOT->client->dev;
+	struct device *dev = &sc301iot->client->dev;
 	u32 id = 0;
 	int ret;
 
-	if (SC301IOT->is_thunderboot) {
+	if (sc301iot->is_thunderboot) {
 		dev_info(dev, "Enable thunderboot mode, skip sensor id check\n");
 		return 0;
 	}
 
-	ret = SC301IOT_read_reg(client, SC301IOT_REG_CHIP_ID,
+	ret = sc301iot_read_reg(client, SC301IOT_REG_CHIP_ID,
 			       SC301IOT_REG_VALUE_16BIT, &id);
 	if (id != CHIP_ID) {
 		dev_err(dev, "Unexpected chip id(0x%04x), ret(%d)\n", id, ret);
@@ -2084,24 +2085,24 @@
 	return 0;
 }
 
-static int SC301IOT_configure_regulators(struct SC301IOT *SC301IOT)
+static int sc301iot_configure_regulators(struct sc301iot *sc301iot)
 {
 	unsigned int i;
 
 	for (i = 0; i < SC301IOT_NUM_SUPPLIES; i++)
-		SC301IOT->supplies[i].supply = SC301IOT_supply_names[i];
+		sc301iot->supplies[i].supply = SC301IOT_supply_names[i];
 
-	return devm_regulator_bulk_get(&SC301IOT->client->dev,
+	return devm_regulator_bulk_get(&sc301iot->client->dev,
 				       SC301IOT_NUM_SUPPLIES,
-				       SC301IOT->supplies);
+				       sc301iot->supplies);
 }
 
-static int SC301IOT_probe(struct i2c_client *client,
+static int sc301iot_probe(struct i2c_client *client,
 			 const struct i2c_device_id *id)
 {
 	struct device *dev = &client->dev;
 	struct device_node *node = dev->of_node;
-	struct SC301IOT *SC301IOT;
+	struct sc301iot *sc301iot;
 	struct v4l2_subdev *sd;
 	char facing[2];
 	int ret;
@@ -2113,39 +2114,39 @@
 		 (DRIVER_VERSION & 0xff00) >> 8,
 		 DRIVER_VERSION & 0x00ff);
 
-	SC301IOT = devm_kzalloc(dev, sizeof(*SC301IOT), GFP_KERNEL);
-	if (!SC301IOT)
+	sc301iot = devm_kzalloc(dev, sizeof(*sc301iot), GFP_KERNEL);
+	if (!sc301iot)
 		return -ENOMEM;
 
 	of_property_read_u32(node, OF_CAMERA_HDR_MODE, &hdr_mode);
 	ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX,
-				   &SC301IOT->module_index);
+				   &sc301iot->module_index);
 	ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING,
-				       &SC301IOT->module_facing);
+				       &sc301iot->module_facing);
 	ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME,
-				       &SC301IOT->module_name);
+				       &sc301iot->module_name);
 	ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME,
-				       &SC301IOT->len_name);
+				       &sc301iot->len_name);
 	if (ret) {
 		dev_err(dev, "could not get module information!\n");
 		return -EINVAL;
 	}
 
-	SC301IOT->is_thunderboot = IS_ENABLED(CONFIG_VIDEO_ROCKCHIP_THUNDER_BOOT_ISP);
-	SC301IOT->sync_mode = NO_SYNC_MODE;
+	sc301iot->is_thunderboot = IS_ENABLED(CONFIG_VIDEO_ROCKCHIP_THUNDER_BOOT_ISP);
+	sc301iot->sync_mode = NO_SYNC_MODE;
 	ret = of_property_read_string(node, RKMODULE_CAMERA_SYNC_MODE, &sync_mode_name);
 	if (!ret) {
 		if (strcmp(sync_mode_name, RKMODULE_EXTERNAL_MASTER_MODE) == 0)
-			SC301IOT->sync_mode = EXTERNAL_MASTER_MODE;
+			sc301iot->sync_mode = EXTERNAL_MASTER_MODE;
 		else if (strcmp(sync_mode_name, RKMODULE_INTERNAL_MASTER_MODE) == 0)
-			SC301IOT->sync_mode = INTERNAL_MASTER_MODE;
+			sc301iot->sync_mode = INTERNAL_MASTER_MODE;
 		else if (strcmp(sync_mode_name, RKMODULE_SLAVE_MODE) == 0)
-			SC301IOT->sync_mode = SLAVE_MODE;
+			sc301iot->sync_mode = SLAVE_MODE;
 	}
 
-	switch (SC301IOT->sync_mode) {
+	switch (sc301iot->sync_mode) {
 	default:
-		SC301IOT->sync_mode = NO_SYNC_MODE; break;
+		sc301iot->sync_mode = NO_SYNC_MODE; break;
 	case NO_SYNC_MODE:
 		dev_info(dev, "sync_mode = [NO_SYNC_MODE]\n"); break;
 	case EXTERNAL_MASTER_MODE:
@@ -2155,100 +2156,100 @@
 		dev_info(dev, "sync_mode = [SLAVE_MODE]\n"); break;
 	}
 
-	SC301IOT->client = client;
+	sc301iot->client = client;
 	for (i = 0; i < ARRAY_SIZE(supported_modes); i++) {
 		if (hdr_mode == supported_modes[i].hdr_mode) {
-			SC301IOT->cur_mode = &supported_modes[i];
+			sc301iot->cur_mode = &supported_modes[i];
 			break;
 		}
 	}
 	if (i == ARRAY_SIZE(supported_modes))
-		SC301IOT->cur_mode = &supported_modes[0];
+		sc301iot->cur_mode = &supported_modes[0];
 
-	SC301IOT->xvclk = devm_clk_get(dev, "xvclk");
-	if (IS_ERR(SC301IOT->xvclk)) {
+	sc301iot->xvclk = devm_clk_get(dev, "xvclk");
+	if (IS_ERR(sc301iot->xvclk)) {
 		dev_err(dev, "Failed to get xvclk\n");
 		return -EINVAL;
 	}
 
-	if (SC301IOT->is_thunderboot) {
-		SC301IOT->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_ASIS);
-		if (IS_ERR(SC301IOT->reset_gpio))
+	if (sc301iot->is_thunderboot) {
+		sc301iot->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_ASIS);
+		if (IS_ERR(sc301iot->reset_gpio))
 			dev_warn(dev, "Failed to get reset-gpios\n");
 
-		SC301IOT->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_ASIS);
-		if (IS_ERR(SC301IOT->pwdn_gpio))
+		sc301iot->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_ASIS);
+		if (IS_ERR(sc301iot->pwdn_gpio))
 			dev_warn(dev, "Failed to get pwdn-gpios\n");
 	} else {
-		SC301IOT->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
-		if (IS_ERR(SC301IOT->reset_gpio))
+		sc301iot->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
+		if (IS_ERR(sc301iot->reset_gpio))
 			dev_warn(dev, "Failed to get reset-gpios\n");
 
-		SC301IOT->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_OUT_LOW);
-		if (IS_ERR(SC301IOT->pwdn_gpio))
+		sc301iot->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_OUT_LOW);
+		if (IS_ERR(sc301iot->pwdn_gpio))
 			dev_warn(dev, "Failed to get pwdn-gpios\n");
 	}
 
-	SC301IOT->pinctrl = devm_pinctrl_get(dev);
-	if (!IS_ERR(SC301IOT->pinctrl)) {
-		SC301IOT->pins_default =
-			pinctrl_lookup_state(SC301IOT->pinctrl,
+	sc301iot->pinctrl = devm_pinctrl_get(dev);
+	if (!IS_ERR(sc301iot->pinctrl)) {
+		sc301iot->pins_default =
+			pinctrl_lookup_state(sc301iot->pinctrl,
 					     OF_CAMERA_PINCTRL_STATE_DEFAULT);
-		if (IS_ERR(SC301IOT->pins_default))
+		if (IS_ERR(sc301iot->pins_default))
 			dev_err(dev, "could not get default pinstate\n");
 
-		SC301IOT->pins_sleep =
-			pinctrl_lookup_state(SC301IOT->pinctrl,
+		sc301iot->pins_sleep =
+			pinctrl_lookup_state(sc301iot->pinctrl,
 					     OF_CAMERA_PINCTRL_STATE_SLEEP);
-		if (IS_ERR(SC301IOT->pins_sleep))
+		if (IS_ERR(sc301iot->pins_sleep))
 			dev_err(dev, "could not get sleep pinstate\n");
 	} else {
 		dev_err(dev, "no pinctrl\n");
 	}
 
-	ret = SC301IOT_configure_regulators(SC301IOT);
+	ret = sc301iot_configure_regulators(sc301iot);
 	if (ret) {
 		dev_err(dev, "Failed to get power regulators\n");
 		return ret;
 	}
 
-	mutex_init(&SC301IOT->mutex);
+	mutex_init(&sc301iot->mutex);
 
-	sd = &SC301IOT->subdev;
-	v4l2_i2c_subdev_init(sd, client, &SC301IOT_subdev_ops);
-	ret = SC301IOT_initialize_controls(SC301IOT);
+	sd = &sc301iot->subdev;
+	v4l2_i2c_subdev_init(sd, client, &sc301iot_subdev_ops);
+	ret = sc301iot_initialize_controls(sc301iot);
 	if (ret)
 		goto err_destroy_mutex;
 
-	ret = __SC301IOT_power_on(SC301IOT);
+	ret = __sc301iot_power_on(sc301iot);
 	if (ret)
 		goto err_free_handler;
 
-	ret = SC301IOT_check_sensor_id(SC301IOT, client);
+	ret = sc301iot_check_sensor_id(sc301iot, client);
 	if (ret)
 		goto err_power_off;
 
 #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
-	sd->internal_ops = &SC301IOT_internal_ops;
+	sd->internal_ops = &sc301iot_internal_ops;
 	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
 		     V4L2_SUBDEV_FL_HAS_EVENTS;
 #endif
 #if defined(CONFIG_MEDIA_CONTROLLER)
-	SC301IOT->pad.flags = MEDIA_PAD_FL_SOURCE;
+	sc301iot->pad.flags = MEDIA_PAD_FL_SOURCE;
 	sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
-	ret = media_entity_pads_init(&sd->entity, 1, &SC301IOT->pad);
+	ret = media_entity_pads_init(&sd->entity, 1, &sc301iot->pad);
 	if (ret < 0)
 		goto err_power_off;
 #endif
 
 	memset(facing, 0, sizeof(facing));
-	if (strcmp(SC301IOT->module_facing, "back") == 0)
+	if (strcmp(sc301iot->module_facing, "back") == 0)
 		facing[0] = 'b';
 	else
 		facing[0] = 'f';
 
 	snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s",
-		 SC301IOT->module_index, facing,
+		 sc301iot->module_index, facing,
 		 SC301IOT_NAME, dev_name(sd->dev));
 	ret = v4l2_async_register_subdev_sensor_common(sd);
 	if (ret) {
@@ -2258,7 +2259,10 @@
 
 	pm_runtime_set_active(dev);
 	pm_runtime_enable(dev);
-	pm_runtime_idle(dev);
+	if (sc301iot->is_thunderboot)
+		pm_runtime_get_sync(dev);
+	else
+		pm_runtime_idle(dev);
 
 	return 0;
 
@@ -2267,67 +2271,67 @@
 	media_entity_cleanup(&sd->entity);
 #endif
 err_power_off:
-	__SC301IOT_power_off(SC301IOT);
+	__sc301iot_power_off(sc301iot);
 err_free_handler:
-	v4l2_ctrl_handler_free(&SC301IOT->ctrl_handler);
+	v4l2_ctrl_handler_free(&sc301iot->ctrl_handler);
 err_destroy_mutex:
-	mutex_destroy(&SC301IOT->mutex);
+	mutex_destroy(&sc301iot->mutex);
 
 	return ret;
 }
 
-static int SC301IOT_remove(struct i2c_client *client)
+static int sc301iot_remove(struct i2c_client *client)
 {
 	struct v4l2_subdev *sd = i2c_get_clientdata(client);
-	struct SC301IOT *SC301IOT = to_SC301IOT(sd);
+	struct sc301iot *sc301iot = to_sc301iot(sd);
 
 	v4l2_async_unregister_subdev(sd);
 #if defined(CONFIG_MEDIA_CONTROLLER)
 	media_entity_cleanup(&sd->entity);
 #endif
-	v4l2_ctrl_handler_free(&SC301IOT->ctrl_handler);
-	mutex_destroy(&SC301IOT->mutex);
+	v4l2_ctrl_handler_free(&sc301iot->ctrl_handler);
+	mutex_destroy(&sc301iot->mutex);
 
 	pm_runtime_disable(&client->dev);
 	if (!pm_runtime_status_suspended(&client->dev))
-		__SC301IOT_power_off(SC301IOT);
+		__sc301iot_power_off(sc301iot);
 	pm_runtime_set_suspended(&client->dev);
 
 	return 0;
 }
 
 #if IS_ENABLED(CONFIG_OF)
-static const struct of_device_id SC301IOT_of_match[] = {
-	{ .compatible = "smartsens,SC301IOT" },
+static const struct of_device_id sc301iot_of_match[] = {
+	{ .compatible = "smartsens,sc301iot" },
 	{},
 };
-MODULE_DEVICE_TABLE(of, SC301IOT_of_match);
+MODULE_DEVICE_TABLE(of, sc301iot_of_match);
 #endif
 
-static const struct i2c_device_id SC301IOT_match_id[] = {
-	{ "smartsens,SC301IOT", 0 },
+static const struct i2c_device_id sc301iot_match_id[] = {
+	{ "smartsens,sc301iot", 0 },
 	{ },
 };
 
-static struct i2c_driver SC301IOT_i2c_driver = {
+static struct i2c_driver sc301iot_i2c_driver = {
 	.driver = {
 		.name = SC301IOT_NAME,
-		.pm = &SC301IOT_pm_ops,
-		.of_match_table = of_match_ptr(SC301IOT_of_match),
+		.pm = &sc301iot_pm_ops,
+		.of_match_table = of_match_ptr(sc301iot_of_match),
 	},
-	.probe		= &SC301IOT_probe,
-	.remove		= &SC301IOT_remove,
-	.id_table	= SC301IOT_match_id,
+	.probe		= &sc301iot_probe,
+	.remove		= &sc301iot_remove,
+	.id_table	= sc301iot_match_id,
 };
 
 static int __init sensor_mod_init(void)
 {
-	return i2c_add_driver(&SC301IOT_i2c_driver);
+	return i2c_add_driver(&sc301iot_i2c_driver);
 }
 
 static void __exit sensor_mod_exit(void)
 {
-	i2c_del_driver(&SC301IOT_i2c_driver);
+	i2c_del_driver(&sc301iot_i2c_driver);
 }
 
 #if defined(CONFIG_VIDEO_ROCKCHIP_THUNDER_BOOT_ISP) && !defined(CONFIG_INITCALL_ASYNC)
@@ -2337,5 +2341,5 @@
 #endif
 module_exit(sensor_mod_exit);
 
-MODULE_DESCRIPTION("smartsens SC301IOT sensor driver");
+MODULE_DESCRIPTION("smartsens sc301iot sensor driver");
 MODULE_LICENSE("GPL");
diff --git a/kernel/drivers/media/i2c/sc3336.c b/kernel/drivers/media/i2c/sc3336.c
index 9500309..51fc48c 100644
--- a/kernel/drivers/media/i2c/sc3336.c
+++ b/kernel/drivers/media/i2c/sc3336.c
@@ -1382,8 +1382,7 @@
 					 (ctrl->val + sc3336->cur_mode->height)
 					 & 0xff);
 		sc3336->cur_vts = ctrl->val + sc3336->cur_mode->height;
-		if (sc3336->cur_vts != sc3336->cur_mode->vts_def)
-			sc3336_modify_fps_info(sc3336);
+		sc3336_modify_fps_info(sc3336);
 		break;
 	case V4L2_CID_TEST_PATTERN:
 		ret = sc3336_enable_test_pattern(sc3336, ctrl->val);
diff --git a/kernel/drivers/media/i2c/sc3338.c b/kernel/drivers/media/i2c/sc3338.c
index 9af90ce..8120f40 100644
--- a/kernel/drivers/media/i2c/sc3338.c
+++ b/kernel/drivers/media/i2c/sc3338.c
@@ -1202,8 +1202,7 @@
 					 (ctrl->val + sc3338->cur_mode->height)
 					 & 0xff);
 		sc3338->cur_vts = ctrl->val + sc3338->cur_mode->height;
-		if (sc3338->cur_vts != sc3338->cur_mode->vts_def)
-			sc3338_modify_fps_info(sc3338);
+		sc3338_modify_fps_info(sc3338);
 		break;
 	case V4L2_CID_TEST_PATTERN:
 		ret = sc3338_enable_test_pattern(sc3338, ctrl->val);
@@ -1395,11 +1394,11 @@
 		return -EINVAL;
 	}
 
-	sc3338->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_ASIS);
+	sc3338->reset_gpio = devm_gpiod_get(dev, "reset", sc3338->is_thunderboot ? GPIOD_ASIS : GPIOD_OUT_LOW);
 	if (IS_ERR(sc3338->reset_gpio))
 		dev_warn(dev, "Failed to get reset-gpios\n");
 
-	sc3338->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_ASIS);
+	sc3338->pwdn_gpio = devm_gpiod_get(dev, "pwdn", sc3338->is_thunderboot ? GPIOD_ASIS : GPIOD_OUT_LOW);
 	if (IS_ERR(sc3338->pwdn_gpio))
 		dev_warn(dev, "Failed to get pwdn-gpios\n");
 
diff --git a/kernel/drivers/media/i2c/sc401ai.c b/kernel/drivers/media/i2c/sc401ai.c
index 450fbd8..e4f965d 100644
--- a/kernel/drivers/media/i2c/sc401ai.c
+++ b/kernel/drivers/media/i2c/sc401ai.c
@@ -1331,8 +1331,7 @@
 					 & 0xff);
 		if (!ret)
 			sc401ai->cur_vts = ctrl->val + sc401ai->cur_mode->height;
-		if (sc401ai->cur_vts != sc401ai->cur_mode->vts_def)
-			sc401ai_modify_fps_info(sc401ai);
+		sc401ai_modify_fps_info(sc401ai);
 		break;
 	case V4L2_CID_TEST_PATTERN:
 		ret = sc401ai_enable_test_pattern(sc401ai, ctrl->val);
diff --git a/kernel/drivers/media/i2c/sc4210.c b/kernel/drivers/media/i2c/sc4210.c
index b327797..9f582a9 100644
--- a/kernel/drivers/media/i2c/sc4210.c
+++ b/kernel/drivers/media/i2c/sc4210.c
@@ -2408,8 +2408,7 @@
 					 SC4210_REG_VALUE_08BIT,
 					 vts & 0xff);
 		sc4210->cur_vts = ctrl->val + sc4210->cur_mode->height;
-		if (sc4210->cur_vts != sc4210->cur_mode->vts_def)
-			sc4210_modify_fps_info(sc4210);
+		sc4210_modify_fps_info(sc4210);
 		dev_dbg(&client->dev, "set vblank 0x%x\n", ctrl->val);
 		break;
 	case V4L2_CID_HFLIP:
diff --git a/kernel/drivers/media/i2c/sc4238.c b/kernel/drivers/media/i2c/sc4238.c
index 8750d5a..5bbf26d 100644
--- a/kernel/drivers/media/i2c/sc4238.c
+++ b/kernel/drivers/media/i2c/sc4238.c
@@ -2484,8 +2484,7 @@
 					ctrl->val + sc4238->cur_mode->height);
 		if (ret == 0)
 			sc4238->cur_vts = ctrl->val + sc4238->cur_mode->height;
-		if (sc4238->cur_vts != sc4238->cur_mode->vts_def)
-			sc4238_modify_fps_info(sc4238);
+		sc4238_modify_fps_info(sc4238);
 		dev_dbg(&client->dev, "set vblank 0x%x\n",
 			ctrl->val);
 		break;
diff --git a/kernel/drivers/media/i2c/sc430cs.c b/kernel/drivers/media/i2c/sc430cs.c
index 5900da6..8c88bc5 100644
--- a/kernel/drivers/media/i2c/sc430cs.c
+++ b/kernel/drivers/media/i2c/sc430cs.c
@@ -1202,8 +1202,7 @@
 					 & 0xff);
 		if (!ret)
 			sc430cs->cur_vts = ctrl->val + sc430cs->cur_mode->height;
-		if (sc430cs->cur_vts != sc430cs->cur_mode->vts_def)
-			sc430cs_modify_fps_info(sc430cs);
+		sc430cs_modify_fps_info(sc430cs);
 		break;
 	case V4L2_CID_TEST_PATTERN:
 		ret = sc430cs_enable_test_pattern(sc430cs, ctrl->val);
diff --git a/kernel/drivers/media/i2c/sc4336.c b/kernel/drivers/media/i2c/sc4336.c
index 31aeebd..f741979 100644
--- a/kernel/drivers/media/i2c/sc4336.c
+++ b/kernel/drivers/media/i2c/sc4336.c
@@ -1199,8 +1199,7 @@
 					 (ctrl->val + sc4336->cur_mode->height)
 					 & 0xff);
 		sc4336->cur_vts = ctrl->val + sc4336->cur_mode->height;
-		if (sc4336->cur_vts != sc4336->cur_mode->vts_def)
-			sc4336_modify_fps_info(sc4336);
+		sc4336_modify_fps_info(sc4336);
 		break;
 	case V4L2_CID_TEST_PATTERN:
 		ret = sc4336_enable_test_pattern(sc4336, ctrl->val);
diff --git a/kernel/drivers/media/i2c/sc500ai.c b/kernel/drivers/media/i2c/sc500ai.c
index 060c5d2..f5a2042 100644
--- a/kernel/drivers/media/i2c/sc500ai.c
+++ b/kernel/drivers/media/i2c/sc500ai.c
@@ -1489,8 +1489,7 @@
 					 vts & 0xff);
 		if (!ret)
 			sc500ai->cur_vts = vts;
-		if (sc500ai->cur_vts != sc500ai->cur_mode->vts_def)
-			sc500ai_modify_fps_info(sc500ai);
+		sc500ai_modify_fps_info(sc500ai);
 		break;
 	case V4L2_CID_HFLIP:
 		ret = sc500ai_read_reg(sc500ai->client, SC500AI_FLIP_MIRROR_REG,
diff --git a/kernel/drivers/media/i2c/sc501ai.c b/kernel/drivers/media/i2c/sc501ai.c
index 6ae7a2b..34324db 100644
--- a/kernel/drivers/media/i2c/sc501ai.c
+++ b/kernel/drivers/media/i2c/sc501ai.c
@@ -1044,8 +1044,7 @@
 					 SC501AI_REG_VALUE_08BIT,
 					 vts & 0xff);
 		sc501ai->cur_vts = vts;
-		if (sc501ai->cur_vts != sc501ai->cur_mode->vts_def)
-			sc501ai_modify_fps_info(sc501ai);
+		sc501ai_modify_fps_info(sc501ai);
 		break;
 	case V4L2_CID_HFLIP:
 		ret = sc501ai_read_reg(sc501ai->client, SC501AI_FLIP_MIRROR_REG,
diff --git a/kernel/drivers/media/i2c/sc530ai.c b/kernel/drivers/media/i2c/sc530ai.c
index 6725245..7738dc5 100644
--- a/kernel/drivers/media/i2c/sc530ai.c
+++ b/kernel/drivers/media/i2c/sc530ai.c
@@ -52,6 +52,7 @@
 #define SC530AI_LINK_FREQ_396M		198000000 // 396Mbps
 #define SC530AI_LINK_FREQ_792M		396000000 // 792Mbps
 #define SC530AI_LINK_FREQ_792M_2LANE	396000000 // 792Mbps
+#define SC530AI_LINK_FREQ_936M_2LANE	468000000 // 936Mbps
 
 #define SC530AI_LINEAR_PIXEL_RATES	(SC530AI_LINK_FREQ_396M / 10 * 2 * 4)
 #define SC530AI_HDR_PIXEL_RATES		(SC530AI_LINK_FREQ_792M / 10 * 2 * 4)
@@ -520,18 +521,22 @@
 	{0x37f9, 0x80},
 	{0x3018, 0x32},
 	{0x3019, 0x0c},
-	{0x301f, 0x18},
+	{0x301f, 0x42},
+	{0x320c, 0x06},
+	{0x320d, 0x27},
+	{0x320e, 0x07},
+	{0x320f, 0xbc},
 	{0x3250, 0x40},
 	{0x3251, 0x98},
 	{0x3253, 0x0c},
 	{0x325f, 0x20},
 	{0x3301, 0x08},
 	{0x3304, 0x50},
-	{0x3306, 0x78},
+	{0x3306, 0x88},
 	{0x3308, 0x14},
 	{0x3309, 0x70},
 	{0x330a, 0x00},
-	{0x330b, 0xd8},
+	{0x330b, 0xf8},
 	{0x330d, 0x10},
 	{0x331e, 0x41},
 	{0x331f, 0x61},
@@ -560,18 +565,18 @@
 	{0x33ae, 0x30},
 	{0x33af, 0x50},
 	{0x33b1, 0x80},
-	{0x33b2, 0x80},
-	{0x33b3, 0x40},
+	{0x33b2, 0x48},
+	{0x33b3, 0x30},
 	{0x349f, 0x02},
 	{0x34a6, 0x48},
-	{0x34a7, 0x49},
-	{0x34a8, 0x40},
-	{0x34a9, 0x30},
-	{0x34f8, 0x4b},
-	{0x34f9, 0x30},
+	{0x34a7, 0x4b},
+	{0x34a8, 0x30},
+	{0x34a9, 0x18},
+	{0x34f8, 0x5f},
+	{0x34f9, 0x08},
 	{0x3632, 0x48},
 	{0x3633, 0x32},
-	{0x3637, 0x2b},
+	{0x3637, 0x29},
 	{0x3638, 0xc1},
 	{0x363b, 0x20},
 	{0x363d, 0x02},
@@ -582,7 +587,7 @@
 	{0x367c, 0x40},
 	{0x367d, 0x48},
 	{0x3690, 0x32},
-	{0x3691, 0x32},
+	{0x3691, 0x43},
 	{0x3692, 0x33},
 	{0x3693, 0x40},
 	{0x3694, 0x4b},
@@ -594,7 +599,10 @@
 	{0x36a3, 0x4b},
 	{0x36a4, 0x4f},
 	{0x36d0, 0x01},
+	{0x36ea, 0x0d},
+	{0x36eb, 0x04},
 	{0x36ec, 0x03},
+	{0x36ed, 0x14},
 	{0x370f, 0x01},
 	{0x3722, 0x00},
 	{0x3728, 0x10},
@@ -603,8 +611,10 @@
 	{0x37b2, 0x83},
 	{0x37b3, 0x48},
 	{0x37b4, 0x49},
-	{0x37fb, 0x25},
+	{0x37fa, 0x0d},
+	{0x37fb, 0x24},
 	{0x37fc, 0x01},
+	{0x37fd, 0x14},
 	{0x3901, 0x00},
 	{0x3902, 0xc5},
 	{0x3904, 0x08},
@@ -614,19 +624,20 @@
 	{0x391f, 0x44},
 	{0x3926, 0x21},
 	{0x3929, 0x18},
-	{0x3933, 0x81},
-	{0x3934, 0x81},
-	{0x3937, 0x69},
+	{0x3933, 0x82},
+	{0x3934, 0x0a},
+	{0x3937, 0x5f},
 	{0x3939, 0x00},
 	{0x393a, 0x00},
 	{0x39dc, 0x02},
-	{0x3e01, 0xcd},
-	{0x3e02, 0xa0},
+	{0x3e01, 0xf6},
+	{0x3e02, 0xe0},
 	{0x440e, 0x02},
 	{0x4509, 0x20},
-	{0x4800, 0x04},
-	{0x4837, 0x14},
+	{0x4837, 0x22},
 	{0x5010, 0x10},
+	{0x5780, 0x66},
+	{0x578d, 0x40},
 	{0x5799, 0x06},
 	{0x57ad, 0x00},
 	{0x5ae0, 0xfe},
@@ -658,8 +669,7 @@
 	{0x5afe, 0x30},
 	{0x5aff, 0x28},
 	{0x36e9, 0x44},
-	{0x37f9, 0x34},
-//	{0x0100, 0x01},
+	{0x37f9, 0x44},
 	{REG_NULL, 0x00},
 };
 
@@ -713,10 +723,10 @@
 		},
 		.exp_def = 0xcda / 2,
 		.hts_def = 0xb40,
-		.vts_def = 0x0672,
+		.vts_def = 0x07bc,
 		.bus_fmt = MEDIA_BUS_FMT_SBGGR10_1X10,
 		.reg_list = sc530ai_10_30fps_2880x1620_2lane_regs,
-		.mipi_freq_idx = 2,
+		.mipi_freq_idx = 3,
 		.bpp = 10,
 		.hdr_mode = NO_HDR,
 		.vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_0,
@@ -727,6 +737,7 @@
 	SC530AI_LINK_FREQ_396M,
 	SC530AI_LINK_FREQ_792M,
 	SC530AI_LINK_FREQ_792M_2LANE,
+	SC530AI_LINK_FREQ_936M_2LANE,
 };
 
 /* Write registers up to 4 at a time */
@@ -1716,8 +1727,7 @@
 					 vts & 0xff);
 		if (!ret)
 			sc530ai->cur_vts = vts;
-		if (sc530ai->cur_vts != sc530ai->cur_mode->vts_def)
-			sc530ai_modify_fps_info(sc530ai);
+		sc530ai_modify_fps_info(sc530ai);
 		dev_dbg(&client->dev, "set vblank 0x%x\n", ctrl->val);
 		break;
 	case V4L2_CID_HFLIP:
diff --git a/kernel/drivers/media/i2c/sc5336.c b/kernel/drivers/media/i2c/sc5336.c
new file mode 100644
index 0000000..eb0b925
--- /dev/null
+++ b/kernel/drivers/media/i2c/sc5336.c
@@ -0,0 +1,1588 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * sc5336 driver
+ *
+ * Copyright (C) 2023 Rockchip Electronics Co., Ltd.
+ *
+ */
+
+//#define DEBUG
+#include <linux/clk.h>
+#include <linux/device.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 <linux/sysfs.h>
+#include <linux/slab.h>
+#include <linux/version.h>
+#include <linux/rk-camera-module.h>
+#include <linux/rk-preisp.h>
+#include <media/media-entity.h>
+#include <media/v4l2-async.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-subdev.h>
+#include <linux/pinctrl/consumer.h>
+#include "../platform/rockchip/isp/rkisp_tb_helper.h"
+
+#define DRIVER_VERSION			KERNEL_VERSION(0, 0x01, 0x01)
+
+#ifndef V4L2_CID_DIGITAL_GAIN
+#define V4L2_CID_DIGITAL_GAIN		V4L2_CID_GAIN
+#endif
+
+#define SC5336_LANES			2
+#define SC5336_BITS_PER_SAMPLE		10
+#define SC5336_LINK_FREQ		432000000
+
+#define PIXEL_RATE_WITH_315M_10BIT	(SC5336_LINK_FREQ * 2 * \
+					SC5336_LANES / SC5336_BITS_PER_SAMPLE)
+#define SC5336_XVCLK_FREQ		24000000
+
+#define CHIP_ID				0xce50
+#define SC5336_REG_CHIP_ID		0x3107
+
+#define SC5336_REG_CTRL_MODE		0x0100
+#define SC5336_MODE_SW_STANDBY		0x0
+#define SC5336_MODE_STREAMING		BIT(0)
+
+#define SC5336_REG_EXPOSURE_H		0x3e00
+#define SC5336_REG_EXPOSURE_M		0x3e01
+#define SC5336_REG_EXPOSURE_L		0x3e02
+#define	SC5336_EXPOSURE_MIN		1
+#define	SC5336_EXPOSURE_STEP		1
+#define SC5336_VTS_MAX			0x7fff
+
+#define SC5336_REG_DIG_GAIN		0x3e06
+#define SC5336_REG_DIG_FINE_GAIN	0x3e07
+#define SC5336_REG_ANA_GAIN		0x3e09
+#define SC5336_GAIN_MIN			0x0020
+#define SC5336_GAIN_MAX			(32 * 15 * 32)    //32*15*32
+#define SC5336_GAIN_STEP		1
+#define SC5336_GAIN_DEFAULT		0x120
+
+
+#define SC5336_REG_GROUP_HOLD		0x3812
+#define SC5336_GROUP_HOLD_START		0x00
+#define SC5336_GROUP_HOLD_END		0x30
+
+#define SC5336_REG_TEST_PATTERN		0x4501
+
+#define SC5336_REG_VTS_H		0x320e
+#define SC5336_REG_VTS_L		0x320f
+
+#define SC5336_FLIP_MIRROR_REG		0x3221
+
+#define SC5336_FETCH_EXP_H(VAL)		(((VAL) >> 12) & 0xF)
+#define SC5336_FETCH_EXP_M(VAL)		(((VAL) >> 4) & 0xFF)
+#define SC5336_FETCH_EXP_L(VAL)		(((VAL) & 0xF) << 4)
+
+#define SC5336_FETCH_AGAIN_H(VAL)	(((VAL) >> 8) & 0x03)
+#define SC5336_FETCH_AGAIN_L(VAL)	((VAL) & 0xFF)
+
+#define SC5336_FETCH_MIRROR(VAL, ENABLE)	(ENABLE ? VAL | 0x06 : VAL & 0xf9)
+#define SC5336_FETCH_FLIP(VAL, ENABLE)		(ENABLE ? VAL | 0x60 : VAL & 0x9f)
+
+#define REG_DELAY			0xFFFE
+#define REG_NULL			0xFFFF
+
+#define SC5336_REG_VALUE_08BIT		1
+#define SC5336_REG_VALUE_16BIT		2
+#define SC5336_REG_VALUE_24BIT		3
+
+#define OF_CAMERA_PINCTRL_STATE_DEFAULT	"rockchip,camera_default"
+#define OF_CAMERA_PINCTRL_STATE_SLEEP	"rockchip,camera_sleep"
+#define SC5336_NAME			"sc5336"
+
+static const char * const sc5336_supply_names[] = {
+	"avdd",		/* Analog power */
+	"dovdd",	/* Digital I/O power */
+	"dvdd",		/* Digital core power */
+};
+
+#define SC5336_NUM_SUPPLIES ARRAY_SIZE(sc5336_supply_names)
+
+struct regval {
+	u16 addr;
+	u8 val;
+};
+
+struct sc5336_mode {
+	u32 bus_fmt;
+	u32 width;
+	u32 height;
+	struct v4l2_fract max_fps;
+	u32 hts_def;
+	u32 vts_def;
+	u32 exp_def;
+	const struct regval *reg_list;
+	u32 hdr_mode;
+	u32 vc[PAD_MAX];
+};
+
+struct sc5336 {
+	struct i2c_client	*client;
+	struct clk		*xvclk;
+	struct gpio_desc	*reset_gpio;
+	struct gpio_desc	*pwdn_gpio;
+	struct regulator_bulk_data supplies[SC5336_NUM_SUPPLIES];
+
+	struct pinctrl		*pinctrl;
+	struct pinctrl_state	*pins_default;
+	struct pinctrl_state	*pins_sleep;
+
+	struct v4l2_subdev	subdev;
+	struct media_pad	pad;
+	struct v4l2_ctrl_handler ctrl_handler;
+	struct v4l2_ctrl	*exposure;
+	struct v4l2_ctrl	*anal_gain;
+	struct v4l2_ctrl	*digi_gain;
+	struct v4l2_ctrl	*hblank;
+	struct v4l2_ctrl	*vblank;
+	struct v4l2_ctrl	*test_pattern;
+	struct mutex		mutex;
+	bool			streaming;
+	bool			power_on;
+	const struct sc5336_mode *cur_mode;
+	struct v4l2_fract	cur_fps;
+	u32			module_index;
+	const char		*module_facing;
+	const char		*module_name;
+	const char		*len_name;
+	u32			cur_vts;
+	bool			is_thunderboot;
+	bool			is_first_streamoff;
+};
+
+#define to_sc5336(sd) container_of(sd, struct sc5336, subdev)
+
+/*
+ * Xclk 24Mhz
+ */
+static const struct regval sc5336_global_regs[] = {
+	{REG_NULL, 0x00},
+};
+
+/*
+ * Xclk 24Mhz
+ * max_framerate 30fps
+ * mipi_datarate per lane 864Mbps, 2lane
+ */
+static const struct regval sc5336_linear_10_2880x1620_regs[] = {
+	{0x0103, 0x01},
+	{0x36e9, 0x80},
+	{0x37f9, 0x80},
+	{0x301f, 0x1a},
+	{0x320e, 0x07},
+	{0x320f, 0x08},
+	{0x3213, 0x04},
+	{0x3241, 0x00},
+	{0x3243, 0x01},
+	{0x3248, 0x02},
+	{0x3249, 0x0b},
+	{0x3253, 0x10},
+	{0x3258, 0x0c},
+	{0x3301, 0x0a},
+	{0x3305, 0x00},
+	{0x3306, 0x58},
+	{0x3308, 0x08},
+	{0x3309, 0xb0},
+	{0x330a, 0x00},
+	{0x330b, 0xc8},
+	{0x3314, 0x14},
+	{0x331f, 0xa1},
+	{0x3321, 0x10},
+	{0x3327, 0x14},
+	{0x3328, 0x0b},
+	{0x3329, 0x0e},
+	{0x3333, 0x10},
+	{0x3334, 0x40},
+	{0x3356, 0x10},
+	{0x3364, 0x5e},
+	{0x338f, 0x80},
+	{0x3390, 0x09},
+	{0x3391, 0x0b},
+	{0x3392, 0x0f},
+	{0x3393, 0x10},
+	{0x3394, 0x16},
+	{0x3395, 0x98},
+	{0x3396, 0x08},
+	{0x3397, 0x09},
+	{0x3398, 0x0f},
+	{0x3399, 0x0a},
+	{0x339a, 0x18},
+	{0x339b, 0x60},
+	{0x339c, 0xff},
+	{0x33ad, 0x0c},
+	{0x33ae, 0x5c},
+	{0x33af, 0x52},
+	{0x33b1, 0xa0},
+	{0x33b2, 0x38},
+	{0x33b3, 0x18},
+	{0x33f8, 0x00},
+	{0x33f9, 0x60},
+	{0x33fa, 0x00},
+	{0x33fb, 0x80},
+	{0x33fc, 0x0b},
+	{0x33fd, 0x1f},
+	{0x349f, 0x03},
+	{0x34a6, 0x0b},
+	{0x34a7, 0x1f},
+	{0x34a8, 0x08},
+	{0x34a9, 0x08},
+	{0x34aa, 0x00},
+	{0x34ab, 0xd0},
+	{0x34ac, 0x00},
+	{0x34ad, 0xf0},
+	{0x34f8, 0x3f},
+	{0x34f9, 0x08},
+	{0x3630, 0xc0},
+	{0x3631, 0x83},
+	{0x3632, 0x54},
+	{0x3633, 0x33},
+	{0x3638, 0xcf},
+	{0x363f, 0xc0},
+	{0x3641, 0x20},
+	{0x3670, 0x56},
+	{0x3674, 0xc0},
+	{0x3675, 0xa0},
+	{0x3676, 0xa0},
+	{0x3677, 0x83},
+	{0x3678, 0x86},
+	{0x3679, 0x8a},
+	{0x367c, 0x08},
+	{0x367d, 0x0f},
+	{0x367e, 0x08},
+	{0x367f, 0x0f},
+	{0x3696, 0x23},
+	{0x3697, 0x33},
+	{0x3698, 0x34},
+	{0x36a0, 0x09},
+	{0x36a1, 0x0f},
+	{0x36b0, 0x85},
+	{0x36b1, 0x8a},
+	{0x36b2, 0x95},
+	{0x36b3, 0xa6},
+	{0x36b4, 0x09},
+	{0x36b5, 0x0b},
+	{0x36b6, 0x0f},
+	{0x36ea, 0x0c},
+	{0x36eb, 0x0c},
+	{0x36ec, 0x0c},
+	{0x36ed, 0xb6},
+	{0x370f, 0x01},
+	{0x3721, 0x6c},
+	{0x3722, 0x89},
+	{0x3724, 0x21},
+	{0x3725, 0xb4},
+	{0x3727, 0x14},
+	{0x3771, 0x89},
+	{0x3772, 0x89},
+	{0x3773, 0xc5},
+	{0x377a, 0x0b},
+	{0x377b, 0x1f},
+	{0x37fa, 0x0c},
+	{0x37fb, 0x24},
+	{0x37fc, 0x01},
+	{0x37fd, 0x36},
+	{0x3901, 0x00},
+	{0x3904, 0x04},
+	{0x3905, 0x8c},
+	{0x391d, 0x04},
+	{0x391f, 0x49},
+	{0x3926, 0x21},
+	{0x3933, 0x80},
+	{0x3934, 0x0a},
+	{0x3935, 0x00},
+	{0x3936, 0xff},
+	{0x3937, 0x75},
+	{0x3938, 0x74},
+	{0x393c, 0x1e},
+	{0x39dc, 0x02},
+	{0x3e00, 0x00},
+	{0x3e01, 0x70},
+	{0x3e02, 0x00},
+	{0x3e09, 0x00},
+	{0x440d, 0x10},
+	{0x440e, 0x02},
+	{0x450d, 0x18},
+	{0x4819, 0x0b},
+	{0x481b, 0x06},
+	{0x481d, 0x17},
+	{0x481f, 0x05},
+	{0x4821, 0x0b},
+	{0x4823, 0x06},
+	{0x4825, 0x05},
+	{0x4827, 0x05},
+	{0x4829, 0x09},
+	{0x5780, 0x66},
+	{0x5787, 0x08},
+	{0x5788, 0x03},
+	{0x5789, 0x00},
+	{0x578a, 0x08},
+	{0x578b, 0x03},
+	{0x578c, 0x00},
+	{0x578d, 0x40},
+	{0x5790, 0x08},
+	{0x5791, 0x04},
+	{0x5792, 0x01},
+	{0x5793, 0x08},
+	{0x5794, 0x04},
+	{0x5795, 0x01},
+	{0x5799, 0x46},
+	{0x57aa, 0x2a},
+	{0x5ae0, 0xfe},
+	{0x5ae1, 0x40},
+	{0x5ae2, 0x38},
+	{0x5ae3, 0x30},
+	{0x5ae4, 0x0c},
+	{0x5ae5, 0x38},
+	{0x5ae6, 0x30},
+	{0x5ae7, 0x28},
+	{0x5ae8, 0x3f},
+	{0x5ae9, 0x34},
+	{0x5aea, 0x2c},
+	{0x5aeb, 0x3f},
+	{0x5aec, 0x34},
+	{0x5aed, 0x2c},
+	{0x36e9, 0x20},
+	{0x37f9, 0x20},
+	{REG_NULL, 0x00},
+};
+
+static const struct sc5336_mode supported_modes[] = {
+	{
+		.width = 2880,
+		.height = 1620,
+		.max_fps = {
+			.numerator = 10000,
+			.denominator = 300000,
+		},
+		.exp_def = 0x0080 * 4,
+		.hts_def = 0x0654 * 2,
+		.vts_def = 0x0708,
+		.bus_fmt = MEDIA_BUS_FMT_SBGGR10_1X10,
+		.reg_list = sc5336_linear_10_2880x1620_regs,
+		.hdr_mode = NO_HDR,
+		.vc[PAD0] = V4L2_MBUS_CSI2_CHANNEL_0,
+	}
+};
+
+static const s64 link_freq_menu_items[] = {
+	SC5336_LINK_FREQ
+};
+
+static const char * const sc5336_test_pattern_menu[] = {
+	"Disabled",
+	"Vertical Gray Bar Type 1",
+};
+
+/* Write registers up to 4 at a time */
+static int sc5336_write_reg(struct i2c_client *client, u16 reg,
+			    u32 len, u32 val)
+{
+	u32 buf_i, val_i;
+	u8 buf[6];
+	u8 *val_p;
+	__be32 val_be;
+
+	if (len > 4)
+		return -EINVAL;
+
+	buf[0] = reg >> 8;
+	buf[1] = reg & 0xff;
+
+	val_be = cpu_to_be32(val);
+	val_p = (u8 *)&val_be;
+	buf_i = 2;
+	val_i = 4 - len;
+
+	while (val_i < 4)
+		buf[buf_i++] = val_p[val_i++];
+
+	if (i2c_master_send(client, buf, len + 2) != len + 2)
+		return -EIO;
+	return 0;
+}
+
+static int sc5336_write_array(struct i2c_client *client,
+			       const struct regval *regs)
+{
+	u32 i;
+	int ret = 0;
+
+	for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++)
+		ret = sc5336_write_reg(client, regs[i].addr,
+					SC5336_REG_VALUE_08BIT, regs[i].val);
+
+	return ret;
+}
+
+/* Read registers up to 4 at a time */
+static int sc5336_read_reg(struct i2c_client *client, u16 reg, unsigned int len,
+			    u32 *val)
+{
+	struct i2c_msg msgs[2];
+	u8 *data_be_p;
+	__be32 data_be = 0;
+	__be16 reg_addr_be = cpu_to_be16(reg);
+	int ret;
+
+	if (len > 4 || !len)
+		return -EINVAL;
+
+	data_be_p = (u8 *)&data_be;
+	/* Write register address */
+	msgs[0].addr = client->addr;
+	msgs[0].flags = 0;
+	msgs[0].len = 2;
+	msgs[0].buf = (u8 *)&reg_addr_be;
+
+	/* Read data from register */
+	msgs[1].addr = client->addr;
+	msgs[1].flags = I2C_M_RD;
+	msgs[1].len = len;
+	msgs[1].buf = &data_be_p[4 - len];
+
+	ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+	if (ret != ARRAY_SIZE(msgs))
+		return -EIO;
+
+	*val = be32_to_cpu(data_be);
+
+	return 0;
+}
+
+static int sc5336_set_gain_reg(struct sc5336 *sc5336, u32 gain)
+{
+	u32 coarse_again = 0, coarse_dgian = 0, fine_dgian = 0;
+	u32 gain_factor;
+	int ret = 0;
+
+	if (gain < 32)
+		gain = 32;
+	else if (gain > SC5336_GAIN_MAX)
+		gain = SC5336_GAIN_MAX;
+
+	gain_factor = gain * 1000 / 32;
+	if (gain_factor < 2000) {
+		coarse_again = 0x00;
+		coarse_dgian = 0x00;
+		fine_dgian = gain_factor * 128 / 1000;
+	} else if (gain_factor < 4000) {
+		coarse_again = 0x08;
+		coarse_dgian = 0x00;
+		fine_dgian = gain_factor * 128 / 2000;
+	} else if (gain_factor < 8000) {
+		coarse_again = 0x09;
+		coarse_dgian = 0x00;
+		fine_dgian = gain_factor * 128 / 4000;
+	} else if (gain_factor < 16000) {
+		coarse_again = 0x0b;
+		coarse_dgian = 0x00;
+		fine_dgian = gain_factor * 128 / 8000;
+	} else if (gain_factor < 32000) {
+		coarse_again = 0x0f;
+		coarse_dgian = 0x00;
+		fine_dgian = gain_factor * 128 / 16000;
+	} else if (gain_factor < 32000 * 2) {
+		coarse_again = 0x1f;
+		coarse_dgian = 0x00;
+		fine_dgian = gain_factor * 128 / 32000;
+	} else if (gain_factor < 32000 * 4) {
+		//open dgain begin  max digital gain 4X
+		coarse_again = 0x1f;
+		coarse_dgian = 0x01;
+		fine_dgian = gain_factor * 128 / 32000 / 2;
+	} else if (gain_factor < 32000 * 8) {
+		coarse_again = 0x1f;
+		coarse_dgian = 0x03;
+		fine_dgian = gain_factor * 128 / 32000 / 4;
+	} else if (gain_factor < 32000 * 15) {
+		coarse_again = 0x1f;
+		coarse_dgian = 0x07;
+		fine_dgian = gain_factor * 128 / 32000 / 8;
+	} else {
+		coarse_again = 0x1f;
+		coarse_dgian = 0x07;
+		fine_dgian = 0xf0;
+	}
+
+	ret = sc5336_write_reg(sc5336->client,
+				SC5336_REG_DIG_GAIN,
+				SC5336_REG_VALUE_08BIT,
+				coarse_dgian);
+	ret |= sc5336_write_reg(sc5336->client,
+				 SC5336_REG_DIG_FINE_GAIN,
+				 SC5336_REG_VALUE_08BIT,
+				 fine_dgian);
+	ret |= sc5336_write_reg(sc5336->client,
+				 SC5336_REG_ANA_GAIN,
+				 SC5336_REG_VALUE_08BIT,
+				 coarse_again);
+
+	return ret;
+}
+
+static int sc5336_get_reso_dist(const struct sc5336_mode *mode,
+				 struct v4l2_mbus_framefmt *framefmt)
+{
+	return abs(mode->width - framefmt->width) +
+	       abs(mode->height - framefmt->height);
+}
+
+static const struct sc5336_mode *
+sc5336_find_best_fit(struct v4l2_subdev_format *fmt)
+{
+	struct v4l2_mbus_framefmt *framefmt = &fmt->format;
+	int dist;
+	int cur_best_fit = 0;
+	int cur_best_fit_dist = -1;
+	unsigned int i;
+
+	for (i = 0; i < ARRAY_SIZE(supported_modes); i++) {
+		dist = sc5336_get_reso_dist(&supported_modes[i], framefmt);
+		if (cur_best_fit_dist == -1 || dist < cur_best_fit_dist) {
+			cur_best_fit_dist = dist;
+			cur_best_fit = i;
+		}
+	}
+
+	return &supported_modes[cur_best_fit];
+}
+
+static int sc5336_set_fmt(struct v4l2_subdev *sd,
+			   struct v4l2_subdev_pad_config *cfg,
+			   struct v4l2_subdev_format *fmt)
+{
+	struct sc5336 *sc5336 = to_sc5336(sd);
+	const struct sc5336_mode *mode;
+	s64 h_blank, vblank_def;
+
+	mutex_lock(&sc5336->mutex);
+
+	mode = sc5336_find_best_fit(fmt);
+	fmt->format.code = mode->bus_fmt;
+	fmt->format.width = mode->width;
+	fmt->format.height = mode->height;
+	fmt->format.field = V4L2_FIELD_NONE;
+	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+		*v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = fmt->format;
+#else
+		mutex_unlock(&sc5336->mutex);
+		return -ENOTTY;
+#endif
+	} else {
+		sc5336->cur_mode = mode;
+		h_blank = mode->hts_def - mode->width;
+		__v4l2_ctrl_modify_range(sc5336->hblank, h_blank,
+					 h_blank, 1, h_blank);
+		vblank_def = mode->vts_def - mode->height;
+		__v4l2_ctrl_modify_range(sc5336->vblank, vblank_def,
+					 SC5336_VTS_MAX - mode->height,
+					 1, vblank_def);
+		sc5336->cur_fps = mode->max_fps;
+	}
+
+	mutex_unlock(&sc5336->mutex);
+
+	return 0;
+}
+
+static int sc5336_get_fmt(struct v4l2_subdev *sd,
+			   struct v4l2_subdev_pad_config *cfg,
+			   struct v4l2_subdev_format *fmt)
+{
+	struct sc5336 *sc5336 = to_sc5336(sd);
+	const struct sc5336_mode *mode = sc5336->cur_mode;
+
+	mutex_lock(&sc5336->mutex);
+	if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+		fmt->format = *v4l2_subdev_get_try_format(sd, cfg, fmt->pad);
+#else
+		mutex_unlock(&sc5336->mutex);
+		return -ENOTTY;
+#endif
+	} else {
+		fmt->format.width = mode->width;
+		fmt->format.height = mode->height;
+		fmt->format.code = mode->bus_fmt;
+		fmt->format.field = V4L2_FIELD_NONE;
+		/* format info: width/height/data type/virctual channel */
+		if (fmt->pad < PAD_MAX && mode->hdr_mode != NO_HDR)
+			fmt->reserved[0] = mode->vc[fmt->pad];
+		else
+			fmt->reserved[0] = mode->vc[PAD0];
+	}
+	mutex_unlock(&sc5336->mutex);
+
+	return 0;
+}
+
+static int sc5336_enum_mbus_code(struct v4l2_subdev *sd,
+				  struct v4l2_subdev_pad_config *cfg,
+				  struct v4l2_subdev_mbus_code_enum *code)
+{
+	struct sc5336 *sc5336 = to_sc5336(sd);
+
+	if (code->index != 0)
+		return -EINVAL;
+	code->code = sc5336->cur_mode->bus_fmt;
+
+	return 0;
+}
+
+static int sc5336_enum_frame_sizes(struct v4l2_subdev *sd,
+				    struct v4l2_subdev_pad_config *cfg,
+				    struct v4l2_subdev_frame_size_enum *fse)
+{
+	if (fse->index >= ARRAY_SIZE(supported_modes))
+		return -EINVAL;
+
+	if (fse->code != supported_modes[0].bus_fmt)
+		return -EINVAL;
+
+	fse->min_width  = supported_modes[fse->index].width;
+	fse->max_width  = supported_modes[fse->index].width;
+	fse->max_height = supported_modes[fse->index].height;
+	fse->min_height = supported_modes[fse->index].height;
+
+	return 0;
+}
+
+static int sc5336_enable_test_pattern(struct sc5336 *sc5336, u32 pattern)
+{
+	int ret = 0;
+
+	if (pattern) {
+		ret |= sc5336_write_reg(sc5336->client, 0x4501, SC5336_REG_VALUE_08BIT, 0xac);
+		ret |= sc5336_write_reg(sc5336->client, 0x3902, SC5336_REG_VALUE_08BIT, 0x80);
+		ret |= sc5336_write_reg(sc5336->client, 0x3e07, SC5336_REG_VALUE_08BIT, 0x40);
+	} else {
+		ret |= sc5336_write_reg(sc5336->client, 0x4501, SC5336_REG_VALUE_08BIT, 0xa4);
+		ret |= sc5336_write_reg(sc5336->client, 0x3902, SC5336_REG_VALUE_08BIT, 0xc0);
+		ret |= sc5336_write_reg(sc5336->client, 0x3e07, SC5336_REG_VALUE_08BIT, 0x80);
+	}
+
+	return ret;
+}
+
+static int sc5336_g_frame_interval(struct v4l2_subdev *sd,
+				    struct v4l2_subdev_frame_interval *fi)
+{
+	struct sc5336 *sc5336 = to_sc5336(sd);
+	const struct sc5336_mode *mode = sc5336->cur_mode;
+
+	if (sc5336->streaming)
+		fi->interval = sc5336->cur_fps;
+	else
+		fi->interval = mode->max_fps;
+
+	return 0;
+}
+
+static int sc5336_g_mbus_config(struct v4l2_subdev *sd,
+				unsigned int pad_id,
+				struct v4l2_mbus_config *config)
+{
+	struct sc5336 *sc5336 = to_sc5336(sd);
+	const struct sc5336_mode *mode = sc5336->cur_mode;
+	u32 val = 1 << (SC5336_LANES - 1) |
+		V4L2_MBUS_CSI2_CHANNEL_0 |
+		V4L2_MBUS_CSI2_CONTINUOUS_CLOCK;
+
+	if (mode->hdr_mode != NO_HDR)
+		val |= V4L2_MBUS_CSI2_CHANNEL_1;
+	if (mode->hdr_mode == HDR_X3)
+		val |= V4L2_MBUS_CSI2_CHANNEL_2;
+
+	config->type = V4L2_MBUS_CSI2_DPHY;
+	config->flags = val;
+
+	return 0;
+}
+
+static void sc5336_get_module_inf(struct sc5336 *sc5336,
+				   struct rkmodule_inf *inf)
+{
+	memset(inf, 0, sizeof(*inf));
+	strscpy(inf->base.sensor, SC5336_NAME, sizeof(inf->base.sensor));
+	strscpy(inf->base.module, sc5336->module_name,
+		sizeof(inf->base.module));
+	strscpy(inf->base.lens, sc5336->len_name, sizeof(inf->base.lens));
+}
+
+static long sc5336_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
+{
+	struct sc5336 *sc5336 = to_sc5336(sd);
+	struct rkmodule_hdr_cfg *hdr;
+	u32 i, h, w;
+	long ret = 0;
+	u32 stream = 0;
+
+	switch (cmd) {
+	case RKMODULE_GET_MODULE_INFO:
+		sc5336_get_module_inf(sc5336, (struct rkmodule_inf *)arg);
+		break;
+	case RKMODULE_GET_HDR_CFG:
+		hdr = (struct rkmodule_hdr_cfg *)arg;
+		hdr->esp.mode = HDR_NORMAL_VC;
+		hdr->hdr_mode = sc5336->cur_mode->hdr_mode;
+		break;
+	case RKMODULE_SET_HDR_CFG:
+		hdr = (struct rkmodule_hdr_cfg *)arg;
+		w = sc5336->cur_mode->width;
+		h = sc5336->cur_mode->height;
+		for (i = 0; i < ARRAY_SIZE(supported_modes); i++) {
+			if (w == supported_modes[i].width &&
+			    h == supported_modes[i].height &&
+			    supported_modes[i].hdr_mode == hdr->hdr_mode) {
+				sc5336->cur_mode = &supported_modes[i];
+				break;
+			}
+		}
+		if (i == ARRAY_SIZE(supported_modes)) {
+			dev_err(&sc5336->client->dev,
+				"not find hdr mode:%d %dx%d config\n",
+				hdr->hdr_mode, w, h);
+			ret = -EINVAL;
+		} else {
+			w = sc5336->cur_mode->hts_def - sc5336->cur_mode->width;
+			h = sc5336->cur_mode->vts_def - sc5336->cur_mode->height;
+			__v4l2_ctrl_modify_range(sc5336->hblank, w, w, 1, w);
+			__v4l2_ctrl_modify_range(sc5336->vblank, h,
+						 SC5336_VTS_MAX - sc5336->cur_mode->height, 1, h);
+		}
+		break;
+	case PREISP_CMD_SET_HDRAE_EXP:
+		break;
+	case RKMODULE_SET_QUICK_STREAM:
+
+		stream = *((u32 *)arg);
+
+		if (stream)
+			ret = sc5336_write_reg(sc5336->client, SC5336_REG_CTRL_MODE,
+				 SC5336_REG_VALUE_08BIT, SC5336_MODE_STREAMING);
+		else
+			ret = sc5336_write_reg(sc5336->client, SC5336_REG_CTRL_MODE,
+				 SC5336_REG_VALUE_08BIT, SC5336_MODE_SW_STANDBY);
+		break;
+	default:
+		ret = -ENOIOCTLCMD;
+		break;
+	}
+
+	return ret;
+}
+
+#ifdef CONFIG_COMPAT
+static long sc5336_compat_ioctl32(struct v4l2_subdev *sd,
+				   unsigned int cmd, unsigned long arg)
+{
+	void __user *up = compat_ptr(arg);
+	struct rkmodule_inf *inf;
+	struct rkmodule_hdr_cfg *hdr;
+	struct preisp_hdrae_exp_s *hdrae;
+	long ret;
+	u32 stream = 0;
+
+	switch (cmd) {
+	case RKMODULE_GET_MODULE_INFO:
+		inf = kzalloc(sizeof(*inf), GFP_KERNEL);
+		if (!inf) {
+			ret = -ENOMEM;
+			return ret;
+		}
+
+		ret = sc5336_ioctl(sd, cmd, inf);
+		if (!ret) {
+			if (copy_to_user(up, inf, sizeof(*inf)))
+				ret = -EFAULT;
+		}
+		kfree(inf);
+		break;
+	case RKMODULE_GET_HDR_CFG:
+		hdr = kzalloc(sizeof(*hdr), GFP_KERNEL);
+		if (!hdr) {
+			ret = -ENOMEM;
+			return ret;
+		}
+
+		ret = sc5336_ioctl(sd, cmd, hdr);
+		if (!ret) {
+			if (copy_to_user(up, hdr, sizeof(*hdr)))
+				ret = -EFAULT;
+		}
+		kfree(hdr);
+		break;
+	case RKMODULE_SET_HDR_CFG:
+		hdr = kzalloc(sizeof(*hdr), GFP_KERNEL);
+		if (!hdr) {
+			ret = -ENOMEM;
+			return ret;
+		}
+
+		ret = copy_from_user(hdr, up, sizeof(*hdr));
+		if (!ret)
+			ret = sc5336_ioctl(sd, cmd, hdr);
+		else
+			ret = -EFAULT;
+		kfree(hdr);
+		break;
+	case PREISP_CMD_SET_HDRAE_EXP:
+		hdrae = kzalloc(sizeof(*hdrae), GFP_KERNEL);
+		if (!hdrae) {
+			ret = -ENOMEM;
+			return ret;
+		}
+
+		ret = copy_from_user(hdrae, up, sizeof(*hdrae));
+		if (!ret)
+			ret = sc5336_ioctl(sd, cmd, hdrae);
+		else
+			ret = -EFAULT;
+		kfree(hdrae);
+		break;
+	case RKMODULE_SET_QUICK_STREAM:
+		ret = copy_from_user(&stream, up, sizeof(u32));
+		if (!ret)
+			ret = sc5336_ioctl(sd, cmd, &stream);
+		else
+			ret = -EFAULT;
+		break;
+	default:
+		ret = -ENOIOCTLCMD;
+		break;
+	}
+
+	return ret;
+}
+#endif
+
+static int __sc5336_start_stream(struct sc5336 *sc5336)
+{
+	int ret;
+
+	if (!sc5336->is_thunderboot) {
+		ret = sc5336_write_array(sc5336->client, sc5336->cur_mode->reg_list);
+		if (ret)
+			return ret;
+
+		/* In case these controls are set before streaming */
+		ret = __v4l2_ctrl_handler_setup(&sc5336->ctrl_handler);
+		if (ret)
+			return ret;
+	}
+
+	return sc5336_write_reg(sc5336->client, SC5336_REG_CTRL_MODE,
+				 SC5336_REG_VALUE_08BIT, SC5336_MODE_STREAMING);
+}
+
+static int __sc5336_stop_stream(struct sc5336 *sc5336)
+{
+	if (sc5336->is_thunderboot) {
+		sc5336->is_first_streamoff = true;
+		pm_runtime_put(&sc5336->client->dev);
+	}
+	return sc5336_write_reg(sc5336->client, SC5336_REG_CTRL_MODE,
+				 SC5336_REG_VALUE_08BIT, SC5336_MODE_SW_STANDBY);
+}
+
+static int __sc5336_power_on(struct sc5336 *sc5336);
+static int sc5336_s_stream(struct v4l2_subdev *sd, int on)
+{
+	struct sc5336 *sc5336 = to_sc5336(sd);
+	struct i2c_client *client = sc5336->client;
+	int ret = 0;
+
+	mutex_lock(&sc5336->mutex);
+	on = !!on;
+	if (on == sc5336->streaming)
+		goto unlock_and_return;
+
+	if (on) {
+		if (sc5336->is_thunderboot && rkisp_tb_get_state() == RKISP_TB_NG) {
+			sc5336->is_thunderboot = false;
+			__sc5336_power_on(sc5336);
+		}
+
+		ret = pm_runtime_get_sync(&client->dev);
+		if (ret < 0) {
+			pm_runtime_put_noidle(&client->dev);
+			goto unlock_and_return;
+		}
+
+		ret = __sc5336_start_stream(sc5336);
+		if (ret) {
+			v4l2_err(sd, "start stream failed while write regs\n");
+			pm_runtime_put(&client->dev);
+			goto unlock_and_return;
+		}
+	} else {
+		__sc5336_stop_stream(sc5336);
+		pm_runtime_put(&client->dev);
+	}
+
+	sc5336->streaming = on;
+
+unlock_and_return:
+	mutex_unlock(&sc5336->mutex);
+
+	return ret;
+}
+
+static int sc5336_s_power(struct v4l2_subdev *sd, int on)
+{
+	struct sc5336 *sc5336 = to_sc5336(sd);
+	struct i2c_client *client = sc5336->client;
+	int ret = 0;
+
+	mutex_lock(&sc5336->mutex);
+
+	/* If the power state is not modified - no work to do. */
+	if (sc5336->power_on == !!on)
+		goto unlock_and_return;
+
+	if (on) {
+		ret = pm_runtime_get_sync(&client->dev);
+		if (ret < 0) {
+			pm_runtime_put_noidle(&client->dev);
+			goto unlock_and_return;
+		}
+
+		if (!sc5336->is_thunderboot) {
+			ret = sc5336_write_array(sc5336->client, sc5336_global_regs);
+			if (ret) {
+				v4l2_err(sd, "could not set init registers\n");
+				pm_runtime_put_noidle(&client->dev);
+				goto unlock_and_return;
+			}
+		}
+
+		sc5336->power_on = true;
+	} else {
+		pm_runtime_put(&client->dev);
+		sc5336->power_on = false;
+	}
+
+unlock_and_return:
+	mutex_unlock(&sc5336->mutex);
+
+	return ret;
+}
+
+/* Calculate the delay in us by clock rate and clock cycles */
+static inline u32 sc5336_cal_delay(u32 cycles)
+{
+	return DIV_ROUND_UP(cycles, SC5336_XVCLK_FREQ / 1000 / 1000);
+}
+
+static int __sc5336_power_on(struct sc5336 *sc5336)
+{
+	int ret;
+	u32 delay_us;
+	struct device *dev = &sc5336->client->dev;
+
+	if (!IS_ERR_OR_NULL(sc5336->pins_default)) {
+		ret = pinctrl_select_state(sc5336->pinctrl,
+					   sc5336->pins_default);
+		if (ret < 0)
+			dev_err(dev, "could not set pins\n");
+	}
+	ret = clk_set_rate(sc5336->xvclk, SC5336_XVCLK_FREQ);
+	if (ret < 0)
+		dev_warn(dev, "Failed to set xvclk rate (24MHz)\n");
+	if (clk_get_rate(sc5336->xvclk) != SC5336_XVCLK_FREQ)
+		dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n");
+	ret = clk_prepare_enable(sc5336->xvclk);
+	if (ret < 0) {
+		dev_err(dev, "Failed to enable xvclk\n");
+		return ret;
+	}
+	if (sc5336->is_thunderboot)
+		return 0;
+
+	if (!IS_ERR(sc5336->reset_gpio))
+		gpiod_set_value_cansleep(sc5336->reset_gpio, 0);
+
+	ret = regulator_bulk_enable(SC5336_NUM_SUPPLIES, sc5336->supplies);
+	if (ret < 0) {
+		dev_err(dev, "Failed to enable regulators\n");
+		goto disable_clk;
+	}
+
+	if (!IS_ERR(sc5336->reset_gpio))
+		gpiod_set_value_cansleep(sc5336->reset_gpio, 1);
+
+	usleep_range(500, 1000);
+	if (!IS_ERR(sc5336->pwdn_gpio))
+		gpiod_set_value_cansleep(sc5336->pwdn_gpio, 1);
+
+	if (!IS_ERR(sc5336->reset_gpio))
+		usleep_range(6000, 8000);
+	else
+		usleep_range(12000, 16000);
+
+	/* 8192 cycles prior to first SCCB transaction */
+	delay_us = sc5336_cal_delay(8192);
+	usleep_range(delay_us, delay_us * 2);
+
+	return 0;
+
+disable_clk:
+	clk_disable_unprepare(sc5336->xvclk);
+
+	return ret;
+}
+
+static void __sc5336_power_off(struct sc5336 *sc5336)
+{
+	int ret;
+	struct device *dev = &sc5336->client->dev;
+
+	clk_disable_unprepare(sc5336->xvclk);
+	if (sc5336->is_thunderboot) {
+		if (sc5336->is_first_streamoff) {
+			sc5336->is_thunderboot = false;
+			sc5336->is_first_streamoff = false;
+		} else {
+			return;
+		}
+	}
+
+	if (!IS_ERR(sc5336->pwdn_gpio))
+		gpiod_set_value_cansleep(sc5336->pwdn_gpio, 0);
+	if (!IS_ERR(sc5336->reset_gpio))
+		gpiod_set_value_cansleep(sc5336->reset_gpio, 0);
+	if (!IS_ERR_OR_NULL(sc5336->pins_sleep)) {
+		ret = pinctrl_select_state(sc5336->pinctrl,
+					   sc5336->pins_sleep);
+		if (ret < 0)
+			dev_dbg(dev, "could not set pins\n");
+	}
+	regulator_bulk_disable(SC5336_NUM_SUPPLIES, sc5336->supplies);
+}
+
+static int sc5336_runtime_resume(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct sc5336 *sc5336 = to_sc5336(sd);
+
+	return __sc5336_power_on(sc5336);
+}
+
+static int sc5336_runtime_suspend(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct sc5336 *sc5336 = to_sc5336(sd);
+
+	__sc5336_power_off(sc5336);
+
+	return 0;
+}
+
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+static int sc5336_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
+{
+	struct sc5336 *sc5336 = to_sc5336(sd);
+	struct v4l2_mbus_framefmt *try_fmt =
+				v4l2_subdev_get_try_format(sd, fh->pad, 0);
+	const struct sc5336_mode *def_mode = &supported_modes[0];
+
+	mutex_lock(&sc5336->mutex);
+	/* Initialize try_fmt */
+	try_fmt->width = def_mode->width;
+	try_fmt->height = def_mode->height;
+	try_fmt->code = def_mode->bus_fmt;
+	try_fmt->field = V4L2_FIELD_NONE;
+
+	mutex_unlock(&sc5336->mutex);
+	/* No crop or compose */
+
+	return 0;
+}
+#endif
+
+static int sc5336_enum_frame_interval(struct v4l2_subdev *sd,
+				       struct v4l2_subdev_pad_config *cfg,
+				       struct v4l2_subdev_frame_interval_enum *fie)
+{
+	if (fie->index >= ARRAY_SIZE(supported_modes))
+		return -EINVAL;
+
+	fie->code = supported_modes[fie->index].bus_fmt;
+	fie->width = supported_modes[fie->index].width;
+	fie->height = supported_modes[fie->index].height;
+	fie->interval = supported_modes[fie->index].max_fps;
+	fie->reserved[0] = supported_modes[fie->index].hdr_mode;
+	return 0;
+}
+
+static const struct dev_pm_ops sc5336_pm_ops = {
+	SET_RUNTIME_PM_OPS(sc5336_runtime_suspend,
+			   sc5336_runtime_resume, NULL)
+};
+
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+static const struct v4l2_subdev_internal_ops sc5336_internal_ops = {
+	.open = sc5336_open,
+};
+#endif
+
+static const struct v4l2_subdev_core_ops sc5336_core_ops = {
+	.s_power = sc5336_s_power,
+	.ioctl = sc5336_ioctl,
+#ifdef CONFIG_COMPAT
+	.compat_ioctl32 = sc5336_compat_ioctl32,
+#endif
+};
+
+static const struct v4l2_subdev_video_ops sc5336_video_ops = {
+	.s_stream = sc5336_s_stream,
+	.g_frame_interval = sc5336_g_frame_interval,
+};
+
+static const struct v4l2_subdev_pad_ops sc5336_pad_ops = {
+	.enum_mbus_code = sc5336_enum_mbus_code,
+	.enum_frame_size = sc5336_enum_frame_sizes,
+	.enum_frame_interval = sc5336_enum_frame_interval,
+	.get_fmt = sc5336_get_fmt,
+	.set_fmt = sc5336_set_fmt,
+	.get_mbus_config = sc5336_g_mbus_config,
+};
+
+static const struct v4l2_subdev_ops sc5336_subdev_ops = {
+	.core	= &sc5336_core_ops,
+	.video	= &sc5336_video_ops,
+	.pad	= &sc5336_pad_ops,
+};
+
+static void sc5336_modify_fps_info(struct sc5336 *sc5336)
+{
+	const struct sc5336_mode *mode = sc5336->cur_mode;
+
+	sc5336->cur_fps.denominator = mode->max_fps.denominator * mode->vts_def /
+				      sc5336->cur_vts;
+}
+
+static int sc5336_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+	struct sc5336 *sc5336 = container_of(ctrl->handler,
+					       struct sc5336, ctrl_handler);
+	struct i2c_client *client = sc5336->client;
+	s64 max;
+	int ret = 0;
+	u32 val = 0;
+
+	/* Propagate change of current control to all related controls */
+	switch (ctrl->id) {
+	case V4L2_CID_VBLANK:
+		/* Update max exposure while meeting expected vblanking */
+		max = sc5336->cur_mode->height + ctrl->val - 8;
+		__v4l2_ctrl_modify_range(sc5336->exposure,
+					 sc5336->exposure->minimum, max,
+					 sc5336->exposure->step,
+					 sc5336->exposure->default_value);
+		break;
+	}
+
+	if (!pm_runtime_get_if_in_use(&client->dev))
+		return 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_EXPOSURE:
+		dev_dbg(&client->dev, "set exposure 0x%x\n", ctrl->val);
+		if (sc5336->cur_mode->hdr_mode == NO_HDR) {
+			val = ctrl->val;
+			/* 4 least significant bits of expsoure are fractional part */
+			ret = sc5336_write_reg(sc5336->client,
+						SC5336_REG_EXPOSURE_H,
+						SC5336_REG_VALUE_08BIT,
+						SC5336_FETCH_EXP_H(val));
+			ret |= sc5336_write_reg(sc5336->client,
+						 SC5336_REG_EXPOSURE_M,
+						 SC5336_REG_VALUE_08BIT,
+						 SC5336_FETCH_EXP_M(val));
+			ret |= sc5336_write_reg(sc5336->client,
+						 SC5336_REG_EXPOSURE_L,
+						 SC5336_REG_VALUE_08BIT,
+						 SC5336_FETCH_EXP_L(val));
+		}
+		break;
+	case V4L2_CID_ANALOGUE_GAIN:
+		dev_dbg(&client->dev, "set gain 0x%x\n", ctrl->val);
+		if (sc5336->cur_mode->hdr_mode == NO_HDR)
+			ret = sc5336_set_gain_reg(sc5336, ctrl->val);
+		break;
+	case V4L2_CID_VBLANK:
+		dev_dbg(&client->dev, "set vblank 0x%x\n", ctrl->val);
+		ret = sc5336_write_reg(sc5336->client,
+					SC5336_REG_VTS_H,
+					SC5336_REG_VALUE_08BIT,
+					(ctrl->val + sc5336->cur_mode->height)
+					>> 8);
+		ret |= sc5336_write_reg(sc5336->client,
+					 SC5336_REG_VTS_L,
+					 SC5336_REG_VALUE_08BIT,
+					 (ctrl->val + sc5336->cur_mode->height)
+					 & 0xff);
+		sc5336->cur_vts = ctrl->val + sc5336->cur_mode->height;
+		if (sc5336->cur_vts != sc5336->cur_mode->vts_def)
+			sc5336_modify_fps_info(sc5336);
+		break;
+	case V4L2_CID_TEST_PATTERN:
+		ret = sc5336_enable_test_pattern(sc5336, ctrl->val);
+		break;
+	case V4L2_CID_HFLIP:
+		ret = sc5336_read_reg(sc5336->client, SC5336_FLIP_MIRROR_REG,
+				       SC5336_REG_VALUE_08BIT, &val);
+		ret |= sc5336_write_reg(sc5336->client, SC5336_FLIP_MIRROR_REG,
+					 SC5336_REG_VALUE_08BIT,
+					 SC5336_FETCH_MIRROR(val, ctrl->val));
+		break;
+	case V4L2_CID_VFLIP:
+		ret = sc5336_read_reg(sc5336->client, SC5336_FLIP_MIRROR_REG,
+				       SC5336_REG_VALUE_08BIT, &val);
+		ret |= sc5336_write_reg(sc5336->client, SC5336_FLIP_MIRROR_REG,
+					 SC5336_REG_VALUE_08BIT,
+					 SC5336_FETCH_FLIP(val, ctrl->val));
+		break;
+	default:
+		dev_warn(&client->dev, "%s Unhandled id:0x%x, val:0x%x\n",
+			 __func__, ctrl->id, ctrl->val);
+		break;
+	}
+
+	pm_runtime_put(&client->dev);
+
+	return ret;
+}
+
+static const struct v4l2_ctrl_ops sc5336_ctrl_ops = {
+	.s_ctrl = sc5336_set_ctrl,
+};
+
+static int sc5336_initialize_controls(struct sc5336 *sc5336)
+{
+	const struct sc5336_mode *mode;
+	struct v4l2_ctrl_handler *handler;
+	struct v4l2_ctrl *ctrl;
+	s64 exposure_max, vblank_def;
+	u32 h_blank;
+	int ret;
+
+	handler = &sc5336->ctrl_handler;
+	mode = sc5336->cur_mode;
+	ret = v4l2_ctrl_handler_init(handler, 9);
+	if (ret)
+		return ret;
+	handler->lock = &sc5336->mutex;
+
+	ctrl = v4l2_ctrl_new_int_menu(handler, NULL, V4L2_CID_LINK_FREQ,
+				      0, 0, link_freq_menu_items);
+	if (ctrl)
+		ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+	v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE,
+			  0, PIXEL_RATE_WITH_315M_10BIT, 1, PIXEL_RATE_WITH_315M_10BIT);
+
+	h_blank = mode->hts_def - mode->width;
+	sc5336->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK,
+					    h_blank, h_blank, 1, h_blank);
+	if (sc5336->hblank)
+		sc5336->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+	vblank_def = mode->vts_def - mode->height;
+	sc5336->vblank = v4l2_ctrl_new_std(handler, &sc5336_ctrl_ops,
+					    V4L2_CID_VBLANK, vblank_def,
+					    SC5336_VTS_MAX - mode->height,
+					    1, vblank_def);
+	sc5336->cur_fps = mode->max_fps;
+	exposure_max = mode->vts_def - 8;
+	sc5336->exposure = v4l2_ctrl_new_std(handler, &sc5336_ctrl_ops,
+					      V4L2_CID_EXPOSURE, SC5336_EXPOSURE_MIN,
+					      exposure_max, SC5336_EXPOSURE_STEP,
+					      mode->exp_def);
+	sc5336->anal_gain = v4l2_ctrl_new_std(handler, &sc5336_ctrl_ops,
+					       V4L2_CID_ANALOGUE_GAIN, SC5336_GAIN_MIN,
+					       SC5336_GAIN_MAX, SC5336_GAIN_STEP,
+					       SC5336_GAIN_DEFAULT);
+	sc5336->test_pattern = v4l2_ctrl_new_std_menu_items(handler,
+							    &sc5336_ctrl_ops,
+					V4L2_CID_TEST_PATTERN,
+					ARRAY_SIZE(sc5336_test_pattern_menu) - 1,
+					0, 0, sc5336_test_pattern_menu);
+	v4l2_ctrl_new_std(handler, &sc5336_ctrl_ops,
+				V4L2_CID_HFLIP, 0, 1, 1, 0);
+	v4l2_ctrl_new_std(handler, &sc5336_ctrl_ops,
+				V4L2_CID_VFLIP, 0, 1, 1, 0);
+	if (handler->error) {
+		ret = handler->error;
+		dev_err(&sc5336->client->dev,
+			"Failed to init controls(%d)\n", ret);
+		goto err_free_handler;
+	}
+
+	sc5336->subdev.ctrl_handler = handler;
+
+	return 0;
+
+err_free_handler:
+	v4l2_ctrl_handler_free(handler);
+
+	return ret;
+}
+
+static int sc5336_check_sensor_id(struct sc5336 *sc5336,
+				   struct i2c_client *client)
+{
+	struct device *dev = &sc5336->client->dev;
+	u32 id = 0;
+	int ret;
+
+	if (sc5336->is_thunderboot) {
+		dev_info(dev, "Enable thunderboot mode, skip sensor id check\n");
+		return 0;
+	}
+
+	ret = sc5336_read_reg(client, SC5336_REG_CHIP_ID,
+			       SC5336_REG_VALUE_16BIT, &id);
+	if (id != CHIP_ID) {
+		dev_err(dev, "Unexpected sensor id(%06x), ret(%d)\n", id, ret);
+		return -ENODEV;
+	}
+
+	dev_info(dev, "Detected OV%06x sensor\n", CHIP_ID);
+
+	return 0;
+}
+
+static int sc5336_configure_regulators(struct sc5336 *sc5336)
+{
+	unsigned int i;
+
+	for (i = 0; i < SC5336_NUM_SUPPLIES; i++)
+		sc5336->supplies[i].supply = sc5336_supply_names[i];
+
+	return devm_regulator_bulk_get(&sc5336->client->dev,
+				       SC5336_NUM_SUPPLIES,
+				       sc5336->supplies);
+}
+
+static int sc5336_probe(struct i2c_client *client,
+			 const struct i2c_device_id *id)
+{
+	struct device *dev = &client->dev;
+	struct device_node *node = dev->of_node;
+	struct sc5336 *sc5336;
+	struct v4l2_subdev *sd;
+	char facing[2];
+	int ret;
+
+	dev_info(dev, "driver version: %02x.%02x.%02x",
+		 DRIVER_VERSION >> 16,
+		 (DRIVER_VERSION & 0xff00) >> 8,
+		 DRIVER_VERSION & 0x00ff);
+
+	sc5336 = devm_kzalloc(dev, sizeof(*sc5336), GFP_KERNEL);
+	if (!sc5336)
+		return -ENOMEM;
+
+	ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX,
+				   &sc5336->module_index);
+	ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING,
+				       &sc5336->module_facing);
+	ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME,
+				       &sc5336->module_name);
+	ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME,
+				       &sc5336->len_name);
+	if (ret) {
+		dev_err(dev, "could not get module information!\n");
+		return -EINVAL;
+	}
+
+	sc5336->is_thunderboot = IS_ENABLED(CONFIG_VIDEO_ROCKCHIP_THUNDER_BOOT_ISP);
+	sc5336->client = client;
+	sc5336->cur_mode = &supported_modes[0];
+
+	sc5336->xvclk = devm_clk_get(dev, "xvclk");
+	if (IS_ERR(sc5336->xvclk)) {
+		dev_err(dev, "Failed to get xvclk\n");
+		return -EINVAL;
+	}
+
+	if (sc5336->is_thunderboot) {
+		sc5336->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_ASIS);
+		if (IS_ERR(sc5336->reset_gpio))
+			dev_warn(dev, "Failed to get reset-gpios\n");
+
+		sc5336->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_ASIS);
+		if (IS_ERR(sc5336->pwdn_gpio))
+			dev_warn(dev, "Failed to get pwdn-gpios\n");
+	} else {
+		sc5336->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
+		if (IS_ERR(sc5336->reset_gpio))
+			dev_warn(dev, "Failed to get reset-gpios\n");
+
+		sc5336->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_OUT_LOW);
+		if (IS_ERR(sc5336->pwdn_gpio))
+			dev_warn(dev, "Failed to get pwdn-gpios\n");
+	}
+	sc5336->pinctrl = devm_pinctrl_get(dev);
+	if (!IS_ERR(sc5336->pinctrl)) {
+		sc5336->pins_default =
+			pinctrl_lookup_state(sc5336->pinctrl,
+					     OF_CAMERA_PINCTRL_STATE_DEFAULT);
+		if (IS_ERR(sc5336->pins_default))
+			dev_err(dev, "could not get default pinstate\n");
+
+		sc5336->pins_sleep =
+			pinctrl_lookup_state(sc5336->pinctrl,
+					     OF_CAMERA_PINCTRL_STATE_SLEEP);
+		if (IS_ERR(sc5336->pins_sleep))
+			dev_err(dev, "could not get sleep pinstate\n");
+	} else {
+		dev_err(dev, "no pinctrl\n");
+	}
+
+	ret = sc5336_configure_regulators(sc5336);
+	if (ret) {
+		dev_err(dev, "Failed to get power regulators\n");
+		return ret;
+	}
+
+	mutex_init(&sc5336->mutex);
+
+	sd = &sc5336->subdev;
+	v4l2_i2c_subdev_init(sd, client, &sc5336_subdev_ops);
+	ret = sc5336_initialize_controls(sc5336);
+	if (ret)
+		goto err_destroy_mutex;
+
+	ret = __sc5336_power_on(sc5336);
+	if (ret)
+		goto err_free_handler;
+
+	ret = sc5336_check_sensor_id(sc5336, client);
+	if (ret)
+		goto err_power_off;
+
+#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
+	sd->internal_ops = &sc5336_internal_ops;
+	sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
+		     V4L2_SUBDEV_FL_HAS_EVENTS;
+#endif
+#if defined(CONFIG_MEDIA_CONTROLLER)
+	sc5336->pad.flags = MEDIA_PAD_FL_SOURCE;
+	sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
+	ret = media_entity_pads_init(&sd->entity, 1, &sc5336->pad);
+	if (ret < 0)
+		goto err_power_off;
+#endif
+
+	memset(facing, 0, sizeof(facing));
+	if (strcmp(sc5336->module_facing, "back") == 0)
+		facing[0] = 'b';
+	else
+		facing[0] = 'f';
+
+	snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s",
+		 sc5336->module_index, facing,
+		 SC5336_NAME, dev_name(sd->dev));
+	ret = v4l2_async_register_subdev_sensor_common(sd);
+	if (ret) {
+		dev_err(dev, "v4l2 async register subdev failed\n");
+		goto err_clean_entity;
+	}
+
+	pm_runtime_set_active(dev);
+	pm_runtime_enable(dev);
+	if (sc5336->is_thunderboot)
+		pm_runtime_get_sync(dev);
+	else
+		pm_runtime_idle(dev);
+
+	return 0;
+
+err_clean_entity:
+#if defined(CONFIG_MEDIA_CONTROLLER)
+	media_entity_cleanup(&sd->entity);
+#endif
+err_power_off:
+	__sc5336_power_off(sc5336);
+err_free_handler:
+	v4l2_ctrl_handler_free(&sc5336->ctrl_handler);
+err_destroy_mutex:
+	mutex_destroy(&sc5336->mutex);
+
+	return ret;
+}
+
+static int sc5336_remove(struct i2c_client *client)
+{
+	struct v4l2_subdev *sd = i2c_get_clientdata(client);
+	struct sc5336 *sc5336 = to_sc5336(sd);
+
+	v4l2_async_unregister_subdev(sd);
+#if defined(CONFIG_MEDIA_CONTROLLER)
+	media_entity_cleanup(&sd->entity);
+#endif
+	v4l2_ctrl_handler_free(&sc5336->ctrl_handler);
+	mutex_destroy(&sc5336->mutex);
+
+	pm_runtime_disable(&client->dev);
+	if (!pm_runtime_status_suspended(&client->dev))
+		__sc5336_power_off(sc5336);
+	pm_runtime_set_suspended(&client->dev);
+
+	return 0;
+}
+
+#if IS_ENABLED(CONFIG_OF)
+static const struct of_device_id sc5336_of_match[] = {
+	{ .compatible = "smartsens,sc5336" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, sc5336_of_match);
+#endif
+
+static const struct i2c_device_id sc5336_match_id[] = {
+	{ "smartsens,sc5336", 0 },
+	{ },
+};
+
+static struct i2c_driver sc5336_i2c_driver = {
+	.driver = {
+		.name = SC5336_NAME,
+		.pm = &sc5336_pm_ops,
+		.of_match_table = of_match_ptr(sc5336_of_match),
+	},
+	.probe		= &sc5336_probe,
+	.remove		= &sc5336_remove,
+	.id_table	= sc5336_match_id,
+};
+
+static int __init sensor_mod_init(void)
+{
+	return i2c_add_driver(&sc5336_i2c_driver);
+}
+
+static void __exit sensor_mod_exit(void)
+{
+	i2c_del_driver(&sc5336_i2c_driver);
+}
+
+#if defined(CONFIG_VIDEO_ROCKCHIP_THUNDER_BOOT_ISP) && !defined(CONFIG_INITCALL_ASYNC)
+subsys_initcall(sensor_mod_init);
+#else
+device_initcall_sync(sensor_mod_init);
+#endif
+module_exit(sensor_mod_exit);
+
+MODULE_DESCRIPTION("smartsens sc5336 sensor driver");
+MODULE_LICENSE("GPL");
diff --git a/kernel/drivers/media/i2c/sc850sl.c b/kernel/drivers/media/i2c/sc850sl.c
index 14a1e54..9273346 100644
--- a/kernel/drivers/media/i2c/sc850sl.c
+++ b/kernel/drivers/media/i2c/sc850sl.c
@@ -1412,8 +1412,7 @@
 					ctrl->val + sc850sl->cur_mode->height);
 		if (!ret)
 			sc850sl->cur_vts = ctrl->val + sc850sl->cur_mode->height;
-		if (sc850sl->cur_vts != sc850sl->cur_mode->vts_def)
-			sc850sl_modify_fps_info(sc850sl);
+		sc850sl_modify_fps_info(sc850sl);
 		dev_dbg(&client->dev, "set vblank 0x%x\n",
 			ctrl->val);
 		break;
diff --git a/kernel/drivers/media/platform/Makefile b/kernel/drivers/media/platform/Makefile
index 740f640..f3f5dca 100644
--- a/kernel/drivers/media/platform/Makefile
+++ b/kernel/drivers/media/platform/Makefile
@@ -57,7 +57,7 @@
 obj-$(CONFIG_VIDEO_ROCKCHIP_RKISP1)	+= rockchip/isp1/
 obj-$(CONFIG_VIDEO_ROCKCHIP_ISP)	+= rockchip/isp/
 obj-$(CONFIG_VIDEO_ROCKCHIP_ISPP)	+= rockchip/ispp/
-obj-$(CONFIG_VIDEO_ROCKCHIP_HDMIRX)	+= rockchip/hdmirx/
+obj-$(CONFIG_VIDEO_ROCKCHIP_HDMIRX_CLASS) += rockchip/hdmirx/
 
 obj-y	+= omap/
 
diff --git a/kernel/drivers/media/platform/rockchip/cif/capture.c b/kernel/drivers/media/platform/rockchip/cif/capture.c
index 0a557bd..983d675 100644
--- a/kernel/drivers/media/platform/rockchip/cif/capture.c
+++ b/kernel/drivers/media/platform/rockchip/cif/capture.c
@@ -847,11 +847,13 @@
 }
 
 const struct
-cif_input_fmt *get_input_fmt(struct v4l2_subdev *sd, struct v4l2_rect *rect,
+cif_input_fmt *rkcif_get_input_fmt(struct rkcif_device *dev, struct v4l2_rect *rect,
 			     u32 pad_id, struct csi_channel_info *csi_info)
 {
 	struct v4l2_subdev_format fmt;
+	struct v4l2_subdev *sd = dev->terminal_sensor.sd;
 	struct rkmodule_channel_info ch_info = {0};
+	struct rkmodule_capture_info capture_info;
 	int ret;
 	u32 i;
 
@@ -909,7 +911,26 @@
 	rect->top = 0;
 	rect->width = fmt.format.width;
 	rect->height = fmt.format.height;
-
+	ret = v4l2_subdev_call(sd,
+			       core, ioctl,
+			       RKMODULE_GET_CAPTURE_MODE,
+			       &capture_info);
+	if (!ret) {
+		if (capture_info.mode == RKMODULE_MULTI_DEV_COMBINE_ONE &&
+		    dev->hw_dev->is_rk3588s2) {
+			for (i = 0; i < capture_info.multi_dev.dev_num; i++) {
+				if (capture_info.multi_dev.dev_idx[i] == 0)
+					capture_info.multi_dev.dev_idx[i] = 2;
+				else if (capture_info.multi_dev.dev_idx[i] == 2)
+					capture_info.multi_dev.dev_idx[i] = 4;
+				else if (capture_info.multi_dev.dev_idx[i] == 3)
+					capture_info.multi_dev.dev_idx[i] = 5;
+			}
+		}
+		csi_info->capture_info = capture_info;
+	} else {
+		csi_info->capture_info.mode = RKMODULE_CAPTURE_MODE_NONE;
+	}
 	for (i = 0; i < ARRAY_SIZE(in_fmts); i++)
 		if (fmt.format.code == in_fmts[i].mbus_code &&
 		    fmt.format.field == in_fmts[i].field)
@@ -1594,7 +1615,7 @@
 	struct rkisp_rx_buf *dbufs;
 	struct rkcif_device *dev = stream->cifdev;
 
-	if (dev->sditf[0] && dev->sditf[0]->num_sensors != 0) {
+	if (dev->sditf[0] && dev->sditf[0]->sd.entity.num_links) {
 		if (dev->sditf[0]->is_combine_mode)
 			pad = media_entity_remote_pad(&dev->sditf[0]->pads[1]);
 		else
@@ -1670,6 +1691,20 @@
 	stream->skip_info.skip_en = false;
 }
 
+static void rkcif_rdbk_with_tools(struct rkcif_stream *stream,
+				      struct rkcif_rx_buffer *active_buf)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&stream->tools_vdev->vbq_lock, flags);
+	if (stream->tools_vdev->state == RKCIF_STATE_STREAMING) {
+		list_add_tail(&active_buf->list, &stream->tools_vdev->buf_done_head);
+		if (!work_busy(&stream->tools_vdev->work))
+			schedule_work(&stream->tools_vdev->work);
+	}
+	spin_unlock_irqrestore(&stream->tools_vdev->vbq_lock, flags);
+}
+
 static void rkcif_rdbk_frame_end_toisp(struct rkcif_stream *stream,
 				       struct rkcif_rx_buffer *buffer)
 {
@@ -1734,6 +1769,12 @@
 		rkcif_s_rx_buffer(dev, &dev->rdbk_rx_buf[RDBK_L]->dbufs);
 		rkcif_s_rx_buffer(dev, &dev->rdbk_rx_buf[RDBK_M]->dbufs);
 		rkcif_s_rx_buffer(dev, &dev->rdbk_rx_buf[RDBK_S]->dbufs);
+		rkcif_rdbk_with_tools(&dev->stream[RDBK_L], dev->rdbk_rx_buf[RDBK_L]);
+		rkcif_rdbk_with_tools(&dev->stream[RDBK_M], dev->rdbk_rx_buf[RDBK_M]);
+		rkcif_rdbk_with_tools(&dev->stream[RDBK_S], dev->rdbk_rx_buf[RDBK_S]);
+		atomic_dec(&dev->stream[RDBK_L].buf_cnt);
+		atomic_dec(&dev->stream[RDBK_M].buf_cnt);
+		atomic_dec(&dev->stream[RDBK_S].buf_cnt);
 		dev->rdbk_rx_buf[RDBK_L] = NULL;
 		dev->rdbk_rx_buf[RDBK_M] = NULL;
 		dev->rdbk_rx_buf[RDBK_S] = NULL;
@@ -1772,6 +1813,10 @@
 		dev->rdbk_rx_buf[RDBK_M]->dbufs.sequence = dev->rdbk_rx_buf[RDBK_L]->dbufs.sequence;
 		rkcif_s_rx_buffer(dev, &dev->rdbk_rx_buf[RDBK_L]->dbufs);
 		rkcif_s_rx_buffer(dev, &dev->rdbk_rx_buf[RDBK_M]->dbufs);
+		rkcif_rdbk_with_tools(&dev->stream[RDBK_L], dev->rdbk_rx_buf[RDBK_L]);
+		rkcif_rdbk_with_tools(&dev->stream[RDBK_M], dev->rdbk_rx_buf[RDBK_M]);
+		atomic_dec(&dev->stream[RDBK_L].buf_cnt);
+		atomic_dec(&dev->stream[RDBK_M].buf_cnt);
 		dev->rdbk_rx_buf[RDBK_L] = NULL;
 		dev->rdbk_rx_buf[RDBK_M] = NULL;
 	}
@@ -1783,14 +1828,51 @@
 	spin_unlock_irqrestore(&dev->hdr_lock, flags);
 }
 
+static void rkcif_write_buff_addr_multi_dev_combine(struct rkcif_stream *stream,
+						    u32 frm_addr_y, u32 frm_addr_uv,
+						    u32 buff_addr_y, u32 buff_addr_cbcr,
+						    bool is_dummy_buf)
+{
+	struct rkcif_device *dev = stream->cifdev;
+	struct rkmodule_capture_info *capture_info = &dev->channels[stream->id].capture_info;
+	u32 addr_y, addr_cbcr;
+	int addr_offset = 0;
+	int i = 0;
+	int tmp_host_index = dev->csi_host_idx;
+
+	for (i = 0; i < capture_info->multi_dev.dev_num; i++) {
+		if (is_dummy_buf) {
+			addr_y = buff_addr_y;
+		} else {
+			addr_offset = dev->channels[stream->id].left_virtual_width;
+			addr_y = buff_addr_y + addr_offset * i;
+		}
+		dev->csi_host_idx = capture_info->multi_dev.dev_idx[i];
+		rkcif_write_register(dev, frm_addr_y, addr_y);
+		if (stream->cif_fmt_out->fmt_type != CIF_FMT_TYPE_RAW &&
+		    frm_addr_uv && buff_addr_cbcr) {
+			if (is_dummy_buf) {
+				addr_cbcr = buff_addr_cbcr;
+			} else {
+				addr_offset = dev->channels[stream->id].left_virtual_width;
+				addr_cbcr = buff_addr_cbcr + addr_offset * i;
+			}
+			rkcif_write_register(dev, frm_addr_uv, addr_cbcr);
+		}
+	}
+	dev->csi_host_idx = tmp_host_index;
+}
+
 static void rkcif_assign_new_buffer_init_toisp(struct rkcif_stream *stream,
 					       int channel_id)
 {
 	struct rkcif_device *dev = stream->cifdev;
 	struct rkcif_rx_buffer *rx_buf;
 	struct v4l2_mbus_config *mbus_cfg = &dev->active_sensor->mbus;
+	struct rkmodule_capture_info *capture_info = &dev->channels[channel_id].capture_info;
 	u32 frm0_addr_y;
 	u32 frm1_addr_y;
+	u32 buff_addr_y;
 	unsigned long flags;
 
 	if (mbus_cfg->type == V4L2_MBUS_CSI2_DPHY ||
@@ -1817,9 +1899,15 @@
 		}
 	}
 
-	if (stream->curr_buf_toisp)
-		rkcif_write_register(dev, frm0_addr_y,
-				     stream->curr_buf_toisp->dummy.dma_addr);
+	if (stream->curr_buf_toisp) {
+		buff_addr_y = stream->curr_buf_toisp->dummy.dma_addr;
+		if (capture_info->mode == RKMODULE_MULTI_DEV_COMBINE_ONE) {
+			rkcif_write_buff_addr_multi_dev_combine(stream, frm0_addr_y, 0,
+								buff_addr_y, 0, false);
+		} else {
+			rkcif_write_register(dev, frm0_addr_y, buff_addr_y);
+		}
+	}
 
 	if (!stream->next_buf_toisp) {
 		if (!list_empty(&stream->rx_buf_head)) {
@@ -1836,9 +1924,15 @@
 		}
 	}
 
-	if (stream->next_buf_toisp)
-		rkcif_write_register(dev, frm1_addr_y,
-				     stream->next_buf_toisp->dummy.dma_addr);
+	if (stream->next_buf_toisp) {
+		buff_addr_y = stream->next_buf_toisp->dummy.dma_addr;
+		if (capture_info->mode == RKMODULE_MULTI_DEV_COMBINE_ONE) {
+			rkcif_write_buff_addr_multi_dev_combine(stream, frm1_addr_y, 0,
+								buff_addr_y, 0, false);
+		} else {
+			rkcif_write_register(dev, frm1_addr_y, buff_addr_y);
+		}
+	}
 
 	spin_unlock_irqrestore(&stream->vbq_lock, flags);
 	stream->buf_owner = RKCIF_DMAEN_BY_ISP;
@@ -1849,10 +1943,11 @@
 {
 	struct rkcif_device *dev = stream->cifdev;
 	struct v4l2_mbus_config *mbus_cfg = &dev->active_sensor->mbus;
+	struct rkmodule_capture_info *capture_info = &dev->channels[channel_id].capture_info;
 	struct rkcif_rx_buffer *buffer = NULL;
 	struct rkcif_rx_buffer *active_buf = NULL;
 	struct sditf_priv *priv = dev->sditf[0];
-	u32 frm_addr_y;
+	u32 frm_addr_y, buff_addr_y;
 	unsigned long flags;
 
 	if (mbus_cfg->type == V4L2_MBUS_CSI2_DPHY ||
@@ -1889,14 +1984,18 @@
 				active_buf->dbufs.timestamp = stream->readout.fs_timestamp;
 				active_buf->fe_timestamp = ktime_get_ns();
 				stream->last_frame_idx = stream->frame_idx;
-				if (dev->hdr.hdr_mode == NO_HDR)
+				if (dev->hdr.hdr_mode == NO_HDR) {
 					rkcif_s_rx_buffer(dev, &active_buf->dbufs);
-				else
+					if (dev->is_support_tools && stream->tools_vdev)
+						rkcif_rdbk_with_tools(stream, active_buf);
+					atomic_dec(&stream->buf_cnt);
+				} else {
 					rkcif_rdbk_frame_end_toisp(stream, active_buf);
-				stream->buf_num_toisp--;
+				}
 			} else {
-				rkcif_s_rx_buffer(dev, &stream->next_buf_toisp->dbufs);
-				stream->buf_num_toisp--;
+				rkcif_s_rx_buffer(dev, &active_buf->dbufs);
+				if (dev->is_support_tools && stream->tools_vdev)
+					rkcif_rdbk_with_tools(stream, active_buf);
 			}
 		} else if (stream->frame_phase == CIF_CSI_FRAME1_READY) {
 			if (stream->curr_buf_toisp == stream->next_buf_toisp)
@@ -1918,14 +2017,18 @@
 				active_buf->dbufs.timestamp = stream->readout.fs_timestamp;
 				active_buf->fe_timestamp = ktime_get_ns();
 				stream->last_frame_idx = stream->frame_idx;
-				if (dev->hdr.hdr_mode == NO_HDR)
+				if (dev->hdr.hdr_mode == NO_HDR) {
 					rkcif_s_rx_buffer(dev, &active_buf->dbufs);
-				else
+					if (dev->is_support_tools && stream->tools_vdev)
+						rkcif_rdbk_with_tools(stream, active_buf);
+					atomic_dec(&stream->buf_cnt);
+				} else {
 					rkcif_rdbk_frame_end_toisp(stream, active_buf);
-				stream->buf_num_toisp--;
+				}
 			} else {
-				rkcif_s_rx_buffer(dev, &stream->curr_buf_toisp->dbufs);
-				stream->buf_num_toisp--;
+				rkcif_s_rx_buffer(dev, &active_buf->dbufs);
+				if (dev->is_support_tools && stream->tools_vdev)
+					rkcif_rdbk_with_tools(stream, active_buf);
 			}
 		}
 		if (stream->lack_buf_cnt)
@@ -1967,10 +2070,14 @@
 			active_buf->dbufs.timestamp = stream->readout.fs_timestamp;
 			active_buf->fe_timestamp = ktime_get_ns();
 			stream->last_frame_idx = stream->frame_idx;
-			if (dev->hdr.hdr_mode == NO_HDR)
+			if (dev->hdr.hdr_mode == NO_HDR) {
 				rkcif_s_rx_buffer(dev, &active_buf->dbufs);
-			else
+				if (dev->is_support_tools && stream->tools_vdev)
+					rkcif_rdbk_with_tools(stream, active_buf);
+				atomic_dec(&stream->buf_cnt);
+			} else {
 				rkcif_rdbk_frame_end_toisp(stream, active_buf);
+			}
 		} else {
 			if (stream->cifdev->rdbk_debug && dev->hw_dev->dummy_buf.vaddr)
 				v4l2_info(&stream->cifdev->v4l2_dev,
@@ -1978,13 +2085,20 @@
 					  stream->id,
 					  stream->frame_idx - 1);
 		}
+		if (dev->is_support_tools && stream->tools_vdev && stream->curr_buf_toisp)
+			rkcif_rdbk_with_tools(stream, stream->curr_buf_toisp);
 	}
 
 out_get_buf:
 	stream->frame_phase_cache = stream->frame_phase;
 	if (buffer) {
-		rkcif_write_register(dev, frm_addr_y,
-				     buffer->dummy.dma_addr);
+		buff_addr_y = buffer->dummy.dma_addr;
+		if (capture_info->mode == RKMODULE_MULTI_DEV_COMBINE_ONE) {
+			rkcif_write_buff_addr_multi_dev_combine(stream, frm_addr_y, 0,
+								buff_addr_y, 0, false);
+		} else {
+			rkcif_write_register(dev, frm_addr_y, buff_addr_y);
+		}
 		if (dev->rdbk_debug > 1 &&
 		    stream->frame_idx < 15)
 			v4l2_info(&dev->v4l2_dev,
@@ -1994,8 +2108,13 @@
 				  frm_addr_y, (u32)buffer->dummy.dma_addr);
 	} else if (dev->hw_dev->dummy_buf.vaddr && priv &&
 		   priv->mode.rdbk_mode == RKISP_VICAP_RDBK_AUTO) {
-		rkcif_write_register(dev, frm_addr_y,
-				     dev->hw_dev->dummy_buf.dma_addr);
+		buff_addr_y = dev->hw_dev->dummy_buf.dma_addr;
+		if (capture_info->mode == RKMODULE_MULTI_DEV_COMBINE_ONE) {
+			rkcif_write_buff_addr_multi_dev_combine(stream, frm_addr_y, 0,
+								buff_addr_y, 0, true);
+		} else {
+			rkcif_write_register(dev, frm_addr_y, buff_addr_y);
+		}
 	}
 	spin_unlock_irqrestore(&stream->vbq_lock, flags);
 	return 0;
@@ -2018,8 +2137,9 @@
 	struct rkcif_device *dev = stream->cifdev;
 	struct v4l2_mbus_config *mbus_cfg = &dev->active_sensor->mbus;
 	struct rkcif_rx_buffer *buffer = NULL;
+	struct rkmodule_capture_info *capture_info = &dev->channels[stream->id].capture_info;
 	struct rkcif_rx_buffer *active_buf = NULL;
-	u32 frm_addr_y;
+	u32 frm_addr_y, buff_addr_y;
 	u32 vblank = 0;
 	u32 vblank_ns = 0;
 	u64 cur_time = 0;
@@ -2082,8 +2202,15 @@
 			if (buffer) {
 				list_del(&buffer->list);
 				stream->curr_buf_toisp = buffer;
-				rkcif_write_register(dev, frm_addr_y,
-						     stream->curr_buf_toisp->dummy.dma_addr);
+				buff_addr_y = stream->curr_buf_toisp->dummy.dma_addr;
+				if (capture_info->mode == RKMODULE_MULTI_DEV_COMBINE_ONE) {
+					rkcif_write_buff_addr_multi_dev_combine(stream,
+										frm_addr_y, 0,
+										buff_addr_y, 0,
+										false);
+				} else {
+					rkcif_write_register(dev, frm_addr_y, buff_addr_y);
+				}
 				if (dev->rdbk_debug > 1 &&
 				    stream->frame_idx < 15)
 					v4l2_info(&dev->v4l2_dev,
@@ -2091,7 +2218,6 @@
 						  stream->id,
 						  stream->frame_idx - 1, frm_addr_y,
 						  (u32)stream->curr_buf_toisp->dummy.dma_addr);
-				stream->buf_num_toisp--;
 			}
 		} else if (frame_phase == CIF_CSI_FRAME1_READY) {
 			active_buf = stream->next_buf_toisp;
@@ -2100,8 +2226,15 @@
 			if (buffer) {
 				list_del(&buffer->list);
 				stream->next_buf_toisp = buffer;
-				rkcif_write_register(dev, frm_addr_y,
-						     stream->next_buf_toisp->dummy.dma_addr);
+				buff_addr_y = stream->next_buf_toisp->dummy.dma_addr;
+				if (capture_info->mode == RKMODULE_MULTI_DEV_COMBINE_ONE) {
+					rkcif_write_buff_addr_multi_dev_combine(stream,
+										frm_addr_y, 0,
+										buff_addr_y, 0,
+										false);
+				} else {
+					rkcif_write_register(dev, frm_addr_y, buff_addr_y);
+				}
 				if (dev->rdbk_debug > 1 &&
 				    stream->frame_idx < 15)
 					v4l2_info(&dev->v4l2_dev,
@@ -2109,7 +2242,6 @@
 						  stream->id,
 						  stream->frame_idx - 1, frm_addr_y,
 						  (u32)stream->next_buf_toisp->dummy.dma_addr);
-				stream->buf_num_toisp--;
 			}
 		}
 		if (stream->lack_buf_cnt)
@@ -2150,8 +2282,13 @@
 			stream->next_buf_toisp = stream->curr_buf_toisp;
 		else
 			stream->curr_buf_toisp = stream->next_buf_toisp;
-		rkcif_write_register(dev, frm_addr_y,
-				     stream->curr_buf_toisp->dummy.dma_addr);
+		buff_addr_y = stream->curr_buf_toisp->dummy.dma_addr;
+		if (capture_info->mode == RKMODULE_MULTI_DEV_COMBINE_ONE) {
+			rkcif_write_buff_addr_multi_dev_combine(stream, frm_addr_y, 0,
+								buff_addr_y, 0, false);
+		} else {
+			rkcif_write_register(dev, frm_addr_y, buff_addr_y);
+		}
 	}
 }
 
@@ -2162,6 +2299,7 @@
 	struct v4l2_mbus_config *mbus_cfg = &dev->active_sensor->mbus;
 	u32 frm0_addr_y, frm0_addr_uv;
 	u32 frm1_addr_y, frm1_addr_uv;
+	u32 buff_addr_y, buff_addr_cbcr;
 	unsigned long flags;
 	struct rkcif_dummy_buffer *dummy_buf = &dev->hw_dev->dummy_buf;
 	struct csi_channel_info *channel = &dev->channels[channel_id];
@@ -2187,21 +2325,45 @@
 			stream->curr_buf = list_first_entry(&stream->buf_head,
 							    struct rkcif_buffer,
 							    queue);
+			v4l2_dbg(4, rkcif_debug, &dev->v4l2_dev, "%s %d, stream[%d] buf idx %d\n",
+				 __func__, __LINE__, stream->id, stream->curr_buf->vb.vb2_buf.index);
 			list_del(&stream->curr_buf->queue);
 		}
 	}
 
 	if (stream->curr_buf) {
-		rkcif_write_register(dev, frm0_addr_y,
-				     stream->curr_buf->buff_addr[RKCIF_PLANE_Y]);
-		if (stream->cif_fmt_out->fmt_type != CIF_FMT_TYPE_RAW)
-			rkcif_write_register(dev, frm0_addr_uv,
-					     stream->curr_buf->buff_addr[RKCIF_PLANE_CBCR]);
+		buff_addr_y = stream->curr_buf->buff_addr[RKCIF_PLANE_Y];
+		buff_addr_cbcr = stream->curr_buf->buff_addr[RKCIF_PLANE_CBCR];
+		if (channel->capture_info.mode == RKMODULE_MULTI_DEV_COMBINE_ONE) {
+			rkcif_write_buff_addr_multi_dev_combine(stream,
+								frm0_addr_y,
+								frm0_addr_uv,
+								buff_addr_y,
+								buff_addr_cbcr,
+								false);
+		} else {
+			rkcif_write_register(dev, frm0_addr_y,
+					     stream->curr_buf->buff_addr[RKCIF_PLANE_Y]);
+			if (stream->cif_fmt_out->fmt_type != CIF_FMT_TYPE_RAW)
+				rkcif_write_register(dev, frm0_addr_uv,
+						     stream->curr_buf->buff_addr[RKCIF_PLANE_CBCR]);
+		}
 	} else {
 		if (dummy_buf->vaddr) {
-			rkcif_write_register(dev, frm0_addr_y, dummy_buf->dma_addr);
-			if (stream->cif_fmt_out->fmt_type != CIF_FMT_TYPE_RAW)
-				rkcif_write_register(dev, frm0_addr_uv, dummy_buf->dma_addr);
+			buff_addr_y = dummy_buf->dma_addr;
+			buff_addr_cbcr = dummy_buf->dma_addr;
+			if (channel->capture_info.mode == RKMODULE_MULTI_DEV_COMBINE_ONE) {
+				rkcif_write_buff_addr_multi_dev_combine(stream,
+									frm0_addr_y,
+									frm0_addr_uv,
+									buff_addr_y,
+									buff_addr_cbcr,
+									true);
+			} else {
+				rkcif_write_register(dev, frm0_addr_y, buff_addr_y);
+				if (stream->cif_fmt_out->fmt_type != CIF_FMT_TYPE_RAW)
+					rkcif_write_register(dev, frm0_addr_uv, buff_addr_cbcr);
+			}
 		} else {
 			if (stream->lack_buf_cnt < 2)
 				stream->lack_buf_cnt++;
@@ -2211,43 +2373,69 @@
 	if (stream->cif_fmt_in->field == V4L2_FIELD_INTERLACED) {
 		stream->next_buf = stream->curr_buf;
 		if (stream->next_buf) {
-			rkcif_write_register(dev, frm1_addr_y,
-					     stream->next_buf->buff_addr[RKCIF_PLANE_Y] + (channel->virtual_width / 2));
-			if (stream->cif_fmt_out->fmt_type != CIF_FMT_TYPE_RAW)
-				rkcif_write_register(dev, frm1_addr_uv,
-						     stream->next_buf->buff_addr[RKCIF_PLANE_CBCR] + (channel->virtual_width / 2));
+			buff_addr_y = stream->next_buf->buff_addr[RKCIF_PLANE_Y];
+			buff_addr_cbcr = stream->next_buf->buff_addr[RKCIF_PLANE_CBCR];
+			if (channel->capture_info.mode == RKMODULE_MULTI_DEV_COMBINE_ONE) {
+				rkcif_write_buff_addr_multi_dev_combine(stream,
+									frm1_addr_y,
+									frm1_addr_uv,
+									buff_addr_y,
+									buff_addr_cbcr,
+									false);
+			} else {
+				rkcif_write_register(dev, frm1_addr_y,
+						     buff_addr_y + (channel->virtual_width / 2));
+				if (stream->cif_fmt_out->fmt_type != CIF_FMT_TYPE_RAW)
+					rkcif_write_register(dev, frm1_addr_uv,
+							     buff_addr_cbcr + (channel->virtual_width / 2));
+			}
 		}
 	} else {
 		if (!stream->next_buf) {
 			if (!list_empty(&stream->buf_head)) {
 				stream->next_buf = list_first_entry(&stream->buf_head,
 								    struct rkcif_buffer, queue);
+				v4l2_dbg(4, rkcif_debug, &dev->v4l2_dev, "%s %d, stream[%d] buf idx %d\n",
+					 __func__, __LINE__, stream->id, stream->next_buf->vb.vb2_buf.index);
 				list_del(&stream->next_buf->queue);
 			}
 		}
 
-		if (stream->next_buf) {
-			rkcif_write_register(dev, frm1_addr_y,
-					     stream->next_buf->buff_addr[RKCIF_PLANE_Y]);
-			if (stream->cif_fmt_out->fmt_type != CIF_FMT_TYPE_RAW)
-				rkcif_write_register(dev, frm1_addr_uv,
-						     stream->next_buf->buff_addr[RKCIF_PLANE_CBCR]);
-		} else {
-			if (dummy_buf->vaddr) {
+		if (!stream->next_buf && dummy_buf->vaddr) {
+			buff_addr_y = dummy_buf->dma_addr;
+			buff_addr_cbcr = dummy_buf->dma_addr;
+			if (channel->capture_info.mode == RKMODULE_MULTI_DEV_COMBINE_ONE) {
+				rkcif_write_buff_addr_multi_dev_combine(stream,
+									frm1_addr_y,
+									frm1_addr_uv,
+									buff_addr_y,
+									buff_addr_cbcr,
+									true);
+			} else {
 				rkcif_write_register(dev, frm1_addr_y, dummy_buf->dma_addr);
 				if (stream->cif_fmt_out->fmt_type != CIF_FMT_TYPE_RAW)
 					rkcif_write_register(dev, frm1_addr_uv, dummy_buf->dma_addr);
+			}
+
+		} else if (!stream->next_buf && stream->curr_buf) {
+			stream->next_buf = stream->curr_buf;
+			if (stream->lack_buf_cnt < 2)
+				stream->lack_buf_cnt++;
+		}
+		if (stream->next_buf) {
+			buff_addr_y = stream->next_buf->buff_addr[RKCIF_PLANE_Y];
+			buff_addr_cbcr = stream->next_buf->buff_addr[RKCIF_PLANE_CBCR];
+			if (channel->capture_info.mode == RKMODULE_MULTI_DEV_COMBINE_ONE) {
+				rkcif_write_buff_addr_multi_dev_combine(stream,
+									frm1_addr_y,
+									frm1_addr_uv,
+									buff_addr_y,
+									buff_addr_cbcr,
+									false);
 			} else {
-				if (stream->curr_buf) {
-					stream->next_buf = stream->curr_buf;
-					rkcif_write_register(dev, frm1_addr_y,
-							     stream->next_buf->buff_addr[RKCIF_PLANE_Y]);
-					if (stream->cif_fmt_out->fmt_type != CIF_FMT_TYPE_RAW)
-						rkcif_write_register(dev, frm1_addr_uv,
-								     stream->next_buf->buff_addr[RKCIF_PLANE_CBCR]);
-				}
-				if (stream->lack_buf_cnt < 2)
-					stream->lack_buf_cnt++;
+				rkcif_write_register(dev, frm1_addr_y, buff_addr_y);
+				if (stream->cif_fmt_out->fmt_type != CIF_FMT_TYPE_RAW)
+					rkcif_write_register(dev, frm1_addr_uv, buff_addr_cbcr);
 			}
 		}
 	}
@@ -2295,6 +2483,7 @@
 	struct rkisp_rx_buf *dbufs = NULL;
 	struct dma_buf *dbuf = NULL;
 	int ret = 0;
+	u32 buff_addr_y, buff_addr_cbcr;
 	unsigned long flags;
 
 	if (mbus_cfg->type == V4L2_MBUS_CSI2_DPHY ||
@@ -2315,16 +2504,15 @@
 			      get_dvp_reg_index_of_frm1_uv_addr(channel_id);
 	}
 
-	if (dev->hdr.hdr_mode != NO_HDR && stream->id != 0 && (!dev->rdbk_buf[RDBK_L])) {
-		v4l2_dbg(3, rkcif_debug, &dev->v4l2_dev, "%s %d\n", __func__, __LINE__);
-		return -EINVAL;
-	}
-
 	if (stream->to_stop_dma) {
 		if (stream->dma_en & RKCIF_DMAEN_BY_ISP) {
 			v4l2_dbg(3, rkcif_debug, &dev->v4l2_dev, "%s %d\n", __func__, __LINE__);
 			goto stop_dma;
 		} else {
+			if (stream->frame_phase == CIF_CSI_FRAME0_READY)
+				stream->curr_buf = NULL;
+			else
+				stream->next_buf = NULL;
 			v4l2_dbg(3, rkcif_debug, &dev->v4l2_dev, "%s %d\n", __func__, __LINE__);
 			return -EINVAL;
 		}
@@ -2347,8 +2535,8 @@
 				list_del(&stream->curr_buf->queue);
 				buffer = stream->curr_buf;
 				v4l2_dbg(3, rkcif_debug, &dev->v4l2_dev,
-					 "stream[%d] update curr_buf 0x%x\n",
-					 stream->id, buffer->buff_addr[0]);
+					 "stream[%d] update curr_buf 0x%x, buf idx %d\n",
+					 stream->id, buffer->buff_addr[0], stream->curr_buf->vb.vb2_buf.index);
 			}
 		} else if (stream->frame_phase == CIF_CSI_FRAME1_READY) {
 			if (!stream->next_buf)
@@ -2368,8 +2556,8 @@
 					list_del(&stream->next_buf->queue);
 					buffer = stream->next_buf;
 					v4l2_dbg(3, rkcif_debug, &dev->v4l2_dev,
-						 "stream[%d] update next_buf 0x%x\n",
-						 stream->id, buffer->buff_addr[0]);
+						 "stream[%d] update next_buf 0x%x, buf idx %d\n",
+						 stream->id, buffer->buff_addr[0], stream->next_buf->vb.vb2_buf.index);
 				}
 			}
 		}
@@ -2407,19 +2595,37 @@
 	stream->frame_phase_cache = stream->frame_phase;
 
 	if (buffer) {
+		buff_addr_y = buffer->buff_addr[RKCIF_PLANE_Y];
+		buff_addr_cbcr = buffer->buff_addr[RKCIF_PLANE_CBCR];
 		if (stream->cif_fmt_in->field == V4L2_FIELD_INTERLACED &&
 		    stream->frame_phase == CIF_CSI_FRAME1_READY) {
-			rkcif_write_register(dev, frm_addr_y,
-					     buffer->buff_addr[RKCIF_PLANE_Y] + (channel->virtual_width / 2));
-			if (stream->cif_fmt_out->fmt_type != CIF_FMT_TYPE_RAW)
-				rkcif_write_register(dev, frm_addr_uv,
-						     buffer->buff_addr[RKCIF_PLANE_CBCR] + (channel->virtual_width / 2));
+			if (channel->capture_info.mode == RKMODULE_MULTI_DEV_COMBINE_ONE) {
+				rkcif_write_buff_addr_multi_dev_combine(stream,
+									frm_addr_y,
+									frm_addr_uv,
+									buff_addr_y,
+									buff_addr_cbcr,
+									false);
+			} else {
+				rkcif_write_register(dev, frm_addr_y,
+						     buff_addr_y + (channel->virtual_width / 2));
+				if (stream->cif_fmt_out->fmt_type != CIF_FMT_TYPE_RAW)
+					rkcif_write_register(dev, frm_addr_uv,
+							     buff_addr_cbcr + (channel->virtual_width / 2));
+			}
 		} else {
-			rkcif_write_register(dev, frm_addr_y,
-					     buffer->buff_addr[RKCIF_PLANE_Y]);
-			if (stream->cif_fmt_out->fmt_type != CIF_FMT_TYPE_RAW)
-				rkcif_write_register(dev, frm_addr_uv,
-						     buffer->buff_addr[RKCIF_PLANE_CBCR]);
+			if (channel->capture_info.mode == RKMODULE_MULTI_DEV_COMBINE_ONE) {
+				rkcif_write_buff_addr_multi_dev_combine(stream,
+									frm_addr_y,
+									frm_addr_uv,
+									buff_addr_y,
+									buff_addr_cbcr,
+									false);
+			} else {
+				rkcif_write_register(dev, frm_addr_y, buff_addr_y);
+				if (stream->cif_fmt_out->fmt_type != CIF_FMT_TYPE_RAW)
+					rkcif_write_register(dev, frm_addr_uv, buff_addr_cbcr);
+			}
 		}
 		if (stream->dma_en & RKCIF_DMAEN_BY_ISP) {
 			if (stream->buf_replace_cnt < 2)
@@ -2439,12 +2645,16 @@
 			}
 			if (dbufs)
 				rkcif_s_rx_buffer(dev, dbufs);
-			stream->buf_num_toisp--;
 		}
 	} else {
 		if (stream->dma_en & RKCIF_DMAEN_BY_ISP) {
-			rkcif_write_register(dev, frm_addr_y,
-					     stream->curr_buf_toisp->dummy.dma_addr);
+			buff_addr_y = stream->curr_buf_toisp->dummy.dma_addr;
+			if (channel->capture_info.mode == RKMODULE_MULTI_DEV_COMBINE_ONE)
+				rkcif_write_buff_addr_multi_dev_combine(stream,
+									frm_addr_y, 0,
+									buff_addr_y, 0, false);
+			else
+				rkcif_write_register(dev, frm_addr_y, buff_addr_y);
 			if (stream->frame_phase == CIF_CSI_FRAME0_READY &&
 			    stream->next_buf)
 				dbuf = stream->next_buf->dbuf;
@@ -2460,7 +2670,6 @@
 				dbufs = &stream->curr_buf_toisp->dbufs;
 			}
 			rkcif_s_rx_buffer(dev, dbufs);
-			stream->buf_num_toisp--;
 			if (stream->curr_buf && stream->frame_phase == CIF_CSI_FRAME0_READY) {
 				stream->curr_buf = NULL;
 				if (stream->buf_replace_cnt)
@@ -2471,11 +2680,24 @@
 					stream->buf_replace_cnt--;
 			}
 		} else if (dummy_buf->vaddr) {
-			rkcif_write_register(dev, frm_addr_y, dummy_buf->dma_addr);
-			if (stream->cif_fmt_out->fmt_type != CIF_FMT_TYPE_RAW)
-				rkcif_write_register(dev, frm_addr_uv, dummy_buf->dma_addr);
+
+			if (channel->capture_info.mode == RKMODULE_MULTI_DEV_COMBINE_ONE) {
+				buff_addr_y = dummy_buf->dma_addr;
+				buff_addr_cbcr = dummy_buf->dma_addr;
+				rkcif_write_buff_addr_multi_dev_combine(stream,
+									frm_addr_y,
+									frm_addr_uv,
+									buff_addr_y,
+									buff_addr_cbcr,
+									true);
+			} else {
+				rkcif_write_register(dev, frm_addr_y, dummy_buf->dma_addr);
+				if (stream->cif_fmt_out->fmt_type != CIF_FMT_TYPE_RAW)
+					rkcif_write_register(dev, frm_addr_uv, dummy_buf->dma_addr);
+			}
 			dev->err_state |= (RKCIF_ERR_ID0_NOT_BUF << stream->id);
 			dev->irq_stats.not_active_buf_cnt[stream->id]++;
+
 		} else {
 			ret = -EINVAL;
 			stream->curr_buf = NULL;
@@ -2489,8 +2711,13 @@
 stop_dma:
 	if (stream->buf_replace_cnt) {
 		spin_lock_irqsave(&stream->vbq_lock, flags);
-		rkcif_write_register(dev, frm_addr_y,
-				     stream->curr_buf_toisp->dummy.dma_addr);
+		buff_addr_y = stream->curr_buf_toisp->dummy.dma_addr;
+		if (channel->capture_info.mode == RKMODULE_MULTI_DEV_COMBINE_ONE)
+			rkcif_write_buff_addr_multi_dev_combine(stream,
+								frm_addr_y, 0,
+								buff_addr_y, 0, false);
+		else
+			rkcif_write_register(dev, frm_addr_y, buff_addr_y);
 		if (stream->frame_phase == CIF_CSI_FRAME0_READY &&
 		    stream->next_buf)
 			dbuf = stream->next_buf->dbuf;
@@ -2507,15 +2734,12 @@
 		}
 		if (dbufs)
 			rkcif_s_rx_buffer(dev, dbufs);
-		stream->buf_num_toisp--;
 
 		if (stream->frame_phase == CIF_CSI_FRAME0_READY &&
 		    stream->curr_buf) {
-			list_add_tail(&stream->curr_buf->queue, &stream->buf_head);
 			stream->curr_buf = NULL;
 		} else if (stream->frame_phase == CIF_CSI_FRAME1_READY &&
 			   stream->next_buf) {
-			list_add_tail(&stream->next_buf->queue, &stream->buf_head);
 			stream->next_buf = NULL;
 		}
 		stream->buf_replace_cnt--;
@@ -2587,8 +2811,10 @@
 	struct rkcif_device *dev = stream->cifdev;
 	struct rkcif_dummy_buffer *dummy_buf = &dev->hw_dev->dummy_buf;
 	struct v4l2_mbus_config *mbus_cfg = &dev->active_sensor->mbus;
+	struct rkmodule_capture_info *capture_info = &dev->channels[stream->id].capture_info;
 	struct rkcif_buffer *buffer = NULL;
 	u32 frm_addr_y, frm_addr_uv;
+	u32 buff_addr_y, buff_addr_cbcr;
 	int channel_id = stream->id;
 	int ret = 0;
 	unsigned long flags;
@@ -2619,16 +2845,35 @@
 	}
 	spin_unlock_irqrestore(&stream->vbq_lock, flags);
 	if (buffer) {
-		rkcif_write_register(dev, frm_addr_y,
-				     buffer->buff_addr[RKCIF_PLANE_Y]);
-		if (stream->cif_fmt_out->fmt_type != CIF_FMT_TYPE_RAW)
-			rkcif_write_register(dev, frm_addr_uv,
-					     buffer->buff_addr[RKCIF_PLANE_CBCR]);
+		buff_addr_y = buffer->buff_addr[RKCIF_PLANE_Y];
+		buff_addr_cbcr = buffer->buff_addr[RKCIF_PLANE_CBCR];
+		if (capture_info->mode == RKMODULE_MULTI_DEV_COMBINE_ONE) {
+			rkcif_write_buff_addr_multi_dev_combine(stream, frm_addr_y,
+								frm_addr_uv,
+								buff_addr_y,
+								buff_addr_cbcr,
+								false);
+		} else {
+			rkcif_write_register(dev, frm_addr_y, buff_addr_y);
+			if (stream->cif_fmt_out->fmt_type != CIF_FMT_TYPE_RAW)
+				rkcif_write_register(dev, frm_addr_uv, buff_addr_cbcr);
+		}
 	} else {
 		if (dummy_buf->vaddr) {
-			rkcif_write_register(dev, frm_addr_y, dummy_buf->dma_addr);
-			if (stream->cif_fmt_out->fmt_type != CIF_FMT_TYPE_RAW)
-				rkcif_write_register(dev, frm_addr_uv, dummy_buf->dma_addr);
+			buff_addr_y = dummy_buf->dma_addr;
+			buff_addr_cbcr = dummy_buf->dma_addr;
+			if (capture_info->mode == RKMODULE_MULTI_DEV_COMBINE_ONE) {
+				rkcif_write_buff_addr_multi_dev_combine(stream,
+									frm_addr_y,
+									frm_addr_uv,
+									buff_addr_y,
+									buff_addr_cbcr,
+									true);
+			} else {
+				rkcif_write_register(dev, frm_addr_y, buff_addr_y);
+				if (stream->cif_fmt_out->fmt_type != CIF_FMT_TYPE_RAW)
+					rkcif_write_register(dev, frm_addr_uv, buff_addr_cbcr);
+			}
 		} else {
 			if (dev->chip_id < CHIP_RK3588_CIF)
 				ret = -EINVAL;
@@ -3163,6 +3408,7 @@
 				  struct csi_channel_info *channel)
 {
 	struct rkcif_device *dev = stream->cifdev;
+	struct sditf_priv *priv = dev->sditf[0];
 	const struct cif_output_fmt *fmt;
 	u32 fourcc;
 	int vc = dev->channels[stream->id].vc;
@@ -3187,7 +3433,7 @@
 			channel->crop_st_x = stream->crop[CROP_SRC_ACT].left;
 
 		channel->crop_st_y = stream->crop[CROP_SRC_ACT].top;
-		if (dev->sditf_cnt > 1 && dev->sditf_cnt <= RKCIF_MAX_SDITF)
+		if (priv && priv->is_combine_mode && dev->sditf_cnt <= RKCIF_MAX_SDITF)
 			channel->crop_st_y *= dev->sditf_cnt;
 		channel->width = stream->crop[CROP_SRC_ACT].width;
 		channel->height = stream->crop[CROP_SRC_ACT].height;
@@ -3197,7 +3443,7 @@
 		channel->crop_en = 0;
 	}
 
-	if (dev->sditf_cnt > 1 && dev->sditf_cnt <= RKCIF_MAX_SDITF)
+	if (priv && priv->is_combine_mode && dev->sditf_cnt <= RKCIF_MAX_SDITF)
 		channel->height *= dev->sditf_cnt;
 
 	fmt = rkcif_find_output_fmt(stream, stream->pixm.pixelformat);
@@ -3207,6 +3453,8 @@
 		return -EINVAL;
 	}
 
+	if (channel->capture_info.mode == RKMODULE_MULTI_DEV_COMBINE_ONE)
+		channel->width /=  channel->capture_info.multi_dev.dev_num;
 	/*
 	 * for mipi or lvds, when enable compact, the virtual width of raw10/raw12
 	 * needs aligned with :ALIGN(bits_per_pixel * width / 8, 8), if enable 16bit mode
@@ -3216,9 +3464,19 @@
 	if (fmt->fmt_type == CIF_FMT_TYPE_RAW && stream->is_compact &&
 	    fmt->csi_fmt_val != CSI_WRDDR_TYPE_RGB888 &&
 	    fmt->csi_fmt_val != CSI_WRDDR_TYPE_RGB565) {
-		channel->virtual_width = ALIGN(channel->width * fmt->raw_bpp / 8, 256);
+		if (channel->capture_info.mode == RKMODULE_MULTI_DEV_COMBINE_ONE) {
+			channel->virtual_width = ALIGN(channel->width * 2 * fmt->raw_bpp / 8, 256);
+			channel->left_virtual_width = channel->width * fmt->raw_bpp / 8;
+		} else {
+			channel->virtual_width = ALIGN(channel->width * fmt->raw_bpp / 8, 256);
+		}
 	} else {
-		channel->virtual_width = ALIGN(channel->width * fmt->bpp[0] / 8, 8);
+		if (channel->capture_info.mode == RKMODULE_MULTI_DEV_COMBINE_ONE) {
+			channel->virtual_width = ALIGN(channel->width * 2 * fmt->bpp[0] / 8, 8);
+			channel->left_virtual_width = ALIGN(channel->width * fmt->bpp[0] / 8, 8);
+		} else {
+			channel->virtual_width = ALIGN(channel->width * fmt->bpp[0] / 8, 8);
+		}
 	}
 
 	if (channel->fmt_val == CSI_WRDDR_TYPE_RGB888 || channel->fmt_val == CSI_WRDDR_TYPE_RGB565)
@@ -3237,6 +3495,8 @@
 			channel->width *= 2;
 		}
 		channel->virtual_width *= 2;
+		if (channel->capture_info.mode == RKMODULE_MULTI_DEV_COMBINE_ONE)
+			channel->left_virtual_width *= 2;
 	}
 	if (stream->cif_fmt_in->field == V4L2_FIELD_INTERLACED) {
 		channel->virtual_width *= 2;
@@ -3585,15 +3845,18 @@
 
 /*config reg for rk3588*/
 static int rkcif_csi_channel_set_v1(struct rkcif_stream *stream,
-				    struct csi_channel_info *channel,
-				    enum v4l2_mbus_type mbus_type, unsigned int mode)
+				       struct csi_channel_info *channel,
+				       enum v4l2_mbus_type mbus_type, unsigned int mode,
+				       int index)
 {
 	unsigned int val = 0x0;
 	struct rkcif_device *dev = stream->cifdev;
 	struct rkcif_stream *detect_stream = &dev->stream[0];
 	struct sditf_priv *priv = dev->sditf[0];
+	struct rkmodule_capture_info *capture_info = &channel->capture_info;
 	unsigned int wait_line = 0x3fff;
 	unsigned int dma_en = 0;
+	int offset = 0;
 
 	if (channel->id >= 4)
 		return -EINVAL;
@@ -3609,21 +3872,28 @@
 				 CSI_DMA_END_INTSTAT(channel->id) |
 				 CSI_LINE_INTSTAT_V1(channel->id)));
 
-	rkcif_write_register_or(dev, CIF_REG_MIPI_LVDS_INTEN,
-				CSI_START_INTEN(channel->id));
+	if (!(capture_info->mode == RKMODULE_MULTI_DEV_COMBINE_ONE &&
+	      index < capture_info->multi_dev.dev_num - 1)) {
 
-	if (priv && priv->mode.rdbk_mode && detect_stream->is_line_wake_up) {
 		rkcif_write_register_or(dev, CIF_REG_MIPI_LVDS_INTEN,
-					CSI_LINE_INTEN_RK3588(channel->id));
-		wait_line = dev->wait_line;
-	}
-	rkcif_write_register(dev, CIF_REG_MIPI_LVDS_LINE_INT_NUM_ID0_1,
-			     wait_line << 16 | wait_line);
-	rkcif_write_register(dev, CIF_REG_MIPI_LVDS_LINE_INT_NUM_ID2_3,
-			     wait_line << 16 | wait_line);
+					CSI_START_INTEN(channel->id));
 
-	rkcif_write_register_or(dev, CIF_REG_MIPI_LVDS_INTEN,
-				CSI_DMA_END_INTEN(channel->id));
+		if (priv && priv->mode.rdbk_mode && detect_stream->is_line_wake_up) {
+			rkcif_write_register_or(dev, CIF_REG_MIPI_LVDS_INTEN,
+						CSI_LINE_INTEN_RK3588(channel->id));
+			wait_line = dev->wait_line;
+		}
+		rkcif_write_register(dev, CIF_REG_MIPI_LVDS_LINE_INT_NUM_ID0_1,
+				     wait_line << 16 | wait_line);
+		rkcif_write_register(dev, CIF_REG_MIPI_LVDS_LINE_INT_NUM_ID2_3,
+				     wait_line << 16 | wait_line);
+
+		rkcif_write_register_or(dev, CIF_REG_MIPI_LVDS_INTEN,
+					CSI_DMA_END_INTEN(channel->id));
+
+		rkcif_write_register_or(dev, CIF_REG_MIPI_LVDS_INTEN,
+					CSI_ALL_ERROR_INTEN_V1);
+	}
 	if (stream->cifdev->id_use_cnt == 0) {
 		val = CIF_MIPI_LVDS_SW_PRESS_VALUE_RK3588(0x3) |
 			CIF_MIPI_LVDS_SW_PRESS_ENABLE |
@@ -3637,24 +3907,25 @@
 		else
 			val |= CIF_MIPI_LVDS_SW_SEL_LVDS_RV1106;
 		rkcif_write_register(dev, CIF_REG_MIPI_LVDS_CTRL, val);
-
-		rkcif_write_register_or(dev, CIF_REG_MIPI_LVDS_INTEN,
-					CSI_ALL_ERROR_INTEN_V1);
 	}
 #if IS_ENABLED(CONFIG_CPU_RV1106)
 	if (channel->id == 1)
 		rv1106_sdmmc_get_lock();
 #endif
+	if (capture_info->mode == RKMODULE_MULTI_DEV_COMBINE_ONE &&
+	    priv && priv->mode.rdbk_mode == RKISP_VICAP_ONLINE &&
+	    (dev->hdr.hdr_mode == NO_HDR ||
+	     (dev->hdr.hdr_mode == HDR_X2 && stream->id == 1) ||
+	     (dev->hdr.hdr_mode == HDR_X3 && stream->id == 2)))
+		offset = channel->capture_info.multi_dev.pixel_offset;
+
 	rkcif_write_register(dev, get_reg_index_of_id_ctrl1(channel->id),
-			     channel->width | (channel->height << 16));
+			     (channel->width + offset) | (channel->height << 16));
 
 #if IS_ENABLED(CONFIG_CPU_RV1106)
 	if (channel->id == 1)
 		rv1106_sdmmc_put_lock();
 #endif
-
-	rkcif_write_register(dev, get_reg_index_of_frm0_y_vlw(channel->id),
-			     channel->virtual_width);
 
 	if (channel->crop_en)
 		rkcif_write_register(dev, get_reg_index_of_id_crop_start(channel->id),
@@ -3673,6 +3944,17 @@
 		rkcif_assign_new_buffer_pingpong_rockit(stream,
 							RKCIF_YUV_ADDR_STATE_INIT,
 							channel->id);
+
+	if (capture_info->mode == RKMODULE_MULTI_DEV_COMBINE_ONE &&
+	    index == (capture_info->multi_dev.dev_num - 1) &&
+	    priv && priv->mode.rdbk_mode != RKISP_VICAP_ONLINE)
+		rkcif_write_register(dev, get_reg_index_of_id_crop_start(channel->id),
+				     channel->crop_st_y << 16 |
+				     (channel->crop_st_x + capture_info->multi_dev.pixel_offset));
+
+	rkcif_write_register(dev, get_reg_index_of_frm0_y_vlw(channel->id),
+			     channel->virtual_width);
+
 	if (stream->lack_buf_cnt == 2)
 		stream->dma_en = 0;
 
@@ -3735,7 +4017,24 @@
 	}
 	if (dev->chip_id >= CHIP_RV1106_CIF)
 		rkcif_modify_frame_skip_config(stream);
-	stream->cifdev->id_use_cnt++;
+	if (capture_info->mode == RKMODULE_MULTI_DEV_COMBINE_ONE) {
+		if (index == (capture_info->multi_dev.dev_num - 1))
+			stream->cifdev->id_use_cnt++;
+	} else {
+		stream->cifdev->id_use_cnt++;
+	}
+	if (!(capture_info->mode == RKMODULE_MULTI_DEV_COMBINE_ONE &&
+	      index < capture_info->multi_dev.dev_num - 1)) {
+		if (mode == RKCIF_STREAM_MODE_CAPTURE)
+			rkcif_assign_new_buffer_pingpong(stream,
+						 RKCIF_YUV_ADDR_STATE_INIT,
+						 channel->id);
+		else if (mode == RKCIF_STREAM_MODE_TOISP ||
+			 mode == RKCIF_STREAM_MODE_TOISP_RDBK)
+			rkcif_assign_new_buffer_pingpong_toisp(stream,
+						       RKCIF_YUV_ADDR_STATE_INIT,
+						       channel->id);
+	}
 	return 0;
 }
 
@@ -3747,6 +4046,7 @@
 	enum v4l2_mbus_type mbus_type = active_sensor->mbus.type;
 	struct csi_channel_info *channel;
 	u32 ret = 0;
+	int i;
 
 	if (stream->state < RKCIF_STATE_STREAMING) {
 		stream->frame_idx = 0;
@@ -3785,10 +4085,18 @@
 		} else if (mode == RKCIF_STREAM_MODE_ROCKIT) {
 			stream->dma_en |= RKCIF_DMAEN_BY_ROCKIT;
 		}
-		if (stream->cifdev->chip_id < CHIP_RK3588_CIF)
+		if (stream->cifdev->chip_id < CHIP_RK3588_CIF) {
 			rkcif_csi_channel_set(stream, channel, mbus_type);
-		else
-			rkcif_csi_channel_set_v1(stream, channel, mbus_type, mode);
+		} else {
+			if (channel->capture_info.mode == RKMODULE_MULTI_DEV_COMBINE_ONE) {
+				for (i = 0; i < channel->capture_info.multi_dev.dev_num; i++) {
+					dev->csi_host_idx = channel->capture_info.multi_dev.dev_idx[i];
+					rkcif_csi_channel_set_v1(stream, channel, mbus_type, mode, i);
+				}
+			} else {
+				rkcif_csi_channel_set_v1(stream, channel, mbus_type, mode, 0);
+			}
+		}
 	} else {
 		if (stream->cifdev->chip_id >= CHIP_RK3588_CIF) {
 			if (mode == RKCIF_STREAM_MODE_CAPTURE) {
@@ -3828,7 +4136,9 @@
 	struct v4l2_mbus_config *mbus_cfg = &cif_dev->active_sensor->mbus;
 	u32 val;
 	int id;
+	int i = 0;
 
+	stream->cifdev->id_use_cnt--;
 	if (mbus_cfg->type == V4L2_MBUS_CSI2_DPHY ||
 	    mbus_cfg->type == V4L2_MBUS_CSI2_CPHY ||
 	    mbus_cfg->type == V4L2_MBUS_CCP2) {
@@ -3840,7 +4150,14 @@
 		else
 			val &= ~LVDS_ENABLE_CAPTURE;
 
-		rkcif_write_register(cif_dev, get_reg_index_of_id_ctrl0(id), val);
+		if (cif_dev->channels[id].capture_info.mode == RKMODULE_MULTI_DEV_COMBINE_ONE) {
+			for (i = 0; i < cif_dev->channels[id].capture_info.multi_dev.dev_num; i++) {
+				cif_dev->csi_host_idx = cif_dev->channels[id].capture_info.multi_dev.dev_idx[i];
+				rkcif_write_register(cif_dev, get_reg_index_of_id_ctrl0(id), val);
+			}
+		} else {
+			rkcif_write_register(cif_dev, get_reg_index_of_id_ctrl0(id), val);
+		}
 
 		rkcif_write_register_or(cif_dev, CIF_REG_MIPI_LVDS_INTSTAT,
 					CSI_START_INTSTAT(id) |
@@ -3859,8 +4176,16 @@
 			if (stream->cifdev->id_use_cnt == 0) {
 				rkcif_write_register_and(cif_dev, CIF_REG_MIPI_LVDS_INTEN,
 						~CSI_ALL_ERROR_INTEN_V1);
-				rkcif_write_register_and(cif_dev, CIF_REG_MIPI_LVDS_CTRL,
-						~CSI_ENABLE_CAPTURE);
+				if (cif_dev->channels[id].capture_info.mode == RKMODULE_MULTI_DEV_COMBINE_ONE) {
+					for (i = 0; i < cif_dev->channels[id].capture_info.multi_dev.dev_num; i++) {
+						cif_dev->csi_host_idx = cif_dev->channels[id].capture_info.multi_dev.dev_idx[i];
+						rkcif_write_register_and(cif_dev, CIF_REG_MIPI_LVDS_CTRL,
+									 ~CSI_ENABLE_CAPTURE);
+					}
+				} else {
+					rkcif_write_register_and(cif_dev, CIF_REG_MIPI_LVDS_CTRL,
+								 ~CSI_ENABLE_CAPTURE);
+				}
 			}
 		}
 
@@ -3876,7 +4201,6 @@
 				rkcif_config_dvp_pin(cif_dev, false);
 		}
 	}
-	stream->cifdev->id_use_cnt--;
 	stream->state = RKCIF_STATE_READY;
 	stream->dma_en = 0;
 }
@@ -3955,7 +4279,7 @@
 		plane_fmt = &pixm->plane_fmt[i];
 		sizes[i] = plane_fmt->sizeimage / height * h;
 	}
-
+	stream->total_buf_num = *num_buffers;
 	v4l2_dbg(1, rkcif_debug, &dev->v4l2_dev, "%s count %d, size %d, extended(%d, %d)\n",
 		 v4l2_type_names[queue->type], *num_buffers, sizes[0],
 		 is_extended, extend_line->is_extended);
@@ -3973,6 +4297,8 @@
 	u32 frm_addr_y = 0, frm_addr_uv = 0;
 	u32 frm0_addr_y = 0, frm0_addr_uv = 0;
 	u32 frm1_addr_y = 0, frm1_addr_uv = 0;
+	u32 buff_addr_y = 0, buff_addr_cbcr = 0;
+	struct rkmodule_capture_info *capture_info = &dev->channels[channel_id].capture_info;
 	unsigned long flags;
 	int frame_phase = 0;
 	bool is_dual_update_buf = false;
@@ -4039,22 +4365,51 @@
 			}
 			if (buffer) {
 				if (is_dual_update_buf) {
-					rkcif_write_register(dev, frm0_addr_y,
-							     buffer->buff_addr[RKCIF_PLANE_Y]);
-					if (stream->cif_fmt_out->fmt_type != CIF_FMT_TYPE_RAW)
-						rkcif_write_register(dev, frm0_addr_uv,
-								     buffer->buff_addr[RKCIF_PLANE_CBCR]);
-					rkcif_write_register(dev, frm1_addr_y,
-							     buffer->buff_addr[RKCIF_PLANE_Y]);
-					if (stream->cif_fmt_out->fmt_type != CIF_FMT_TYPE_RAW)
-						rkcif_write_register(dev, frm1_addr_uv,
-								     buffer->buff_addr[RKCIF_PLANE_CBCR]);
+					buff_addr_y = buffer->buff_addr[RKCIF_PLANE_Y];
+					buff_addr_cbcr = buffer->buff_addr[RKCIF_PLANE_CBCR];
+					if (capture_info->mode == RKMODULE_MULTI_DEV_COMBINE_ONE) {
+						rkcif_write_buff_addr_multi_dev_combine(stream,
+											frm0_addr_y,
+											frm0_addr_uv,
+											buff_addr_y,
+											buff_addr_cbcr,
+											false);
+						rkcif_write_buff_addr_multi_dev_combine(stream,
+											frm1_addr_y,
+											frm1_addr_uv,
+											buff_addr_y,
+											buff_addr_cbcr,
+											false);
+					} else {
+						rkcif_write_register(dev, frm0_addr_y, buff_addr_y);
+						if (stream->cif_fmt_out->fmt_type != CIF_FMT_TYPE_RAW)
+							rkcif_write_register(dev,
+									     frm0_addr_uv,
+									     buff_addr_cbcr);
+						rkcif_write_register(dev, frm1_addr_y, buff_addr_y);
+						if (stream->cif_fmt_out->fmt_type != CIF_FMT_TYPE_RAW)
+							rkcif_write_register(dev,
+									     frm1_addr_uv,
+									     buff_addr_cbcr);
+					}
 				} else {
-					rkcif_write_register(dev, frm_addr_y,
-							     buffer->buff_addr[RKCIF_PLANE_Y]);
-					if (stream->cif_fmt_out->fmt_type != CIF_FMT_TYPE_RAW)
-						rkcif_write_register(dev, frm_addr_uv,
-								     buffer->buff_addr[RKCIF_PLANE_CBCR]);
+
+					buff_addr_y = buffer->buff_addr[RKCIF_PLANE_Y];
+					buff_addr_cbcr = buffer->buff_addr[RKCIF_PLANE_CBCR];
+					if (capture_info->mode == RKMODULE_MULTI_DEV_COMBINE_ONE) {
+						rkcif_write_buff_addr_multi_dev_combine(stream,
+											frm_addr_y,
+											frm_addr_uv,
+											buff_addr_y,
+											buff_addr_cbcr,
+											false);
+					} else {
+						rkcif_write_register(dev, frm_addr_y, buff_addr_y);
+						if (stream->cif_fmt_out->fmt_type != CIF_FMT_TYPE_RAW)
+							rkcif_write_register(dev,
+									     frm_addr_uv,
+									     buff_addr_cbcr);
+					}
 				}
 			}
 		} else {
@@ -4065,11 +4420,17 @@
 				if (stream->frame_phase_cache == CIF_CSI_FRAME0_READY) {
 					stream->curr_buf = list_first_entry(&stream->buf_head,
 									    struct rkcif_buffer, queue);
+					v4l2_dbg(3, rkcif_debug, &dev->v4l2_dev,
+						 "%s %d, stream[%d] buf idx %d\n",
+						 __func__, __LINE__, stream->id, stream->curr_buf->vb.vb2_buf.index);
 					if (stream->curr_buf)
 						list_del(&stream->curr_buf->queue);
 				} else if (stream->frame_phase_cache == CIF_CSI_FRAME1_READY) {
 					stream->next_buf = list_first_entry(&stream->buf_head,
 									    struct rkcif_buffer, queue);
+					v4l2_dbg(4, rkcif_debug, &dev->v4l2_dev,
+						 "%s %d, stream[%d] buf idx %d\n",
+						 __func__, __LINE__, stream->id, stream->next_buf->vb.vb2_buf.index);
 					if (stream->next_buf)
 						list_del(&stream->next_buf->queue);
 				}
@@ -4098,6 +4459,7 @@
 		}
 		if (stream->lack_buf_cnt)
 			stream->lack_buf_cnt--;
+
 	} else {
 		v4l2_info(&dev->v4l2_dev, "%s %d, state %d, curr_buf %p, next_buf %p\n",
 			  __func__, __LINE__, stream->state, stream->curr_buf, stream->next_buf);
@@ -4200,6 +4562,7 @@
 	v4l2_dbg(3, rkcif_debug, &stream->cifdev->v4l2_dev,
 		 "stream[%d] buf queue, index: %d, dma_addr 0x%x\n",
 		 stream->id, vb->index, cifbuf->buff_addr[0]);
+	atomic_inc(&stream->buf_cnt);
 }
 
 void rkcif_free_rx_buf(struct rkcif_stream *stream, int buf_num)
@@ -4229,6 +4592,8 @@
 			rkcif_free_buffer(dev, &buf->dummy);
 		else
 			list_add_tail(&buf->list_free, &priv->buf_free_list);
+		atomic_dec(&stream->buf_cnt);
+		stream->total_buf_num--;
 	}
 
 	if (dev->is_thunderboot) {
@@ -4322,7 +4687,6 @@
 		if (priv && priv->mode.rdbk_mode == RKISP_VICAP_ONLINE && i == 0) {
 			buf->dbufs.is_first = true;
 			rkcif_s_rx_buffer(dev, &buf->dbufs);
-			stream->buf_num_toisp--;
 		}
 		i++;
 		if (!dev->is_thunderboot && i >= buf_num) {
@@ -4338,7 +4702,8 @@
 			(u64)dummy->dma_addr, pixm->plane_fmt[0].sizeimage);
 	}
 	if (priv->buf_num) {
-		stream->buf_num_toisp = priv->buf_num;
+		stream->total_buf_num = priv->buf_num;
+		atomic_set(&stream->buf_cnt, priv->buf_num);
 		return 0;
 	} else {
 		return -EINVAL;
@@ -4352,6 +4717,7 @@
 	struct rkcif_dummy_buffer *dummy_buf = &hw->dummy_buf;
 	struct rkcif_device *tmp_dev = NULL;
 	struct v4l2_subdev_frame_interval_enum fie;
+	struct v4l2_subdev_format fmt;
 	u32 max_size = 0;
 	u32 size = 0;
 	int ret = 0;
@@ -4386,6 +4752,21 @@
 			continue;
 		}
 	}
+
+	if (max_size == 0 && dev->terminal_sensor.sd) {
+		fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
+		ret = v4l2_subdev_call(dev->terminal_sensor.sd,
+				       pad, get_fmt, NULL, &fmt);
+		if (!ret) {
+			if (fmt.format.code == MEDIA_BUS_FMT_RGB888_1X24)
+				size = fmt.format.width  * fmt.format.height * 3;
+			else
+				size = fmt.format.width * fmt.format.height * 2;
+			if (size > max_size)
+				max_size = size;
+		}
+	}
+
 	dummy_buf->size = max_size;
 
 	dummy_buf->is_need_vaddr = true;
@@ -4621,14 +5002,16 @@
 	} else if (mode == RKCIF_STREAM_MODE_CAPTURE && stream->dma_en & RKCIF_DMAEN_BY_VICAP) {
 		//only stop dma
 		stream->to_stop_dma = RKCIF_DMAEN_BY_VICAP;
+		stream->is_wait_dma_stop = true;
 		wait_event_timeout(stream->wq_stopped,
-				   stream->to_stop_dma != RKCIF_DMAEN_BY_VICAP,
+				   !stream->is_wait_dma_stop,
 				   msecs_to_jiffies(1000));
 	} else if (mode == RKCIF_STREAM_MODE_TOISP && stream->dma_en & RKCIF_DMAEN_BY_VICAP) {
 		//only stop dma
 		stream->to_stop_dma = RKCIF_DMAEN_BY_ISP;
+		stream->is_wait_dma_stop = true;
 		wait_event_timeout(stream->wq_stopped,
-				   stream->to_stop_dma != RKCIF_DMAEN_BY_ISP,
+				   !stream->is_wait_dma_stop,
 				   msecs_to_jiffies(1000));
 	}
 	if ((mode & RKCIF_STREAM_MODE_CAPTURE) == RKCIF_STREAM_MODE_CAPTURE) {
@@ -4664,6 +5047,8 @@
 				vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
 			}
 		}
+		stream->total_buf_num = 0;
+		atomic_set(&stream->buf_cnt, 0);
 		stream->lack_buf_cnt = 0;
 		stream->dma_en &= ~RKCIF_DMAEN_BY_VICAP;
 	}
@@ -4704,10 +5089,12 @@
 			dev->wait_line = 0;
 			stream->is_line_wake_up = false;
 		}
-		tasklet_disable(&stream->vb_done_tasklet);
+		if (can_reset && hw_dev->dummy_buf.vaddr)
+			rkcif_destroy_dummy_buf(stream);
 	}
-	if (can_reset && hw_dev->dummy_buf.vaddr)
-		rkcif_destroy_dummy_buf(stream);
+	if (mode == RKCIF_STREAM_MODE_CAPTURE)
+		tasklet_disable(&stream->vb_done_tasklet);
+
 	stream->cur_stream_mode &= ~mode;
 	INIT_LIST_HEAD(&stream->vb_done_list);
 	v4l2_info(&dev->v4l2_dev, "stream[%d] stopping finished, dma_en 0x%x\n", stream->id, stream->dma_en);
@@ -4981,6 +5368,7 @@
 			stream->crop_mask |= CROP_SRC_SENSOR_MASK;
 			dev->terminal_sensor.selection = input_sel;
 		} else {
+			stream->crop_mask &= ~CROP_SRC_SENSOR_MASK;
 			dev->terminal_sensor.selection.r = dev->terminal_sensor.raw_rect;
 		}
 	}
@@ -5001,8 +5389,10 @@
 			stream->crop[CROP_SRC_ACT].top = stream->crop[CROP_SRC_USR].top +
 							  stream->crop[CROP_SRC_SENSOR].top;
 		}
-	} else {
+	} else if (stream->crop_mask & CROP_SRC_SENSOR_MASK) {
 		stream->crop[CROP_SRC_ACT] = stream->crop[CROP_SRC_SENSOR];
+	} else {
+		stream->crop[CROP_SRC_ACT] = dev->terminal_sensor.raw_rect;
 	}
 }
 
@@ -5018,9 +5408,9 @@
 	struct v4l2_rect input, *crop;
 
 	if (dev->terminal_sensor.sd) {
-		stream->cif_fmt_in = get_input_fmt(dev->terminal_sensor.sd,
-						   &input, stream->id,
-						   &dev->channels[stream->id]);
+		stream->cif_fmt_in = rkcif_get_input_fmt(dev,
+							 &input, stream->id,
+							 &dev->channels[stream->id]);
 		if (!stream->cif_fmt_in) {
 			v4l2_err(v4l2_dev, "Input fmt is invalid\n");
 			return -EINVAL;
@@ -5562,6 +5952,7 @@
 {
 	struct rkcif_hw *hw = cifdev->hw_dev;
 	struct rkcif_device *dev;
+	struct sditf_priv *priv;
 	int i = 0, j = 0;
 	int ret = 0;
 	int count = 0;
@@ -5610,12 +6001,12 @@
 		else
 			sync_cfg.group = 0;
 	}
+	cifdev->sync_cfg = sync_cfg;
 	if (sync_cfg.type == NO_SYNC_MODE ||
 	    hw->sync_config[sync_cfg.group].is_attach) {
 		mutex_unlock(&hw->dev_lock);
 		return;
 	}
-	cifdev->sync_cfg = sync_cfg;
 
 	sync_config = &hw->sync_config[sync_cfg.group];
 	memset(sync_config, 0, sizeof(struct rkcif_multi_sync_config));
@@ -5639,26 +6030,31 @@
 			else
 				sync_cfg.group = 0;
 		} else {
-			for (j = 0; j < dev->sditf_cnt; j++) {
-				ret |= v4l2_subdev_call(dev->sditf[j]->sensor_sd,
-						core, ioctl,
-						RKMODULE_GET_SYNC_MODE,
-						&sync_type);
-				if (!ret && sync_type)
-					break;
+			priv = dev->sditf[0];
+			if (priv && priv->is_combine_mode && dev->sditf_cnt <= RKCIF_MAX_SDITF) {
+				for (j = 0; j < dev->sditf_cnt; j++) {
+					ret |= v4l2_subdev_call(dev->sditf[j]->sensor_sd,
+							core, ioctl,
+							RKMODULE_GET_SYNC_MODE,
+							&sync_type);
+					if (!ret && sync_type) {
+						priv = dev->sditf[j];
+						break;
+					}
+				}
+				if (!ret)
+					sync_cfg.type = sync_type;
+				else
+					sync_cfg.type = NO_SYNC_MODE;
+				ret = v4l2_subdev_call(priv->sensor_sd,
+						       core, ioctl,
+						       RKMODULE_GET_GROUP_ID,
+						       &sync_group);
+				if (!ret && sync_group < RKCIF_MAX_GROUP)
+					sync_cfg.group = sync_group;
+				else
+					sync_cfg.group = 0;
 			}
-			if (!ret)
-				sync_cfg.type = sync_type;
-			else
-				sync_cfg.type = NO_SYNC_MODE;
-			ret = v4l2_subdev_call(dev->sditf[j]->sensor_sd,
-					       core, ioctl,
-					       RKMODULE_GET_GROUP_ID,
-					       &sync_group);
-			if (!ret && sync_group < RKCIF_MAX_GROUP)
-				sync_cfg.group = sync_group;
-			else
-				sync_cfg.group = 0;
 		}
 		if (sync_cfg.group == cifdev->sync_cfg.group) {
 			if (sync_cfg.type == EXTERNAL_MASTER_MODE) {
@@ -5681,6 +6077,11 @@
 				sync_config->sync_mask |= BIT(dev->csi_host_idx);
 			}
 			dev->sync_cfg = sync_cfg;
+		} else {
+			ret = v4l2_subdev_call(dev->terminal_sensor.sd,
+					       core, ioctl,
+					       RKMODULE_GET_SYNC_MODE,
+					       &sync_type);
 		}
 	}
 	if (sync_config->int_master.count == 1) {
@@ -5714,15 +6115,18 @@
 	struct rkcif_hw *hw_dev = dev->hw_dev;
 	struct v4l2_device *v4l2_dev = &dev->v4l2_dev;
 	struct rkcif_sensor_info *sensor_info = dev->active_sensor;
-	struct rkcif_sensor_info *terminal_sensor = &dev->terminal_sensor;
+	struct rkcif_sensor_info *terminal_sensor = NULL;
 	struct rkmodule_hdr_cfg hdr_cfg;
+	struct rkcif_csi_info csi_info = {0};
 	int rkmodule_stream_seq = RKMODULE_START_STREAM_DEFAULT;
 	int ret;
+	int i = 0;
 
 	v4l2_info(&dev->v4l2_dev, "stream[%d] start streaming\n", stream->id);
 
 	rkcif_attach_sync_mode(dev);
 	mutex_lock(&dev->stream_lock);
+
 	if ((stream->cur_stream_mode & RKCIF_STREAM_MODE_CAPTURE) == mode) {
 		ret = -EBUSY;
 		v4l2_err(v4l2_dev, "stream in busy state\n");
@@ -5735,7 +6139,7 @@
 	else
 		stream->is_line_inten = false;
 
-	if (dev->active_sensor) {
+	if (!dev->active_sensor) {
 		ret = rkcif_update_sensor_info(stream);
 		if (ret < 0) {
 			v4l2_err(v4l2_dev,
@@ -5744,7 +6148,7 @@
 			goto out;
 		}
 	}
-
+	terminal_sensor = &dev->terminal_sensor;
 	if (terminal_sensor->sd) {
 		ret = v4l2_subdev_call(terminal_sensor->sd,
 				       core, ioctl,
@@ -5775,6 +6179,39 @@
 		goto destroy_buf;
 
 	mutex_lock(&hw_dev->dev_lock);
+	if (atomic_read(&dev->pipe.stream_cnt) == 0 &&
+	    dev->active_sensor &&
+	    (dev->active_sensor->mbus.type == V4L2_MBUS_CSI2_DPHY ||
+	     dev->active_sensor->mbus.type == V4L2_MBUS_CSI2_CPHY ||
+	     dev->active_sensor->mbus.type == V4L2_MBUS_CCP2)) {
+		if (dev->channels[0].capture_info.mode == RKMODULE_MULTI_DEV_COMBINE_ONE) {
+			csi_info.csi_num = dev->channels[0].capture_info.multi_dev.dev_num;
+			if (csi_info.csi_num > RKCIF_MAX_CSI_NUM) {
+				v4l2_err(v4l2_dev,
+					 "csi num %d, max %d\n",
+					 csi_info.csi_num, RKCIF_MAX_CSI_NUM);
+				goto out;
+			}
+			for (i = 0; i < csi_info.csi_num; i++) {
+				csi_info.csi_idx[i] = dev->channels[0].capture_info.multi_dev.dev_idx[i];
+				if (dev->hw_dev->is_rk3588s2)
+					v4l2_info(v4l2_dev, "rk3588s2 combine mode attach to mipi%d\n",
+						  csi_info.csi_idx[i]);
+			}
+		} else {
+			csi_info.csi_num = 1;
+			dev->csi_host_idx = dev->csi_host_idx_def;
+			csi_info.csi_idx[0] = dev->csi_host_idx;
+		}
+		ret = v4l2_subdev_call(dev->active_sensor->sd,
+				       core, ioctl,
+				       RKCIF_CMD_SET_CSI_IDX,
+				       &csi_info);
+		if (ret)
+			v4l2_err(&dev->v4l2_dev, "set csi idx %d fail\n", dev->csi_host_idx);
+
+	}
+
 	if (((dev->active_sensor && dev->active_sensor->mbus.type == V4L2_MBUS_BT656) ||
 	     dev->is_use_dummybuf) &&
 	    (!dev->hw_dev->dummy_buf.vaddr) &&
@@ -5788,8 +6225,10 @@
 	}
 	mutex_unlock(&hw_dev->dev_lock);
 
-	if (stream->cur_stream_mode == RKCIF_STREAM_MODE_NONE) {
+	if (mode == RKCIF_STREAM_MODE_CAPTURE)
 		tasklet_enable(&stream->vb_done_tasklet);
+
+	if (stream->cur_stream_mode == RKCIF_STREAM_MODE_NONE) {
 		ret = dev->pipe.open(&dev->pipe, &node->vdev.entity, true);
 		if (ret < 0) {
 			v4l2_err(v4l2_dev, "open cif pipeline failed %d\n",
@@ -5807,7 +6246,7 @@
 		    rkmodule_stream_seq == RKMODULE_START_STREAM_FRONT) {
 			ret = dev->pipe.set_stream(&dev->pipe, true);
 			if (ret < 0)
-				goto runtime_put;
+				goto destroy_buf;
 		}
 	}
 	if (dev->chip_id >= CHIP_RK1808_CIF) {
@@ -5823,7 +6262,7 @@
 	}
 
 	if (ret < 0)
-		goto runtime_put;
+		goto destroy_buf;
 
 	if (stream->cur_stream_mode == RKCIF_STREAM_MODE_NONE) {
 		ret = media_pipeline_start(&node->vdev.entity, &dev->pipe.pipe);
@@ -5869,15 +6308,19 @@
 	rkcif_stream_stop(stream);
 pipe_stream_off:
 	dev->pipe.set_stream(&dev->pipe, false);
-runtime_put:
-	pm_runtime_put_sync(dev->dev);
+
 destroy_buf:
-	if (stream->next_buf)
-		vb2_buffer_done(&stream->next_buf->vb.vb2_buf,
-				VB2_BUF_STATE_QUEUED);
+	if (mode == RKCIF_STREAM_MODE_CAPTURE)
+		tasklet_disable(&stream->vb_done_tasklet);
 	if (stream->curr_buf)
-		vb2_buffer_done(&stream->curr_buf->vb.vb2_buf,
-				VB2_BUF_STATE_QUEUED);
+		list_add_tail(&stream->curr_buf->queue, &stream->buf_head);
+	if (stream->next_buf &&
+	    stream->next_buf != stream->curr_buf)
+		list_add_tail(&stream->next_buf->queue, &stream->buf_head);
+
+	stream->curr_buf = NULL;
+	stream->next_buf = NULL;
+	atomic_set(&stream->buf_cnt, 0);
 	while (!list_empty(&stream->buf_head)) {
 		struct rkcif_buffer *buf;
 
@@ -5942,6 +6385,7 @@
 		  bool try)
 {
 	struct rkcif_device *dev = stream->cifdev;
+	struct sditf_priv *priv = dev->sditf[0];
 	const struct cif_output_fmt *fmt;
 	const struct cif_input_fmt *cif_fmt_in = NULL;
 	struct v4l2_rect input_rect;
@@ -5949,6 +6393,7 @@
 	u32 xsubs = 1, ysubs = 1, i;
 	struct rkmodule_hdr_cfg hdr_cfg;
 	struct rkcif_extend_info *extend_line = &stream->extend_line;
+	struct csi_channel_info *channel_info = &dev->channels[stream->id];
 	int ret;
 
 	for (i = 0; i < RKCIF_MAX_PLANE; i++)
@@ -5962,9 +6407,9 @@
 	input_rect.height = RKCIF_DEFAULT_HEIGHT;
 
 	if (dev->terminal_sensor.sd) {
-		cif_fmt_in = get_input_fmt(dev->terminal_sensor.sd,
-					   &input_rect, stream->id,
-					   &dev->channels[stream->id]);
+		cif_fmt_in = rkcif_get_input_fmt(dev,
+						 &input_rect, stream->id,
+						 channel_info);
 		stream->cif_fmt_in = cif_fmt_in;
 	} else {
 		v4l2_err(&stream->cifdev->v4l2_dev,
@@ -6006,8 +6451,9 @@
 
 	planes = fmt->cplanes ? fmt->cplanes : fmt->mplanes;
 
-	if (cif_fmt_in && (cif_fmt_in->mbus_code == MEDIA_BUS_FMT_SPD_2X8 ||
-			   cif_fmt_in->mbus_code == MEDIA_BUS_FMT_EBD_1X8))
+	if (cif_fmt_in &&
+	    (cif_fmt_in->mbus_code == MEDIA_BUS_FMT_SPD_2X8 ||
+	     cif_fmt_in->mbus_code == MEDIA_BUS_FMT_EBD_1X8))
 		stream->crop_enable = false;
 
 	for (i = 0; i < planes; i++) {
@@ -6032,7 +6478,7 @@
 			}
 		}
 
-		if (dev->sditf_cnt > 1 && dev->sditf_cnt <= RKCIF_MAX_SDITF)
+		if (priv && priv->is_combine_mode && dev->sditf_cnt <= RKCIF_MAX_SDITF)
 			height *= dev->sditf_cnt;
 
 		extend_line->pixm.height = height + RKMODULE_EXTEND_LINE;
@@ -6042,8 +6488,9 @@
 		 * to optimize reading and writing of ddr, aliged with 256.
 		 */
 		if (fmt->fmt_type == CIF_FMT_TYPE_RAW &&
-			(stream->cif_fmt_in->mbus_code == MEDIA_BUS_FMT_EBD_1X8 ||
-			stream->cif_fmt_in->mbus_code == MEDIA_BUS_FMT_SPD_2X8)) {
+		    cif_fmt_in &&
+		    (cif_fmt_in->mbus_code == MEDIA_BUS_FMT_EBD_1X8 ||
+		     cif_fmt_in->mbus_code == MEDIA_BUS_FMT_SPD_2X8)) {
 			stream->is_compact = false;
 		}
 
@@ -6157,6 +6604,7 @@
 
 	stream->is_high_align = false;
 	stream->is_finish_stop_dma = false;
+	stream->is_wait_dma_stop = false;
 
 	if (dev->chip_id == CHIP_RV1126_CIF ||
 	    dev->chip_id == CHIP_RV1126_CIF_LITE)
@@ -6184,6 +6632,7 @@
 	stream->buf_owner = 0;
 	stream->buf_replace_cnt = 0;
 	stream->is_stop_capture = false;
+	atomic_set(&stream->buf_cnt, 0);
 }
 
 static int rkcif_fh_open(struct file *filp)
@@ -6329,9 +6778,9 @@
 	input_rect.height = RKCIF_DEFAULT_HEIGHT;
 
 	if (dev->terminal_sensor.sd)
-		get_input_fmt(dev->terminal_sensor.sd,
-			      &input_rect, stream->id,
-			      &csi_info);
+		rkcif_get_input_fmt(dev,
+				    &input_rect, stream->id,
+				    &csi_info);
 
 	if (dev->hw_dev->adapt_to_usbcamerahal) {
 		fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
@@ -6410,7 +6859,7 @@
 		return -EINVAL;
 
 	if (dev->terminal_sensor.sd) {
-		cif_fmt_in = get_input_fmt(dev->terminal_sensor.sd,
+		cif_fmt_in = rkcif_get_input_fmt(dev,
 					   &input_rect, stream->id,
 					   &dev->channels[stream->id]);
 		stream->cif_fmt_in = cif_fmt_in;
@@ -6796,8 +7245,8 @@
 		break;
 	case RKCIF_CMD_SET_CSI_MEMORY_MODE:
 		if (dev->terminal_sensor.sd) {
-			in_fmt = get_input_fmt(dev->terminal_sensor.sd,
-					       &rect, 0, &csi_info);
+			in_fmt = rkcif_get_input_fmt(dev,
+						     &rect, 0, &csi_info);
 			if (in_fmt == NULL) {
 				v4l2_err(&dev->v4l2_dev, "can't get sensor input format\n");
 				return -EINVAL;
@@ -6876,6 +7325,7 @@
 	v4l2_dbg(2, rkcif_debug, &stream->cifdev->v4l2_dev,
 		 "stream[%d] vb done, index: %d, sequence %d\n", stream->id,
 		 vb_done->vb2_buf.index, vb_done->sequence);
+	atomic_dec(&stream->buf_cnt);
 }
 
 static void rkcif_tasklet_handle(unsigned long data)
@@ -8147,7 +8597,11 @@
 
 	if (active_buf) {
 		vb_done = &active_buf->vb;
-		vb_done->vb2_buf.timestamp = stream->readout.fs_timestamp;
+		if (cif_dev->chip_id < CHIP_RK3588_CIF &&
+		    cif_dev->active_sensor->mbus.type == V4L2_MBUS_BT656)
+			vb_done->vb2_buf.timestamp = stream->readout.fe_timestamp;
+		else
+			vb_done->vb2_buf.timestamp = stream->readout.fs_timestamp;
 		vb_done->sequence = stream->frame_idx - 1;
 		active_buf->fe_timestamp = ktime_get_ns();
 		if (stream->is_line_wake_up) {
@@ -8308,8 +8762,10 @@
 	spin_lock_irqsave(&priv->cif_dev->buffree_lock, flags);
 	for (i = 0; i < priv->buf_num; i++) {
 		rx_buf = &stream->rx_buf[i];
-		if (rx_buf && (!rx_buf->dummy.is_free) && rx_buf != buf)
+		if (rx_buf && (!rx_buf->dummy.is_free) && rx_buf != buf) {
 			list_add_tail(&rx_buf->list_free, &priv->buf_free_list);
+			stream->total_buf_num--;
+		}
 	}
 	spin_unlock_irqrestore(&priv->cif_dev->buffree_lock, flags);
 	schedule_work(&priv->buffree_work.work);
@@ -8379,11 +8835,13 @@
 			active_buf->dbufs.timestamp = stream->readout.fs_timestamp;
 			active_buf->fe_timestamp = ktime_get_ns();
 			stream->last_frame_idx = stream->frame_idx;
-			if (stream->cifdev->hdr.hdr_mode == NO_HDR)
+			if (stream->cifdev->hdr.hdr_mode == NO_HDR) {
 				rkcif_s_rx_buffer(stream->cifdev, &active_buf->dbufs);
-			else
+				if (stream->cifdev->is_support_tools && stream->tools_vdev)
+					rkcif_rdbk_with_tools(stream, active_buf);
+			} else {
 				rkcif_rdbk_frame_end_toisp(stream, active_buf);
-			stream->buf_num_toisp--;
+			}
 		}
 	}
 }
@@ -8455,8 +8913,8 @@
 
 	if (!stream->is_line_wake_up) {
 		ret = rkcif_assign_new_buffer_pingpong(stream,
-					 RKCIF_YUV_ADDR_STATE_UPDATE,
-					 mipi_id);
+						       RKCIF_YUV_ADDR_STATE_UPDATE,
+						       mipi_id);
 		if (ret && cif_dev->chip_id < CHIP_RK3588_CIF)
 			return;
 	} else {
@@ -8464,6 +8922,10 @@
 		if (ret && cif_dev->chip_id < CHIP_RK3588_CIF)
 			return;
 	}
+	if (cif_dev->chip_id < CHIP_RK3588_CIF &&
+	    cif_dev->active_sensor->mbus.type == V4L2_MBUS_BT656 &&
+	    stream->id != 0)
+		stream->frame_idx++;
 	if (!stream->is_line_wake_up && stream->dma_en & RKCIF_DMAEN_BY_VICAP)
 		rkcif_buf_done_prepare(stream, active_buf, mipi_id, 0);
 
@@ -8590,6 +9052,7 @@
 	struct rkcif_sensor_info *terminal_sensor = &cif_dev->terminal_sensor;
 	struct rkcif_resume_info *resume_info = &cif_dev->reset_work.resume_info;
 	struct rkcif_timer *timer = &cif_dev->reset_watchdog_timer;
+	struct sditf_priv *priv = cif_dev->sditf[0];
 	int i, j, ret = 0;
 	u32 on, sof_cnt;
 	int capture_mode = 0;
@@ -8658,10 +9121,12 @@
 				 __func__, on ? "on" : "off", p->subdevs[i]->name);
 	}
 
-	for (i = 0; i < cif_dev->sditf_cnt; i++) {
-		if (cif_dev->sditf[i] && cif_dev->sditf[i]->sensor_sd)
-			ret = v4l2_subdev_call(cif_dev->sditf[i]->sensor_sd, core, ioctl,
-					       RKMODULE_SET_QUICK_STREAM, &on);
+	if (priv && priv->is_combine_mode && cif_dev->sditf_cnt <= RKCIF_MAX_SDITF) {
+		for (i = 0; i < cif_dev->sditf_cnt; i++) {
+			if (cif_dev->sditf[i] && cif_dev->sditf[i]->sensor_sd)
+				ret = v4l2_subdev_call(cif_dev->sditf[i]->sensor_sd, core, ioctl,
+						       RKMODULE_SET_QUICK_STREAM, &on);
+		}
 	}
 
 	rockchip_clear_system_status(SYS_STATUS_CIF0);
@@ -8756,10 +9221,12 @@
 				 p->subdevs[i]->name);
 	}
 
-	for (i = 0; i < cif_dev->sditf_cnt; i++) {
-		if (cif_dev->sditf[i] && cif_dev->sditf[i]->sensor_sd)
-			v4l2_subdev_call(cif_dev->sditf[i]->sensor_sd, core, ioctl,
-					 RKMODULE_SET_QUICK_STREAM, &on);
+	if (priv && priv->is_combine_mode && cif_dev->sditf_cnt <= RKCIF_MAX_SDITF) {
+		for (i = 0; i < cif_dev->sditf_cnt; i++) {
+			if (cif_dev->sditf[i] && cif_dev->sditf[i]->sensor_sd)
+				v4l2_subdev_call(cif_dev->sditf[i]->sensor_sd, core, ioctl,
+						 RKMODULE_SET_QUICK_STREAM, &on);
+		}
 	}
 
 	if (cif_dev->chip_id < CHIP_RK3588_CIF)
@@ -9437,6 +9904,11 @@
 		rkcif_write_register(cif_dev, CIF_REG_DVP_CTRL, val);
 	}
 	stream->to_stop_dma = 0;
+	v4l2_dbg(4, rkcif_debug, &cif_dev->v4l2_dev,
+		 "stream[%d] replace_cnt %d, y_addr 0x%x, 0x%x\n",
+		 stream->id, stream->buf_replace_cnt,
+		 rkcif_read_register(cif_dev, get_reg_index_of_frm0_y_addr(stream->id)),
+		 rkcif_read_register(cif_dev, get_reg_index_of_frm1_y_addr(stream->id)));
 	return 0;
 }
 
@@ -9616,15 +10088,16 @@
 
 	sync_config = &hw->sync_config[cif_dev->sync_cfg.group];
 	sync_config->sync_code |= BIT(cif_dev->csi_host_idx);
-	if (sync_config->sync_code != sync_config->sync_mask)
-		return -EINVAL;
-
 	v4l2_dbg(3, rkcif_debug, &cif_dev->v4l2_dev,
-		 "sync code 0x%x, mask 0x%x, update 0x%x, cache 0x%x\n",
+		 "sync code 0x%x, mask 0x%x, update 0x%x, cache 0x%x, timestamp %llu\n",
 		 sync_config->sync_code,
 		 sync_config->sync_mask,
 		 sync_config->update_code,
-		 sync_config->update_cache);
+		 sync_config->update_cache,
+		 detect_stream->readout.fs_timestamp);
+
+	if (sync_config->sync_code != sync_config->sync_mask)
+		return -EINVAL;
 
 	for (i = 0; i < sync_config->dev_cnt; i++) {
 		if (sync_config->mode == RKCIF_MASTER_MASTER) {
@@ -9973,7 +10446,12 @@
 					  stream->frame_idx - 1,
 					  stream->frame_phase,
 					  ktime_get_ns());
-
+			if (stream->is_finish_stop_dma && stream->is_wait_dma_stop) {
+				stream->is_wait_dma_stop = false;
+				wake_up(&stream->wq_stopped);
+				stream->is_finish_stop_dma = false;
+				continue;
+			}
 			if (stream->crop_dyn_en)
 				rkcif_dynamic_crop(stream);
 
@@ -9982,11 +10460,20 @@
 					is_update = true;
 				else
 					is_update = rkcif_check_buffer_prepare(stream);
+				v4l2_dbg(4, rkcif_debug, &cif_dev->v4l2_dev,
+					 "dma capture by vicap, is_updata %d, group mode %d, dma_en %d\n",
+					 is_update, cif_dev->sync_cfg.type, stream->dma_en);
 				if (is_update)
 					rkcif_update_stream(cif_dev, stream, mipi_id);
 			} else if (stream->dma_en & RKCIF_DMAEN_BY_ISP) {
+				v4l2_dbg(4, rkcif_debug, &cif_dev->v4l2_dev,
+					 "dma capture by isp, dma_en 0x%x\n",
+					 stream->dma_en);
 				rkcif_update_stream_toisp(cif_dev, stream, mipi_id);
 			} else if (stream->dma_en & RKCIF_DMAEN_BY_ROCKIT) {
+				v4l2_dbg(4, rkcif_debug, &cif_dev->v4l2_dev,
+					 "dma capture by rockit, dma_en 0x%x\n",
+					 stream->dma_en);
 				rkcif_update_stream_rockit(cif_dev, stream, mipi_id);
 			}
 
@@ -10003,11 +10490,10 @@
 			}
 
 			spin_lock_irqsave(&stream->vbq_lock, flags);
-			if (stream->is_finish_stop_dma) {
-				wake_up(&stream->wq_stopped);
-				stream->is_finish_stop_dma = false;
-			}
 			if (!(stream->dma_en & RKCIF_DMAEN_BY_ISP) && stream->lack_buf_cnt == 2) {
+				v4l2_dbg(4, rkcif_debug, &cif_dev->v4l2_dev,
+					 "stream[%d] to stop dma, lack_buf_cnt %d\n",
+					 stream->id, stream->lack_buf_cnt);
 				stream->to_stop_dma = RKCIF_DMAEN_BY_VICAP;
 				rkcif_stop_dma_capture(stream);
 			}
diff --git a/kernel/drivers/media/platform/rockchip/cif/cif-scale.c b/kernel/drivers/media/platform/rockchip/cif/cif-scale.c
index 7ea3404..3beede6 100644
--- a/kernel/drivers/media/platform/rockchip/cif/cif-scale.c
+++ b/kernel/drivers/media/platform/rockchip/cif/cif-scale.c
@@ -364,8 +364,8 @@
 	input_rect.height = RKCIF_DEFAULT_HEIGHT;
 
 	if (terminal_sensor && terminal_sensor->sd)
-		get_input_fmt(terminal_sensor->sd,
-			      &input_rect, 0, &csi_info);
+		rkcif_get_input_fmt(dev,
+				    &input_rect, 0, &csi_info);
 
 	switch (fsize->index) {
 	case SCALE_8TIMES:
diff --git a/kernel/drivers/media/platform/rockchip/cif/cif-tools.c b/kernel/drivers/media/platform/rockchip/cif/cif-tools.c
index 5c9f2b8..60103bf 100644
--- a/kernel/drivers/media/platform/rockchip/cif/cif-tools.c
+++ b/kernel/drivers/media/platform/rockchip/cif/cif-tools.c
@@ -300,8 +300,8 @@
 	input_rect.height = RKCIF_DEFAULT_HEIGHT;
 
 	if (terminal_sensor && terminal_sensor->sd)
-		get_input_fmt(terminal_sensor->sd,
-			      &input_rect, 0, &csi_info);
+		rkcif_get_input_fmt(dev,
+				    &input_rect, 0, &csi_info);
 
 	fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
 	s->width = input_rect.width;
@@ -589,11 +589,8 @@
 	return vb2_queue_init(q);
 }
 
-static void rkcif_tools_work(struct work_struct *work)
+static void rkcif_tools_buf_done(struct rkcif_tools_vdev *tools_vdev)
 {
-	struct rkcif_tools_vdev *tools_vdev = container_of(work,
-						struct rkcif_tools_vdev,
-						work);
 	struct rkcif_stream *stream = tools_vdev->stream;
 	struct rkcif_tools_buffer *tools_buf;
 	const struct cif_output_fmt *fmt = tools_vdev->tools_out_fmt;
@@ -700,6 +697,99 @@
 	spin_unlock_irqrestore(&tools_vdev->vbq_lock, flags);
 }
 
+static void rkcif_tools_buf_done_rdbk(struct rkcif_tools_vdev *tools_vdev)
+{
+	struct rkcif_stream *stream = tools_vdev->stream;
+	struct rkcif_device *dev = stream->cifdev;
+	const struct cif_output_fmt *fmt = tools_vdev->tools_out_fmt;
+	struct rkcif_rx_buffer *buf = NULL;
+	int i = 0;
+	unsigned long flags;
+
+retry_done_rdbk_buf:
+	spin_lock_irqsave(&tools_vdev->vbq_lock, flags);
+	if (!list_empty(&tools_vdev->buf_done_head)) {
+		buf = list_first_entry(&tools_vdev->buf_done_head,
+				       struct rkcif_rx_buffer, list);
+		if (buf)
+			list_del(&buf->list);
+	}
+	spin_unlock_irqrestore(&tools_vdev->vbq_lock, flags);
+	if (!buf) {
+		v4l2_err(&dev->v4l2_dev, "stream[%d] tools fail to get buf form list\n",
+			 stream->id);
+		return;
+	}
+
+	if (tools_vdev->stopping) {
+		rkcif_tools_stop(tools_vdev);
+		tools_vdev->stopping = false;
+		spin_lock_irqsave(&tools_vdev->vbq_lock, flags);
+		while (!list_empty(&tools_vdev->buf_done_head)) {
+			buf = list_first_entry(&tools_vdev->buf_done_head,
+					       struct rkcif_rx_buffer, list);
+			if (buf)
+				list_del(&buf->list);
+		}
+		spin_unlock_irqrestore(&tools_vdev->vbq_lock, flags);
+		wake_up(&tools_vdev->wq_stopped);
+		return;
+	}
+
+	if (!list_empty(&tools_vdev->buf_head)) {
+		tools_vdev->curr_buf = list_first_entry(&tools_vdev->buf_head,
+						    struct rkcif_buffer, queue);
+		if (!tools_vdev->curr_buf || tools_vdev->state != RKCIF_STATE_STREAMING) {
+			spin_lock_irqsave(&tools_vdev->vbq_lock, flags);
+			if (!list_empty(&tools_vdev->buf_done_head)) {
+				spin_unlock_irqrestore(&stream->tools_vdev->vbq_lock, flags);
+				goto retry_done_rdbk_buf;
+			}
+			spin_unlock_irqrestore(&tools_vdev->vbq_lock, flags);
+			return;
+		}
+		list_del(&tools_vdev->curr_buf->queue);
+		/* Dequeue a filled buffer */
+		for (i = 0; i < fmt->mplanes; i++) {
+			u32 payload_size = tools_vdev->pixm.plane_fmt[i].sizeimage;
+			void *src = buf->dummy.vaddr;
+			void *dst = vb2_plane_vaddr(&tools_vdev->curr_buf->vb.vb2_buf, i);
+
+			if (!src || !dst)
+				break;
+			dma_sync_single_for_device(dev->dev,
+						   buf->dummy.dma_addr,
+						   buf->dummy.size,
+						   DMA_FROM_DEVICE);
+			vb2_set_plane_payload(&tools_vdev->curr_buf->vb.vb2_buf, i,
+					      payload_size);
+			memcpy(dst, src, payload_size);
+		}
+		tools_vdev->curr_buf->vb.sequence = buf->dbufs.sequence;
+		tools_vdev->curr_buf->vb.vb2_buf.timestamp = buf->dbufs.timestamp;
+		vb2_buffer_done(&tools_vdev->curr_buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
+		tools_vdev->curr_buf = NULL;
+	}
+
+	spin_lock_irqsave(&tools_vdev->vbq_lock, flags);
+	if (!list_empty(&tools_vdev->buf_done_head)) {
+		spin_unlock_irqrestore(&stream->tools_vdev->vbq_lock, flags);
+		goto retry_done_rdbk_buf;
+	}
+	spin_unlock_irqrestore(&tools_vdev->vbq_lock, flags);
+}
+
+static void rkcif_tools_work(struct work_struct *work)
+{
+	struct rkcif_tools_vdev *tools_vdev = container_of(work,
+						struct rkcif_tools_vdev,
+						work);
+	if (tools_vdev->stream->dma_en & RKCIF_DMAEN_BY_VICAP)
+		rkcif_tools_buf_done(tools_vdev);
+	else if (tools_vdev->stream->dma_en & RKCIF_DMAEN_BY_ISP)
+		rkcif_tools_buf_done_rdbk(tools_vdev);
+}
+
 void rkcif_init_tools_vdev(struct rkcif_device *cif_dev, u32 ch)
 {
 	struct rkcif_tools_vdev *tools_vdev = &cif_dev->tools_vdev[ch];
diff --git a/kernel/drivers/media/platform/rockchip/cif/dev.c b/kernel/drivers/media/platform/rockchip/cif/dev.c
index ca18756..6f41ab3 100644
--- a/kernel/drivers/media/platform/rockchip/cif/dev.c
+++ b/kernel/drivers/media/platform/rockchip/cif/dev.c
@@ -644,12 +644,16 @@
 		}
 	}
 	if (index < CIF_REG_INDEX_MAX) {
-		if (index == CIF_REG_DVP_CTRL || reg->offset != 0x0)
+		if (index == CIF_REG_DVP_CTRL || reg->offset != 0x0) {
 			write_cif_reg(base, reg->offset + csi_offset, val);
-		else
+			v4l2_dbg(4, rkcif_debug, &dev->v4l2_dev,
+				 "write reg[0x%x]:0x%x!!!\n",
+				 reg->offset + csi_offset, val);
+		} else {
 			v4l2_dbg(1, rkcif_debug, &dev->v4l2_dev,
 				 "write reg[%d]:0x%x failed, maybe useless!!!\n",
 				 index, val);
+		}
 	}
 }
 
@@ -681,6 +685,9 @@
 			reg_val = read_cif_reg(base, reg->offset + csi_offset);
 			reg_val |= val;
 			write_cif_reg(base, reg->offset + csi_offset, reg_val);
+			v4l2_dbg(4, rkcif_debug, &dev->v4l2_dev,
+				 "write or reg[0x%x]:0x%x!!!\n",
+				 reg->offset + csi_offset, val);
 		} else {
 			v4l2_dbg(1, rkcif_debug, &dev->v4l2_dev,
 				 "write reg[%d]:0x%x with OR failed, maybe useless!!!\n",
@@ -717,6 +724,9 @@
 			reg_val = read_cif_reg(base, reg->offset + csi_offset);
 			reg_val &= val;
 			write_cif_reg(base, reg->offset + csi_offset, reg_val);
+			v4l2_dbg(4, rkcif_debug, &dev->v4l2_dev,
+				 "write and reg[0x%x]:0x%x!!!\n",
+				 reg->offset + csi_offset, val);
 		} else {
 			v4l2_dbg(1, rkcif_debug, &dev->v4l2_dev,
 				 "write reg[%d]:0x%x with OR failed, maybe useless!!!\n",
@@ -1561,16 +1571,6 @@
 
 	if (!completion_done(&dev->cmpl_ntf))
 		complete(&dev->cmpl_ntf);
-	if (dev->active_sensor &&
-	    (dev->active_sensor->mbus.type == V4L2_MBUS_CSI2_DPHY ||
-	     dev->active_sensor->mbus.type == V4L2_MBUS_CSI2_CPHY)) {
-		ret = v4l2_subdev_call(dev->active_sensor->sd,
-				       core, ioctl,
-				       RKCIF_CMD_SET_CSI_IDX,
-				       &dev->csi_host_idx);
-		if (ret)
-			v4l2_err(&dev->v4l2_dev, "set csi idx %d fail\n", dev->csi_host_idx);
-	}
 	v4l2_info(&dev->v4l2_dev, "Async subdev notifier completed\n");
 
 	return ret;
@@ -1940,6 +1940,7 @@
 	cif_dev->early_line = 0;
 	cif_dev->is_thunderboot = false;
 	cif_dev->rdbk_debug = 0;
+	memset(&cif_dev->channels[0].capture_info, 0, sizeof(cif_dev->channels[0].capture_info));
 	if (cif_dev->chip_id == CHIP_RV1126_CIF_LITE)
 		cif_dev->isr_hdl = rkcif_irq_lite_handler;
 
@@ -1997,6 +1998,17 @@
 	cif_dev->csi_host_idx = of_alias_get_id(node, "rkcif_mipi_lvds");
 	if (cif_dev->csi_host_idx < 0 || cif_dev->csi_host_idx > 5)
 		cif_dev->csi_host_idx = 0;
+	if (cif_dev->hw_dev->is_rk3588s2) {
+		if (cif_dev->csi_host_idx == 0)
+			cif_dev->csi_host_idx = 2;
+		else if (cif_dev->csi_host_idx == 2)
+			cif_dev->csi_host_idx = 4;
+		else if (cif_dev->csi_host_idx == 3)
+			cif_dev->csi_host_idx = 5;
+		v4l2_info(&cif_dev->v4l2_dev, "rk3588s2 attach to mipi%d\n",
+			  cif_dev->csi_host_idx);
+	}
+	cif_dev->csi_host_idx_def = cif_dev->csi_host_idx;
 	cif_dev->media_dev.dev = dev;
 	v4l2_dev = &cif_dev->v4l2_dev;
 	v4l2_dev->mdev = &cif_dev->media_dev;
@@ -2159,7 +2171,9 @@
 	if (sysfs_create_group(&pdev->dev.kobj, &dev_attr_grp))
 		return -ENODEV;
 
-	rkcif_attach_hw(cif_dev);
+	ret = rkcif_attach_hw(cif_dev);
+	if (ret)
+		return ret;
 
 	rkcif_parse_dts(cif_dev);
 
diff --git a/kernel/drivers/media/platform/rockchip/cif/dev.h b/kernel/drivers/media/platform/rockchip/cif/dev.h
index a23d20a..d995f58 100644
--- a/kernel/drivers/media/platform/rockchip/cif/dev.h
+++ b/kernel/drivers/media/platform/rockchip/cif/dev.h
@@ -193,6 +193,7 @@
 
 struct rkcif_tools_buffer {
 	struct vb2_v4l2_buffer *vb;
+	struct rkisp_rx_buf *dbufs;
 	struct list_head list;
 	u32 frame_idx;
 	u64 timestamp;
@@ -279,10 +280,12 @@
 	unsigned int width;
 	unsigned int height;
 	unsigned int virtual_width;
+	unsigned int left_virtual_width;
 	unsigned int crop_st_x;
 	unsigned int crop_st_y;
 	unsigned int dsi_input;
 	struct rkmodule_lvds_cfg lvds_cfg;
+	struct rkmodule_capture_info capture_info;
 };
 
 struct rkcif_vdev_node {
@@ -526,7 +529,7 @@
 	unsigned int			cur_stream_mode;
 	struct rkcif_rx_buffer		rx_buf[RKISP_VICAP_BUF_CNT_MAX];
 	struct list_head		rx_buf_head;
-	int				buf_num_toisp;
+	int				total_buf_num;
 	u64				line_int_cnt;
 	int				lack_buf_cnt;
 	unsigned int			buf_wake_up_cnt;
@@ -536,6 +539,7 @@
 	int				last_rx_buf_idx;
 	int				last_frame_idx;
 	int				new_fource_idx;
+	atomic_t			buf_cnt;
 	bool				stopping;
 	bool				crop_enable;
 	bool				crop_dyn_en;
@@ -552,6 +556,7 @@
 	bool				is_in_vblank;
 	bool				is_change_toisp;
 	bool				is_stop_capture;
+	bool				is_wait_dma_stop;
 };
 
 struct rkcif_lvds_subdev {
@@ -841,6 +846,7 @@
 	struct rkcif_work_struct	reset_work;
 	int				id_use_cnt;
 	unsigned int			csi_host_idx;
+	unsigned int			csi_host_idx_def;
 	unsigned int			dvp_sof_in_oneframe;
 	unsigned int			wait_line;
 	unsigned int			wait_line_bak;
@@ -883,7 +889,7 @@
 int rkcif_scale_start(struct rkcif_scale_vdev *scale_vdev);
 
 const struct
-cif_input_fmt *get_input_fmt(struct v4l2_subdev *sd,
+cif_input_fmt *rkcif_get_input_fmt(struct rkcif_device *dev,
 				 struct v4l2_rect *rect,
 				 u32 pad_id, struct csi_channel_info *csi_info);
 
diff --git a/kernel/drivers/media/platform/rockchip/cif/hw.c b/kernel/drivers/media/platform/rockchip/cif/hw.c
index 55179f7..bf56539 100644
--- a/kernel/drivers/media/platform/rockchip/cif/hw.c
+++ b/kernel/drivers/media/platform/rockchip/cif/hw.c
@@ -8,6 +8,7 @@
 #include <linux/delay.h>
 #include <linux/interrupt.h>
 #include <linux/module.h>
+#include <linux/nvmem-consumer.h>
 #include <linux/of.h>
 #include <linux/of_gpio.h>
 #include <linux/of_graph.h>
@@ -1252,6 +1253,51 @@
 		rkcif_iommu_enable(cif_hw);
 }
 
+static int rkcif_get_efuse_value(struct device_node *np, char *porp_name,
+				    u8 *value)
+{
+	struct nvmem_cell *cell;
+	unsigned char *buf;
+	size_t len;
+
+	cell = of_nvmem_cell_get(np, porp_name);
+	if (IS_ERR(cell))
+		return PTR_ERR(cell);
+
+	buf = (unsigned char *)nvmem_cell_read(cell, &len);
+
+	nvmem_cell_put(cell);
+
+	if (IS_ERR(buf))
+		return PTR_ERR(buf);
+
+	*value = buf[0];
+
+	kfree(buf);
+
+	return 0;
+}
+
+static int rkcif_get_speciand_package_number(struct device_node *np)
+{
+	u8 spec = 0, package = 0, low = 0, high = 0;
+
+	if (rkcif_get_efuse_value(np, "specification", &spec))
+		return -EINVAL;
+	if (rkcif_get_efuse_value(np, "package_low", &low))
+		return -EINVAL;
+	if (rkcif_get_efuse_value(np, "package_high", &high))
+		return -EINVAL;
+
+	package = ((high & 0x1) << 3) | low;
+
+	/* RK3588S */
+	if (spec == 0x13)
+		return package;
+
+	return -EINVAL;
+}
+
 static int rkcif_plat_hw_probe(struct platform_device *pdev)
 {
 	const struct of_device_id *match;
@@ -1265,6 +1311,7 @@
 	int i, ret, irq;
 	bool is_mem_reserved = false;
 	struct notifier_block *notifier;
+	int package = 0;
 
 	match = of_match_node(rkcif_plat_of_match, node);
 	if (IS_ERR(match))
@@ -1278,6 +1325,13 @@
 	dev_set_drvdata(dev, cif_hw);
 	cif_hw->dev = dev;
 
+	package = rkcif_get_speciand_package_number(node);
+	if (package == 0x2) {
+		cif_hw->is_rk3588s2 = true;
+		dev_info(dev, "attach rk3588s2\n");
+	} else {
+		cif_hw->is_rk3588s2 = false;
+	}
 	irq = platform_get_irq(pdev, 0);
 	if (irq < 0)
 		return irq;
@@ -1506,6 +1560,7 @@
 	ret = platform_driver_register(&rkcif_hw_plat_drv);
 	if (ret)
 		return ret;
+	rkcif_csi2_hw_plat_drv_init();
 	return rkcif_csi2_plat_drv_init();
 }
 
@@ -1513,6 +1568,7 @@
 {
 	platform_driver_unregister(&rkcif_hw_plat_drv);
 	rkcif_csi2_plat_drv_exit();
+	rkcif_csi2_hw_plat_drv_exit();
 }
 
 #if defined(CONFIG_VIDEO_ROCKCHIP_THUNDER_BOOT_ISP) && !defined(CONFIG_INITCALL_ASYNC)
diff --git a/kernel/drivers/media/platform/rockchip/cif/hw.h b/kernel/drivers/media/platform/rockchip/cif/hw.h
index 56bc785..4faa9c4 100644
--- a/kernel/drivers/media/platform/rockchip/cif/hw.h
+++ b/kernel/drivers/media/platform/rockchip/cif/hw.h
@@ -150,6 +150,7 @@
 	bool				is_dma_contig;
 	bool				adapt_to_usbcamerahal;
 	u64				irq_time;
+	bool				is_rk3588s2;
 };
 
 void rkcif_hw_soft_reset(struct rkcif_hw *cif_hw, bool is_rst_iommu);
diff --git a/kernel/drivers/media/platform/rockchip/cif/mipi-csi2.c b/kernel/drivers/media/platform/rockchip/cif/mipi-csi2.c
index c0e5999..f526b43 100644
--- a/kernel/drivers/media/platform/rockchip/cif/mipi-csi2.c
+++ b/kernel/drivers/media/platform/rockchip/cif/mipi-csi2.c
@@ -13,12 +13,12 @@
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/of_graph.h>
+#include <linux/of_platform.h>
 #include <linux/platform_device.h>
 #include <linux/reset.h>
 #include <linux/rk-camera-module.h>
 #include <media/v4l2-ioctl.h>
 #include "mipi-csi2.h"
-#include <linux/rkcif-config.h>
 #include <linux/regulator/consumer.h>
 
 static int csi2_debug;
@@ -147,47 +147,55 @@
 
 }
 
-static void csi2_hw_do_reset(struct csi2_dev *csi2)
+static void csi2_hw_do_reset(struct csi2_hw *csi2_hw)
 {
-	reset_control_assert(csi2->rsts_bulk);
+
+	if (!csi2_hw->rsts_bulk)
+		return;
+
+	reset_control_assert(csi2_hw->rsts_bulk);
 
 	udelay(5);
 
-	reset_control_deassert(csi2->rsts_bulk);
+	reset_control_deassert(csi2_hw->rsts_bulk);
 }
 
-static int csi2_enable_clks(struct csi2_dev *csi2)
+static int csi2_enable_clks(struct csi2_hw *csi2_hw)
 {
 	int ret = 0;
 
-	ret = clk_bulk_prepare_enable(csi2->clks_num, csi2->clks_bulk);
+	if (!csi2_hw->clks_bulk)
+		return -EINVAL;
+
+	ret = clk_bulk_prepare_enable(csi2_hw->clks_num, csi2_hw->clks_bulk);
 	if (ret)
-		dev_err(csi2->dev, "failed to enable clks\n");
+		dev_err(csi2_hw->dev, "failed to enable clks\n");
 
 	return ret;
 }
 
-static void csi2_disable_clks(struct csi2_dev *csi2)
+static void csi2_disable_clks(struct csi2_hw *csi2_hw)
 {
-	clk_bulk_disable_unprepare(csi2->clks_num,  csi2->clks_bulk);
+	if (!csi2_hw->clks_bulk)
+		return;
+	clk_bulk_disable_unprepare(csi2_hw->clks_num,  csi2_hw->clks_bulk);
 }
 
-static void csi2_disable(struct csi2_dev *csi2)
+static void csi2_disable(struct csi2_hw *csi2_hw)
 {
-	void __iomem *base = csi2->base;
-
-	write_csihost_reg(base, CSIHOST_RESETN, 0);
-	write_csihost_reg(base, CSIHOST_MSK1, 0xffffffff);
-	write_csihost_reg(base, CSIHOST_MSK2, 0xffffffff);
+	write_csihost_reg(csi2_hw->base, CSIHOST_RESETN, 0);
+	write_csihost_reg(csi2_hw->base, CSIHOST_MSK1, 0xffffffff);
+	write_csihost_reg(csi2_hw->base, CSIHOST_MSK2, 0xffffffff);
 }
 
 static int csi2_g_mbus_config(struct v4l2_subdev *sd, unsigned int pad_id,
 			      struct v4l2_mbus_config *mbus);
 
-static void csi2_enable(struct csi2_dev *csi2,
+static void csi2_enable(struct csi2_hw *csi2_hw,
 			enum host_type_t host_type)
 {
-	void __iomem *base = csi2->base;
+	void __iomem *base = csi2_hw->base;
+	struct csi2_dev *csi2 = csi2_hw->csi2;
 	int lanes = csi2->bus.num_data_lanes;
 	struct v4l2_mbus_config mbus;
 	u32 val = 0;
@@ -225,15 +233,9 @@
 {
 	enum host_type_t host_type;
 	int ret, i;
+	int csi_idx = 0;
 
 	atomic_set(&csi2->frm_sync_seq, 0);
-
-	csi2_hw_do_reset(csi2);
-	ret = csi2_enable_clks(csi2);
-	if (ret) {
-		v4l2_err(&csi2->sd, "%s: enable clks failed\n", __func__);
-		return ret;
-	}
 
 	csi2_update_sensor_info(csi2);
 
@@ -242,7 +244,16 @@
 	else
 		host_type = RK_CSI_RXHOST;
 
-	csi2_enable(csi2, host_type);
+	for (i = 0; i < csi2->csi_info.csi_num; i++) {
+		csi_idx = csi2->csi_info.csi_idx[i];
+		csi2_hw_do_reset(csi2->csi2_hw[csi_idx]);
+		ret = csi2_enable_clks(csi2->csi2_hw[csi_idx]);
+		if (ret) {
+			v4l2_err(&csi2->sd, "%s: enable clks failed\n", __func__);
+			return ret;
+		}
+		csi2_enable(csi2->csi2_hw[csi_idx], host_type);
+	}
 
 	pr_debug("stream sd: %s\n", csi2->src_sd->name);
 	ret = v4l2_subdev_call(csi2->src_sd, video, s_stream, 1);
@@ -256,20 +267,29 @@
 	return 0;
 
 err_assert_reset:
-	csi2_disable(csi2);
-	csi2_disable_clks(csi2);
+	for (i = 0; i < csi2->csi_info.csi_num; i++) {
+		csi_idx = csi2->csi_info.csi_idx[i];
+		csi2_disable(csi2->csi2_hw[csi_idx]);
+		csi2_disable_clks(csi2->csi2_hw[csi_idx]);
+	}
 
 	return ret;
 }
 
 static void csi2_stop(struct csi2_dev *csi2)
 {
+	int i = 0;
+	int csi_idx = 0;
+
 	/* stop upstream */
 	v4l2_subdev_call(csi2->src_sd, video, s_stream, 0);
 
-	csi2_disable(csi2);
-	csi2_hw_do_reset(csi2);
-	csi2_disable_clks(csi2);
+	for (i = 0; i < csi2->csi_info.csi_num; i++) {
+		csi_idx = csi2->csi_info.csi_idx[i];
+		csi2_disable(csi2->csi2_hw[csi_idx]);
+		csi2_hw_do_reset(csi2->csi2_hw[csi_idx]);
+		csi2_disable_clks(csi2->csi2_hw[csi_idx]);
+	}
 }
 
 /*
@@ -379,7 +399,6 @@
 	csi2->crop.left = 0;
 	csi2->crop.width = RKCIF_DEFAULT_WIDTH;
 	csi2->crop.height = RKCIF_DEFAULT_HEIGHT;
-	csi2->csi_idx = 0;
 
 	return media_entity_pads_init(&sd->entity, num_pads, csi2->pad);
 }
@@ -567,11 +586,19 @@
 static long rkcif_csi2_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
 {
 	struct csi2_dev *csi2 = sd_to_dev(sd);
+	struct v4l2_subdev *sensor = get_remote_sensor(sd);
 	long ret = 0;
+	int i = 0;
 
 	switch (cmd) {
 	case RKCIF_CMD_SET_CSI_IDX:
-		csi2->csi_idx = *((u32 *)arg);
+		csi2->csi_info = *((struct rkcif_csi_info *)arg);
+		for (i = 0; i < csi2->csi_info.csi_num; i++)
+			csi2->csi2_hw[csi2->csi_info.csi_idx[i]]->csi2 = csi2;
+		if (csi2->match_data->chip_id > CHIP_RV1126_CSI2)
+			ret = v4l2_subdev_call(sensor, core, ioctl,
+					       RKCIF_CMD_SET_CSI_IDX,
+					       arg);
 		break;
 	default:
 		ret = -ENOIOCTLCMD;
@@ -586,15 +613,15 @@
 				      unsigned int cmd, unsigned long arg)
 {
 	void __user *up = compat_ptr(arg);
-	u32 csi_idx = 0;
+	struct rkcif_csi_info csi_info;
 	long ret;
 
 	switch (cmd) {
 	case RKCIF_CMD_SET_CSI_IDX:
-		if (copy_from_user(&csi_idx, up, sizeof(u32)))
+		if (copy_from_user(&csi_info, up, sizeof(struct rkcif_csi_info)))
 			return -EFAULT;
 
-		ret = rkcif_csi2_ioctl(sd, cmd, &csi_idx);
+		ret = rkcif_csi2_ioctl(sd, cmd, &csi_info);
 		break;
 	default:
 		ret = -ENOIOCTLCMD;
@@ -749,7 +776,8 @@
 static irqreturn_t rk_csirx_irq1_handler(int irq, void *ctx)
 {
 	struct device *dev = ctx;
-	struct csi2_dev *csi2 = sd_to_dev(dev_get_drvdata(dev));
+	struct csi2_hw *csi2_hw = dev_get_drvdata(dev);
+	struct csi2_dev *csi2 = csi2_hw->csi2;
 	struct csi2_err_stats *err_list = NULL;
 	unsigned long err_stat = 0;
 	u32 val;
@@ -758,7 +786,7 @@
 	char vc_info[CSI_VCINFO_LEN] = {0};
 	bool is_add_cnt = false;
 
-	val = read_csihost_reg(csi2->base, CSIHOST_ERR1);
+	val = read_csihost_reg(csi2_hw->base, CSIHOST_ERR1);
 	if (val) {
 		if (val & CSIHOST_ERR1_PHYERR_SPTSYNCHS) {
 			err_list = &csi2->err_list[RK_CSI2_ERR_SOTSYN];
@@ -767,7 +795,7 @@
 				if (err_list->cnt > 3 &&
 				    csi2->err_list[RK_CSI2_ERR_ALL].cnt <= err_list->cnt) {
 					csi2->is_check_sot_sync = false;
-					write_csihost_reg(csi2->base, CSIHOST_MSK1, 0xf);
+					write_csihost_reg(csi2_hw->base, CSIHOST_MSK1, 0xf);
 				}
 				if (csi2->is_check_sot_sync) {
 					csi2_find_err_vc(val & 0xf, vc_info);
@@ -832,7 +860,7 @@
 			csi2_err_strncat(err_str, cur_str);
 		}
 
-		pr_err("%s ERR1:0x%x %s\n", csi2->dev_name, val, err_str);
+		pr_err("%s ERR1:0x%x %s\n", csi2_hw->dev_name, val, err_str);
 
 		if (is_add_cnt) {
 			csi2->err_list[RK_CSI2_ERR_ALL].cnt++;
@@ -841,7 +869,7 @@
 
 			atomic_notifier_call_chain(&g_csi_host_chain,
 						   err_stat,
-						   &csi2->csi_idx);
+						   &csi2->csi_info.csi_idx[csi2->csi_info.csi_num - 1]);
 		}
 
 	}
@@ -852,13 +880,13 @@
 static irqreturn_t rk_csirx_irq2_handler(int irq, void *ctx)
 {
 	struct device *dev = ctx;
-	struct csi2_dev *csi2 = sd_to_dev(dev_get_drvdata(dev));
+	struct csi2_hw *csi2_hw = dev_get_drvdata(dev);
 	u32 val;
 	char cur_str[CSI_ERRSTR_LEN] = {0};
 	char err_str[CSI_ERRSTR_LEN] = {0};
 	char vc_info[CSI_VCINFO_LEN] = {0};
 
-	val = read_csihost_reg(csi2->base, CSIHOST_ERR2);
+	val = read_csihost_reg(csi2_hw->base, CSIHOST_ERR2);
 	if (val) {
 		if (val & CSIHOST_ERR2_PHYERR_ESC) {
 			csi2_find_err_vc(val & 0xf, vc_info);
@@ -885,7 +913,7 @@
 			csi2_err_strncat(err_str, cur_str);
 		}
 
-		pr_err("%s ERR2:0x%x %s\n", csi2->dev_name, val, err_str);
+		pr_err("%s ERR2:0x%x %s\n", csi2_hw->dev_name, val, err_str);
 	}
 
 	return IRQ_HANDLED;
@@ -924,31 +952,43 @@
 static const struct csi2_match_data rk1808_csi2_match_data = {
 	.chip_id = CHIP_RK1808_CSI2,
 	.num_pads = CSI2_NUM_PADS,
+	.num_hw = 1,
 };
 
 static const struct csi2_match_data rk3288_csi2_match_data = {
 	.chip_id = CHIP_RK3288_CSI2,
 	.num_pads = CSI2_NUM_PADS_SINGLE_LINK,
+	.num_hw = 1,
 };
 
 static const struct csi2_match_data rv1126_csi2_match_data = {
 	.chip_id = CHIP_RV1126_CSI2,
 	.num_pads = CSI2_NUM_PADS,
+	.num_hw = 1,
 };
 
 static const struct csi2_match_data rk3568_csi2_match_data = {
 	.chip_id = CHIP_RK3568_CSI2,
 	.num_pads = CSI2_NUM_PADS,
+	.num_hw = 1,
 };
 
 static const struct csi2_match_data rk3588_csi2_match_data = {
 	.chip_id = CHIP_RK3588_CSI2,
 	.num_pads = CSI2_NUM_PADS_MAX,
+	.num_hw = 6,
+};
+
+static const struct csi2_match_data rv1106_csi2_match_data = {
+	.chip_id = CHIP_RV1106_CSI2,
+	.num_pads = CSI2_NUM_PADS_MAX,
+	.num_hw = 2,
 };
 
 static const struct csi2_match_data rk3562_csi2_match_data = {
 	.chip_id = CHIP_RK3562_CSI2,
 	.num_pads = CSI2_NUM_PADS_MAX,
+	.num_hw = 4,
 };
 
 static const struct of_device_id csi2_dt_ids[] = {
@@ -973,6 +1013,10 @@
 		.data = &rk3588_csi2_match_data,
 	},
 	{
+		.compatible = "rockchip,rv1106-mipi-csi2",
+		.data = &rv1106_csi2_match_data,
+	},
+	{
 		.compatible = "rockchip,rk3562-mipi-csi2",
 		.data = &rk3562_csi2_match_data,
 	},
@@ -980,15 +1024,48 @@
 };
 MODULE_DEVICE_TABLE(of, csi2_dt_ids);
 
+static int csi2_attach_hw(struct csi2_dev *csi2)
+{
+	struct device_node *np;
+	struct platform_device *pdev;
+	struct csi2_hw *hw;
+	int i = 0;
+
+	for (i = 0; i < csi2->match_data->num_hw; i++) {
+		np = of_parse_phandle(csi2->dev->of_node, "rockchip,hw", i);
+		if (!np || !of_device_is_available(np)) {
+			dev_err(csi2->dev, "failed to get csi2 hw node\n");
+			return -ENODEV;
+		}
+
+		pdev = of_find_device_by_node(np);
+		of_node_put(np);
+		if (!pdev) {
+			dev_err(csi2->dev, "failed to get csi2 hw from node\n");
+			return -ENODEV;
+		}
+
+		hw = platform_get_drvdata(pdev);
+		if (!hw) {
+			dev_err(csi2->dev, "failed attach csi2 hw\n");
+			return -EINVAL;
+		}
+
+		hw->csi2 = csi2;
+		csi2->csi2_hw[i] = hw;
+	}
+	dev_info(csi2->dev, "attach to csi2 hw node\n");
+
+	return 0;
+}
+
 static int csi2_probe(struct platform_device *pdev)
 {
 	const struct of_device_id *match;
-	struct device *dev = &pdev->dev;
 	struct device_node *node = pdev->dev.of_node;
 	struct csi2_dev *csi2 = NULL;
-	struct resource *res;
 	const struct csi2_match_data *data;
-	int ret, irq;
+	int ret;
 
 	match = of_match_node(csi2_dt_ids, node);
 	if (IS_ERR(match))
@@ -1014,63 +1091,11 @@
 		v4l2_err(&csi2->sd, "failed to copy name\n");
 	platform_set_drvdata(pdev, &csi2->sd);
 
-	csi2->clks_num = devm_clk_bulk_get_all(dev, &csi2->clks_bulk);
-	if (csi2->clks_num < 0) {
-		csi2->clks_num = 0;
-		dev_err(dev, "failed to get csi2 clks\n");
+	ret = csi2_attach_hw(csi2);
+	if (ret) {
+		v4l2_err(&csi2->sd, "must enable all mipi csi2 hw node\n");
+		return -EINVAL;
 	}
-
-	csi2->rsts_bulk = devm_reset_control_array_get_optional_exclusive(dev);
-	if (IS_ERR(csi2->rsts_bulk)) {
-		if (PTR_ERR(csi2->rsts_bulk) != -EPROBE_DEFER)
-			dev_err(dev, "failed to get csi2 reset\n");
-		csi2->rsts_bulk = NULL;
-	}
-
-	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	csi2->base = devm_ioremap_resource(&pdev->dev, res);
-	if (IS_ERR(csi2->base)) {
-		resource_size_t offset = res->start;
-		resource_size_t size = resource_size(res);
-
-		dev_warn(&pdev->dev, "avoid secondary mipi resource check!\n");
-
-		csi2->base = devm_ioremap(&pdev->dev, offset, size);
-		if (IS_ERR(csi2->base)) {
-			dev_err(&pdev->dev, "Failed to ioremap resource\n");
-
-			return PTR_ERR(csi2->base);
-		}
-	}
-
-	irq = platform_get_irq_byname(pdev, "csi-intr1");
-	if (irq > 0) {
-		ret = devm_request_irq(&pdev->dev, irq,
-				       rk_csirx_irq1_handler, 0,
-				       dev_driver_string(&pdev->dev),
-				       &pdev->dev);
-		if (ret < 0)
-			v4l2_err(&csi2->sd, "request csi-intr1 irq failed: %d\n",
-				 ret);
-		csi2->irq1 = irq;
-	} else {
-		v4l2_err(&csi2->sd, "No found irq csi-intr1\n");
-	}
-
-	irq = platform_get_irq_byname(pdev, "csi-intr2");
-	if (irq > 0) {
-		ret = devm_request_irq(&pdev->dev, irq,
-				       rk_csirx_irq2_handler, 0,
-				       dev_driver_string(&pdev->dev),
-				       &pdev->dev);
-		if (ret < 0)
-			v4l2_err(&csi2->sd, "request csi-intr2 failed: %d\n",
-				 ret);
-		csi2->irq2 = irq;
-	} else {
-		v4l2_err(&csi2->sd, "No found irq csi-intr2\n");
-	}
-
 	mutex_init(&csi2->lock);
 
 	ret = csi2_media_init(&csi2->sd);
@@ -1115,11 +1140,183 @@
 	return platform_driver_register(&csi2_driver);
 }
 
-void __exit rkcif_csi2_plat_drv_exit(void)
+void rkcif_csi2_plat_drv_exit(void)
 {
 	platform_driver_unregister(&csi2_driver);
 }
 
+static const struct csi2_hw_match_data rk1808_csi2_hw_match_data = {
+	.chip_id = CHIP_RK1808_CSI2,
+};
+
+static const struct csi2_hw_match_data rk3288_csi2_hw_match_data = {
+	.chip_id = CHIP_RK3288_CSI2,
+};
+
+static const struct csi2_hw_match_data rv1126_csi2_hw_match_data = {
+	.chip_id = CHIP_RV1126_CSI2,
+};
+
+static const struct csi2_hw_match_data rk3568_csi2_hw_match_data = {
+	.chip_id = CHIP_RK3568_CSI2,
+};
+
+static const struct csi2_hw_match_data rk3588_csi2_hw_match_data = {
+	.chip_id = CHIP_RK3588_CSI2,
+};
+
+static const struct csi2_hw_match_data rv1106_csi2_hw_match_data = {
+	.chip_id = CHIP_RV1106_CSI2,
+};
+
+static const struct csi2_hw_match_data rk3562_csi2_hw_match_data = {
+	.chip_id = CHIP_RK3562_CSI2,
+};
+
+static const struct of_device_id csi2_hw_ids[] = {
+	{
+		.compatible = "rockchip,rk1808-mipi-csi2-hw",
+		.data = &rk1808_csi2_hw_match_data,
+	},
+	{
+		.compatible = "rockchip,rk3288-mipi-csi2-hw",
+		.data = &rk3288_csi2_hw_match_data,
+	},
+	{
+		.compatible = "rockchip,rk3568-mipi-csi2-hw",
+		.data = &rk3568_csi2_hw_match_data,
+	},
+	{
+		.compatible = "rockchip,rv1126-mipi-csi2-hw",
+		.data = &rv1126_csi2_hw_match_data,
+	},
+	{
+		.compatible = "rockchip,rk3588-mipi-csi2-hw",
+		.data = &rk3588_csi2_hw_match_data,
+	},
+	{
+		.compatible = "rockchip,rv1106-mipi-csi2-hw",
+		.data = &rv1106_csi2_hw_match_data,
+	},
+	{
+		.compatible = "rockchip,rk3562-mipi-csi2-hw",
+		.data = &rk3588_csi2_hw_match_data,
+	},
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, csi2_hw_ids);
+
+static int csi2_hw_probe(struct platform_device *pdev)
+{
+	const struct of_device_id *match;
+	struct device *dev = &pdev->dev;
+	struct device_node *node = pdev->dev.of_node;
+	struct csi2_hw *csi2_hw = NULL;
+	struct resource *res;
+	const struct csi2_hw_match_data *data;
+	int ret, irq;
+
+	dev_info(&pdev->dev, "enter mipi csi2 hw probe!\n");
+	match = of_match_node(csi2_hw_ids, node);
+	if (IS_ERR(match))
+		return PTR_ERR(match);
+	data = match->data;
+
+	csi2_hw = devm_kzalloc(&pdev->dev, sizeof(*csi2_hw), GFP_KERNEL);
+	if (!csi2_hw)
+		return -ENOMEM;
+
+	csi2_hw->dev = &pdev->dev;
+	csi2_hw->match_data = data;
+
+	csi2_hw->dev_name = node->name;
+
+	csi2_hw->clks_num = devm_clk_bulk_get_all(dev, &csi2_hw->clks_bulk);
+	if (csi2_hw->clks_num < 0) {
+		csi2_hw->clks_num = 0;
+		dev_err(dev, "failed to get csi2 clks\n");
+	}
+
+	csi2_hw->rsts_bulk = devm_reset_control_array_get_optional_exclusive(dev);
+	if (IS_ERR(csi2_hw->rsts_bulk)) {
+		if (PTR_ERR(csi2_hw->rsts_bulk) != -EPROBE_DEFER)
+			dev_err(dev, "failed to get csi2 reset\n");
+		csi2_hw->rsts_bulk = NULL;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	csi2_hw->base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(csi2_hw->base)) {
+		resource_size_t offset = res->start;
+		resource_size_t size = resource_size(res);
+
+		dev_warn(&pdev->dev, "avoid secondary mipi resource check!\n");
+
+		csi2_hw->base = devm_ioremap(&pdev->dev, offset, size);
+		if (IS_ERR(csi2_hw->base)) {
+			dev_err(&pdev->dev, "Failed to ioremap resource\n");
+
+			return PTR_ERR(csi2_hw->base);
+		}
+	}
+
+	irq = platform_get_irq_byname(pdev, "csi-intr1");
+	if (irq > 0) {
+		ret = devm_request_irq(&pdev->dev, irq,
+				       rk_csirx_irq1_handler, 0,
+				       dev_driver_string(&pdev->dev),
+				       &pdev->dev);
+		if (ret < 0)
+			dev_err(&pdev->dev, "request csi-intr1 irq failed: %d\n",
+				 ret);
+		csi2_hw->irq1 = irq;
+	} else {
+		dev_err(&pdev->dev, "No found irq csi-intr1\n");
+	}
+
+	irq = platform_get_irq_byname(pdev, "csi-intr2");
+	if (irq > 0) {
+		ret = devm_request_irq(&pdev->dev, irq,
+				       rk_csirx_irq2_handler, 0,
+				       dev_driver_string(&pdev->dev),
+				       &pdev->dev);
+		if (ret < 0)
+			dev_err(&pdev->dev, "request csi-intr2 failed: %d\n",
+				 ret);
+		csi2_hw->irq2 = irq;
+	} else {
+		dev_err(&pdev->dev, "No found irq csi-intr2\n");
+	}
+	platform_set_drvdata(pdev, csi2_hw);
+	dev_info(&pdev->dev, "probe success, v4l2_dev:%s!\n", csi2_hw->dev_name);
+
+	return 0;
+}
+
+static int csi2_hw_remove(struct platform_device *pdev)
+{
+	return 0;
+}
+
+static struct platform_driver csi2_hw_driver = {
+	.driver = {
+		.name = DEVICE_NAME_HW,
+		.of_match_table = csi2_hw_ids,
+	},
+	.probe = csi2_hw_probe,
+	.remove = csi2_hw_remove,
+};
+
+int rkcif_csi2_hw_plat_drv_init(void)
+{
+	return platform_driver_register(&csi2_hw_driver);
+}
+
+void rkcif_csi2_hw_plat_drv_exit(void)
+{
+	platform_driver_unregister(&csi2_hw_driver);
+}
+
 MODULE_DESCRIPTION("Rockchip MIPI CSI2 driver");
 MODULE_AUTHOR("Macrofly.xu <xuhf@rock-chips.com>");
 MODULE_LICENSE("GPL");
diff --git a/kernel/drivers/media/platform/rockchip/cif/mipi-csi2.h b/kernel/drivers/media/platform/rockchip/cif/mipi-csi2.h
index 28bc7c8..fc21782 100644
--- a/kernel/drivers/media/platform/rockchip/cif/mipi-csi2.h
+++ b/kernel/drivers/media/platform/rockchip/cif/mipi-csi2.h
@@ -8,6 +8,7 @@
 #include <media/v4l2-fwnode.h>
 #include <media/v4l2-subdev.h>
 #include <media/v4l2-event.h>
+#include <linux/rkcif-config.h>
 
 #define CSI2_ERR_FSFE_MASK	(0xff << 8)
 #define CSI2_ERR_COUNT_ALL_MASK	(0xff)
@@ -42,6 +43,7 @@
 #define CSIHOST_MAX_ERRINT_COUNT	10
 
 #define DEVICE_NAME "rockchip-mipi-csi2"
+#define DEVICE_NAME_HW "rockchip-mipi-csi2-hw"
 
 /* CSI Host Registers Define */
 #define CSIHOST_N_LANES		0x04
@@ -76,6 +78,8 @@
 #define SW_DATATYPE_LS(x)	((x) << 20)
 #define SW_DATATYPE_LE(x)	((x) << 26)
 
+#define RK_MAX_CSI_HW		(6)
+
 /*
  * add new chip id in tail in time order
  * by increasing to distinguish csi2 host version
@@ -88,6 +92,7 @@
 	CHIP_RV1126_CSI2,
 	CHIP_RK3568_CSI2,
 	CHIP_RK3588_CSI2,
+	CHIP_RV1106_CSI2,
 	CHIP_RK3562_CSI2,
 };
 
@@ -117,6 +122,11 @@
 struct csi2_match_data {
 	int chip_id;
 	int num_pads;
+	int num_hw;
+};
+
+struct csi2_hw_match_data {
+	int chip_id;
 };
 
 struct csi2_sensor_info {
@@ -155,10 +165,29 @@
 	int			num_sensors;
 	atomic_t		frm_sync_seq;
 	struct csi2_err_stats	err_list[RK_CSI2_ERR_MAX];
+	struct csi2_hw		*csi2_hw[RK_MAX_CSI_HW];
 	int			irq1;
 	int			irq2;
 	int			dsi_input_en;
-	u32			csi_idx;
+	struct rkcif_csi_info	csi_info;
+	const char		*dev_name;
+};
+
+struct csi2_hw {
+	struct device		*dev;
+	struct clk_bulk_data	*clks_bulk;
+	int			clks_num;
+	struct reset_control	*rsts_bulk;
+	struct csi2_dev		*csi2;
+	const struct csi2_hw_match_data	*match_data;
+
+	void __iomem		*base;
+
+	/* lock to protect all members below */
+	struct mutex lock;
+
+	int			irq1;
+	int			irq2;
 	const char		*dev_name;
 };
 
@@ -166,7 +195,9 @@
 void rkcif_csi2_set_sof(struct csi2_dev *csi2_dev, u32 seq);
 void rkcif_csi2_event_inc_sof(struct csi2_dev *csi2_dev);
 int rkcif_csi2_plat_drv_init(void);
-void __exit rkcif_csi2_plat_drv_exit(void);
+void rkcif_csi2_plat_drv_exit(void);
+int rkcif_csi2_hw_plat_drv_init(void);
+void rkcif_csi2_hw_plat_drv_exit(void);
 int rkcif_csi2_register_notifier(struct notifier_block *nb);
 int rkcif_csi2_unregister_notifier(struct notifier_block *nb);
 void rkcif_csi2_event_reset_pipe(struct csi2_dev *csi2_dev, int reset_src);
diff --git a/kernel/drivers/media/platform/rockchip/cif/procfs.c b/kernel/drivers/media/platform/rockchip/cif/procfs.c
index e8f61ed..d9f063f 100644
--- a/kernel/drivers/media/platform/rockchip/cif/procfs.c
+++ b/kernel/drivers/media/platform/rockchip/cif/procfs.c
@@ -371,6 +371,16 @@
 		seq_printf(f, "dma enable: 0x%x 0x%x 0x%x 0x%x\n",
 			   dev->stream[0].dma_en, dev->stream[1].dma_en,
 			   dev->stream[2].dma_en, dev->stream[3].dma_en);
+		seq_printf(f, "buf_cnt in drv: %d %d %d %d\n",
+			   atomic_read(&dev->stream[0].buf_cnt),
+			   atomic_read(&dev->stream[1].buf_cnt),
+			   atomic_read(&dev->stream[2].buf_cnt),
+			   atomic_read(&dev->stream[3].buf_cnt));
+		seq_printf(f, "total buf_cnt: %d %d %d %d\n",
+			   dev->stream[0].total_buf_num,
+			   dev->stream[1].total_buf_num,
+			   dev->stream[2].total_buf_num,
+			   dev->stream[3].total_buf_num);
 	}
 }
 
diff --git a/kernel/drivers/media/platform/rockchip/cif/subdev-itf.c b/kernel/drivers/media/platform/rockchip/cif/subdev-itf.c
index 7f850cc..01aefd4 100644
--- a/kernel/drivers/media/platform/rockchip/cif/subdev-itf.c
+++ b/kernel/drivers/media/platform/rockchip/cif/subdev-itf.c
@@ -342,7 +342,10 @@
 		mode = (struct rkisp_vicap_mode *)arg;
 		memcpy(&priv->mode, mode, sizeof(*mode));
 		sditf_reinit_mode(priv, &priv->mode);
-		mode->input.merge_num = cif_dev->sditf_cnt;
+		if (priv->is_combine_mode)
+			mode->input.merge_num = cif_dev->sditf_cnt;
+		else
+			mode->input.merge_num = 1;
 		mode->input.index = priv->combine_index;
 		return 0;
 	case RKISP_VICAP_CMD_INIT_BUF:
@@ -430,6 +433,7 @@
 static int sditf_channel_enable(struct sditf_priv *priv, int user)
 {
 	struct rkcif_device *cif_dev = priv->cif_dev;
+	struct rkmodule_capture_info *capture_info = &cif_dev->channels[0].capture_info;
 	unsigned int ch0 = 0, ch1 = 0, ch2 = 0;
 	unsigned int ctrl_val = 0;
 	unsigned int int_en = 0;
@@ -437,11 +441,25 @@
 	unsigned int offset_y = 0;
 	unsigned int width = priv->cap_info.width;
 	unsigned int height = priv->cap_info.height;
+	int csi_idx = cif_dev->csi_host_idx;
+
+	if (capture_info->mode == RKMODULE_MULTI_DEV_COMBINE_ONE &&
+	    priv->toisp_inf.link_mode == TOISP_UNITE) {
+		if (capture_info->multi_dev.dev_num != 2 ||
+		    capture_info->multi_dev.pixel_offset != RKMOUDLE_UNITE_EXTEND_PIXEL) {
+			v4l2_err(&cif_dev->v4l2_dev,
+				 "param error of online mode, combine dev num %d, offset %d\n",
+				 capture_info->multi_dev.dev_num,
+				 capture_info->multi_dev.pixel_offset);
+			return -EINVAL;
+		}
+		csi_idx = capture_info->multi_dev.dev_idx[user];
+	}
 
 	if (priv->hdr_cfg.hdr_mode == NO_HDR ||
 	    priv->hdr_cfg.hdr_mode == HDR_COMPR) {
 		if (cif_dev->inf_id == RKCIF_MIPI_LVDS)
-			ch0 = cif_dev->csi_host_idx * 4;
+			ch0 = csi_idx * 4;
 		else
 			ch0 = 24;//dvp
 		ctrl_val = (ch0 << 3) | 0x1;
@@ -496,7 +514,10 @@
 		}
 	} else {
 		if (priv->toisp_inf.link_mode == TOISP_UNITE) {
-			offset_x = priv->cap_info.width / 2 - RKMOUDLE_UNITE_EXTEND_PIXEL;
+			if (capture_info->mode == RKMODULE_MULTI_DEV_COMBINE_ONE)
+				offset_x = 0;
+			else
+				offset_x = priv->cap_info.width / 2 - RKMOUDLE_UNITE_EXTEND_PIXEL;
 			width = priv->cap_info.width / 2 + RKMOUDLE_UNITE_EXTEND_PIXEL;
 		}
 		rkcif_write_register(cif_dev, CIF_REG_TOISP1_CTRL, ctrl_val);
@@ -606,36 +627,39 @@
 	struct rkcif_device *cif_dev = priv->cif_dev;
 	struct v4l2_subdev_format fmt;
 	unsigned int mode = RKCIF_STREAM_MODE_TOISP;
+	int ret = 0;
 
 	sditf_check_capture_mode(cif_dev);
 	sditf_get_set_fmt(&priv->sd, NULL, &fmt);
 	if (priv->mode.rdbk_mode == RKISP_VICAP_ONLINE) {
 		if (priv->toisp_inf.link_mode == TOISP0) {
-			sditf_channel_enable(priv, 0);
+			ret = sditf_channel_enable(priv, 0);
 		} else if (priv->toisp_inf.link_mode == TOISP1) {
-			sditf_channel_enable(priv, 1);
+			ret = sditf_channel_enable(priv, 1);
 		} else if (priv->toisp_inf.link_mode == TOISP_UNITE) {
-			sditf_channel_enable(priv, 0);
-			sditf_channel_enable(priv, 1);
+			ret = sditf_channel_enable(priv, 0);
+			ret |= sditf_channel_enable(priv, 1);
 		}
 		mode = RKCIF_STREAM_MODE_TOISP;
 	} else if (priv->mode.rdbk_mode == RKISP_VICAP_RDBK_AUTO) {
 		mode = RKCIF_STREAM_MODE_TOISP_RDBK;
 	}
+	if (ret)
+		return ret;
 
 	if (priv->hdr_cfg.hdr_mode == NO_HDR ||
 	    priv->hdr_cfg.hdr_mode == HDR_COMPR) {
-		rkcif_do_start_stream(&cif_dev->stream[0], mode);
+		ret = rkcif_do_start_stream(&cif_dev->stream[0], mode);
 	} else if (priv->hdr_cfg.hdr_mode == HDR_X2) {
-		rkcif_do_start_stream(&cif_dev->stream[0], mode);
-		rkcif_do_start_stream(&cif_dev->stream[1], mode);
+		ret = rkcif_do_start_stream(&cif_dev->stream[0], mode);
+		ret |= rkcif_do_start_stream(&cif_dev->stream[1], mode);
 	} else if (priv->hdr_cfg.hdr_mode == HDR_X3) {
-		rkcif_do_start_stream(&cif_dev->stream[0], mode);
-		rkcif_do_start_stream(&cif_dev->stream[1], mode);
-		rkcif_do_start_stream(&cif_dev->stream[2], mode);
+		ret = rkcif_do_start_stream(&cif_dev->stream[0], mode);
+		ret |= rkcif_do_start_stream(&cif_dev->stream[1], mode);
+		ret |= rkcif_do_start_stream(&cif_dev->stream[2], mode);
 	}
 	INIT_LIST_HEAD(&priv->buf_free_list);
-	return 0;
+	return ret;
 }
 
 static int sditf_stop_stream(struct sditf_priv *priv)
@@ -697,6 +721,8 @@
 		}
 
 	}
+	if (on && ret)
+		atomic_dec(&priv->stream_cnt);
 	return ret;
 }
 
@@ -724,6 +750,7 @@
 		} else {
 			v4l2_pipeline_pm_put(&node->vdev.entity);
 			pm_runtime_put_sync(cif_dev->dev);
+			priv->mode.rdbk_mode = RKISP_VICAP_RDBK_AIQ;
 		}
 		v4l2_info(&node->vdev, "s_power %d, entity use_count %d\n",
 			  on, node->vdev.entity.use_count);
@@ -779,10 +806,11 @@
 		return -EINVAL;
 
 	rx_buf = to_cif_rx_buf(dbufs);
-
+	v4l2_dbg(rkcif_debug, 3, &cif_dev->v4l2_dev, "buf back to vicap 0x%x\n",
+		 (u32)rx_buf->dummy.dma_addr);
 	spin_lock_irqsave(&stream->vbq_lock, flags);
-	stream->buf_num_toisp++;
 	stream->last_rx_buf_idx = dbufs->sequence + 1;
+	atomic_inc(&stream->buf_cnt);
 
 	if (!list_empty(&stream->rx_buf_head) &&
 	    cif_dev->is_thunderboot &&
@@ -791,6 +819,8 @@
 		spin_lock_irqsave(&cif_dev->buffree_lock, buffree_flags);
 		list_add_tail(&rx_buf->list_free, &priv->buf_free_list);
 		spin_unlock_irqrestore(&cif_dev->buffree_lock, buffree_flags);
+		atomic_dec(&stream->buf_cnt);
+		stream->total_buf_num--;
 		schedule_work(&priv->buffree_work.work);
 		is_free = true;
 	}
diff --git a/kernel/drivers/media/platform/rockchip/cif/version.h b/kernel/drivers/media/platform/rockchip/cif/version.h
index 627b4a3..dbebed0c 100644
--- a/kernel/drivers/media/platform/rockchip/cif/version.h
+++ b/kernel/drivers/media/platform/rockchip/cif/version.h
@@ -67,6 +67,8 @@
  *7. add keepint time to csi2 err for resetting
  *8. mipi supports pdaf/embedded data
  *9. mipi supports interlaced capture
+ *v0.2.0
+ *1. vicap support combine multi mipi dev to one dev, this function is mainly used for rk3588
  */
 
 #define RKCIF_DRIVER_VERSION RKCIF_API_VERSION
diff --git a/kernel/drivers/media/platform/rockchip/hdmirx/Kconfig b/kernel/drivers/media/platform/rockchip/hdmirx/Kconfig
index 9933c2e..be9ea70 100644
--- a/kernel/drivers/media/platform/rockchip/hdmirx/Kconfig
+++ b/kernel/drivers/media/platform/rockchip/hdmirx/Kconfig
@@ -1,5 +1,16 @@
 # SPDX-License-Identifier: GPL-2.0
 
+config VIDEO_ROCKCHIP_HDMIRX_CLASS
+	tristate "Rockchip HDMI Receiver Devices Class Support"
+	help
+	  There are many hdmirx devices in Rockchip SOCs, eg.
+	  rkhdmirx rk628 lut6911 it6616
+	  This driver create a class for those hdmirx devices
+	  And hdmirx drivers can add hdmirx properties for those
+	  hdmirx devices
+
+	  To compile this driver as a module, choose M here.
+
 config VIDEO_ROCKCHIP_HDMIRX
 	tristate "Rockchip HDMI Receiver driver"
 	depends on VIDEO_V4L2
@@ -8,6 +19,7 @@
 	select VIDEO_V4L2_SUBDEV_API
 	select VIDEOBUF2_DMA_CONTIG
 	select HDMI
+	select VIDEO_ROCKCHIP_HDMIRX_CLASS
 	help
 	  Support for Rockchip HDMI RX PHY and Controller.
 	  This driver supports HDMI 2.0 version.
diff --git a/kernel/drivers/media/platform/rockchip/hdmirx/Makefile b/kernel/drivers/media/platform/rockchip/hdmirx/Makefile
index 280fc19..9e7fb30 100644
--- a/kernel/drivers/media/platform/rockchip/hdmirx/Makefile
+++ b/kernel/drivers/media/platform/rockchip/hdmirx/Makefile
@@ -1,4 +1,6 @@
 # SPDX-License-Identifier: GPL-2.0
+rockchip-hdmirx-class-objs := rk_hdmirx_class.o
 rockchip-hdmirx-objs := rk_hdmirx.o rk_hdmirx_cec.o rk_hdmirx_hdcp.o
 
+obj-$(CONFIG_VIDEO_ROCKCHIP_HDMIRX_CLASS) += rockchip-hdmirx-class.o
 obj-$(CONFIG_VIDEO_ROCKCHIP_HDMIRX) += rockchip-hdmirx.o
diff --git a/kernel/drivers/media/platform/rockchip/hdmirx/rk_hdmirx.c b/kernel/drivers/media/platform/rockchip/hdmirx/rk_hdmirx.c
index 067314d..dcd49b6 100644
--- a/kernel/drivers/media/platform/rockchip/hdmirx/rk_hdmirx.c
+++ b/kernel/drivers/media/platform/rockchip/hdmirx/rk_hdmirx.c
@@ -45,11 +45,11 @@
 #include <media/videobuf2-v4l2.h>
 #include <soc/rockchip/rockchip-system-status.h>
 #include <sound/hdmi-codec.h>
+#include <linux/rk_hdmirx_class.h>
 #include "rk_hdmirx.h"
 #include "rk_hdmirx_cec.h"
 #include "rk_hdmirx_hdcp.h"
 
-static struct class *hdmirx_class;
 static int debug;
 module_param(debug, int, 0644);
 MODULE_PARM_DESC(debug, "debug level (0-3)");
@@ -269,6 +269,7 @@
 static void hdmirx_cancel_cpu_limit_freq(struct rk_hdmirx_dev *hdmirx_dev);
 static void hdmirx_plugout(struct rk_hdmirx_dev *hdmirx_dev);
 static void process_signal_change(struct rk_hdmirx_dev *hdmirx_dev);
+static void hdmirx_interrupts_setup(struct rk_hdmirx_dev *hdmirx_dev, bool en);
 
 static u8 edid_init_data_340M[] = {
 	0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00,
@@ -529,6 +530,16 @@
 	struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
 	u32 dma_cfg1;
 
+	if (port_no_link(hdmirx_dev)) {
+		v4l2_err(v4l2_dev, "%s port has no link!\n", __func__);
+		return -ENOLINK;
+	}
+
+	if (signal_not_lock(hdmirx_dev)) {
+		v4l2_err(v4l2_dev, "%s signal is not locked!\n", __func__);
+		return -ENOLCK;
+	}
+
 	*timings = hdmirx_dev->timings;
 	dma_cfg1 = hdmirx_readl(hdmirx_dev, DMA_CONFIG1);
 	v4l2_dbg(1, debug, v4l2_dev, "%s: pix_fmt: %s, DMA_CONFIG1:%#x\n",
@@ -615,8 +626,10 @@
 static void hdmirx_get_pix_fmt(struct rk_hdmirx_dev *hdmirx_dev)
 {
 	u32 val;
+	int timeout = 10;
 	struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
 
+try_loop:
 	val = hdmirx_readl(hdmirx_dev, DMA_STATUS11);
 	hdmirx_dev->pix_fmt = val & HDMIRX_FORMAT_MASK;
 
@@ -635,11 +648,16 @@
 		break;
 
 	default:
+		if (timeout-- > 0) {
+			usleep_range(200 * 1000, 200 * 1010);
+			v4l2_err(v4l2_dev, "%s: get format failed, read again!\n", __func__);
+			goto try_loop;
+		}
+		hdmirx_dev->pix_fmt = HDMIRX_RGB888;
+		hdmirx_dev->cur_fmt_fourcc = V4L2_PIX_FMT_BGR24;
 		v4l2_err(v4l2_dev,
 			"%s: err pix_fmt: %d, set RGB888 as default\n",
 			__func__, hdmirx_dev->pix_fmt);
-		hdmirx_dev->pix_fmt = HDMIRX_RGB888;
-		hdmirx_dev->cur_fmt_fourcc = V4L2_PIX_FMT_BGR24;
 		break;
 	}
 
@@ -880,9 +898,12 @@
 	struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
 	u32 last_w, last_h;
 	struct v4l2_bt_timings *bt = &timings->bt;
+	enum hdmirx_pix_fmt last_fmt;
 
 	last_w = 0;
 	last_h = 0;
+	last_fmt = HDMIRX_RGB888;
+
 	for (i = 0; i < try_cnt; i++) {
 		ret = hdmirx_get_detected_timings(hdmirx_dev, timings, from_dma);
 
@@ -891,7 +912,8 @@
 			last_h = bt->height;
 		}
 
-		if (ret || (last_w != bt->width) || (last_h != bt->height))
+		if (ret || (last_w != bt->width) || (last_h != bt->height)
+			|| (last_fmt != hdmirx_dev->pix_fmt))
 			cnt = 0;
 		else
 			cnt++;
@@ -901,6 +923,7 @@
 
 		last_w = bt->width;
 		last_h = bt->height;
+		last_fmt = hdmirx_dev->pix_fmt;
 		usleep_range(10*1000, 10*1100);
 	}
 
@@ -1545,7 +1568,7 @@
 	}
 
 	hdmirx_reset_dma(hdmirx_dev);
-	usleep_range(200*1000, 200*1010);
+	usleep_range(500*1000, 500*1010);
 	hdmirx_format_change(hdmirx_dev);
 
 	return 0;
@@ -2349,6 +2372,7 @@
 			FIFO_UNDERFLOW_INT_EN |
 			HDMIRX_AXI_ERROR_INT_EN, 0);
 	hdmirx_reset_dma(hdmirx_dev);
+	hdmirx_interrupts_setup(hdmirx_dev, false);
 	v4l2_event_queue(&stream->vdev, &evt_signal_lost);
 	if (hdmirx_dev->hdcp && hdmirx_dev->hdcp->hdcp_stop)
 		hdmirx_dev->hdcp->hdcp_stop(hdmirx_dev->hdcp);
@@ -2459,13 +2483,28 @@
 	hdmirx_writel(hdmirx_dev, MAINUNIT_2_INT_FORCE, 0x0);
 }
 
+/*
+ * In the normal preview, some scenarios will trigger the change interrupt
+ * by mistake, and the trigger source of the interrupt needs to be detected
+ * to avoid the problem.
+ */
 static void pkt_0_int_handler(struct rk_hdmirx_dev *hdmirx_dev,
 		int status, bool *handled)
 {
 	struct v4l2_device *v4l2_dev = &hdmirx_dev->v4l2_dev;
+	u32 pre_fmt_fourcc = hdmirx_dev->cur_fmt_fourcc;
+	u32 pre_color_range = hdmirx_dev->cur_color_range;
+	u32 pre_color_space = hdmirx_dev->cur_color_space;
 
 	if ((status & PKTDEC_AVIIF_CHG_IRQ)) {
-		process_signal_change(hdmirx_dev);
+		hdmirx_get_color_range(hdmirx_dev);
+		hdmirx_get_color_space(hdmirx_dev);
+		hdmirx_get_pix_fmt(hdmirx_dev);
+		if (hdmirx_dev->cur_fmt_fourcc != pre_fmt_fourcc ||
+		    hdmirx_dev->cur_color_range != pre_color_range ||
+		    hdmirx_dev->cur_color_space != pre_color_space) {
+			process_signal_change(hdmirx_dev);
+		}
 		v4l2_dbg(2, debug, v4l2_dev, "%s: ptk0_st:%#x\n",
 				__func__, status);
 		*handled = true;
@@ -3158,7 +3197,7 @@
 							struct rk_hdmirx_dev,
 							delayed_work_audio);
 	struct hdmirx_audiostate *as = &hdmirx_dev->audio_state;
-	u32 fs_audio, ch_audio;
+	u32 fs_audio, ch_audio, sample_flat;
 	int cur_state, init_state, pre_state, fifo_status2;
 	unsigned long delay = 200;
 
@@ -3223,6 +3262,10 @@
 		}
 	}
 	as->pre_state = cur_state;
+
+	sample_flat = hdmirx_readl(hdmirx_dev, AUDIO_PROC_STATUS1) & AUD_SAMPLE_FLAT;
+	hdmirx_update_bits(hdmirx_dev, AUDIO_PROC_CONFIG0, I2S_EN, sample_flat ? 0 : I2S_EN);
+
 exit:
 	schedule_delayed_work_on(hdmirx_dev->bound_cpu,
 			&hdmirx_dev->delayed_work_audio,
@@ -3243,7 +3286,6 @@
 	plugin = tx_5v_power_present(hdmirx_dev);
 	v4l2_dbg(1, debug, v4l2_dev, "%s: plugin:%d\n", __func__, plugin);
 	if (plugin) {
-		hdmirx_interrupts_setup(hdmirx_dev, false);
 		hdmirx_submodule_init(hdmirx_dev);
 		hdmirx_update_bits(hdmirx_dev, SCDC_CONFIG, POWERPROVIDED,
 					POWERPROVIDED);
@@ -4280,7 +4322,7 @@
 	if (ret)
 		goto err_unreg_video_dev;
 
-	hdmirx_dev->classdev = device_create_with_groups(hdmirx_class,
+	hdmirx_dev->classdev = device_create_with_groups(rk_hdmirx_class(),
 							 dev, MKDEV(0, 0),
 							 hdmirx_dev,
 							 hdmirx_groups,
@@ -4438,9 +4480,6 @@
 
 static int __init hdmirx_init(void)
 {
-	hdmirx_class = class_create(THIS_MODULE, "hdmirx");
-	if (IS_ERR(hdmirx_class))
-		return PTR_ERR(hdmirx_class);
 	return platform_driver_register(&hdmirx_driver);
 }
 module_init(hdmirx_init);
@@ -4448,7 +4487,6 @@
 static void __exit hdmirx_exit(void)
 {
 	platform_driver_unregister(&hdmirx_driver);
-	class_destroy(hdmirx_class);
 }
 module_exit(hdmirx_exit);
 
diff --git a/kernel/drivers/media/platform/rockchip/hdmirx/rk_hdmirx.h b/kernel/drivers/media/platform/rockchip/hdmirx/rk_hdmirx.h
index a1af89e..d49f7c4 100644
--- a/kernel/drivers/media/platform/rockchip/hdmirx/rk_hdmirx.h
+++ b/kernel/drivers/media/platform/rockchip/hdmirx/rk_hdmirx.h
@@ -228,6 +228,7 @@
 #define AUDIO_PROC_CONFIG3			0x048c
 #define AUDIO_PROC_STATUS1			0x0490
 #define AUD_SAMPLE_PRESENT			GENMASK(20, 17)
+#define AUD_SAMPLE_FLAT				GENMASK(16, 13)
 #define SCDC_CONFIG				0x0580
 #define HPDLOW					BIT(1)
 #define POWERPROVIDED				BIT(0)
diff --git a/kernel/drivers/media/platform/rockchip/hdmirx/rk_hdmirx_class.c b/kernel/drivers/media/platform/rockchip/hdmirx/rk_hdmirx_class.c
new file mode 100644
index 0000000..1aa66d3
--- /dev/null
+++ b/kernel/drivers/media/platform/rockchip/hdmirx/rk_hdmirx_class.c
@@ -0,0 +1,37 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2023 Rockchip Electronics Co. Ltd.
+ *
+ * Author: Shunhua Lan <lsh@rock-chips.com>
+ */
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/rk_hdmirx_class.h>
+
+static struct class *hdmirx_class;
+
+struct class *rk_hdmirx_class(void)
+{
+	return hdmirx_class;
+}
+EXPORT_SYMBOL(rk_hdmirx_class);
+
+static int __init rk_hdmirx_class_init(void)
+{
+	hdmirx_class = class_create(THIS_MODULE, "hdmirx");
+	if (IS_ERR(hdmirx_class))
+		return PTR_ERR(hdmirx_class);
+	return 0;
+}
+subsys_initcall(rk_hdmirx_class_init)
+
+static void __exit rk_hdmirx_class_exit(void)
+{
+	class_destroy(hdmirx_class);
+}
+module_exit(rk_hdmirx_class_exit);
+
+MODULE_DESCRIPTION("Rockchip HDMI Receiver Class Driver");
+MODULE_LICENSE("GPL");
diff --git a/kernel/drivers/media/platform/rockchip/isp/capture.c b/kernel/drivers/media/platform/rockchip/isp/capture.c
index fcad97f..a43e6e4 100644
--- a/kernel/drivers/media/platform/rockchip/isp/capture.c
+++ b/kernel/drivers/media/platform/rockchip/isp/capture.c
@@ -626,7 +626,7 @@
 
 		max_rsz->width = ALIGN(DIV_ROUND_UP(input_win->width, div), 4);
 		max_rsz->height = DIV_ROUND_UP(input_win->height, div);
-	} else if (dev->hw_dev->is_unite) {
+	} else if (dev->hw_dev->unite) {
 		/* scale down only for unite mode */
 		max_rsz->width = min_t(int, input_win->width, cfg->max_rsz_width);
 		max_rsz->height = min_t(int, input_win->height, cfg->max_rsz_height);
@@ -1140,7 +1140,8 @@
 
 	if (dev->isp_ver != ISP_V32 ||
 	    dev->hw_dev->dev_link_num > 1 ||
-	    !stream->ops->set_wrap) {
+	    !stream->ops->set_wrap ||
+	    dev->hw_dev->unite) {
 		v4l2_err(&dev->v4l2_dev,
 			 "wrap only support for single sensor and mainpath\n");
 		return -EINVAL;
@@ -1465,7 +1466,7 @@
 					    const struct v4l2_rect *in)
 {
 	struct rkisp_device *dev = stream->ispdev;
-	bool is_unite = dev->hw_dev->is_unite;
+	bool is_unite = !!dev->hw_dev->unite;
 	u32 align = is_unite ? 4 : 2;
 
 	/* Not crop for MP bayer raw data and dmatx path */
@@ -1616,7 +1617,9 @@
 	if (ispdev->isp_ver != ISP_V32)
 		return;
 
+	mutex_lock(&ispdev->hw_dev->dev_lock);
 	rkisp_chk_tb_over(ispdev);
+	mutex_unlock(&ispdev->hw_dev->dev_lock);
 	if (ispdev->tb_head.complete != RKISP_TB_OK)
 		return;
 	ret = v4l2_pipeline_pm_get(&stream->vnode.vdev.entity);
@@ -1733,17 +1736,21 @@
 		st_cfg->max_rsz_height = CIF_ISP_INPUT_H_MAX_V21;
 		ret = rkisp_register_stream_v21(dev);
 	} else if (dev->isp_ver == ISP_V30) {
-		st_cfg->max_rsz_width = dev->hw_dev->is_unite ?
+		st_cfg->max_rsz_width = dev->hw_dev->unite ?
 					CIF_ISP_INPUT_W_MAX_V30_UNITE : CIF_ISP_INPUT_W_MAX_V30;
-		st_cfg->max_rsz_height = dev->hw_dev->is_unite ?
+		st_cfg->max_rsz_height = dev->hw_dev->unite ?
 					 CIF_ISP_INPUT_H_MAX_V30_UNITE : CIF_ISP_INPUT_H_MAX_V30;
 		ret = rkisp_register_stream_v30(dev);
 	} else if (dev->isp_ver == ISP_V32) {
-		st_cfg->max_rsz_width = CIF_ISP_INPUT_W_MAX_V32;
-		st_cfg->max_rsz_height = CIF_ISP_INPUT_H_MAX_V32;
+		st_cfg->max_rsz_width = dev->hw_dev->unite ?
+					CIF_ISP_INPUT_W_MAX_V32_UNITE : CIF_ISP_INPUT_W_MAX_V32;
+		st_cfg->max_rsz_height = dev->hw_dev->unite ?
+					CIF_ISP_INPUT_H_MAX_V32_UNITE : CIF_ISP_INPUT_H_MAX_V32;
 		st_cfg = &rkisp_sp_stream_config;
-		st_cfg->max_rsz_width = CIF_ISP_INPUT_W_MAX_V32;
-		st_cfg->max_rsz_height = CIF_ISP_INPUT_H_MAX_V32;
+		st_cfg->max_rsz_width = dev->hw_dev->unite ?
+					CIF_ISP_INPUT_W_MAX_V32_UNITE : CIF_ISP_INPUT_W_MAX_V32;
+		st_cfg->max_rsz_height = dev->hw_dev->unite ?
+					 CIF_ISP_INPUT_H_MAX_V32_UNITE : CIF_ISP_INPUT_H_MAX_V32;
 		ret = rkisp_register_stream_v32(dev);
 	} else if (dev->isp_ver == ISP_V32_L) {
 		st_cfg->max_rsz_width = CIF_ISP_INPUT_W_MAX_V32_L;
diff --git a/kernel/drivers/media/platform/rockchip/isp/capture_v21.c b/kernel/drivers/media/platform/rockchip/isp/capture_v21.c
index 7983651..67cb9f6 100644
--- a/kernel/drivers/media/platform/rockchip/isp/capture_v21.c
+++ b/kernel/drivers/media/platform/rockchip/isp/capture_v21.c
@@ -1206,6 +1206,9 @@
 	unsigned long lock_flags = 0;
 	int i = 0;
 
+	if (stream->id == RKISP_STREAM_VIR)
+		return 0;
+
 	if (!stream->next_buf && stream->streaming &&
 	    dev->dmarx_dev.trigger == T_MANUAL &&
 	    is_rdbk_stream(stream))
@@ -1218,6 +1221,7 @@
 	    (!interlaced ||
 	     (stream->u.sp.field_rec == RKISP_FIELD_ODD &&
 	      stream->u.sp.field == RKISP_FIELD_EVEN))) {
+		struct rkisp_stream *vir = &dev->cap_dev.stream[RKISP_STREAM_VIR];
 		struct vb2_buffer *vb2_buf = &stream->curr_buf->vb.vb2_buf;
 		u64 ns = 0;
 
@@ -1268,7 +1272,16 @@
 				rdbk_frame_end(stream);
 			}
 		} else {
-			rkisp_stream_buf_done(stream, stream->curr_buf);
+			if (vir->streaming && vir->conn_id == stream->id) {
+				spin_lock_irqsave(&vir->vbq_lock, lock_flags);
+				list_add_tail(&stream->curr_buf->queue,
+					      &dev->cap_dev.vir_cpy.queue);
+				spin_unlock_irqrestore(&vir->vbq_lock, lock_flags);
+				if (!completion_done(&dev->cap_dev.vir_cpy.cmpl))
+					complete(&dev->cap_dev.vir_cpy.cmpl);
+			} else {
+				rkisp_stream_buf_done(stream, stream->curr_buf);
+			}
 		}
 
 		stream->curr_buf = NULL;
@@ -1378,6 +1391,98 @@
 		CIF_MI_CTRL_BURST_LEN_CHROM_16;
 	stream->interlaced = false;
 }
+
+static void vir_cpy_image(struct work_struct *work)
+{
+	struct rkisp_vir_cpy *cpy =
+	container_of(work, struct rkisp_vir_cpy, work);
+	struct rkisp_stream *vir = cpy->stream;
+	struct rkisp_buffer *src_buf = NULL;
+	unsigned long lock_flags = 0;
+	u32 i;
+
+	v4l2_dbg(1, rkisp_debug, &vir->ispdev->v4l2_dev,
+		 "%s enter\n", __func__);
+
+	vir->streaming = true;
+	spin_lock_irqsave(&vir->vbq_lock, lock_flags);
+	if (!list_empty(&cpy->queue)) {
+		src_buf = list_first_entry(&cpy->queue,
+				struct rkisp_buffer, queue);
+		list_del(&src_buf->queue);
+	}
+	spin_unlock_irqrestore(&vir->vbq_lock, lock_flags);
+
+	while (src_buf || vir->streaming) {
+		if (vir->stopping || !vir->streaming)
+			goto end;
+
+		if (!src_buf)
+			wait_for_completion(&cpy->cmpl);
+
+		vir->frame_end = false;
+		spin_lock_irqsave(&vir->vbq_lock, lock_flags);
+
+		if (!src_buf && !list_empty(&cpy->queue)) {
+			src_buf = list_first_entry(&cpy->queue,
+					struct rkisp_buffer, queue);
+			list_del(&src_buf->queue);
+		}
+
+		if (src_buf && !vir->curr_buf && !list_empty(&vir->buf_queue)) {
+			vir->curr_buf = list_first_entry(&vir->buf_queue,
+					struct rkisp_buffer, queue);
+			list_del(&vir->curr_buf->queue);
+		}
+		spin_unlock_irqrestore(&vir->vbq_lock, lock_flags);
+
+		if (!vir->curr_buf || !src_buf)
+			goto end;
+
+		for (i = 0; i < vir->out_isp_fmt.mplanes; i++) {
+			u32 payload_size = vir->out_fmt.plane_fmt[i].sizeimage;
+			void *src = vb2_plane_vaddr(&src_buf->vb.vb2_buf, i);
+			void *dst = vb2_plane_vaddr(&vir->curr_buf->vb.vb2_buf, i);
+
+			if (!src || !dst)
+				break;
+			vb2_set_plane_payload(&vir->curr_buf->vb.vb2_buf, i, payload_size);
+			memcpy(dst, src, payload_size);
+		}
+
+		vir->curr_buf->vb.sequence = src_buf->vb.sequence;
+		vir->curr_buf->vb.vb2_buf.timestamp = src_buf->vb.vb2_buf.timestamp;
+		vb2_buffer_done(&vir->curr_buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
+		vir->curr_buf = NULL;
+end:
+		if (src_buf)
+			vb2_buffer_done(&src_buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
+		src_buf = NULL;
+		spin_lock_irqsave(&vir->vbq_lock, lock_flags);
+
+		if (!list_empty(&cpy->queue)) {
+			src_buf = list_first_entry(&cpy->queue,
+					struct rkisp_buffer, queue);
+			list_del(&src_buf->queue);
+		} else if (vir->stopping) {
+			vir->streaming = false;
+		}
+
+		spin_unlock_irqrestore(&vir->vbq_lock, lock_flags);
+	}
+
+	vir->frame_end = true;
+
+	if (vir->stopping) {
+		vir->stopping = false;
+		vir->streaming = false;
+		wake_up(&vir->done);
+	}
+
+	v4l2_dbg(1, rkisp_debug, &vir->ispdev->v4l2_dev,
+		 "%s exit\n", __func__);
+}
+
 
 /*
  * Most of registers inside rockchip isp1 have shadow register since
@@ -1571,6 +1676,21 @@
 	if (!stream->streaming)
 		goto end;
 
+	if (stream->id == RKISP_STREAM_VIR) {
+		stream->stopping = true;
+		wait_event_timeout(stream->done,
+				   stream->frame_end,
+				   msecs_to_jiffies(500));
+		stream->streaming = false;
+		stream->stopping = false;
+		destroy_buf_queue(stream, VB2_BUF_STATE_ERROR);
+
+		if (!completion_done(&dev->cap_dev.vir_cpy.cmpl))
+			complete(&dev->cap_dev.vir_cpy.cmpl);
+		stream->conn_id = -1;
+		goto end;
+	}
+
 	rkisp_stream_stop(stream);
 	if (stream->id == RKISP_STREAM_MP ||
 	    stream->id == RKISP_STREAM_SP) {
@@ -1650,6 +1770,28 @@
 	if (WARN_ON(stream->streaming)) {
 		mutex_unlock(&dev->hw_dev->dev_lock);
 		return -EBUSY;
+	}
+
+	if (stream->id == RKISP_STREAM_VIR) {
+		struct rkisp_stream *t = &dev->cap_dev.stream[stream->conn_id];
+
+		if (t->streaming) {
+			INIT_WORK(&dev->cap_dev.vir_cpy.work, vir_cpy_image);
+			init_completion(&dev->cap_dev.vir_cpy.cmpl);
+			INIT_LIST_HEAD(&dev->cap_dev.vir_cpy.queue);
+			dev->cap_dev.vir_cpy.stream = stream;
+			schedule_work(&dev->cap_dev.vir_cpy.work);
+			ret = 0;
+		} else {
+			v4l2_err(&dev->v4l2_dev,
+				 "no stream enable for iqtool\n");
+			destroy_buf_queue(stream, VB2_BUF_STATE_QUEUED);
+			ret = -EINVAL;
+		}
+
+		mutex_unlock(&dev->hw_dev->dev_lock);
+
+		return ret;
 	}
 
 	memset(&stream->dbg, 0, sizeof(stream->dbg));
@@ -1787,7 +1929,7 @@
 
 	switch (id) {
 	case RKISP_STREAM_SP:
-		strlcpy(vdev->name, SP_VDEV_NAME,
+		strscpy(vdev->name, SP_VDEV_NAME,
 			sizeof(vdev->name));
 		stream->ops = &rkisp_sp_streams_ops;
 		stream->config = &rkisp_sp_stream_config;
@@ -1795,25 +1937,32 @@
 		stream->config->fmt_size = ARRAY_SIZE(sp_fmts);
 		break;
 	case RKISP_STREAM_DMATX0:
-		strlcpy(vdev->name, DMATX0_VDEV_NAME,
+		strscpy(vdev->name, DMATX0_VDEV_NAME,
 			sizeof(vdev->name));
 		stream->ops = &rkisp2_dmatx0_streams_ops;
 		stream->config = &rkisp2_dmatx0_stream_config;
 		break;
 	case RKISP_STREAM_DMATX2:
-		strlcpy(vdev->name, DMATX2_VDEV_NAME,
+		strscpy(vdev->name, DMATX2_VDEV_NAME,
 			sizeof(vdev->name));
 		stream->ops = &rkisp2_dmatx2_streams_ops;
 		stream->config = &rkisp2_dmatx1_stream_config;
 		break;
 	case RKISP_STREAM_DMATX3:
-		strlcpy(vdev->name, DMATX3_VDEV_NAME,
+		strscpy(vdev->name, DMATX3_VDEV_NAME,
 			sizeof(vdev->name));
 		stream->ops = &rkisp2_dmatx3_streams_ops;
 		stream->config = &rkisp2_dmatx3_stream_config;
 		break;
+	case RKISP_STREAM_VIR:
+		strscpy(vdev->name, VIR_VDEV_NAME,
+			sizeof(vdev->name));
+		stream->ops = NULL;
+		stream->config = &rkisp_mp_stream_config;
+		stream->conn_id = -1;
+		break;
 	default:
-		strlcpy(vdev->name, MP_VDEV_NAME,
+		strscpy(vdev->name, MP_VDEV_NAME,
 			sizeof(vdev->name));
 		stream->ops = &rkisp_mp_streams_ops;
 		stream->config = &rkisp_mp_stream_config;
@@ -1857,8 +2006,13 @@
 	ret = rkisp_stream_init(dev, RKISP_STREAM_DMATX3);
 	if (ret < 0)
 		goto err_free_tx2;
+	ret = rkisp_stream_init(dev, RKISP_STREAM_VIR);
+	if (ret < 0)
+		goto err_free_tx3;
 
 	return 0;
+err_free_tx3:
+	rkisp_unregister_stream_vdev(&cap_dev->stream[RKISP_STREAM_DMATX3]);
 err_free_tx2:
 	rkisp_unregister_stream_vdev(&cap_dev->stream[RKISP_STREAM_DMATX2]);
 err_free_tx0:
@@ -1886,6 +2040,8 @@
 	rkisp_unregister_stream_vdev(stream);
 	stream = &cap_dev->stream[RKISP_STREAM_DMATX3];
 	rkisp_unregister_stream_vdev(stream);
+	stream = &cap_dev->stream[RKISP_STREAM_VIR];
+	rkisp_unregister_stream_vdev(stream);
 }
 
 /****************  Interrupter Handler ****************/
@@ -1905,7 +2061,7 @@
 	for (i = 0; i < RKISP_MAX_STREAM; ++i) {
 		stream = &dev->cap_dev.stream[i];
 
-		if (!(mis_val & CIF_MI_FRAME(stream)))
+		if (!(mis_val & CIF_MI_FRAME(stream)) || stream->id == RKISP_STREAM_VIR)
 			continue;
 
 		if (i == RKISP_STREAM_DMATX0)
diff --git a/kernel/drivers/media/platform/rockchip/isp/capture_v30.c b/kernel/drivers/media/platform/rockchip/isp/capture_v30.c
index c3ef571..5f6616c 100644
--- a/kernel/drivers/media/platform/rockchip/isp/capture_v30.c
+++ b/kernel/drivers/media/platform/rockchip/isp/capture_v30.c
@@ -332,7 +332,7 @@
 	if (dcrop->width == input_win->width &&
 	    dcrop->height == input_win->height &&
 	    dcrop->left == 0 && dcrop->top == 0 &&
-	    !dev->hw_dev->is_unite) {
+	    !dev->hw_dev->unite) {
 		rkisp_disable_dcrop(stream, async);
 		v4l2_dbg(1, rkisp_debug, &dev->v4l2_dev,
 			 "stream %d crop disabled\n", stream->id);
@@ -472,7 +472,7 @@
 {
 	struct rkisp_device *dev = stream->ispdev;
 	struct v4l2_pix_format_mplane *out_fmt = &stream->out_fmt;
-	bool is_unite = dev->hw_dev->is_unite;
+	bool is_unite = !!dev->hw_dev->unite;
 	u32 val, mask;
 
        /*
@@ -480,26 +480,26 @@
 	* memory plane formats, so calculate the size explicitly.
 	*/
 	val = out_fmt->plane_fmt[0].bytesperline * out_fmt->height;
-	rkisp_unite_write(dev, stream->config->mi.y_size_init, val, false, is_unite);
+	rkisp_unite_write(dev, stream->config->mi.y_size_init, val, false);
 
 	val = out_fmt->plane_fmt[1].sizeimage;
-	rkisp_unite_write(dev, stream->config->mi.cb_size_init, val, false, is_unite);
+	rkisp_unite_write(dev, stream->config->mi.cb_size_init, val, false);
 
 	val = out_fmt->plane_fmt[2].sizeimage;
-	rkisp_unite_write(dev, stream->config->mi.cr_size_init, val, false, is_unite);
+	rkisp_unite_write(dev, stream->config->mi.cr_size_init, val, false);
 
 	val = is_unite ? out_fmt->width / 2 : out_fmt->width;
-	rkisp_unite_write(dev, ISP3X_MI_MP_WR_Y_PIC_WIDTH, val, false, is_unite);
+	rkisp_unite_write(dev, ISP3X_MI_MP_WR_Y_PIC_WIDTH, val, false);
 
 	val = out_fmt->height;
-	rkisp_unite_write(dev, ISP3X_MI_MP_WR_Y_PIC_HEIGHT, val, false, is_unite);
+	rkisp_unite_write(dev, ISP3X_MI_MP_WR_Y_PIC_HEIGHT, val, false);
 
 	val = out_fmt->plane_fmt[0].bytesperline;
-	rkisp_unite_write(dev, ISP3X_MI_MP_WR_Y_LLENGTH, val, false, is_unite);
+	rkisp_unite_write(dev, ISP3X_MI_MP_WR_Y_LLENGTH, val, false);
 
 	val = stream->out_isp_fmt.uv_swap ? ISP3X_MI_XTD_FORMAT_MP_UV_SWAP : 0;
 	mask = ISP3X_MI_XTD_FORMAT_MP_UV_SWAP;
-	rkisp_unite_set_bits(dev, ISP3X_MI_WR_XTD_FORMAT_CTRL, mask, val, false, is_unite);
+	rkisp_unite_set_bits(dev, ISP3X_MI_WR_XTD_FORMAT_CTRL, mask, val, false);
 
 	mask = ISP3X_MPFBC_FORCE_UPD | ISP3X_MP_YUV_MODE;
 	val = rkisp_read_reg_cache(dev, ISP3X_MPFBC_CTRL) & ~mask;
@@ -511,13 +511,13 @@
 		val |= ISP3X_SEPERATE_YUV_CFG;
 	else
 		val |= ISP3X_SEPERATE_YUV_CFG | ISP3X_MP_YUV_MODE;
-	rkisp_unite_write(dev, ISP3X_MPFBC_CTRL, val, false, is_unite);
+	rkisp_unite_write(dev, ISP3X_MPFBC_CTRL, val, false);
 
 	val = calc_burst_len(stream) | CIF_MI_CTRL_INIT_BASE_EN |
 		CIF_MI_CTRL_INIT_OFFSET_EN | CIF_MI_MP_AUTOUPDATE_ENABLE |
 		stream->out_isp_fmt.write_format;
 	mask = GENMASK(19, 16) | MI_CTRL_MP_FMT_MASK;
-	rkisp_unite_set_bits(dev, ISP3X_MI_WR_CTRL, mask, val, false, is_unite);
+	rkisp_unite_set_bits(dev, ISP3X_MI_WR_CTRL, mask, val, false);
 
 	mi_frame_end_int_enable(stream);
 	/* set up first buffer */
@@ -558,7 +558,7 @@
 	struct v4l2_pix_format_mplane *out_fmt = &stream->out_fmt;
 	struct ispsd_out_fmt *input_isp_fmt =
 			rkisp_get_ispsd_out_fmt(&dev->isp_sdev);
-	bool is_unite = dev->hw_dev->is_unite;
+	bool is_unite = !!dev->hw_dev->unite;
 	u32 sp_in_fmt, val, mask;
 
 	if (mbus_code_sp_in_fmt(input_isp_fmt->mbus_code,
@@ -572,26 +572,26 @@
 	* memory plane formats, so calculate the size explicitly.
 	*/
 	val = out_fmt->plane_fmt[0].bytesperline * out_fmt->height;
-	rkisp_unite_write(dev, stream->config->mi.y_size_init, val, false, is_unite);
+	rkisp_unite_write(dev, stream->config->mi.y_size_init, val, false);
 
 	val = out_fmt->plane_fmt[1].sizeimage;
-	rkisp_unite_write(dev, stream->config->mi.cb_size_init, val, false, is_unite);
+	rkisp_unite_write(dev, stream->config->mi.cb_size_init, val, false);
 
 	val = out_fmt->plane_fmt[2].sizeimage;
-	rkisp_unite_write(dev, stream->config->mi.cr_size_init, val, false, is_unite);
+	rkisp_unite_write(dev, stream->config->mi.cr_size_init, val, false);
 
 	val = is_unite ? out_fmt->width / 2 : out_fmt->width;
-	rkisp_unite_write(dev, ISP3X_MI_SP_WR_Y_PIC_WIDTH, val, false, is_unite);
+	rkisp_unite_write(dev, ISP3X_MI_SP_WR_Y_PIC_WIDTH, val, false);
 
 	val = out_fmt->height;
-	rkisp_unite_write(dev, ISP3X_MI_SP_WR_Y_PIC_HEIGHT, val, false, is_unite);
+	rkisp_unite_write(dev, ISP3X_MI_SP_WR_Y_PIC_HEIGHT, val, false);
 
 	val = stream->u.sp.y_stride;
-	rkisp_unite_write(dev, ISP3X_MI_SP_WR_Y_LLENGTH, val, false, is_unite);
+	rkisp_unite_write(dev, ISP3X_MI_SP_WR_Y_LLENGTH, val, false);
 
 	val = stream->out_isp_fmt.uv_swap ? ISP3X_MI_XTD_FORMAT_SP_UV_SWAP : 0;
 	mask = ISP3X_MI_XTD_FORMAT_SP_UV_SWAP;
-	rkisp_unite_set_bits(dev, ISP3X_MI_WR_XTD_FORMAT_CTRL, mask, val, false, is_unite);
+	rkisp_unite_set_bits(dev, ISP3X_MI_WR_XTD_FORMAT_CTRL, mask, val, false);
 
 	mask = ISP3X_MPFBC_FORCE_UPD | ISP3X_SP_YUV_MODE;
 	val = rkisp_read_reg_cache(dev, ISP3X_MPFBC_CTRL) & ~mask;
@@ -603,14 +603,14 @@
 		val |= ISP3X_SEPERATE_YUV_CFG;
 	else
 		val |= ISP3X_SEPERATE_YUV_CFG | ISP3X_SP_YUV_MODE;
-	rkisp_unite_write(dev, ISP3X_MPFBC_CTRL, val, false, is_unite);
+	rkisp_unite_write(dev, ISP3X_MPFBC_CTRL, val, false);
 
 	val = calc_burst_len(stream) | CIF_MI_CTRL_INIT_BASE_EN |
 		CIF_MI_CTRL_INIT_OFFSET_EN | stream->out_isp_fmt.write_format |
 		sp_in_fmt | stream->out_isp_fmt.output_format |
 		CIF_MI_SP_AUTOUPDATE_ENABLE;
 	mask = GENMASK(19, 16) | MI_CTRL_SP_FMT_MASK;
-	rkisp_unite_set_bits(dev, ISP3X_MI_WR_CTRL, mask, val, false, is_unite);
+	rkisp_unite_set_bits(dev, ISP3X_MI_WR_CTRL, mask, val, false);
 
 	mi_frame_end_int_enable(stream);
 	/* set up first buffer */
@@ -625,12 +625,12 @@
 	u32 h = ALIGN(stream->out_fmt.height, 16);
 	u32 w = ALIGN(stream->out_fmt.width, 16);
 	u32 offs = ALIGN(w * h / 16, RK_MPP_ALIGN);
-	bool is_unite = stream->ispdev->hw_dev->is_unite;
+	bool is_unite = !!stream->ispdev->hw_dev->unite;
 
 	rkisp_write(stream->ispdev, ISP3X_MPFBC_HEAD_OFFSET, offs, false);
-	rkisp_unite_write(stream->ispdev, ISP3X_MPFBC_VIR_WIDTH, w, false, is_unite);
-	rkisp_unite_write(stream->ispdev, ISP3X_MPFBC_PAYL_WIDTH, w, false, is_unite);
-	rkisp_unite_write(stream->ispdev, ISP3X_MPFBC_VIR_HEIGHT, h, false, is_unite);
+	rkisp_unite_write(stream->ispdev, ISP3X_MPFBC_VIR_WIDTH, w, false);
+	rkisp_unite_write(stream->ispdev, ISP3X_MPFBC_PAYL_WIDTH, w, false);
+	rkisp_unite_write(stream->ispdev, ISP3X_MPFBC_VIR_HEIGHT, h, false);
 	if (is_unite) {
 		u32 left_w = (stream->out_fmt.width / 2) & ~0xf;
 
@@ -638,8 +638,7 @@
 		rkisp_next_write(stream->ispdev, ISP3X_MPFBC_HEAD_OFFSET, offs, false);
 	}
 	rkisp_unite_set_bits(stream->ispdev, ISP3X_MI_WR_CTRL, 0,
-			     CIF_MI_CTRL_INIT_BASE_EN | CIF_MI_CTRL_INIT_OFFSET_EN,
-			     false, is_unite);
+			     CIF_MI_CTRL_INIT_BASE_EN | CIF_MI_CTRL_INIT_OFFSET_EN, false);
 	mi_frame_end_int_enable(stream);
 	/* set up first buffer */
 	mi_frame_end(stream, FRAME_INIT);
@@ -650,7 +649,7 @@
 {
 	struct v4l2_pix_format_mplane *out_fmt = &stream->out_fmt;
 	struct rkisp_device *dev = stream->ispdev;
-	bool is_unite = dev->hw_dev->is_unite;
+	bool is_unite = dev->hw_dev->unite;
 	u32 val, mask;
 
        /*
@@ -658,19 +657,19 @@
 	* memory plane formats, so calculate the size explicitly.
 	*/
 	val = out_fmt->plane_fmt[0].bytesperline * out_fmt->height;
-	rkisp_unite_write(dev, stream->config->mi.y_size_init, val, false, is_unite);
+	rkisp_unite_write(dev, stream->config->mi.y_size_init, val, false);
 
 	val = out_fmt->plane_fmt[1].sizeimage;
-	rkisp_unite_write(dev, stream->config->mi.cb_size_init, val, false, is_unite);
+	rkisp_unite_write(dev, stream->config->mi.cb_size_init, val, false);
 
 	val = is_unite ? out_fmt->width / 2 : out_fmt->width;
-	rkisp_unite_write(dev, ISP3X_MI_BP_WR_Y_PIC_WIDTH, val, false, is_unite);
+	rkisp_unite_write(dev, ISP3X_MI_BP_WR_Y_PIC_WIDTH, val, false);
 
 	val = out_fmt->height;
-	rkisp_unite_write(dev, ISP3X_MI_BP_WR_Y_PIC_HEIGHT, val, false, is_unite);
+	rkisp_unite_write(dev, ISP3X_MI_BP_WR_Y_PIC_HEIGHT, val, false);
 
 	val = out_fmt->plane_fmt[0].bytesperline;
-	rkisp_unite_write(dev, ISP3X_MI_BP_WR_Y_LLENGTH, val, false, is_unite);
+	rkisp_unite_write(dev, ISP3X_MI_BP_WR_Y_LLENGTH, val, false);
 
 	mask = ISP3X_MPFBC_FORCE_UPD | ISP3X_BP_YUV_MODE;
 	val = rkisp_read_reg_cache(dev, ISP3X_MPFBC_CTRL) & ~mask;
@@ -680,9 +679,9 @@
 		val |= ISP3X_SEPERATE_YUV_CFG;
 	else
 		val |= ISP3X_SEPERATE_YUV_CFG | ISP3X_BP_YUV_MODE;
-	rkisp_unite_write(dev, ISP3X_MPFBC_CTRL, val, false, is_unite);
+	rkisp_unite_write(dev, ISP3X_MPFBC_CTRL, val, false);
 	val = CIF_MI_CTRL_INIT_BASE_EN | CIF_MI_CTRL_INIT_OFFSET_EN;
-	rkisp_unite_set_bits(dev, ISP3X_MI_WR_CTRL, 0, val, false, is_unite);
+	rkisp_unite_set_bits(dev, ISP3X_MI_WR_CTRL, 0, val, false);
 	mi_frame_end_int_enable(stream);
 	/* set up first buffer */
 	mi_frame_end(stream, FRAME_INIT);
@@ -697,8 +696,7 @@
 
 	if (isp_fmt->fmt_type == FMT_BAYER)
 		val = CIF_MI_CTRL_RAW_ENABLE;
-	rkisp_unite_set_bits(stream->ispdev, ISP3X_MI_WR_CTRL, mask, val,
-			     false, stream->ispdev->hw_dev->is_unite);
+	rkisp_unite_set_bits(stream->ispdev, ISP3X_MI_WR_CTRL, mask, val, false);
 }
 
 static void sp_enable_mi(struct rkisp_stream *stream)
@@ -711,21 +709,18 @@
 	if (fmt->fmt_type == FMT_RGB &&
 	    dev->isp_sdev.quantization == V4L2_QUANTIZATION_FULL_RANGE)
 		val |= mask;
-	rkisp_unite_set_bits(stream->ispdev, ISP3X_MI_WR_CTRL,
-			     mask, val, false,
-			     stream->ispdev->hw_dev->is_unite);
+	rkisp_unite_set_bits(stream->ispdev, ISP3X_MI_WR_CTRL, mask, val, false);
 }
 
 static void fbc_enable_mi(struct rkisp_stream *stream)
 {
 	u32 val, mask = ISP3X_MPFBC_FORCE_UPD | ISP3X_MPFBC_YUV_MASK |
 			ISP3X_MPFBC_SPARSE_MODE;
-	bool is_unite = stream->ispdev->hw_dev->is_unite;
 
 	/* config no effect immediately, read back is shadow, get config value from cache */
 	val = rkisp_read_reg_cache(stream->ispdev, ISP3X_MPFBC_CTRL) & ~mask;
 	val |= stream->out_isp_fmt.write_format | ISP3X_HEAD_OFFSET_EN | ISP3X_MPFBC_EN;
-	rkisp_unite_write(stream->ispdev, ISP3X_MPFBC_CTRL, val, false, is_unite);
+	rkisp_unite_write(stream->ispdev, ISP3X_MPFBC_CTRL, val, false);
 }
 
 static void bp_enable_mi(struct rkisp_stream *stream)
@@ -733,36 +728,31 @@
 	u32 val = stream->out_isp_fmt.write_format |
 		ISP3X_BP_ENABLE | ISP3X_BP_AUTO_UPD;
 
-	rkisp_unite_write(stream->ispdev, ISP3X_MI_BP_WR_CTRL, val, false,
-			  stream->ispdev->hw_dev->is_unite);
+	rkisp_unite_write(stream->ispdev, ISP3X_MI_BP_WR_CTRL, val, false);
 }
 
 static void mp_disable_mi(struct rkisp_stream *stream)
 {
 	u32 mask = CIF_MI_CTRL_MP_ENABLE | CIF_MI_CTRL_RAW_ENABLE;
 
-	rkisp_unite_clear_bits(stream->ispdev, ISP3X_MI_WR_CTRL, mask, false,
-			       stream->ispdev->hw_dev->is_unite);
+	rkisp_unite_clear_bits(stream->ispdev, ISP3X_MI_WR_CTRL, mask, false);
 }
 
 static void sp_disable_mi(struct rkisp_stream *stream)
 {
-	rkisp_unite_clear_bits(stream->ispdev, ISP3X_MI_WR_CTRL, CIF_MI_CTRL_SP_ENABLE,
-			       false, stream->ispdev->hw_dev->is_unite);
+	rkisp_unite_clear_bits(stream->ispdev, ISP3X_MI_WR_CTRL, CIF_MI_CTRL_SP_ENABLE, false);
 }
 
 static void fbc_disable_mi(struct rkisp_stream *stream)
 {
 	u32 mask = ISP3X_MPFBC_FORCE_UPD | ISP3X_MPFBC_EN;
 
-	rkisp_unite_clear_bits(stream->ispdev, ISP3X_MPFBC_CTRL, mask,
-			       false, stream->ispdev->hw_dev->is_unite);
+	rkisp_unite_clear_bits(stream->ispdev, ISP3X_MPFBC_CTRL, mask, false);
 }
 
 static void bp_disable_mi(struct rkisp_stream *stream)
 {
-	rkisp_unite_clear_bits(stream->ispdev, ISP3X_MI_BP_WR_CTRL, ISP3X_BP_ENABLE,
-			       false, stream->ispdev->hw_dev->is_unite);
+	rkisp_unite_clear_bits(stream->ispdev, ISP3X_MI_BP_WR_CTRL, ISP3X_BP_ENABLE, false);
 }
 
 static void update_mi(struct rkisp_stream *stream)
@@ -786,24 +776,25 @@
 			rkisp_write(dev, reg, val, false);
 		}
 
-		if (dev->hw_dev->is_unite) {
+		if (dev->hw_dev->unite) {
 			u32 mult = stream->id != RKISP_STREAM_FBC ? 1 :
 				   (stream->out_isp_fmt.write_format ? 32 : 24);
+			u32 div = stream->out_isp_fmt.fourcc == V4L2_PIX_FMT_UYVY ? 1 : 2;
 
 			reg = stream->config->mi.y_base_ad_init;
 			val = stream->next_buf->buff_addr[RKISP_PLANE_Y];
-			val += ((stream->out_fmt.width / 2) & ~0xf);
+			val += ((stream->out_fmt.width / div) & ~0xf);
 			rkisp_next_write(dev, reg, val, false);
 
 			reg = stream->config->mi.cb_base_ad_init;
 			val = stream->next_buf->buff_addr[RKISP_PLANE_CB];
-			val += ((stream->out_fmt.width / 2) & ~0xf) * mult;
+			val += ((stream->out_fmt.width / div) & ~0xf) * mult;
 			rkisp_next_write(dev, reg, val, false);
 
 			if (stream->id != RKISP_STREAM_FBC && stream->id != RKISP_STREAM_BP) {
 				reg = stream->config->mi.cr_base_ad_init;
 				val = stream->next_buf->buff_addr[RKISP_PLANE_CR];
-				val += ((stream->out_fmt.width / 2) & ~0xf);
+				val += ((stream->out_fmt.width / div) & ~0xf);
 				rkisp_next_write(dev, reg, val, false);
 			}
 		}
@@ -817,22 +808,22 @@
 		stream->dbg.frameloss++;
 		val = dummy_buf->dma_addr;
 		reg = stream->config->mi.y_base_ad_init;
-		rkisp_unite_write(dev, reg, val, false, dev->hw_dev->is_unite);
+		rkisp_unite_write(dev, reg, val, false);
 		reg = stream->config->mi.cb_base_ad_init;
-		rkisp_unite_write(dev, reg, val, false, dev->hw_dev->is_unite);
+		rkisp_unite_write(dev, reg, val, false);
 		reg = stream->config->mi.cr_base_ad_init;
 		if (stream->id != RKISP_STREAM_FBC && stream->id != RKISP_STREAM_BP)
-			rkisp_unite_write(dev, reg, val, false, dev->hw_dev->is_unite);
+			rkisp_unite_write(dev, reg, val, false);
 	}
 
 	if (stream->id != RKISP_STREAM_FBC) {
 		reg = stream->config->mi.y_offs_cnt_init;
-		rkisp_unite_write(dev, reg, 0, false, dev->hw_dev->is_unite);
+		rkisp_unite_write(dev, reg, 0, false);
 		reg = stream->config->mi.cb_offs_cnt_init;
-		rkisp_unite_write(dev, reg, 0, false, dev->hw_dev->is_unite);
+		rkisp_unite_write(dev, reg, 0, false);
 		reg = stream->config->mi.cr_offs_cnt_init;
 		if (stream->id != RKISP_STREAM_BP)
-			rkisp_unite_write(dev, reg, 0, false, dev->hw_dev->is_unite);
+			rkisp_unite_write(dev, reg, 0, false);
 	}
 
 	v4l2_dbg(2, rkisp_debug, &dev->v4l2_dev,
@@ -841,7 +832,7 @@
 		 rkisp_read(dev, stream->config->mi.y_base_ad_init, false),
 		 rkisp_read(dev, stream->config->mi.cb_base_ad_init, false),
 		 rkisp_read(dev, stream->config->mi.y_base_ad_shd, true));
-	if (dev->hw_dev->is_unite)
+	if (dev->hw_dev->unite)
 		v4l2_dbg(2, rkisp_debug, &dev->v4l2_dev,
 			 "%s stream:%d Y:0x%x CB:0x%x | Y_SHD:0x%x, right\n",
 			 __func__, stream->id,
@@ -896,11 +887,10 @@
 {
 	struct rkisp_device *dev = stream->ispdev;
 	u32 val, mask = ISP3X_MPSELF_UPD | ISP3X_SPSELF_UPD | ISP3X_BPSELF_UPD;
-	bool is_unite = dev->hw_dev->is_unite;
 
 	if (stream->id == RKISP_STREAM_FBC) {
 		val = ISP3X_MPFBC_FORCE_UPD;
-		rkisp_unite_set_bits(dev, ISP3X_MPFBC_CTRL, 0, val, false, is_unite);
+		rkisp_unite_set_bits(dev, ISP3X_MPFBC_CTRL, 0, val, false);
 		return;
 	}
 
@@ -918,7 +908,7 @@
 		return;
 	}
 
-	rkisp_unite_set_bits(dev, ISP3X_MI_WR_CTRL2, mask, val, false, is_unite);
+	rkisp_unite_set_bits(dev, ISP3X_MI_WR_CTRL2, mask, val, false);
 }
 
 static int mi_frame_start(struct rkisp_stream *stream, u32 mis)
@@ -976,7 +966,10 @@
 		    (stream->frame_early && state == FRAME_IRQ))
 			goto end;
 	} else {
+		spin_lock_irqsave(&stream->vbq_lock, lock_flags);
 		buf = stream->curr_buf;
+		stream->curr_buf = NULL;
+		spin_unlock_irqrestore(&stream->vbq_lock, lock_flags);
 	}
 
 	if (buf) {
@@ -1686,7 +1679,7 @@
 	struct rkisp_stream *stream;
 	unsigned int i;
 
-	if (dev->hw_dev->is_unite) {
+	if (dev->hw_dev->unite) {
 		u32 val = rkisp_read(dev, ISP3X_MI_RIS, true);
 
 		if (val) {
diff --git a/kernel/drivers/media/platform/rockchip/isp/capture_v32.c b/kernel/drivers/media/platform/rockchip/isp/capture_v32.c
index 3b23e9c..c71a701 100644
--- a/kernel/drivers/media/platform/rockchip/isp/capture_v32.c
+++ b/kernel/drivers/media/platform/rockchip/isp/capture_v32.c
@@ -554,7 +554,8 @@
 
 	if (dcrop->width == input_win->width &&
 	    dcrop->height == input_win->height &&
-	    dcrop->left == 0 && dcrop->top == 0) {
+	    dcrop->left == 0 && dcrop->top == 0 &&
+	    !dev->hw_dev->unite) {
 		rkisp_disable_dcrop(stream, async);
 		v4l2_dbg(1, rkisp_debug, &dev->v4l2_dev,
 			 "stream %d crop disabled\n", stream->id);
@@ -704,29 +705,29 @@
 	/* in bytes for isp32 */
 	if (dev->isp_ver == ISP_V32 &&
 	    stream->out_isp_fmt.write_format != MI_CTRL_MP_WRITE_YUVINT)
-		rkisp_write(dev, ISP3X_MI_MP_WR_Y_LLENGTH, val, false);
+		rkisp_unite_write(dev, ISP3X_MI_MP_WR_Y_LLENGTH, val, false);
 	val /= DIV_ROUND_UP(fmt->bpp[0], 8);
 	/* in pixels for isp32 lite */
 	if (dev->isp_ver == ISP_V32_L)
-		rkisp_write(dev, ISP3X_MI_MP_WR_Y_LLENGTH, val, false);
+		rkisp_unite_write(dev, ISP3X_MI_MP_WR_Y_LLENGTH, val, false);
 	val *= height;
-	rkisp_write(dev, stream->config->mi.y_pic_size, val, false);
+	rkisp_unite_write(dev, stream->config->mi.y_pic_size, val, false);
 	val = out_fmt->plane_fmt[0].bytesperline * height;
-	rkisp_write(dev, stream->config->mi.y_size_init, val, false);
+	rkisp_unite_write(dev, stream->config->mi.y_size_init, val, false);
 
 	val = out_fmt->plane_fmt[1].sizeimage;
 	if (dev->cap_dev.wrap_line)
 		val = out_fmt->plane_fmt[0].bytesperline * height / 2;
-	rkisp_write(dev, stream->config->mi.cb_size_init, val, false);
+	rkisp_unite_write(dev, stream->config->mi.cb_size_init, val, false);
 
 	val = out_fmt->plane_fmt[2].sizeimage;
 	if (dev->cap_dev.wrap_line)
 		val = out_fmt->plane_fmt[0].bytesperline * height / 2;
-	rkisp_write(dev, stream->config->mi.cr_size_init, val, false);
+	rkisp_unite_write(dev, stream->config->mi.cr_size_init, val, false);
 
 	val = stream->out_isp_fmt.uv_swap ? ISP3X_MI_XTD_FORMAT_MP_UV_SWAP : 0;
 	mask = ISP3X_MI_XTD_FORMAT_MP_UV_SWAP;
-	rkisp_set_bits(dev, ISP3X_MI_WR_XTD_FORMAT_CTRL, mask, val, false);
+	rkisp_unite_set_bits(dev, ISP3X_MI_WR_XTD_FORMAT_CTRL, mask, val, false);
 
 	mask = ISP3X_MPFBC_FORCE_UPD | ISP3X_MP_YUV_MODE;
 	val = rkisp_read_reg_cache(dev, ISP3X_MPFBC_CTRL) & ~mask;
@@ -738,24 +739,24 @@
 		val |= ISP3X_SEPERATE_YUV_CFG;
 	else
 		val |= ISP3X_SEPERATE_YUV_CFG | ISP3X_MP_YUV_MODE;
-	rkisp_write(dev, ISP3X_MPFBC_CTRL, val, false);
+	rkisp_unite_write(dev, ISP3X_MPFBC_CTRL, val, false);
 
 	val = stream->out_isp_fmt.output_format;
-	rkisp_write(dev, ISP32_MI_MP_WR_CTRL, val, false);
+	rkisp_unite_write(dev, ISP32_MI_MP_WR_CTRL, val, false);
 
 	val = calc_burst_len(stream) | CIF_MI_CTRL_INIT_BASE_EN |
 		CIF_MI_CTRL_INIT_OFFSET_EN | CIF_MI_MP_AUTOUPDATE_ENABLE |
 		stream->out_isp_fmt.write_format;
 	mask = GENMASK(19, 16) | MI_CTRL_MP_FMT_MASK;
-	rkisp_set_bits(dev, ISP3X_MI_WR_CTRL, mask, val, false);
+	rkisp_unite_set_bits(dev, ISP3X_MI_WR_CTRL, mask, val, false);
 
 	mi_frame_end_int_enable(stream);
 	/* set up first buffer */
 	mi_frame_end(stream, FRAME_INIT);
 
-	rkisp_write(dev, stream->config->mi.y_offs_cnt_init, 0, false);
-	rkisp_write(dev, stream->config->mi.cb_offs_cnt_init, 0, false);
-	rkisp_write(dev, stream->config->mi.cr_offs_cnt_init, 0, false);
+	rkisp_unite_write(dev, stream->config->mi.y_offs_cnt_init, 0, false);
+	rkisp_unite_write(dev, stream->config->mi.cb_offs_cnt_init, 0, false);
+	rkisp_unite_write(dev, stream->config->mi.cr_offs_cnt_init, 0, false);
 	return 0;
 }
 
@@ -805,21 +806,21 @@
 	* memory plane formats, so calculate the size explicitly.
 	*/
 	val = stream->u.sp.y_stride;
-	rkisp_write(dev, ISP3X_MI_SP_WR_Y_LLENGTH, val, false);
+	rkisp_unite_write(dev, ISP3X_MI_SP_WR_Y_LLENGTH, val, false);
 	val *= out_fmt->height;
-	rkisp_write(dev, stream->config->mi.y_pic_size, val, false);
+	rkisp_unite_write(dev, stream->config->mi.y_pic_size, val, false);
 	val = out_fmt->plane_fmt[0].bytesperline * out_fmt->height;
-	rkisp_write(dev, stream->config->mi.y_size_init, val, false);
+	rkisp_unite_write(dev, stream->config->mi.y_size_init, val, false);
 
 	val = out_fmt->plane_fmt[1].sizeimage;
-	rkisp_write(dev, stream->config->mi.cb_size_init, val, false);
+	rkisp_unite_write(dev, stream->config->mi.cb_size_init, val, false);
 
 	val = out_fmt->plane_fmt[2].sizeimage;
-	rkisp_write(dev, stream->config->mi.cr_size_init, val, false);
+	rkisp_unite_write(dev, stream->config->mi.cr_size_init, val, false);
 
 	val = stream->out_isp_fmt.uv_swap ? ISP3X_MI_XTD_FORMAT_SP_UV_SWAP : 0;
 	mask = ISP3X_MI_XTD_FORMAT_SP_UV_SWAP;
-	rkisp_set_bits(dev, ISP3X_MI_WR_XTD_FORMAT_CTRL, mask, val, false);
+	rkisp_unite_set_bits(dev, ISP3X_MI_WR_XTD_FORMAT_CTRL, mask, val, false);
 
 	mask = ISP3X_MPFBC_FORCE_UPD | ISP3X_SP_YUV_MODE;
 	val = rkisp_read_reg_cache(dev, ISP3X_MPFBC_CTRL) & ~mask;
@@ -831,22 +832,22 @@
 		val |= ISP3X_SEPERATE_YUV_CFG;
 	else
 		val |= ISP3X_SEPERATE_YUV_CFG | ISP3X_SP_YUV_MODE;
-	rkisp_write(dev, ISP3X_MPFBC_CTRL, val, false);
+	rkisp_unite_write(dev, ISP3X_MPFBC_CTRL, val, false);
 
 	val = calc_burst_len(stream) | CIF_MI_CTRL_INIT_BASE_EN |
 		CIF_MI_CTRL_INIT_OFFSET_EN | stream->out_isp_fmt.write_format |
 		sp_in_fmt | stream->out_isp_fmt.output_format |
 		CIF_MI_SP_AUTOUPDATE_ENABLE;
 	mask = GENMASK(19, 16) | MI_CTRL_SP_FMT_MASK;
-	rkisp_set_bits(dev, ISP3X_MI_WR_CTRL, mask, val, false);
+	rkisp_unite_set_bits(dev, ISP3X_MI_WR_CTRL, mask, val, false);
 
 	mi_frame_end_int_enable(stream);
 	/* set up first buffer */
 	mi_frame_end(stream, FRAME_INIT);
 
-	rkisp_write(dev, stream->config->mi.y_offs_cnt_init, 0, false);
-	rkisp_write(dev, stream->config->mi.cb_offs_cnt_init, 0, false);
-	rkisp_write(dev, stream->config->mi.cr_offs_cnt_init, 0, false);
+	rkisp_unite_write(dev, stream->config->mi.y_offs_cnt_init, 0, false);
+	rkisp_unite_write(dev, stream->config->mi.cb_offs_cnt_init, 0, false);
+	rkisp_unite_write(dev, stream->config->mi.cr_offs_cnt_init, 0, false);
 	return 0;
 }
 
@@ -864,18 +865,18 @@
 	val = out_fmt->plane_fmt[0].bytesperline;
 	/* in bytes */
 	if (stream->out_isp_fmt.write_format != ISP3X_BP_FORMAT_INT)
-		rkisp_write(dev, ISP3X_MI_BP_WR_Y_LLENGTH, val, false);
+		rkisp_unite_write(dev, ISP3X_MI_BP_WR_Y_LLENGTH, val, false);
 	val /= DIV_ROUND_UP(fmt->bpp[0], 8);
 	/* in pixels */
 	if (stream->out_isp_fmt.write_format == ISP3X_BP_FORMAT_INT)
-		rkisp_write(dev, ISP3X_MI_BP_WR_Y_LLENGTH, val, false);
+		rkisp_unite_write(dev, ISP3X_MI_BP_WR_Y_LLENGTH, val, false);
 	val *= out_fmt->height;
-	rkisp_write(dev, stream->config->mi.y_pic_size, val, false);
+	rkisp_unite_write(dev, stream->config->mi.y_pic_size, val, false);
 	val = out_fmt->plane_fmt[0].bytesperline * out_fmt->height;
-	rkisp_write(dev, stream->config->mi.y_size_init, val, false);
+	rkisp_unite_write(dev, stream->config->mi.y_size_init, val, false);
 
 	val = out_fmt->plane_fmt[1].sizeimage;
-	rkisp_write(dev, stream->config->mi.cb_size_init, val, false);
+	rkisp_unite_write(dev, stream->config->mi.cb_size_init, val, false);
 
 	mask = ISP3X_MPFBC_FORCE_UPD | ISP3X_BP_YUV_MODE;
 	val = rkisp_read_reg_cache(dev, ISP3X_MPFBC_CTRL) & ~mask;
@@ -885,15 +886,15 @@
 		val |= ISP3X_SEPERATE_YUV_CFG;
 	else
 		val |= ISP3X_SEPERATE_YUV_CFG | ISP3X_BP_YUV_MODE;
-	rkisp_write(dev, ISP3X_MPFBC_CTRL, val, false);
+	rkisp_unite_write(dev, ISP3X_MPFBC_CTRL, val, false);
 	val = CIF_MI_CTRL_INIT_BASE_EN | CIF_MI_CTRL_INIT_OFFSET_EN;
-	rkisp_set_bits(dev, ISP3X_MI_WR_CTRL, 0, val, false);
+	rkisp_unite_set_bits(dev, ISP3X_MI_WR_CTRL, 0, val, false);
 	mi_frame_end_int_enable(stream);
 	/* set up first buffer */
 	mi_frame_end(stream, FRAME_INIT);
 
-	rkisp_write(dev, stream->config->mi.y_offs_cnt_init, 0, false);
-	rkisp_write(dev, stream->config->mi.cb_offs_cnt_init, 0, false);
+	rkisp_unite_write(dev, stream->config->mi.y_offs_cnt_init, 0, false);
+	rkisp_unite_write(dev, stream->config->mi.cb_offs_cnt_init, 0, false);
 	return 0;
 }
 
@@ -906,27 +907,27 @@
 
 	val = out_fmt->plane_fmt[0].bytesperline;
 	if (stream->out_isp_fmt.write_format != ISP3X_BP_FORMAT_INT)
-		rkisp_write(dev, stream->config->mi.length, val, false);
+		rkisp_unite_write(dev, stream->config->mi.length, val, false);
 	val /= DIV_ROUND_UP(fmt->bpp[0], 8);
 	if (stream->out_isp_fmt.write_format == ISP3X_BP_FORMAT_INT)
-		rkisp_write(dev, stream->config->mi.length, val, false);
+		rkisp_unite_write(dev, stream->config->mi.length, val, false);
 	val *= out_fmt->height;
-	rkisp_write(dev, stream->config->mi.y_pic_size, val, false);
+	rkisp_unite_write(dev, stream->config->mi.y_pic_size, val, false);
 	val = out_fmt->plane_fmt[0].bytesperline * out_fmt->height;
-	rkisp_write(dev, stream->config->mi.y_size_init, val, false);
+	rkisp_unite_write(dev, stream->config->mi.y_size_init, val, false);
 
 	val = out_fmt->plane_fmt[1].sizeimage;
-	rkisp_write(dev, stream->config->mi.cb_size_init, val, false);
+	rkisp_unite_write(dev, stream->config->mi.cb_size_init, val, false);
 
 	val = CIF_MI_CTRL_INIT_BASE_EN | CIF_MI_CTRL_INIT_OFFSET_EN;
-	rkisp_set_bits(dev, ISP3X_MI_WR_CTRL, 0, val, false);
+	rkisp_unite_set_bits(dev, ISP3X_MI_WR_CTRL, 0, val, false);
 
 	mi_frame_end_int_enable(stream);
 
 	mi_frame_end(stream, FRAME_INIT);
 
-	rkisp_write(dev, stream->config->mi.y_offs_cnt_init, 0, false);
-	rkisp_write(dev, stream->config->mi.cb_offs_cnt_init, 0, false);
+	rkisp_unite_write(dev, stream->config->mi.y_offs_cnt_init, 0, false);
+	rkisp_unite_write(dev, stream->config->mi.cb_offs_cnt_init, 0, false);
 	return 0;
 }
 
@@ -940,7 +941,7 @@
 
 	if (isp_fmt->fmt_type == FMT_BAYER)
 		val = CIF_MI_CTRL_RAW_ENABLE;
-	rkisp_set_bits(stream->ispdev, ISP3X_MI_WR_CTRL, mask, val, false);
+	rkisp_unite_set_bits(stream->ispdev, ISP3X_MI_WR_CTRL, mask, val, false);
 
 	/* enable bpds path output */
 	if (t->streaming && !t->is_pause)
@@ -957,7 +958,7 @@
 	if (fmt->fmt_type == FMT_RGB &&
 	    dev->isp_sdev.quantization == V4L2_QUANTIZATION_FULL_RANGE)
 		val |= mask;
-	rkisp_set_bits(stream->ispdev, ISP3X_MI_WR_CTRL, mask, val, false);
+	rkisp_unite_set_bits(stream->ispdev, ISP3X_MI_WR_CTRL, mask, val, false);
 }
 
 static void bp_enable_mi(struct rkisp_stream *stream)
@@ -969,7 +970,7 @@
 		  stream->out_isp_fmt.output_format |
 		  ISP3X_BP_ENABLE | ISP3X_BP_AUTO_UPD;
 
-	rkisp_write(stream->ispdev, ISP3X_MI_BP_WR_CTRL, val, false);
+	rkisp_unite_write(stream->ispdev, ISP3X_MI_BP_WR_CTRL, val, false);
 
 	/* enable bpds path output */
 	if (t->streaming && !t->is_pause)
@@ -982,7 +983,7 @@
 		  stream->out_isp_fmt.output_format |
 		  ISP32_DS_ENABLE | ISP32_DS_AUTO_UPD;
 
-	rkisp_write(stream->ispdev, stream->config->mi.ctrl, val, false);
+	rkisp_unite_write(stream->ispdev, stream->config->mi.ctrl, val, false);
 }
 
 static void mp_disable_mi(struct rkisp_stream *stream)
@@ -991,8 +992,7 @@
 	struct rkisp_stream *t = &dev->cap_dev.stream[stream->conn_id];
 	u32 mask = CIF_MI_CTRL_MP_ENABLE | CIF_MI_CTRL_RAW_ENABLE;
 
-	rkisp_set_bits(dev, 0x1814, 0, BIT(0), false);
-	rkisp_clear_bits(stream->ispdev, ISP3X_MI_WR_CTRL, mask, false);
+	rkisp_unite_clear_bits(stream->ispdev, ISP3X_MI_WR_CTRL, mask, false);
 
 	/* disable mpds path output */
 	if (!stream->is_pause && t->streaming)
@@ -1001,7 +1001,7 @@
 
 static void sp_disable_mi(struct rkisp_stream *stream)
 {
-	rkisp_clear_bits(stream->ispdev, ISP3X_MI_WR_CTRL, CIF_MI_CTRL_SP_ENABLE, false);
+	rkisp_unite_clear_bits(stream->ispdev, ISP3X_MI_WR_CTRL, CIF_MI_CTRL_SP_ENABLE, false);
 }
 
 static void bp_disable_mi(struct rkisp_stream *stream)
@@ -1009,7 +1009,7 @@
 	struct rkisp_device *dev = stream->ispdev;
 	struct rkisp_stream *t = &dev->cap_dev.stream[stream->conn_id];
 
-	rkisp_clear_bits(stream->ispdev, ISP3X_MI_BP_WR_CTRL, ISP3X_BP_ENABLE, false);
+	rkisp_unite_clear_bits(stream->ispdev, ISP3X_MI_BP_WR_CTRL, ISP3X_BP_ENABLE, false);
 
 	/* disable bpds path output */
 	if (!stream->is_pause && t->streaming)
@@ -1018,7 +1018,7 @@
 
 static void ds_disable_mi(struct rkisp_stream *stream)
 {
-	rkisp_clear_bits(stream->ispdev, stream->config->mi.ctrl, ISP32_DS_ENABLE, false);
+	rkisp_unite_clear_bits(stream->ispdev, stream->config->mi.ctrl, ISP32_DS_ENABLE, false);
 }
 
 static void update_mi(struct rkisp_stream *stream)
@@ -1046,6 +1046,25 @@
 			reg = stream->config->mi.cr_base_ad_init;
 			val = stream->next_buf->buff_addr[RKISP_PLANE_CR];
 			rkisp_write(dev, reg, val, false);
+		}
+
+		if (dev->hw_dev->unite) {
+			reg = stream->config->mi.y_base_ad_init;
+			val = stream->next_buf->buff_addr[RKISP_PLANE_Y];
+			val += ((stream->out_fmt.width / 2) & ~0xf);
+			rkisp_next_write(dev, reg, val, false);
+
+			reg = stream->config->mi.cb_base_ad_init;
+			val = stream->next_buf->buff_addr[RKISP_PLANE_CB];
+			val += ((stream->out_fmt.width / 2) & ~0xf);
+			rkisp_next_write(dev, reg, val, false);
+
+			if (is_cr_cfg) {
+				reg = stream->config->mi.cr_base_ad_init;
+				val = stream->next_buf->buff_addr[RKISP_PLANE_CR];
+				val += ((stream->out_fmt.width / 2) & ~0xf);
+				rkisp_next_write(dev, reg, val, false);
+			}
 		}
 
 		if (stream->is_pause) {
@@ -1141,9 +1160,9 @@
 
 	stream->is_mf_upd = false;
 	if (dev->cap_dev.is_mirror)
-		rkisp_set_bits(dev, ISP3X_ISP_CTRL0, 0, ISP32_MIR_ENABLE, false);
+		rkisp_unite_set_bits(dev, ISP3X_ISP_CTRL0, 0, ISP32_MIR_ENABLE, false);
 	else
-		rkisp_clear_bits(dev, ISP3X_ISP_CTRL0, ISP32_MIR_ENABLE, false);
+		rkisp_unite_clear_bits(dev, ISP3X_ISP_CTRL0, ISP32_MIR_ENABLE, false);
 
 	switch (stream->id) {
 	case RKISP_STREAM_SP:
@@ -1169,9 +1188,9 @@
 
 	tmp = rkisp_read_reg_cache(dev, ISP32_MI_WR_VFLIP_CTRL);
 	if (stream->is_flip)
-		rkisp_write(dev, ISP32_MI_WR_VFLIP_CTRL, tmp | val, false);
+		rkisp_unite_write(dev, ISP32_MI_WR_VFLIP_CTRL, tmp | val, false);
 	else
-		rkisp_write(dev, ISP32_MI_WR_VFLIP_CTRL, tmp & ~val, false);
+		rkisp_unite_write(dev, ISP32_MI_WR_VFLIP_CTRL, tmp & ~val, false);
 	return 0;
 }
 
@@ -1408,7 +1427,10 @@
 		    (stream->frame_early && state == FRAME_IRQ))
 			goto end;
 	} else {
+		spin_lock_irqsave(&stream->vbq_lock, lock_flags);
 		buf = stream->curr_buf;
+		stream->curr_buf = NULL;
+		spin_unlock_irqrestore(&stream->vbq_lock, lock_flags);
 	}
 
 	if (buf) {
@@ -1546,6 +1568,7 @@
 	struct rkisp_device *dev = stream->ispdev;
 	int ret;
 
+	stream->is_pause = false;
 	if (stream->ops->set_data_path)
 		stream->ops->set_data_path(stream);
 	if (stream->ops->config_mi) {
@@ -2264,6 +2287,12 @@
 	v4l2_dbg(3, rkisp_debug, &dev->v4l2_dev,
 		 "mi isr:0x%x\n", mis_val);
 
+	if (dev->hw_dev->unite == ISP_UNITE_ONE &&
+	    dev->unite_index == ISP_UNITE_LEFT) {
+		rkisp_write(dev, ISP3X_MI_ICR, mis_val, true);
+		goto end;
+	}
+
 	for (i = 0; i < RKISP_MAX_STREAM; ++i) {
 		stream = &dev->cap_dev.stream[i];
 
@@ -2306,7 +2335,7 @@
 			mi_frame_end(stream, FRAME_IRQ);
 		}
 	}
-
+end:
 	if (mis_val & ISP3X_MI_MP_FRAME) {
 		stream = &dev->cap_dev.stream[RKISP_STREAM_MP];
 		if (!stream->streaming)
diff --git a/kernel/drivers/media/platform/rockchip/isp/common.c b/kernel/drivers/media/platform/rockchip/isp/common.c
index 875d6da..e7421ab 100644
--- a/kernel/drivers/media/platform/rockchip/isp/common.c
+++ b/kernel/drivers/media/platform/rockchip/isp/common.c
@@ -6,6 +6,7 @@
 #include <linux/of_platform.h>
 #include <linux/slab.h>
 #include "dev.h"
+#include "hw.h"
 #include "isp_ispp.h"
 #include "regs.h"
 
@@ -36,6 +37,8 @@
 	*flag = SW_REG_CACHE;
 	if (dev->hw_dev->is_single || is_direct) {
 		*flag = SW_REG_CACHE_SYNC;
+		if (dev->hw_dev->unite == ISP_UNITE_ONE)
+			return;
 		writel(val, dev->hw_dev->base_next_addr + reg);
 	}
 }
@@ -166,13 +169,16 @@
 				continue;
 		}
 
+		if (hw->unite == ISP_UNITE_ONE && dev->unite_index == ISP_UNITE_RIGHT)
+			val = dev->sw_base_addr + i + RKISP_ISP_SW_MAX_SIZE;
+
 		if (*flag == SW_REG_CACHE) {
 			if ((i == ISP3X_MAIN_RESIZE_CTRL ||
 			     i == ISP32_BP_RESIZE_CTRL ||
 			     i == ISP3X_SELF_RESIZE_CTRL) && *val == 0)
 				*val = CIF_RSZ_CTRL_CFG_UPD;
 			writel(*val, base + i);
-			if (hw->is_unite) {
+			if (hw->unite == ISP_UNITE_TWO) {
 				val = dev->sw_base_addr + i + RKISP_ISP_SW_MAX_SIZE;
 				if ((i == ISP3X_MAIN_RESIZE_CTRL ||
 				     i == ISP32_BP_RESIZE_CTRL ||
diff --git a/kernel/drivers/media/platform/rockchip/isp/common.h b/kernel/drivers/media/platform/rockchip/isp/common.h
index 609d950..66c7ad8 100644
--- a/kernel/drivers/media/platform/rockchip/isp/common.h
+++ b/kernel/drivers/media/platform/rockchip/isp/common.h
@@ -184,32 +184,6 @@
 void rkisp_next_set_reg_cache_bits(struct rkisp_device *dev, u32 reg, u32 mask, u32 val);
 void rkisp_next_clear_reg_cache_bits(struct rkisp_device *dev, u32 reg, u32 mask);
 
-static inline void
-rkisp_unite_write(struct rkisp_device *dev, u32 reg, u32 val, bool is_direct, bool is_unite)
-{
-	rkisp_write(dev, reg, val, is_direct);
-	if (is_unite)
-		rkisp_next_write(dev, reg, val, is_direct);
-}
-
-static inline void
-rkisp_unite_set_bits(struct rkisp_device *dev, u32 reg, u32 mask,
-		     u32 val, bool is_direct, bool is_unite)
-{
-	rkisp_set_bits(dev, reg, mask, val, is_direct);
-	if (is_unite)
-		rkisp_next_set_bits(dev, reg, mask, val, is_direct);
-}
-
-static inline void
-rkisp_unite_clear_bits(struct rkisp_device *dev, u32 reg, u32 mask,
-		       bool is_direct, bool is_unite)
-{
-	rkisp_clear_bits(dev, reg, mask, is_direct);
-	if (is_unite)
-		rkisp_next_clear_bits(dev, reg, mask, is_direct);
-}
-
 void rkisp_update_regs(struct rkisp_device *dev, u32 start, u32 end);
 
 int rkisp_alloc_buffer(struct rkisp_device *dev, struct rkisp_dummy_buffer *buf);
diff --git a/kernel/drivers/media/platform/rockchip/isp/csi.c b/kernel/drivers/media/platform/rockchip/isp/csi.c
index cb64f02..a747d67 100644
--- a/kernel/drivers/media/platform/rockchip/isp/csi.c
+++ b/kernel/drivers/media/platform/rockchip/isp/csi.c
@@ -626,8 +626,7 @@
 
 		if (!dev->hw_dev->is_mi_update)
 			rkisp_unite_write(dev, CSI2RX_CTRL0,
-					  SW_IBUF_OP_MODE(dev->hdr.op_mode),
-					  true, dev->hw_dev->is_unite);
+					  SW_IBUF_OP_MODE(dev->hdr.op_mode), true);
 
 		/* hdr merge */
 		switch (dev->hdr.op_mode) {
@@ -651,25 +650,22 @@
 				return -EINVAL;
 			}
 		}
-		rkisp_unite_write(dev, ISP_HDRMGE_BASE, val, false, dev->hw_dev->is_unite);
+		rkisp_unite_write(dev, ISP_HDRMGE_BASE, val, false);
 
 		val = RAW_RD_SIZE_ERR;
 		if (!IS_HDR_RDBK(dev->hdr.op_mode))
 			val |= ISP21_MIPI_DROP_FRM;
-		rkisp_unite_set_bits(dev, CSI2RX_MASK_STAT, 0, val, true, dev->hw_dev->is_unite);
+		rkisp_unite_set_bits(dev, CSI2RX_MASK_STAT, 0, val, true);
 	}
 
 	if (IS_HDR_RDBK(dev->hdr.op_mode))
-		rkisp_unite_set_bits(dev, CTRL_SWS_CFG, 0, SW_MPIP_DROP_FRM_DIS,
-				     true, dev->hw_dev->is_unite);
+		rkisp_unite_set_bits(dev, CTRL_SWS_CFG, 0, SW_MPIP_DROP_FRM_DIS, true);
 
 	if (dev->isp_ver >= ISP_V30)
-		rkisp_unite_set_bits(dev, CTRL_SWS_CFG, 0, ISP3X_SW_ACK_FRM_PRO_DIS,
-				     true, dev->hw_dev->is_unite);
+		rkisp_unite_set_bits(dev, CTRL_SWS_CFG, 0, ISP3X_SW_ACK_FRM_PRO_DIS, true);
 	/* line counter from isp out, default from mp out */
 	if (dev->isp_ver == ISP_V32_L)
-		rkisp_unite_set_bits(dev, CTRL_SWS_CFG, 0, ISP32L_ISP2ENC_CNT_MUX,
-				     true, dev->hw_dev->is_unite);
+		rkisp_unite_set_bits(dev, CTRL_SWS_CFG, 0, ISP32L_ISP2ENC_CNT_MUX, true);
 	dev->rdbk_cnt = -1;
 	dev->rdbk_cnt_x1 = -1;
 	dev->rdbk_cnt_x2 = -1;
diff --git a/kernel/drivers/media/platform/rockchip/isp/dev.c b/kernel/drivers/media/platform/rockchip/isp/dev.c
index cf981d6..7da2c73 100644
--- a/kernel/drivers/media/platform/rockchip/isp/dev.c
+++ b/kernel/drivers/media/platform/rockchip/isp/dev.c
@@ -239,6 +239,8 @@
 	data_rate >>= 3;
 end:
 	do_div(data_rate, 1000 * 1000);
+	if (hw_dev->unite == ISP_UNITE_ONE)
+		data_rate *= 4;
 
 	/* increase 25% margin */
 	data_rate += data_rate >> 2;
@@ -252,7 +254,7 @@
 
 	/* set isp clock rate */
 	rkisp_set_clk_rate(hw_dev->clks[0], hw_dev->clk_rate_tbl[i].clk_rate * 1000000UL);
-	if (hw_dev->is_unite)
+	if (hw_dev->unite == ISP_UNITE_TWO)
 		rkisp_set_clk_rate(hw_dev->clks[5], hw_dev->clk_rate_tbl[i].clk_rate * 1000000UL);
 	/* aclk equal to core clk */
 	if (dev->isp_ver == ISP_V32)
@@ -842,7 +844,7 @@
 	if (ret)
 		return ret;
 
-	if (isp_dev->hw_dev->is_unite)
+	if (isp_dev->hw_dev->unite)
 		mult = 2;
 	isp_dev->sw_base_addr = devm_kzalloc(dev, RKISP_ISP_SW_MAX_SIZE * mult, GFP_KERNEL);
 	if (!isp_dev->sw_base_addr)
@@ -854,7 +856,7 @@
 
 	snprintf(isp_dev->media_dev.model, sizeof(isp_dev->media_dev.model),
 		 "%s%d", DRIVER_NAME, isp_dev->dev_id);
-	if (!isp_dev->hw_dev->is_unite)
+	if (!isp_dev->hw_dev->unite)
 		strscpy(isp_dev->name, dev_name(dev), sizeof(isp_dev->name));
 	else
 		snprintf(isp_dev->name, sizeof(isp_dev->name),
@@ -978,6 +980,13 @@
 	    rkisp_update_sensor_info(isp_dev) >= 0)
 		_set_pipeline_default_fmt(isp_dev, false);
 
+	if (isp_dev->hw_dev->is_assigned_clk)
+		rkisp_clk_dbg = true;
+
+	if (isp_dev->hw_dev->unite == ISP_UNITE_ONE &&
+	    !(isp_dev->isp_inp & INP_RAWRD2))
+		rkisp_rdbk_auto = true;
+
 	isp_dev->cap_dev.wait_line = rkisp_wait_line;
 	isp_dev->cap_dev.wrap_line = rkisp_wrap_line;
 	isp_dev->is_rdbk_auto = rkisp_rdbk_auto;
diff --git a/kernel/drivers/media/platform/rockchip/isp/dev.h b/kernel/drivers/media/platform/rockchip/isp/dev.h
index 0b73b4f..4510f9e 100644
--- a/kernel/drivers/media/platform/rockchip/isp/dev.h
+++ b/kernel/drivers/media/platform/rockchip/isp/dev.h
@@ -103,6 +103,25 @@
 	RDBK_F_MAX
 };
 
+/* unite mode for isp to process high resolution
+ * ISP_UNITE_TWO: image splits left and right to two isp hardware
+ * ISP_UNITE_ONE: image splits left and right to single isp hardware
+ */
+enum {
+	ISP_UNITE_NONE = 0,
+	ISP_UNITE_TWO = 1,
+	ISP_UNITE_ONE = 2,
+};
+
+/* left and right index
+ * ISP_UNITE_LEFT: left of image to isp process
+ * ISP_UNITE_RIGHT: right of image to isp process
+ */
+enum {
+	ISP_UNITE_LEFT = 0,
+	ISP_UNITE_RIGHT = 1,
+};
+
 /*
  * struct rkisp_pipeline - An ISP hardware pipeline
  *
@@ -249,11 +268,40 @@
 	bool is_pre_on;
 	bool is_first_double;
 	bool is_probe_end;
+	bool is_frame_double;
 
 	struct rkisp_vicap_input vicap_in;
 
 	u8 multi_mode;
 	u8 multi_index;
 	u8 rawaf_irq_cnt;
+	u8 unite_index;
 };
+
+static inline void
+rkisp_unite_write(struct rkisp_device *dev, u32 reg, u32 val, bool is_direct)
+{
+	rkisp_write(dev, reg, val, is_direct);
+	if (dev->hw_dev->unite)
+		rkisp_next_write(dev, reg, val, is_direct);
+}
+
+static inline void
+rkisp_unite_set_bits(struct rkisp_device *dev, u32 reg, u32 mask,
+		     u32 val, bool is_direct)
+{
+	rkisp_set_bits(dev, reg, mask, val, is_direct);
+	if (dev->hw_dev->unite)
+		rkisp_next_set_bits(dev, reg, mask, val, is_direct);
+}
+
+static inline void
+rkisp_unite_clear_bits(struct rkisp_device *dev, u32 reg, u32 mask,
+		       bool is_direct)
+{
+	rkisp_clear_bits(dev, reg, mask, is_direct);
+	if (dev->hw_dev->unite)
+		rkisp_next_clear_bits(dev, reg, mask, is_direct);
+}
+
 #endif
diff --git a/kernel/drivers/media/platform/rockchip/isp/dmarx.c b/kernel/drivers/media/platform/rockchip/isp/dmarx.c
index b7af0af..529c099 100644
--- a/kernel/drivers/media/platform/rockchip/isp/dmarx.c
+++ b/kernel/drivers/media/platform/rockchip/isp/dmarx.c
@@ -318,7 +318,6 @@
 static int rawrd_config_mi(struct rkisp_stream *stream)
 {
 	struct rkisp_device *dev = stream->ispdev;
-	bool is_unite = dev->hw_dev->is_unite;
 	u32 val;
 
 	val = rkisp_read(dev, CSI2RX_DATA_IDS_1, true);
@@ -348,8 +347,8 @@
 		val |= CIF_CSI2_DT_RAW12;
 	}
 	rkisp_unite_write(dev, CSI2RX_RAW_RD_CTRL,
-			  stream->memory << 2, false, is_unite);
-	rkisp_unite_write(dev, CSI2RX_DATA_IDS_1, val, false, is_unite);
+			  stream->memory << 2, false);
+	rkisp_unite_write(dev, CSI2RX_DATA_IDS_1, val, false);
 	rkisp_rawrd_set_pic_size(dev, stream->out_fmt.width,
 				 stream->out_fmt.height);
 	mi_raw_length(stream);
@@ -376,7 +375,7 @@
 		}
 		val += stream->curr_buf->buff_addr[RKISP_PLANE_Y];
 		rkisp_write(dev, stream->config->mi.y_base_ad_init, val, false);
-		if (dev->hw_dev->is_unite) {
+		if (dev->hw_dev->unite) {
 			u32 offs = stream->out_fmt.width / 2 - RKMOUDLE_UNITE_EXTEND_PIXEL;
 
 			if (stream->memory)
@@ -464,13 +463,10 @@
 				}
 				dev->hdr.op_mode = dev->rd_mode;
 				rkisp_unite_write(dev, CSI2RX_CTRL0,
-						  SW_IBUF_OP_MODE(dev->hdr.op_mode),
-						  true, dev->hw_dev->is_unite);
+						  SW_IBUF_OP_MODE(dev->hdr.op_mode), true);
 				rkisp_unite_set_bits(dev, CSI2RX_MASK_STAT,
-						     0, ISP21_MIPI_DROP_FRM,
-						     true, dev->hw_dev->is_unite);
-				rkisp_unite_clear_bits(dev, CIF_ISP_IMSC, CIF_ISP_FRAME_IN,
-						       true, dev->hw_dev->is_unite);
+						     0, ISP21_MIPI_DROP_FRM, true);
+				rkisp_unite_clear_bits(dev, CIF_ISP_IMSC, CIF_ISP_FRAME_IN, true);
 				dev_info(dev->dev,
 					 "switch online seq:%d mode:0x%x\n",
 					 rx_buf->sequence, dev->rd_mode);
@@ -1135,7 +1131,7 @@
 {
 	struct rkisp_isp_subdev *sdev = &dev->isp_sdev;
 	u8 mult = sdev->in_fmt.fmt_type == FMT_YUV ? 2 : 1;
-	bool is_unite = dev->hw_dev->is_unite;
+	bool is_unite = !!dev->hw_dev->unite;
 	u32 w = !is_unite ? width : width / 2 + RKMOUDLE_UNITE_EXTEND_PIXEL;
 
 	/* rx height should equal to isp height + offset for read back mode */
@@ -1149,7 +1145,7 @@
 		height += RKMODULE_EXTEND_LINE;
 
 	w *= mult;
-	rkisp_unite_write(dev, CSI2RX_RAW_RD_PIC_SIZE, height << 16 | w, false, is_unite);
+	rkisp_unite_write(dev, CSI2RX_RAW_RD_PIC_SIZE, height << 16 | w, false);
 }
 
 void rkisp_dmarx_get_frame(struct rkisp_device *dev, u32 *id,
diff --git a/kernel/drivers/media/platform/rockchip/isp/hw.c b/kernel/drivers/media/platform/rockchip/isp/hw.c
index e6e4d9a..d9d1627 100644
--- a/kernel/drivers/media/platform/rockchip/isp/hw.c
+++ b/kernel/drivers/media/platform/rockchip/isp/hw.c
@@ -132,7 +132,7 @@
 	for (i = 0; i < size; i++) {
 		flag = dev->sw_base_addr + reg[i] + RKISP_ISP_SW_REG_SIZE;
 		*flag = SW_REG_CACHE;
-		if (dev->hw_dev->is_unite) {
+		if (dev->hw_dev->unite) {
 			flag += RKISP_ISP_SW_MAX_SIZE / 4;
 			*flag = SW_REG_CACHE;
 		}
@@ -144,7 +144,7 @@
 	struct device *dev = ctx;
 	struct rkisp_hw_dev *hw_dev = dev_get_drvdata(dev);
 	struct rkisp_device *isp = hw_dev->isp[hw_dev->mipi_dev_id];
-	void __iomem *base = !hw_dev->is_unite ?
+	void __iomem *base = hw_dev->unite != ISP_UNITE_TWO ?
 		hw_dev->base_addr : hw_dev->base_next_addr;
 	ktime_t t = 0;
 	s64 us;
@@ -201,7 +201,7 @@
 	struct device *dev = ctx;
 	struct rkisp_hw_dev *hw_dev = dev_get_drvdata(dev);
 	struct rkisp_device *isp = hw_dev->isp[hw_dev->cur_dev_id];
-	void __iomem *base = !hw_dev->is_unite ?
+	void __iomem *base = hw_dev->unite != ISP_UNITE_TWO ?
 		hw_dev->base_addr : hw_dev->base_next_addr;
 	u32 mis_val, tx_isr = MI_RAW0_WR_FRAME | MI_RAW1_WR_FRAME |
 		MI_RAW2_WR_FRAME | MI_RAW3_WR_FRAME;
@@ -237,7 +237,7 @@
 	struct device *dev = ctx;
 	struct rkisp_hw_dev *hw_dev = dev_get_drvdata(dev);
 	struct rkisp_device *isp = hw_dev->isp[hw_dev->cur_dev_id];
-	void __iomem *base = !hw_dev->is_unite ?
+	void __iomem *base = hw_dev->unite != ISP_UNITE_TWO ?
 		hw_dev->base_addr : hw_dev->base_next_addr;
 	unsigned int mis_val, mis_3a = 0;
 	ktime_t t = 0;
@@ -455,6 +455,9 @@
 	}, {
 		.clk_rate = 350,
 		.refer_data = 3072,
+	}, {
+		.clk_rate = 440,
+		.refer_data = 3840,
 	}
 };
 
@@ -636,7 +639,7 @@
 	/* record clk config and recover */
 	iccl0 = readl(base + CIF_ICCL);
 	clk_ctrl0 = readl(base + CTRL_VI_ISP_CLK_CTRL);
-	if (dev->is_unite) {
+	if (dev->unite == ISP_UNITE_TWO) {
 		iccl1 = readl(dev->base_next_addr + CIF_ICCL);
 		clk_ctrl1 = readl(dev->base_next_addr + CTRL_VI_ISP_CLK_CTRL);
 	}
@@ -646,7 +649,7 @@
 		 * isp soft reset first to protect isp reset.
 		 */
 		writel(0xffff, base + CIF_IRCL);
-		if (dev->is_unite)
+		if (dev->unite == ISP_UNITE_TWO)
 			writel(0xffff, dev->base_next_addr + CIF_IRCL);
 		udelay(10);
 	}
@@ -669,7 +672,7 @@
 	writel(val, base + CIF_IRCL);
 	if (dev->isp_ver == ISP_V32)
 		rv1106_sdmmc_put_lock();
-	if (dev->is_unite)
+	if (dev->unite == ISP_UNITE_TWO)
 		writel(0xffff, dev->base_next_addr + CIF_IRCL);
 	udelay(10);
 
@@ -681,7 +684,7 @@
 
 	writel(iccl0, base + CIF_ICCL);
 	writel(clk_ctrl0, base + CTRL_VI_ISP_CLK_CTRL);
-	if (dev->is_unite) {
+	if (dev->unite == ISP_UNITE_TWO) {
 		writel(iccl1, dev->base_next_addr + CIF_ICCL);
 		writel(clk_ctrl1, dev->base_next_addr + CTRL_VI_ISP_CLK_CTRL);
 	}
@@ -722,7 +725,7 @@
 	writel(val, dev->base_addr + CIF_ICCL);
 	if (dev->isp_ver == ISP_V32)
 		rv1106_sdmmc_put_lock();
-	if (dev->is_unite)
+	if (dev->unite == ISP_UNITE_TWO)
 		writel(val, dev->base_next_addr + CIF_ICCL);
 
 	if (dev->isp_ver == ISP_V12 || dev->isp_ver == ISP_V13) {
@@ -752,7 +755,7 @@
 		writel(val, dev->base_addr + CTRL_VI_ISP_CLK_CTRL);
 		if (dev->isp_ver == ISP_V32)
 			rv1106_sdmmc_put_lock();
-		if (dev->is_unite)
+		if (dev->unite == ISP_UNITE_TWO)
 			writel(val, dev->base_next_addr + CTRL_VI_ISP_CLK_CTRL);
 	}
 }
@@ -786,10 +789,12 @@
 		}
 	}
 
-	rate = dev->clk_rate_tbl[0].clk_rate * 1000000UL;
-	rkisp_set_clk_rate(dev->clks[0], rate);
-	if (dev->is_unite)
-		rkisp_set_clk_rate(dev->clks[5], rate);
+	if (!dev->is_assigned_clk) {
+		rate = dev->clk_rate_tbl[0].clk_rate * 1000000UL;
+		rkisp_set_clk_rate(dev->clks[0], rate);
+		if (dev->unite == ISP_UNITE_TWO)
+			rkisp_set_clk_rate(dev->clks[5], rate);
+	}
 	rkisp_soft_reset(dev, false);
 	isp_config_clk(dev, true);
 	return 0;
@@ -848,6 +853,7 @@
 	struct resource *res;
 	int i, ret;
 	bool is_mem_reserved = true;
+	u32 clk_rate = 0;
 
 	match = of_match_node(rkisp_hw_of_match, node);
 	if (IS_ERR(match))
@@ -858,21 +864,11 @@
 		return -ENOMEM;
 
 	match_data = match->data;
-	hw_dev->is_unite = match_data->unite;
 	dev_set_drvdata(dev, hw_dev);
 	hw_dev->dev = dev;
 	hw_dev->is_thunderboot = IS_ENABLED(CONFIG_VIDEO_ROCKCHIP_THUNDER_BOOT_ISP);
 	dev_info(dev, "is_thunderboot: %d\n", hw_dev->is_thunderboot);
-	memset(&hw_dev->max_in, 0, sizeof(hw_dev->max_in));
-	if (!of_property_read_u32_array(node, "max-input", &hw_dev->max_in.w, 3)) {
-		hw_dev->max_in.is_fix = true;
-		if (hw_dev->is_unite) {
-			hw_dev->max_in.w /= 2;
-			hw_dev->max_in.w += RKMOUDLE_UNITE_EXTEND_PIXEL;
-		}
-	}
-	dev_info(dev, "max input:%dx%d@%dfps\n",
-		 hw_dev->max_in.w, hw_dev->max_in.h, hw_dev->max_in.fps);
+
 	hw_dev->grf = syscon_regmap_lookup_by_phandle(node, "rockchip,grf");
 	if (IS_ERR(hw_dev->grf))
 		dev_warn(dev, "Missing rockchip,grf property\n");
@@ -919,6 +915,27 @@
 		}
 	}
 
+	hw_dev->isp_ver = match_data->isp_ver;
+	if (match_data->unite) {
+		hw_dev->unite = ISP_UNITE_TWO;
+	} else if (device_property_read_bool(dev, "rockchip,unite-en")) {
+		hw_dev->unite = ISP_UNITE_ONE;
+		hw_dev->base_next_addr = hw_dev->base_addr;
+	} else {
+		hw_dev->unite = ISP_UNITE_NONE;
+	}
+
+	memset(&hw_dev->max_in, 0, sizeof(hw_dev->max_in));
+	if (!of_property_read_u32_array(node, "max-input", &hw_dev->max_in.w, 3)) {
+		hw_dev->max_in.is_fix = true;
+		if (hw_dev->unite) {
+			hw_dev->max_in.w /= 2;
+			hw_dev->max_in.w += RKMOUDLE_UNITE_EXTEND_PIXEL;
+		}
+	}
+	dev_info(dev, "max input:%dx%d@%dfps\n",
+		 hw_dev->max_in.w, hw_dev->max_in.h, hw_dev->max_in.fps);
+
 	rkisp_monitor = device_property_read_bool(dev, "rockchip,restart-monitor-en");
 	hw_dev->mipi_irq = -1;
 
@@ -941,6 +958,11 @@
 	hw_dev->clk_rate_tbl = match_data->clk_rate_tbl;
 	hw_dev->num_clk_rate_tbl = match_data->num_clk_rate_tbl;
 
+	hw_dev->is_assigned_clk = false;
+	ret = of_property_read_u32(node, "assigned-clock-rates", &clk_rate);
+	if (!ret && clk_rate)
+		hw_dev->is_assigned_clk = true;
+
 	hw_dev->reset = devm_reset_control_array_get(dev, false, false);
 	if (IS_ERR(hw_dev->reset)) {
 		dev_dbg(dev, "failed to get reset\n");
@@ -959,10 +981,8 @@
 	hw_dev->dev_link_num = 0;
 	hw_dev->cur_dev_id = 0;
 	hw_dev->mipi_dev_id = 0;
-	hw_dev->pre_dev_id = 0;
+	hw_dev->pre_dev_id = -1;
 	hw_dev->is_multi_overflow = false;
-	hw_dev->isp_ver = match_data->isp_ver;
-	hw_dev->is_unite = match_data->unite;
 	mutex_init(&hw_dev->dev_lock);
 	spin_lock_init(&hw_dev->rdbk_lock);
 	atomic_set(&hw_dev->refcnt, 0);
@@ -1012,7 +1032,7 @@
 	hw_dev->is_shutdown = true;
 	if (pm_runtime_active(&pdev->dev)) {
 		writel(0xffff, hw_dev->base_addr + CIF_IRCL);
-		if (hw_dev->is_unite)
+		if (hw_dev->unite == ISP_UNITE_TWO)
 			writel(0xffff, hw_dev->base_next_addr + CIF_IRCL);
 	}
 	dev_info(&pdev->dev, "%s\n", __func__);
@@ -1052,7 +1072,7 @@
 			hw_dev->is_single = false;
 		w = isp->isp_sdev.in_crop.width;
 		h = isp->isp_sdev.in_crop.height;
-		if (hw_dev->is_unite)
+		if (hw_dev->unite)
 			w = w / 2 + RKMOUDLE_UNITE_EXTEND_PIXEL;
 		hw_dev->isp_size[i].w = w;
 		hw_dev->isp_size[i].h = h;
@@ -1064,6 +1084,8 @@
 				hw_dev->max_in.h = h;
 		}
 	}
+	if (hw_dev->unite == ISP_UNITE_ONE)
+		hw_dev->is_single = false;
 	for (i = 0; i < hw_dev->dev_num; i++) {
 		isp = hw_dev->isp[i];
 		if (!isp || (isp && !isp->is_hw_link))
@@ -1077,7 +1099,7 @@
 	struct rkisp_hw_dev *hw_dev = dev_get_drvdata(dev);
 	void __iomem *base = hw_dev->base_addr;
 	struct rkisp_device *isp;
-	int mult = hw_dev->is_unite ? 2 : 1;
+	int mult = hw_dev->unite ? 2 : 1;
 	int ret, i;
 	void *buf;
 
@@ -1093,7 +1115,7 @@
 		buf = isp->sw_base_addr;
 		memset(buf, 0, RKISP_ISP_SW_MAX_SIZE * mult);
 		memcpy_fromio(buf, base, RKISP_ISP_SW_REG_SIZE);
-		if (hw_dev->is_unite) {
+		if (hw_dev->unite) {
 			buf += RKISP_ISP_SW_MAX_SIZE;
 			base = hw_dev->base_next_addr;
 			memcpy_fromio(buf, base, RKISP_ISP_SW_REG_SIZE);
diff --git a/kernel/drivers/media/platform/rockchip/isp/hw.h b/kernel/drivers/media/platform/rockchip/isp/hw.h
index 04dedc2..5c71294 100644
--- a/kernel/drivers/media/platform/rockchip/isp/hw.h
+++ b/kernel/drivers/media/platform/rockchip/isp/hw.h
@@ -89,6 +89,7 @@
 	struct rkisp_monitor monitor;
 	u64 iq_feature;
 	int buf_init_cnt;
+	u32 unite;
 	bool is_feature_on;
 	bool is_dma_contig;
 	bool is_dma_sg_ops;
@@ -99,11 +100,11 @@
 	bool is_thunderboot;
 	bool is_buf_init;
 	bool is_shutdown;
-	bool is_unite;
 	bool is_multi_overflow;
 	bool is_runing;
 	bool is_frm_buf;
 	bool is_dvfs;
+	bool is_assigned_clk;
 };
 
 int rkisp_register_irq(struct rkisp_hw_dev *dev);
diff --git a/kernel/drivers/media/platform/rockchip/isp/isp_params.c b/kernel/drivers/media/platform/rockchip/isp/isp_params.c
index 020ff5a..3db85a2 100644
--- a/kernel/drivers/media/platform/rockchip/isp/isp_params.c
+++ b/kernel/drivers/media/platform/rockchip/isp/isp_params.c
@@ -146,11 +146,12 @@
 	struct rkisp_buffer *params_buf = to_rkisp_buffer(vbuf);
 	struct vb2_queue *vq = vb->vb2_queue;
 	struct rkisp_isp_params_vdev *params_vdev = vq->drv_priv;
+	struct rkisp_device *dev = params_vdev->dev;
 	void *first_param;
 	unsigned long flags;
 	unsigned int cur_frame_id = -1;
 
-	cur_frame_id = atomic_read(&params_vdev->dev->isp_sdev.frm_sync_seq) - 1;
+	cur_frame_id = atomic_read(&dev->isp_sdev.frm_sync_seq) - 1;
 	if (params_vdev->first_params) {
 		first_param = vb2_plane_vaddr(vb, 0);
 		params_vdev->ops->save_first_param(params_vdev, first_param);
@@ -159,16 +160,22 @@
 		vb2_buffer_done(&params_buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
 		params_vdev->first_params = false;
 		wake_up(&params_vdev->dev->sync_onoff);
-		if (params_vdev->dev->is_first_double) {
-			dev_info(params_vdev->dev->dev, "first params for fast\n");
-			params_vdev->dev->is_first_double = false;
-			rkisp_trigger_read_back(params_vdev->dev, false, false, false);
+		if (dev->is_first_double) {
+			dev_info(dev->dev, "first params for fast\n");
+			dev->is_first_double = false;
+			dev->sw_rd_cnt = 0;
+			if (dev->hw_dev->unite == ISP_UNITE_ONE) {
+				dev->unite_index = ISP_UNITE_LEFT;
+				dev->sw_rd_cnt += dev->hw_dev->is_multi_overflow ? 3 : 1;
+			}
+			params_vdev->rdbk_times = dev->sw_rd_cnt + 1;
+			rkisp_trigger_read_back(dev, false, false, false);
 		}
-		dev_info(params_vdev->dev->dev, "first params buf queue\n");
+		dev_info(dev->dev, "first params buf queue\n");
 		return;
 	}
 
-	if (params_vdev->dev->procfs.mode &
+	if (dev->procfs.mode &
 	    (RKISP_PROCFS_FIL_AIQ | RKISP_PROCFS_FIL_SW)) {
 		vb2_buffer_done(&params_buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
 		return;
@@ -208,9 +215,7 @@
 			break;
 		}
 
-		if (buf)
-			vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
-		buf = NULL;
+		vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
 	}
 
 	if (params_vdev->cur_buf) {
@@ -284,10 +289,21 @@
 	return ret;
 }
 
+static __poll_t rkisp_params_fop_poll(struct file *file, poll_table *wait)
+{
+	struct video_device *vdev = video_devdata(file);
+
+	/* buf done or subscribe event */
+	if (vdev->queue->owner == file->private_data)
+		return vb2_fop_poll(file, wait);
+	else
+		return v4l2_ctrl_poll(file, wait);
+}
+
 struct v4l2_file_operations rkisp_params_fops = {
 	.mmap = vb2_fop_mmap,
 	.unlocked_ioctl = video_ioctl2,
-	.poll = vb2_fop_poll,
+	.poll = rkisp_params_fop_poll,
 	.open = rkisp_params_fh_open,
 	.release = rkisp_params_fop_release
 };
@@ -395,8 +411,7 @@
 		    stream->out_isp_fmt.fmt_type == FMT_RGB)
 			rkisp_unite_set_bits(dev, ISP3X_MI_WR_CTRL, mask,
 					     quantization == V4L2_QUANTIZATION_FULL_RANGE ?
-					     mask : 0,
-					     false, dev->hw_dev->is_unite);
+					     mask : 0, false);
 		dev->isp_sdev.quantization = quantization;
 	}
 }
diff --git a/kernel/drivers/media/platform/rockchip/isp/isp_params_v21.c b/kernel/drivers/media/platform/rockchip/isp/isp_params_v21.c
index d8b978b..19f2a62 100644
--- a/kernel/drivers/media/platform/rockchip/isp/isp_params_v21.c
+++ b/kernel/drivers/media/platform/rockchip/isp/isp_params_v21.c
@@ -3740,19 +3740,6 @@
 		ops->rawaf_enable(params_vdev, !!(module_ens & ISP2X_MODULE_RAWAF));
 }
 
-static __maybe_unused
-void __isp_config_hdrshd(struct rkisp_isp_params_vdev *params_vdev)
-{
-	struct rkisp_isp_params_v21_ops *ops =
-		(struct rkisp_isp_params_v21_ops *)params_vdev->priv_ops;
-	struct rkisp_isp_params_val_v21 *priv_val =
-		(struct rkisp_isp_params_val_v21 *)params_vdev->priv_val;
-
-	ops->hdrmge_config(params_vdev, &priv_val->last_hdrmge, RKISP_PARAMS_SHD);
-
-	ops->hdrdrc_config(params_vdev, &priv_val->last_hdrdrc, RKISP_PARAMS_SHD);
-}
-
 static
 void rkisp_params_cfgsram_v21(struct rkisp_isp_params_vdev *params_vdev)
 {
@@ -3998,11 +3985,6 @@
 		rkisp_set_bits(params_vdev->dev, ISP_CTRL1,
 			       ISP2X_SYS_BIGMODE_MANUAL | ISP2X_SYS_BIGMODE_FORCEEN,
 			       ISP2X_SYS_BIGMODE_MANUAL | ISP2X_SYS_BIGMODE_FORCEEN, false);
-
-	priv_val->cur_hdrmge = params_vdev->isp21_params->others.hdrmge_cfg;
-	priv_val->cur_hdrdrc = params_vdev->isp21_params->others.drc_cfg;
-	priv_val->last_hdrmge = priv_val->cur_hdrmge;
-	priv_val->last_hdrdrc = priv_val->cur_hdrdrc;
 	spin_unlock(&params_vdev->config_lock);
 }
 
@@ -4210,8 +4192,6 @@
 {
 	struct isp21_isp_params_cfg *new_params = NULL;
 	struct rkisp_buffer *cur_buf = params_vdev->cur_buf;
-	struct rkisp_device *dev = params_vdev->dev;
-	struct rkisp_hw_dev *hw_dev = dev->hw_dev;
 
 	spin_lock(&params_vdev->config_lock);
 	if (!params_vdev->streamon)
@@ -4257,17 +4237,8 @@
 	__isp_isr_other_config(params_vdev, new_params, type);
 	__isp_isr_other_en(params_vdev, new_params, type);
 	__isp_isr_meas_en(params_vdev, new_params, type);
-	if (!hw_dev->is_single && type != RKISP_PARAMS_SHD)
-		__isp_config_hdrshd(params_vdev);
 
 	if (type != RKISP_PARAMS_IMD) {
-		struct rkisp_isp_params_val_v21 *priv_val =
-			(struct rkisp_isp_params_val_v21 *)params_vdev->priv_val;
-
-		priv_val->last_hdrmge = priv_val->cur_hdrmge;
-		priv_val->last_hdrdrc = priv_val->cur_hdrdrc;
-		priv_val->cur_hdrmge = new_params->others.hdrmge_cfg;
-		priv_val->cur_hdrdrc = new_params->others.drc_cfg;
 		new_params->module_cfg_update = 0;
 		vb2_buffer_done(&cur_buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
 		cur_buf = NULL;
diff --git a/kernel/drivers/media/platform/rockchip/isp/isp_params_v21.h b/kernel/drivers/media/platform/rockchip/isp/isp_params_v21.h
index 40ffa76..7b374e2 100644
--- a/kernel/drivers/media/platform/rockchip/isp/isp_params_v21.h
+++ b/kernel/drivers/media/platform/rockchip/isp/isp_params_v21.h
@@ -154,11 +154,6 @@
 
 	struct rkisp_dummy_buffer buf_3dnr;
 
-	struct isp2x_hdrmge_cfg last_hdrmge;
-	struct isp21_drc_cfg last_hdrdrc;
-	struct isp2x_hdrmge_cfg cur_hdrmge;
-	struct isp21_drc_cfg cur_hdrdrc;
-
 	u8 dhaz_en;
 	u8 wdr_en;
 	u8 tmo_en;
diff --git a/kernel/drivers/media/platform/rockchip/isp/isp_params_v32.c b/kernel/drivers/media/platform/rockchip/isp/isp_params_v32.c
index 7326ee3..4343412 100644
--- a/kernel/drivers/media/platform/rockchip/isp/isp_params_v32.c
+++ b/kernel/drivers/media/platform/rockchip/isp/isp_params_v32.c
@@ -47,9 +47,12 @@
 
 static inline void
 isp3_param_write(struct rkisp_isp_params_vdev *params_vdev,
-		 u32 value, u32 addr)
+		 u32 value, u32 addr, u32 id)
 {
-	rkisp_write(params_vdev->dev, addr, value, false);
+	if (id == ISP3_LEFT)
+		rkisp_write(params_vdev->dev, addr, value, false);
+	else
+		rkisp_next_write(params_vdev->dev, addr, value, false);
 }
 
 static inline u32
@@ -59,45 +62,63 @@
 }
 
 static inline u32
-isp3_param_read(struct rkisp_isp_params_vdev *params_vdev, u32 addr)
+isp3_param_read(struct rkisp_isp_params_vdev *params_vdev, u32 addr, u32 id)
 {
-	return rkisp_read(params_vdev->dev, addr, false);
+	u32 val;
+
+	if (id == ISP3_LEFT)
+		val = rkisp_read(params_vdev->dev, addr, false);
+	else
+		val = rkisp_next_read(params_vdev->dev, addr, false);
+	return val;
 }
 
 static inline u32
-isp3_param_read_cache(struct rkisp_isp_params_vdev *params_vdev, u32 addr)
+isp3_param_read_cache(struct rkisp_isp_params_vdev *params_vdev, u32 addr, u32 id)
 {
-	return rkisp_read_reg_cache(params_vdev->dev, addr);
+	u32 val;
+
+	if (id == ISP3_LEFT)
+		val = rkisp_read_reg_cache(params_vdev->dev, addr);
+	else
+		val = rkisp_next_read_reg_cache(params_vdev->dev, addr);
+	return val;
 }
 
 static inline void
 isp3_param_set_bits(struct rkisp_isp_params_vdev *params_vdev,
-		    u32 reg, u32 bit_mask)
+		    u32 reg, u32 bit_mask, u32 id)
 {
-	rkisp_set_bits(params_vdev->dev, reg, 0, bit_mask, false);
+	if (id == ISP3_LEFT)
+		rkisp_set_bits(params_vdev->dev, reg, 0, bit_mask, false);
+	else
+		rkisp_next_set_bits(params_vdev->dev, reg, 0, bit_mask, false);
 }
 
 static inline void
 isp3_param_clear_bits(struct rkisp_isp_params_vdev *params_vdev,
-		      u32 reg, u32 bit_mask)
+		      u32 reg, u32 bit_mask, u32 id)
 {
-	rkisp_clear_bits(params_vdev->dev, reg, bit_mask, false);
+	if (id == ISP3_LEFT)
+		rkisp_clear_bits(params_vdev->dev, reg, bit_mask, false);
+	else
+		rkisp_next_clear_bits(params_vdev->dev, reg, bit_mask, false);
 }
 
 static void
 isp_dpcc_config(struct rkisp_isp_params_vdev *params_vdev,
-		const struct isp2x_dpcc_cfg *arg)
+		const struct isp2x_dpcc_cfg *arg, u32 id)
 {
 	u32 value;
 	int i;
 
-	value = isp3_param_read(params_vdev, ISP3X_DPCC0_MODE);
+	value = isp3_param_read(params_vdev, ISP3X_DPCC0_MODE, id);
 	value &= ISP_DPCC_EN;
 
 	value |= !!arg->stage1_enable << 2 |
 		 !!arg->grayscale_mode << 1;
-	isp3_param_write(params_vdev, value, ISP3X_DPCC0_MODE);
-	isp3_param_write(params_vdev, value, ISP3X_DPCC1_MODE);
+	isp3_param_write(params_vdev, value, ISP3X_DPCC0_MODE, id);
+	isp3_param_write(params_vdev, value, ISP3X_DPCC1_MODE, id);
 
 	value = (arg->sw_rk_out_sel & 0x03) << 5 |
 		!!arg->sw_dpcc_output_sel << 4 |
@@ -105,15 +126,15 @@
 		!!arg->stage1_g_3x3 << 2 |
 		!!arg->stage1_incl_rb_center << 1 |
 		!!arg->stage1_incl_green_center;
-	isp3_param_write(params_vdev, value, ISP3X_DPCC0_OUTPUT_MODE);
-	isp3_param_write(params_vdev, value, ISP3X_DPCC1_OUTPUT_MODE);
+	isp3_param_write(params_vdev, value, ISP3X_DPCC0_OUTPUT_MODE, id);
+	isp3_param_write(params_vdev, value, ISP3X_DPCC1_OUTPUT_MODE, id);
 
 	value = !!arg->stage1_use_fix_set << 3 |
 		!!arg->stage1_use_set_3 << 2 |
 		!!arg->stage1_use_set_2 << 1 |
 		!!arg->stage1_use_set_1;
-	isp3_param_write(params_vdev, value, ISP3X_DPCC0_SET_USE);
-	isp3_param_write(params_vdev, value, ISP3X_DPCC1_SET_USE);
+	isp3_param_write(params_vdev, value, ISP3X_DPCC0_SET_USE, id);
+	isp3_param_write(params_vdev, value, ISP3X_DPCC1_SET_USE, id);
 
 	value = !!arg->sw_rk_red_blue1_en << 13 |
 		!!arg->rg_red_blue1_enable << 12 |
@@ -127,8 +148,8 @@
 		!!arg->ro_green1_enable << 2 |
 		!!arg->lc_green1_enable << 1 |
 		!!arg->pg_green1_enable;
-	isp3_param_write(params_vdev, value, ISP3X_DPCC0_METHODS_SET_1);
-	isp3_param_write(params_vdev, value, ISP3X_DPCC1_METHODS_SET_1);
+	isp3_param_write(params_vdev, value, ISP3X_DPCC0_METHODS_SET_1, id);
+	isp3_param_write(params_vdev, value, ISP3X_DPCC1_METHODS_SET_1, id);
 
 	value = !!arg->sw_rk_red_blue2_en << 13 |
 		!!arg->rg_red_blue2_enable << 12 |
@@ -142,8 +163,8 @@
 		!!arg->ro_green2_enable << 2 |
 		!!arg->lc_green2_enable << 1 |
 		!!arg->pg_green2_enable;
-	isp3_param_write(params_vdev, value, ISP3X_DPCC0_METHODS_SET_2);
-	isp3_param_write(params_vdev, value, ISP3X_DPCC1_METHODS_SET_2);
+	isp3_param_write(params_vdev, value, ISP3X_DPCC0_METHODS_SET_2, id);
+	isp3_param_write(params_vdev, value, ISP3X_DPCC1_METHODS_SET_2, id);
 
 	value = !!arg->sw_rk_red_blue3_en << 13 |
 		!!arg->rg_red_blue3_enable << 12 |
@@ -157,74 +178,74 @@
 		!!arg->ro_green3_enable << 2 |
 		!!arg->lc_green3_enable << 1 |
 		!!arg->pg_green3_enable;
-	isp3_param_write(params_vdev, value, ISP3X_DPCC0_METHODS_SET_3);
-	isp3_param_write(params_vdev, value, ISP3X_DPCC1_METHODS_SET_3);
+	isp3_param_write(params_vdev, value, ISP3X_DPCC0_METHODS_SET_3, id);
+	isp3_param_write(params_vdev, value, ISP3X_DPCC1_METHODS_SET_3, id);
 
 	value = ISP_PACK_4BYTE(arg->line_thr_1_g, arg->line_thr_1_rb,
 				arg->sw_mindis1_g, arg->sw_mindis1_rb);
-	isp3_param_write(params_vdev, value, ISP3X_DPCC0_LINE_THRESH_1);
-	isp3_param_write(params_vdev, value, ISP3X_DPCC1_LINE_THRESH_1);
+	isp3_param_write(params_vdev, value, ISP3X_DPCC0_LINE_THRESH_1, id);
+	isp3_param_write(params_vdev, value, ISP3X_DPCC1_LINE_THRESH_1, id);
 
 	value = ISP_PACK_4BYTE(arg->line_mad_fac_1_g, arg->line_mad_fac_1_rb,
 				arg->sw_dis_scale_max1, arg->sw_dis_scale_min1);
-	isp3_param_write(params_vdev, value, ISP3X_DPCC0_LINE_MAD_FAC_1);
-	isp3_param_write(params_vdev, value, ISP3X_DPCC1_LINE_MAD_FAC_1);
+	isp3_param_write(params_vdev, value, ISP3X_DPCC0_LINE_MAD_FAC_1, id);
+	isp3_param_write(params_vdev, value, ISP3X_DPCC1_LINE_MAD_FAC_1, id);
 
 	value = ISP_PACK_4BYTE(arg->pg_fac_1_g, arg->pg_fac_1_rb, 0, 0);
-	isp3_param_write(params_vdev, value, ISP3X_DPCC0_PG_FAC_1);
-	isp3_param_write(params_vdev, value, ISP3X_DPCC1_PG_FAC_1);
+	isp3_param_write(params_vdev, value, ISP3X_DPCC0_PG_FAC_1, id);
+	isp3_param_write(params_vdev, value, ISP3X_DPCC1_PG_FAC_1, id);
 
 	value = ISP_PACK_4BYTE(arg->rnd_thr_1_g, arg->rnd_thr_1_rb, 0, 0);
-	isp3_param_write(params_vdev, value, ISP3X_DPCC0_RND_THRESH_1);
-	isp3_param_write(params_vdev, value, ISP3X_DPCC1_RND_THRESH_1);
+	isp3_param_write(params_vdev, value, ISP3X_DPCC0_RND_THRESH_1, id);
+	isp3_param_write(params_vdev, value, ISP3X_DPCC1_RND_THRESH_1, id);
 
 	value = ISP_PACK_4BYTE(arg->rg_fac_1_g, arg->rg_fac_1_rb, 0, 0);
-	isp3_param_write(params_vdev, value, ISP3X_DPCC0_RG_FAC_1);
-	isp3_param_write(params_vdev, value, ISP3X_DPCC1_RG_FAC_1);
+	isp3_param_write(params_vdev, value, ISP3X_DPCC0_RG_FAC_1, id);
+	isp3_param_write(params_vdev, value, ISP3X_DPCC1_RG_FAC_1, id);
 
 	value = ISP_PACK_4BYTE(arg->line_thr_2_g, arg->line_thr_2_rb,
 				arg->sw_mindis2_g, arg->sw_mindis2_rb);
-	isp3_param_write(params_vdev, value, ISP3X_DPCC0_LINE_THRESH_2);
-	isp3_param_write(params_vdev, value, ISP3X_DPCC1_LINE_THRESH_2);
+	isp3_param_write(params_vdev, value, ISP3X_DPCC0_LINE_THRESH_2, id);
+	isp3_param_write(params_vdev, value, ISP3X_DPCC1_LINE_THRESH_2, id);
 
 	value = ISP_PACK_4BYTE(arg->line_mad_fac_2_g, arg->line_mad_fac_2_rb,
 				arg->sw_dis_scale_max2, arg->sw_dis_scale_min2);
-	isp3_param_write(params_vdev, value, ISP3X_DPCC0_LINE_MAD_FAC_2);
-	isp3_param_write(params_vdev, value, ISP3X_DPCC1_LINE_MAD_FAC_2);
+	isp3_param_write(params_vdev, value, ISP3X_DPCC0_LINE_MAD_FAC_2, id);
+	isp3_param_write(params_vdev, value, ISP3X_DPCC1_LINE_MAD_FAC_2, id);
 
 	value = ISP_PACK_4BYTE(arg->pg_fac_2_g, arg->pg_fac_2_rb, 0, 0);
-	isp3_param_write(params_vdev, value, ISP3X_DPCC0_PG_FAC_2);
-	isp3_param_write(params_vdev, value, ISP3X_DPCC1_PG_FAC_2);
+	isp3_param_write(params_vdev, value, ISP3X_DPCC0_PG_FAC_2, id);
+	isp3_param_write(params_vdev, value, ISP3X_DPCC1_PG_FAC_2, id);
 
 	value = ISP_PACK_4BYTE(arg->rnd_thr_2_g, arg->rnd_thr_2_rb, 0, 0);
-	isp3_param_write(params_vdev, value, ISP3X_DPCC0_RND_THRESH_2);
-	isp3_param_write(params_vdev, value, ISP3X_DPCC1_RND_THRESH_2);
+	isp3_param_write(params_vdev, value, ISP3X_DPCC0_RND_THRESH_2, id);
+	isp3_param_write(params_vdev, value, ISP3X_DPCC1_RND_THRESH_2, id);
 
 	value = ISP_PACK_4BYTE(arg->rg_fac_2_g, arg->rg_fac_2_rb, 0, 0);
-	isp3_param_write(params_vdev, value, ISP3X_DPCC0_RG_FAC_2);
-	isp3_param_write(params_vdev, value, ISP3X_DPCC1_RG_FAC_2);
+	isp3_param_write(params_vdev, value, ISP3X_DPCC0_RG_FAC_2, id);
+	isp3_param_write(params_vdev, value, ISP3X_DPCC1_RG_FAC_2, id);
 
 	value = ISP_PACK_4BYTE(arg->line_thr_3_g, arg->line_thr_3_rb,
 				 arg->sw_mindis3_g, arg->sw_mindis3_rb);
-	isp3_param_write(params_vdev, value, ISP3X_DPCC0_LINE_THRESH_3);
-	isp3_param_write(params_vdev, value, ISP3X_DPCC1_LINE_THRESH_3);
+	isp3_param_write(params_vdev, value, ISP3X_DPCC0_LINE_THRESH_3, id);
+	isp3_param_write(params_vdev, value, ISP3X_DPCC1_LINE_THRESH_3, id);
 
 	value = ISP_PACK_4BYTE(arg->line_mad_fac_3_g, arg->line_mad_fac_3_rb,
 				arg->sw_dis_scale_max3, arg->sw_dis_scale_min3);
-	isp3_param_write(params_vdev, value, ISP3X_DPCC0_LINE_MAD_FAC_3);
-	isp3_param_write(params_vdev, value, ISP3X_DPCC1_LINE_MAD_FAC_3);
+	isp3_param_write(params_vdev, value, ISP3X_DPCC0_LINE_MAD_FAC_3, id);
+	isp3_param_write(params_vdev, value, ISP3X_DPCC1_LINE_MAD_FAC_3, id);
 
 	value = ISP_PACK_4BYTE(arg->pg_fac_3_g, arg->pg_fac_3_rb, 0, 0);
-	isp3_param_write(params_vdev, value, ISP3X_DPCC0_PG_FAC_3);
-	isp3_param_write(params_vdev, value, ISP3X_DPCC1_PG_FAC_3);
+	isp3_param_write(params_vdev, value, ISP3X_DPCC0_PG_FAC_3, id);
+	isp3_param_write(params_vdev, value, ISP3X_DPCC1_PG_FAC_3, id);
 
 	value = ISP_PACK_4BYTE(arg->rnd_thr_3_g, arg->rnd_thr_3_rb, 0, 0);
-	isp3_param_write(params_vdev, value, ISP3X_DPCC0_RND_THRESH_3);
-	isp3_param_write(params_vdev, value, ISP3X_DPCC1_RND_THRESH_3);
+	isp3_param_write(params_vdev, value, ISP3X_DPCC0_RND_THRESH_3, id);
+	isp3_param_write(params_vdev, value, ISP3X_DPCC1_RND_THRESH_3, id);
 
 	value = ISP_PACK_4BYTE(arg->rg_fac_3_g, arg->rg_fac_3_rb, 0, 0);
-	isp3_param_write(params_vdev, value, ISP3X_DPCC0_RG_FAC_3);
-	isp3_param_write(params_vdev, value, ISP3X_DPCC1_RG_FAC_3);
+	isp3_param_write(params_vdev, value, ISP3X_DPCC0_RG_FAC_3, id);
+	isp3_param_write(params_vdev, value, ISP3X_DPCC1_RG_FAC_3, id);
 
 	value = (arg->ro_lim_3_rb & 0x03) << 10 |
 		(arg->ro_lim_3_g & 0x03) << 8 |
@@ -232,8 +253,8 @@
 		(arg->ro_lim_2_g & 0x03) << 4 |
 		(arg->ro_lim_1_rb & 0x03) << 2 |
 		(arg->ro_lim_1_g & 0x03);
-	isp3_param_write(params_vdev, value, ISP3X_DPCC0_RO_LIMITS);
-	isp3_param_write(params_vdev, value, ISP3X_DPCC1_RO_LIMITS);
+	isp3_param_write(params_vdev, value, ISP3X_DPCC0_RO_LIMITS, id);
+	isp3_param_write(params_vdev, value, ISP3X_DPCC1_RO_LIMITS, id);
 
 	value = (arg->rnd_offs_3_rb & 0x03) << 10 |
 		(arg->rnd_offs_3_g & 0x03) << 8 |
@@ -241,8 +262,8 @@
 		(arg->rnd_offs_2_g & 0x03) << 4 |
 		(arg->rnd_offs_1_rb & 0x03) << 2 |
 		(arg->rnd_offs_1_g & 0x03);
-	isp3_param_write(params_vdev, value, ISP3X_DPCC0_RND_OFFS);
-	isp3_param_write(params_vdev, value, ISP3X_DPCC1_RND_OFFS);
+	isp3_param_write(params_vdev, value, ISP3X_DPCC0_RND_OFFS, id);
+	isp3_param_write(params_vdev, value, ISP3X_DPCC1_RND_OFFS, id);
 
 	value = !!arg->bpt_rb_3x3 << 11 |
 		!!arg->bpt_g_3x3 << 10 |
@@ -254,75 +275,75 @@
 		!!arg->bpt_use_set_1 << 4 |
 		!!arg->bpt_cor_en << 1 |
 		!!arg->bpt_det_en;
-	isp3_param_write(params_vdev, value, ISP3X_DPCC0_BPT_CTRL);
-	isp3_param_write(params_vdev, value, ISP3X_DPCC1_BPT_CTRL);
+	isp3_param_write(params_vdev, value, ISP3X_DPCC0_BPT_CTRL, id);
+	isp3_param_write(params_vdev, value, ISP3X_DPCC1_BPT_CTRL, id);
 
-	isp3_param_write(params_vdev, arg->bp_number, ISP3X_DPCC0_BPT_NUMBER);
-	isp3_param_write(params_vdev, arg->bp_number, ISP3X_DPCC1_BPT_NUMBER);
-	isp3_param_write(params_vdev, arg->bp_table_addr, ISP3X_DPCC0_BPT_ADDR);
-	isp3_param_write(params_vdev, arg->bp_table_addr, ISP3X_DPCC1_BPT_ADDR);
+	isp3_param_write(params_vdev, arg->bp_number, ISP3X_DPCC0_BPT_NUMBER, id);
+	isp3_param_write(params_vdev, arg->bp_number, ISP3X_DPCC1_BPT_NUMBER, id);
+	isp3_param_write(params_vdev, arg->bp_table_addr, ISP3X_DPCC0_BPT_ADDR, id);
+	isp3_param_write(params_vdev, arg->bp_table_addr, ISP3X_DPCC1_BPT_ADDR, id);
 
 	value = ISP_PACK_2SHORT(arg->bpt_h_addr, arg->bpt_v_addr);
-	isp3_param_write(params_vdev, value, ISP3X_DPCC0_BPT_DATA);
-	isp3_param_write(params_vdev, value, ISP3X_DPCC1_BPT_DATA);
+	isp3_param_write(params_vdev, value, ISP3X_DPCC0_BPT_DATA, id);
+	isp3_param_write(params_vdev, value, ISP3X_DPCC1_BPT_DATA, id);
 
-	isp3_param_write(params_vdev, arg->bp_cnt, ISP3X_DPCC0_BP_CNT);
-	isp3_param_write(params_vdev, arg->bp_cnt, ISP3X_DPCC1_BP_CNT);
+	isp3_param_write(params_vdev, arg->bp_cnt, ISP3X_DPCC0_BP_CNT, id);
+	isp3_param_write(params_vdev, arg->bp_cnt, ISP3X_DPCC1_BP_CNT, id);
 
-	isp3_param_write(params_vdev, arg->sw_pdaf_en, ISP3X_DPCC0_PDAF_EN);
-	isp3_param_write(params_vdev, arg->sw_pdaf_en, ISP3X_DPCC1_PDAF_EN);
+	isp3_param_write(params_vdev, arg->sw_pdaf_en, ISP3X_DPCC0_PDAF_EN, id);
+	isp3_param_write(params_vdev, arg->sw_pdaf_en, ISP3X_DPCC1_PDAF_EN, id);
 
 	value = 0;
 	for (i = 0; i < ISP32_DPCC_PDAF_POINT_NUM; i++)
 		value |= !!arg->pdaf_point_en[i] << i;
-	isp3_param_write(params_vdev, value, ISP3X_DPCC0_PDAF_POINT_EN);
-	isp3_param_write(params_vdev, value, ISP3X_DPCC1_PDAF_POINT_EN);
+	isp3_param_write(params_vdev, value, ISP3X_DPCC0_PDAF_POINT_EN, id);
+	isp3_param_write(params_vdev, value, ISP3X_DPCC1_PDAF_POINT_EN, id);
 
 	value = ISP_PACK_2SHORT(arg->pdaf_offsetx, arg->pdaf_offsety);
-	isp3_param_write(params_vdev, value, ISP3X_DPCC0_PDAF_OFFSET);
-	isp3_param_write(params_vdev, value, ISP3X_DPCC1_PDAF_OFFSET);
+	isp3_param_write(params_vdev, value, ISP3X_DPCC0_PDAF_OFFSET, id);
+	isp3_param_write(params_vdev, value, ISP3X_DPCC1_PDAF_OFFSET, id);
 
 	value = ISP_PACK_2SHORT(arg->pdaf_wrapx, arg->pdaf_wrapy);
-	isp3_param_write(params_vdev, value, ISP3X_DPCC0_PDAF_WRAP);
-	isp3_param_write(params_vdev, value, ISP3X_DPCC1_PDAF_WRAP);
+	isp3_param_write(params_vdev, value, ISP3X_DPCC0_PDAF_WRAP, id);
+	isp3_param_write(params_vdev, value, ISP3X_DPCC1_PDAF_WRAP, id);
 
 	value = ISP_PACK_2SHORT(arg->pdaf_wrapx_num, arg->pdaf_wrapy_num);
-	isp3_param_write(params_vdev, value, ISP_DPCC0_PDAF_SCOPE);
-	isp3_param_write(params_vdev, value, ISP_DPCC1_PDAF_SCOPE);
+	isp3_param_write(params_vdev, value, ISP_DPCC0_PDAF_SCOPE, id);
+	isp3_param_write(params_vdev, value, ISP_DPCC1_PDAF_SCOPE, id);
 
 	for (i = 0; i < ISP32_DPCC_PDAF_POINT_NUM / 2; i++) {
 		value = ISP_PACK_4BYTE(arg->point[2 * i].x, arg->point[2 * i].y,
 					arg->point[2 * i + 1].x, arg->point[2 * i + 1].y);
-		isp3_param_write(params_vdev, value, ISP3X_DPCC0_PDAF_POINT_0 + 4 * i);
-		isp3_param_write(params_vdev, value, ISP3X_DPCC1_PDAF_POINT_0 + 4 * i);
+		isp3_param_write(params_vdev, value, ISP3X_DPCC0_PDAF_POINT_0 + 4 * i, id);
+		isp3_param_write(params_vdev, value, ISP3X_DPCC1_PDAF_POINT_0 + 4 * i, id);
 	}
 
-	isp3_param_write(params_vdev, arg->pdaf_forward_med, ISP3X_DPCC0_PDAF_FORWARD_MED);
-	isp3_param_write(params_vdev, arg->pdaf_forward_med, ISP3X_DPCC1_PDAF_FORWARD_MED);
+	isp3_param_write(params_vdev, arg->pdaf_forward_med, ISP3X_DPCC0_PDAF_FORWARD_MED, id);
+	isp3_param_write(params_vdev, arg->pdaf_forward_med, ISP3X_DPCC1_PDAF_FORWARD_MED, id);
 }
 
 static void
-isp_dpcc_enable(struct rkisp_isp_params_vdev *params_vdev, bool en)
+isp_dpcc_enable(struct rkisp_isp_params_vdev *params_vdev, bool en, u32 id)
 {
 	u32 value;
 
-	value = isp3_param_read(params_vdev, ISP3X_DPCC0_MODE);
+	value = isp3_param_read(params_vdev, ISP3X_DPCC0_MODE, id);
 	value &= ~ISP_DPCC_EN;
 
 	if (en)
 		value |= ISP_DPCC_EN;
-	isp3_param_write(params_vdev, value, ISP3X_DPCC0_MODE);
-	isp3_param_write(params_vdev, value, ISP3X_DPCC1_MODE);
+	isp3_param_write(params_vdev, value, ISP3X_DPCC0_MODE, id);
+	isp3_param_write(params_vdev, value, ISP3X_DPCC1_MODE, id);
 }
 
 static void
 isp_bls_config(struct rkisp_isp_params_vdev *params_vdev,
-	       const struct isp32_bls_cfg *arg)
+	       const struct isp32_bls_cfg *arg, u32 id)
 {
 	const struct isp2x_bls_fixed_val *pval;
 	u32 new_control, value;
 
-	new_control = isp3_param_read(params_vdev, ISP3X_BLS_CTRL);
+	new_control = isp3_param_read(params_vdev, ISP3X_BLS_CTRL, id);
 	new_control &= (ISP_BLS_ENA | ISP32_BLS_BLS2_EN);
 
 	pval = &arg->bls1_val;
@@ -331,29 +352,29 @@
 
 		switch (params_vdev->raw_type) {
 		case RAW_BGGR:
-			isp3_param_write(params_vdev, pval->r, ISP3X_BLS1_D_FIXED);
-			isp3_param_write(params_vdev, pval->gr, ISP3X_BLS1_C_FIXED);
-			isp3_param_write(params_vdev, pval->gb, ISP3X_BLS1_B_FIXED);
-			isp3_param_write(params_vdev, pval->b, ISP3X_BLS1_A_FIXED);
+			isp3_param_write(params_vdev, pval->r, ISP3X_BLS1_D_FIXED, id);
+			isp3_param_write(params_vdev, pval->gr, ISP3X_BLS1_C_FIXED, id);
+			isp3_param_write(params_vdev, pval->gb, ISP3X_BLS1_B_FIXED, id);
+			isp3_param_write(params_vdev, pval->b, ISP3X_BLS1_A_FIXED, id);
 			break;
 		case RAW_GBRG:
-			isp3_param_write(params_vdev, pval->r, ISP3X_BLS1_C_FIXED);
-			isp3_param_write(params_vdev, pval->gr, ISP3X_BLS1_D_FIXED);
-			isp3_param_write(params_vdev, pval->gb, ISP3X_BLS1_A_FIXED);
-			isp3_param_write(params_vdev, pval->b, ISP3X_BLS1_B_FIXED);
+			isp3_param_write(params_vdev, pval->r, ISP3X_BLS1_C_FIXED, id);
+			isp3_param_write(params_vdev, pval->gr, ISP3X_BLS1_D_FIXED, id);
+			isp3_param_write(params_vdev, pval->gb, ISP3X_BLS1_A_FIXED, id);
+			isp3_param_write(params_vdev, pval->b, ISP3X_BLS1_B_FIXED, id);
 			break;
 		case RAW_GRBG:
-			isp3_param_write(params_vdev, pval->r, ISP3X_BLS1_B_FIXED);
-			isp3_param_write(params_vdev, pval->gr, ISP3X_BLS1_A_FIXED);
-			isp3_param_write(params_vdev, pval->gb, ISP3X_BLS1_D_FIXED);
-			isp3_param_write(params_vdev, pval->b, ISP3X_BLS1_C_FIXED);
+			isp3_param_write(params_vdev, pval->r, ISP3X_BLS1_B_FIXED, id);
+			isp3_param_write(params_vdev, pval->gr, ISP3X_BLS1_A_FIXED, id);
+			isp3_param_write(params_vdev, pval->gb, ISP3X_BLS1_D_FIXED, id);
+			isp3_param_write(params_vdev, pval->b, ISP3X_BLS1_C_FIXED, id);
 			break;
 		case RAW_RGGB:
 		default:
-			isp3_param_write(params_vdev, pval->r, ISP3X_BLS1_A_FIXED);
-			isp3_param_write(params_vdev, pval->gr, ISP3X_BLS1_B_FIXED);
-			isp3_param_write(params_vdev, pval->gb, ISP3X_BLS1_C_FIXED);
-			isp3_param_write(params_vdev, pval->b, ISP3X_BLS1_D_FIXED);
+			isp3_param_write(params_vdev, pval->r, ISP3X_BLS1_A_FIXED, id);
+			isp3_param_write(params_vdev, pval->gr, ISP3X_BLS1_B_FIXED, id);
+			isp3_param_write(params_vdev, pval->gb, ISP3X_BLS1_C_FIXED, id);
+			isp3_param_write(params_vdev, pval->b, ISP3X_BLS1_D_FIXED, id);
 			break;
 		}
 	}
@@ -363,107 +384,107 @@
 	if (!arg->enable_auto) {
 		switch (params_vdev->raw_type) {
 		case RAW_BGGR:
-			isp3_param_write(params_vdev, pval->r, ISP3X_BLS_D_FIXED);
-			isp3_param_write(params_vdev, pval->gr, ISP3X_BLS_C_FIXED);
-			isp3_param_write(params_vdev, pval->gb, ISP3X_BLS_B_FIXED);
-			isp3_param_write(params_vdev, pval->b, ISP3X_BLS_A_FIXED);
+			isp3_param_write(params_vdev, pval->r, ISP3X_BLS_D_FIXED, id);
+			isp3_param_write(params_vdev, pval->gr, ISP3X_BLS_C_FIXED, id);
+			isp3_param_write(params_vdev, pval->gb, ISP3X_BLS_B_FIXED, id);
+			isp3_param_write(params_vdev, pval->b, ISP3X_BLS_A_FIXED, id);
 			break;
 		case RAW_GBRG:
-			isp3_param_write(params_vdev, pval->r, ISP3X_BLS_C_FIXED);
-			isp3_param_write(params_vdev, pval->gr, ISP3X_BLS_D_FIXED);
-			isp3_param_write(params_vdev, pval->gb, ISP3X_BLS_A_FIXED);
-			isp3_param_write(params_vdev, pval->b, ISP3X_BLS_B_FIXED);
+			isp3_param_write(params_vdev, pval->r, ISP3X_BLS_C_FIXED, id);
+			isp3_param_write(params_vdev, pval->gr, ISP3X_BLS_D_FIXED, id);
+			isp3_param_write(params_vdev, pval->gb, ISP3X_BLS_A_FIXED, id);
+			isp3_param_write(params_vdev, pval->b, ISP3X_BLS_B_FIXED, id);
 			break;
 		case RAW_GRBG:
-			isp3_param_write(params_vdev, pval->r, ISP3X_BLS_B_FIXED);
-			isp3_param_write(params_vdev, pval->gr, ISP3X_BLS_A_FIXED);
-			isp3_param_write(params_vdev, pval->gb, ISP3X_BLS_D_FIXED);
-			isp3_param_write(params_vdev, pval->b, ISP3X_BLS_C_FIXED);
+			isp3_param_write(params_vdev, pval->r, ISP3X_BLS_B_FIXED, id);
+			isp3_param_write(params_vdev, pval->gr, ISP3X_BLS_A_FIXED, id);
+			isp3_param_write(params_vdev, pval->gb, ISP3X_BLS_D_FIXED, id);
+			isp3_param_write(params_vdev, pval->b, ISP3X_BLS_C_FIXED, id);
 			break;
 		case RAW_RGGB:
 		default:
-			isp3_param_write(params_vdev, pval->r, ISP3X_BLS_A_FIXED);
-			isp3_param_write(params_vdev, pval->gr, ISP3X_BLS_B_FIXED);
-			isp3_param_write(params_vdev, pval->gb, ISP3X_BLS_C_FIXED);
-			isp3_param_write(params_vdev, pval->b, ISP3X_BLS_D_FIXED);
+			isp3_param_write(params_vdev, pval->r, ISP3X_BLS_A_FIXED, id);
+			isp3_param_write(params_vdev, pval->gr, ISP3X_BLS_B_FIXED, id);
+			isp3_param_write(params_vdev, pval->gb, ISP3X_BLS_C_FIXED, id);
+			isp3_param_write(params_vdev, pval->b, ISP3X_BLS_D_FIXED, id);
 			break;
 		}
 	} else {
 		if (arg->en_windows & BIT(1)) {
-			isp3_param_write(params_vdev, arg->bls_window2.h_offs, ISP3X_BLS_H2_START);
+			isp3_param_write(params_vdev, arg->bls_window2.h_offs, ISP3X_BLS_H2_START, id);
 			value = arg->bls_window2.h_offs + arg->bls_window2.h_size;
-			isp3_param_write(params_vdev, value, ISP3X_BLS_H2_STOP);
-			isp3_param_write(params_vdev, arg->bls_window2.v_offs, ISP3X_BLS_V2_START);
+			isp3_param_write(params_vdev, value, ISP3X_BLS_H2_STOP, id);
+			isp3_param_write(params_vdev, arg->bls_window2.v_offs, ISP3X_BLS_V2_START, id);
 			value = arg->bls_window2.v_offs + arg->bls_window2.v_size;
-			isp3_param_write(params_vdev, value, ISP3X_BLS_V2_STOP);
+			isp3_param_write(params_vdev, value, ISP3X_BLS_V2_STOP, id);
 			new_control |= ISP_BLS_WINDOW_2;
 		}
 
 		if (arg->en_windows & BIT(0)) {
-			isp3_param_write(params_vdev, arg->bls_window1.h_offs, ISP3X_BLS_H1_START);
+			isp3_param_write(params_vdev, arg->bls_window1.h_offs, ISP3X_BLS_H1_START, id);
 			value = arg->bls_window1.h_offs + arg->bls_window1.h_size;
-			isp3_param_write(params_vdev, value, ISP3X_BLS_H1_STOP);
-			isp3_param_write(params_vdev, arg->bls_window1.v_offs, ISP3X_BLS_V1_START);
+			isp3_param_write(params_vdev, value, ISP3X_BLS_H1_STOP, id);
+			isp3_param_write(params_vdev, arg->bls_window1.v_offs, ISP3X_BLS_V1_START, id);
 			value = arg->bls_window1.v_offs + arg->bls_window1.v_size;
-			isp3_param_write(params_vdev, value, ISP3X_BLS_V1_STOP);
+			isp3_param_write(params_vdev, value, ISP3X_BLS_V1_STOP, id);
 			new_control |= ISP_BLS_WINDOW_1;
 		}
 
-		isp3_param_write(params_vdev, arg->bls_samples, ISP3X_BLS_SAMPLES);
+		isp3_param_write(params_vdev, arg->bls_samples, ISP3X_BLS_SAMPLES, id);
 
 		new_control |= ISP_BLS_MODE_MEASURED;
 	}
-	isp3_param_write(params_vdev, new_control, ISP3X_BLS_CTRL);
+	isp3_param_write(params_vdev, new_control, ISP3X_BLS_CTRL, id);
 
-	isp3_param_write(params_vdev, arg->isp_ob_offset, ISP32_BLS_ISP_OB_OFFSET);
-	isp3_param_write(params_vdev, arg->isp_ob_predgain, ISP32_BLS_ISP_OB_PREDGAIN);
-	isp3_param_write(params_vdev, arg->isp_ob_max, ISP32_BLS_ISP_OB_MAX);
+	isp3_param_write(params_vdev, arg->isp_ob_offset, ISP32_BLS_ISP_OB_OFFSET, id);
+	isp3_param_write(params_vdev, arg->isp_ob_predgain, ISP32_BLS_ISP_OB_PREDGAIN, id);
+	isp3_param_write(params_vdev, arg->isp_ob_max, ISP32_BLS_ISP_OB_MAX, id);
 }
 
 static void
-isp_bls_enable(struct rkisp_isp_params_vdev *params_vdev, bool en)
+isp_bls_enable(struct rkisp_isp_params_vdev *params_vdev, bool en, u32 id)
 {
 	u32 new_control;
 
-	new_control = isp3_param_read(params_vdev, ISP3X_BLS_CTRL);
+	new_control = isp3_param_read(params_vdev, ISP3X_BLS_CTRL, id);
 	if (en)
 		new_control |= ISP_BLS_ENA;
 	else
 		new_control &= ~ISP_BLS_ENA;
-	isp3_param_write(params_vdev, new_control, ISP3X_BLS_CTRL);
+	isp3_param_write(params_vdev, new_control, ISP3X_BLS_CTRL, id);
 }
 
 static void
 isp_sdg_config(struct rkisp_isp_params_vdev *params_vdev,
-	       const struct isp2x_sdg_cfg *arg)
+	       const struct isp2x_sdg_cfg *arg, u32 id)
 {
 	int i;
 
-	isp3_param_write(params_vdev, arg->xa_pnts.gamma_dx0, ISP3X_ISP_GAMMA_DX_LO);
-	isp3_param_write(params_vdev, arg->xa_pnts.gamma_dx1, ISP3X_ISP_GAMMA_DX_HI);
+	isp3_param_write(params_vdev, arg->xa_pnts.gamma_dx0, ISP3X_ISP_GAMMA_DX_LO, id);
+	isp3_param_write(params_vdev, arg->xa_pnts.gamma_dx1, ISP3X_ISP_GAMMA_DX_HI, id);
 
 	for (i = 0; i < ISP32_DEGAMMA_CURVE_SIZE; i++) {
 		isp3_param_write(params_vdev, arg->curve_r.gamma_y[i],
-				 ISP3X_ISP_GAMMA_R_Y_0 + i * 4);
+				 ISP3X_ISP_GAMMA_R_Y_0 + i * 4, id);
 		isp3_param_write(params_vdev, arg->curve_g.gamma_y[i],
-				 ISP3X_ISP_GAMMA_G_Y_0 + i * 4);
+				 ISP3X_ISP_GAMMA_G_Y_0 + i * 4, id);
 		isp3_param_write(params_vdev, arg->curve_b.gamma_y[i],
-				 ISP3X_ISP_GAMMA_B_Y_0 + i * 4);
+				 ISP3X_ISP_GAMMA_B_Y_0 + i * 4, id);
 	}
 }
 
 static void
-isp_sdg_enable(struct rkisp_isp_params_vdev *params_vdev, bool en)
+isp_sdg_enable(struct rkisp_isp_params_vdev *params_vdev, bool en, u32 id)
 {
 	u32 val;
 
-	val = isp3_param_read_cache(params_vdev, ISP3X_ISP_CTRL0);
+	val = isp3_param_read_cache(params_vdev, ISP3X_ISP_CTRL0, id);
 	if (en)
 		isp3_param_write(params_vdev, val | CIF_ISP_CTRL_ISP_GAMMA_IN_ENA,
-				 ISP3X_ISP_CTRL0);
+				 ISP3X_ISP_CTRL0, id);
 	else
 		isp3_param_write(params_vdev, val & ~CIF_ISP_CTRL_ISP_GAMMA_IN_ENA,
-				      ISP3X_ISP_CTRL0);
+				 ISP3X_ISP_CTRL0, id);
 }
 
 static void __maybe_unused
@@ -524,21 +545,21 @@
 	}
 	rkisp_prepare_buffer(params_vdev->dev, &priv_val->buf_lsclut[buf_idx]);
 	data = priv_val->buf_lsclut[buf_idx].dma_addr;
-	isp3_param_write(params_vdev, data, ISP3X_MI_LUT_LSC_RD_BASE);
-	isp3_param_write(params_vdev, ISP32_LSC_LUT_TBL_SIZE, ISP3X_MI_LUT_LSC_RD_WSIZE);
+	isp3_param_write(params_vdev, data, ISP3X_MI_LUT_LSC_RD_BASE, 0);
+	isp3_param_write(params_vdev, ISP32_LSC_LUT_TBL_SIZE, ISP3X_MI_LUT_LSC_RD_WSIZE, 0);
 }
 
 static void
 isp_lsc_matrix_cfg_sram(struct rkisp_isp_params_vdev *params_vdev,
 			const struct isp3x_lsc_cfg *pconfig,
-			bool is_check)
+			bool is_check, u32 id)
 {
 	struct rkisp_device *dev = params_vdev->dev;
 	u32 sram_addr, data, table;
 	int i, j;
 
 	if (is_check &&
-	    !(isp3_param_read(params_vdev, ISP3X_LSC_CTRL) & ISP_LSC_EN))
+	    !(isp3_param_read(params_vdev, ISP3X_LSC_CTRL, id) & ISP_LSC_EN))
 		return;
 
 	table = isp3_param_read_direct(params_vdev, ISP3X_LSC_STATUS);
@@ -601,21 +622,21 @@
 		(struct rkisp_isp_params_vdev *)data;
 	struct isp32_isp_params_cfg *params = params_vdev->isp32_params;
 
-	isp_lsc_matrix_cfg_sram(params_vdev, &params->others.lsc_cfg, true);
+	isp_lsc_matrix_cfg_sram(params_vdev, &params->others.lsc_cfg, true, 0);
 }
 
 static void
 isp_lsc_config(struct rkisp_isp_params_vdev *params_vdev,
-	       const struct isp3x_lsc_cfg *arg)
+	       const struct isp3x_lsc_cfg *arg, u32 id)
 {
 	struct rkisp_isp_params_val_v32 *priv_val =
 		(struct rkisp_isp_params_val_v32 *)params_vdev->priv_val;
-	struct isp32_isp_params_cfg *params_rec = params_vdev->isp32_params;
+	struct isp32_isp_params_cfg *params_rec = params_vdev->isp32_params + id;
 	struct rkisp_device *dev = params_vdev->dev;
 	u32 data, lsc_ctrl;
 	int i;
 
-	lsc_ctrl = isp3_param_read(params_vdev, ISP3X_LSC_CTRL);
+	lsc_ctrl = isp3_param_read(params_vdev, ISP3X_LSC_CTRL, id);
 	if (dev->isp_ver == ISP_V32_L) {
 		/* one lsc sram table
 		 * online mode lsc lut load from ddr quick for some sensor VB short
@@ -625,7 +646,7 @@
 		if (!IS_HDR_RDBK(dev->rd_mode))
 			isp_lsc_matrix_cfg_ddr(params_vdev, arg);
 		else if (dev->hw_dev->is_single)
-			isp_lsc_matrix_cfg_sram(params_vdev, arg, false);
+			isp_lsc_matrix_cfg_sram(params_vdev, arg, false, id);
 		else
 			params_rec->others.lsc_cfg = *arg;
 	} else {
@@ -637,27 +658,27 @@
 	for (i = 0; i < ISP32_LSC_SIZE_TBL_SIZE / 4; i++) {
 		/* program x size tables */
 		data = CIF_ISP_LSC_SECT_SIZE(arg->x_size_tbl[i * 2], arg->x_size_tbl[i * 2 + 1]);
-		isp3_param_write(params_vdev, data, ISP3X_LSC_XSIZE_01 + i * 4);
+		isp3_param_write(params_vdev, data, ISP3X_LSC_XSIZE_01 + i * 4, id);
 		data = CIF_ISP_LSC_SECT_SIZE(arg->x_size_tbl[i * 2 + 8], arg->x_size_tbl[i * 2 + 9]);
-		isp3_param_write(params_vdev, data, ISP3X_LSC_XSIZE_89 + i * 4);
+		isp3_param_write(params_vdev, data, ISP3X_LSC_XSIZE_89 + i * 4, id);
 
 		/* program x grad tables */
 		data = CIF_ISP_LSC_SECT_SIZE(arg->x_grad_tbl[i * 2], arg->x_grad_tbl[i * 2 + 1]);
-		isp3_param_write(params_vdev, data, ISP3X_LSC_XGRAD_01 + i * 4);
+		isp3_param_write(params_vdev, data, ISP3X_LSC_XGRAD_01 + i * 4, id);
 		data = CIF_ISP_LSC_SECT_SIZE(arg->x_grad_tbl[i * 2 + 8], arg->x_grad_tbl[i * 2 + 9]);
-		isp3_param_write(params_vdev, data, ISP3X_LSC_XGRAD_89 + i * 4);
+		isp3_param_write(params_vdev, data, ISP3X_LSC_XGRAD_89 + i * 4, id);
 
 		/* program y size tables */
 		data = CIF_ISP_LSC_SECT_SIZE(arg->y_size_tbl[i * 2], arg->y_size_tbl[i * 2 + 1]);
-		isp3_param_write(params_vdev, data, ISP3X_LSC_YSIZE_01 + i * 4);
+		isp3_param_write(params_vdev, data, ISP3X_LSC_YSIZE_01 + i * 4, id);
 		data = CIF_ISP_LSC_SECT_SIZE(arg->y_size_tbl[i * 2 + 8], arg->y_size_tbl[i * 2 + 9]);
-		isp3_param_write(params_vdev, data, ISP3X_LSC_YSIZE_89 + i * 4);
+		isp3_param_write(params_vdev, data, ISP3X_LSC_YSIZE_89 + i * 4, id);
 
 		/* program y grad tables */
 		data = CIF_ISP_LSC_SECT_SIZE(arg->y_grad_tbl[i * 2], arg->y_grad_tbl[i * 2 + 1]);
-		isp3_param_write(params_vdev, data, ISP3X_LSC_YGRAD_01 + i * 4);
+		isp3_param_write(params_vdev, data, ISP3X_LSC_YGRAD_01 + i * 4, id);
 		data = CIF_ISP_LSC_SECT_SIZE(arg->y_grad_tbl[i * 2 + 8], arg->y_grad_tbl[i * 2 + 9]);
-		isp3_param_write(params_vdev, data, ISP3X_LSC_YGRAD_89 + i * 4);
+		isp3_param_write(params_vdev, data, ISP3X_LSC_YGRAD_89 + i * 4, id);
 	}
 
 	if (arg->sector_16x16)
@@ -666,15 +687,15 @@
 		lsc_ctrl &= ~ISP3X_LSC_SECTOR_16X16;
 	if (dev->isp_ver == ISP_V32_L && !IS_HDR_RDBK(dev->rd_mode))
 		lsc_ctrl |= ISP3X_LSC_LUT_EN;
-	isp3_param_write(params_vdev, lsc_ctrl, ISP3X_LSC_CTRL);
+	isp3_param_write(params_vdev, lsc_ctrl, ISP3X_LSC_CTRL, id);
 }
 
 static void
-isp_lsc_enable(struct rkisp_isp_params_vdev *params_vdev, bool en)
+isp_lsc_enable(struct rkisp_isp_params_vdev *params_vdev, bool en, u32 id)
 {
 	struct rkisp_device *dev = params_vdev->dev;
-	struct isp32_isp_params_cfg *params_rec = params_vdev->isp32_params;
-	u32 val = isp3_param_read(params_vdev, ISP3X_LSC_CTRL);
+	struct isp32_isp_params_cfg *params_rec = params_vdev->isp32_params + id;
+	u32 val = isp3_param_read(params_vdev, ISP3X_LSC_CTRL, id);
 
 	if (en == !!(val & ISP_LSC_EN))
 		return;
@@ -683,99 +704,99 @@
 		val = ISP_LSC_EN | ISP32_SELF_FORCE_UPD;
 		if (dev->isp_ver == ISP_V32_L && !IS_HDR_RDBK(dev->rd_mode))
 			val |= ISP3X_LSC_LUT_EN;
-		isp3_param_set_bits(params_vdev, ISP3X_LSC_CTRL, val);
+		isp3_param_set_bits(params_vdev, ISP3X_LSC_CTRL, val, id);
 		if (dev->isp_ver == ISP_V32 && params_vdev->dev->hw_dev->is_single)
 			isp_lsc_matrix_cfg_sram(params_vdev,
-						&params_rec->others.lsc_cfg, false);
+						&params_rec->others.lsc_cfg, false, id);
 	} else {
-		isp3_param_clear_bits(params_vdev, ISP3X_LSC_CTRL, ISP_LSC_EN);
-		isp3_param_clear_bits(params_vdev, ISP3X_GAIN_CTRL, BIT(8));
+		isp3_param_clear_bits(params_vdev, ISP3X_LSC_CTRL, ISP_LSC_EN, id);
+		isp3_param_clear_bits(params_vdev, ISP3X_GAIN_CTRL, BIT(8), id);
 	}
 }
 
 static void
 isp_debayer_config(struct rkisp_isp_params_vdev *params_vdev,
-		   const struct isp32_debayer_cfg *arg)
+		   const struct isp32_debayer_cfg *arg, u32 id)
 {
 	u32 value;
 
-	value = isp3_param_read(params_vdev, ISP3X_DEBAYER_CONTROL);
+	value = isp3_param_read(params_vdev, ISP3X_DEBAYER_CONTROL, id);
 	value &= ISP_DEBAYER_EN;
 
 	value |= !!arg->filter_g_en << 4;
 	if (params_vdev->dev->isp_ver == ISP_V32)
 		value |= !!arg->filter_c_en << 8;
-	isp3_param_write(params_vdev, value, ISP3X_DEBAYER_CONTROL);
+	isp3_param_write(params_vdev, value, ISP3X_DEBAYER_CONTROL, id);
 
 	value = (arg->max_ratio & 0x3F) << 24 | arg->select_thed << 16 |
 		(arg->thed1 & 0x0F) << 12 | (arg->thed0 & 0x0F) << 8 |
 		(arg->dist_scale & 0x0F) << 4 | !!arg->clip_en;
-	isp3_param_write(params_vdev, value, ISP3X_DEBAYER_G_INTERP);
+	isp3_param_write(params_vdev, value, ISP3X_DEBAYER_G_INTERP, id);
 
 	value = (arg->filter1_coe4 & 0x1F) << 24 | (arg->filter1_coe3 & 0x1F) << 16 |
 		(arg->filter1_coe2 & 0x1F) << 8 | (arg->filter1_coe1 & 0x1F);
-	isp3_param_write(params_vdev, value, ISP3X_DEBAYER_G_INTERP_FILTER1);
+	isp3_param_write(params_vdev, value, ISP3X_DEBAYER_G_INTERP_FILTER1, id);
 
 	value = (arg->filter2_coe4 & 0x1F) << 24 | (arg->filter2_coe3 & 0x1F) << 16 |
 		(arg->filter2_coe2 & 0x1F) << 8 | (arg->filter2_coe1 & 0x1F);
-	isp3_param_write(params_vdev, value, ISP3X_DEBAYER_G_INTERP_FILTER2);
+	isp3_param_write(params_vdev, value, ISP3X_DEBAYER_G_INTERP_FILTER2, id);
 
 	value = arg->hf_offset << 16 | (arg->gain_offset & 0xFFF);
-	isp3_param_write(params_vdev, value, ISP32_DEBAYER_G_INTERP_OFFSET);
+	isp3_param_write(params_vdev, value, ISP32_DEBAYER_G_INTERP_OFFSET, id);
 
 	value = (arg->offset & 0x7FF);
-	isp3_param_write(params_vdev, value, ISP32_DEBAYER_G_FILTER_OFFSET);
+	isp3_param_write(params_vdev, value, ISP32_DEBAYER_G_FILTER_OFFSET, id);
 
 	if (params_vdev->dev->isp_ver != ISP_V32)
 		return;
 
 	value = arg->guid_gaus_coe2 << 16 |
 		arg->guid_gaus_coe1 << 8 | arg->guid_gaus_coe0;
-	isp3_param_write(params_vdev, value, ISP32_DEBAYER_C_FILTER_GUIDE_GAUS);
+	isp3_param_write(params_vdev, value, ISP32_DEBAYER_C_FILTER_GUIDE_GAUS, id);
 
 	value = arg->ce_gaus_coe2 << 16 |
 		arg->ce_gaus_coe1 << 8 | arg->ce_gaus_coe0;
-	isp3_param_write(params_vdev, value, ISP32_DEBAYER_C_FILTER_CE_GAUS);
+	isp3_param_write(params_vdev, value, ISP32_DEBAYER_C_FILTER_CE_GAUS, id);
 
 	value = arg->alpha_gaus_coe2 << 16 |
 		arg->alpha_gaus_coe1 << 8 | arg->alpha_gaus_coe0;
-	isp3_param_write(params_vdev, value, ISP32_DEBAYER_C_FILTER_ALPHA_GAUS);
+	isp3_param_write(params_vdev, value, ISP32_DEBAYER_C_FILTER_ALPHA_GAUS, id);
 
 	value = (arg->loggd_offset & 0xfff) << 16 | (arg->loghf_offset & 0x1fff);
-	isp3_param_write(params_vdev, value, ISP32_DEBAYER_C_FILTER_LOG_OFFSET);
+	isp3_param_write(params_vdev, value, ISP32_DEBAYER_C_FILTER_LOG_OFFSET, id);
 
 	value = (arg->alpha_scale & 0xfffff) << 12 | (arg->alpha_offset & 0xfff);
-	isp3_param_write(params_vdev, value, ISP32_DEBAYER_C_FILTER_ALPHA);
+	isp3_param_write(params_vdev, value, ISP32_DEBAYER_C_FILTER_ALPHA, id);
 
 	value = (arg->edge_scale & 0xfffff) << 12 | (arg->edge_offset & 0xfff);
-	isp3_param_write(params_vdev, value, ISP32_DEBAYER_C_FILTER_EDGE);
+	isp3_param_write(params_vdev, value, ISP32_DEBAYER_C_FILTER_EDGE, id);
 
 	value = (arg->wgtslope & 0xfff) << 16 |
 		(arg->exp_shift & 0x3f) << 8 | arg->ce_sgm;
-	isp3_param_write(params_vdev, value, ISP32_DEBAYER_C_FILTER_IIR_0);
+	isp3_param_write(params_vdev, value, ISP32_DEBAYER_C_FILTER_IIR_0, id);
 
 	value = (arg->wet_ghost & 0x3f) << 8 | (arg->wet_clip & 0x7f);
-	isp3_param_write(params_vdev, value, ISP32_DEBAYER_C_FILTER_IIR_1);
+	isp3_param_write(params_vdev, value, ISP32_DEBAYER_C_FILTER_IIR_1, id);
 
 	value = (arg->bf_curwgt & 0x7f) << 24 |
 		(arg->bf_clip & 0x7f) << 16 | arg->bf_sgm;
-	isp3_param_write(params_vdev, value, ISP32_DEBAYER_C_FILTER_BF);
+	isp3_param_write(params_vdev, value, ISP32_DEBAYER_C_FILTER_BF, id);
 }
 
 static void
-isp_debayer_enable(struct rkisp_isp_params_vdev *params_vdev, bool en)
+isp_debayer_enable(struct rkisp_isp_params_vdev *params_vdev, bool en, u32 id)
 {
 	if (en)
 		isp3_param_set_bits(params_vdev,
-				    ISP3X_DEBAYER_CONTROL, ISP32_MODULE_EN);
+				    ISP3X_DEBAYER_CONTROL, ISP32_MODULE_EN, id);
 	else
 		isp3_param_clear_bits(params_vdev,
-				      ISP3X_DEBAYER_CONTROL, ISP32_MODULE_EN);
+				      ISP3X_DEBAYER_CONTROL, ISP32_MODULE_EN, id);
 }
 
 static void
 isp_awbgain_config(struct rkisp_isp_params_vdev *params_vdev,
-		   const struct isp32_awb_gain_cfg *arg)
+		   const struct isp32_awb_gain_cfg *arg, u32 id)
 {
 	struct rkisp_device *dev = params_vdev->dev;
 
@@ -791,184 +812,184 @@
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->gain0_green_b, arg->gain0_green_r),
-			 ISP3X_ISP_AWB_GAIN0_G);
+			 ISP3X_ISP_AWB_GAIN0_G, id);
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->gain0_blue, arg->gain0_red),
-			 ISP3X_ISP_AWB_GAIN0_RB);
+			 ISP3X_ISP_AWB_GAIN0_RB, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->gain1_green_b, arg->gain1_green_r),
-			 ISP3X_ISP_AWB_GAIN1_G);
+			 ISP3X_ISP_AWB_GAIN1_G, id);
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->gain1_blue, arg->gain1_red),
-			 ISP3X_ISP_AWB_GAIN1_RB);
+			 ISP3X_ISP_AWB_GAIN1_RB, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->gain2_green_b, arg->gain2_green_r),
-			 ISP3X_ISP_AWB_GAIN2_G);
+			 ISP3X_ISP_AWB_GAIN2_G, id);
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->gain2_blue, arg->gain2_red),
-			 ISP3X_ISP_AWB_GAIN2_RB);
+			 ISP3X_ISP_AWB_GAIN2_RB, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->awb1_gain_gb, arg->awb1_gain_gr),
-			 ISP32_ISP_AWB1_GAIN_G);
+			 ISP32_ISP_AWB1_GAIN_G, id);
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->awb1_gain_b, arg->awb1_gain_r),
-			 ISP32_ISP_AWB1_GAIN_RB);
+			 ISP32_ISP_AWB1_GAIN_RB, id);
 }
 
 static void
-isp_awbgain_enable(struct rkisp_isp_params_vdev *params_vdev, bool en)
+isp_awbgain_enable(struct rkisp_isp_params_vdev *params_vdev, bool en, u32 id)
 {
 	u32 val;
 
-	val = isp3_param_read_cache(params_vdev, ISP3X_ISP_CTRL0);
+	val = isp3_param_read_cache(params_vdev, ISP3X_ISP_CTRL0, id);
 	if (en)
 		isp3_param_write(params_vdev, val | CIF_ISP_CTRL_ISP_AWB_ENA,
-				 ISP3X_ISP_CTRL0);
+				 ISP3X_ISP_CTRL0, id);
 	else
 		isp3_param_write(params_vdev, val & ~CIF_ISP_CTRL_ISP_AWB_ENA,
-				 ISP3X_ISP_CTRL0);
+				 ISP3X_ISP_CTRL0, id);
 }
 
 static void
 isp_ccm_config(struct rkisp_isp_params_vdev *params_vdev,
-	       const struct isp32_ccm_cfg *arg)
+	       const struct isp32_ccm_cfg *arg, u32 id)
 {
 	u32 value;
 	u32 i;
 
-	value = isp3_param_read(params_vdev, ISP3X_CCM_CTRL);
+	value = isp3_param_read(params_vdev, ISP3X_CCM_CTRL, id);
 	value &= ISP_CCM_EN;
 
 	value |= !!arg->asym_adj_en << 3 |
 		 !!arg->enh_adj_en << 2 |
 		 !!arg->highy_adjust_dis << 1;
-	isp3_param_write(params_vdev, value, ISP3X_CCM_CTRL);
+	isp3_param_write(params_vdev, value, ISP3X_CCM_CTRL, id);
 
 	value = ISP_PACK_2SHORT(arg->coeff0_r, arg->coeff1_r);
-	isp3_param_write(params_vdev, value, ISP3X_CCM_COEFF0_R);
+	isp3_param_write(params_vdev, value, ISP3X_CCM_COEFF0_R, id);
 
 	value = ISP_PACK_2SHORT(arg->coeff2_r, arg->offset_r);
-	isp3_param_write(params_vdev, value, ISP3X_CCM_COEFF1_R);
+	isp3_param_write(params_vdev, value, ISP3X_CCM_COEFF1_R, id);
 
 	value = ISP_PACK_2SHORT(arg->coeff0_g, arg->coeff1_g);
-	isp3_param_write(params_vdev, value, ISP3X_CCM_COEFF0_G);
+	isp3_param_write(params_vdev, value, ISP3X_CCM_COEFF0_G, id);
 
 	value = ISP_PACK_2SHORT(arg->coeff2_g, arg->offset_g);
-	isp3_param_write(params_vdev, value, ISP3X_CCM_COEFF1_G);
+	isp3_param_write(params_vdev, value, ISP3X_CCM_COEFF1_G, id);
 
 	value = ISP_PACK_2SHORT(arg->coeff0_b, arg->coeff1_b);
-	isp3_param_write(params_vdev, value, ISP3X_CCM_COEFF0_B);
+	isp3_param_write(params_vdev, value, ISP3X_CCM_COEFF0_B, id);
 
 	value = ISP_PACK_2SHORT(arg->coeff2_b, arg->offset_b);
-	isp3_param_write(params_vdev, value, ISP3X_CCM_COEFF1_B);
+	isp3_param_write(params_vdev, value, ISP3X_CCM_COEFF1_B, id);
 
 	value = ISP_PACK_2SHORT(arg->coeff0_y, arg->coeff1_y);
-	isp3_param_write(params_vdev, value, ISP3X_CCM_COEFF0_Y);
+	isp3_param_write(params_vdev, value, ISP3X_CCM_COEFF0_Y, id);
 
 	value = ISP_PACK_2SHORT(arg->coeff2_y, 0);
-	isp3_param_write(params_vdev, value, ISP3X_CCM_COEFF1_Y);
+	isp3_param_write(params_vdev, value, ISP3X_CCM_COEFF1_Y, id);
 
 	for (i = 0; i < ISP32_CCM_CURVE_NUM / 2; i++) {
 		value = ISP_PACK_2SHORT(arg->alp_y[2 * i], arg->alp_y[2 * i + 1]);
-		isp3_param_write(params_vdev, value, ISP3X_CCM_ALP_Y0 + 4 * i);
+		isp3_param_write(params_vdev, value, ISP3X_CCM_ALP_Y0 + 4 * i, id);
 	}
 
 	value = (arg->right_bit & 0xf) << 4 | (arg->bound_bit & 0xf);
-	isp3_param_write(params_vdev, value, ISP3X_CCM_BOUND_BIT);
+	isp3_param_write(params_vdev, value, ISP3X_CCM_BOUND_BIT, id);
 
 	value = (arg->color_coef1_g2y & 0x7ff) << 16 |
 		(arg->color_coef0_r2y & 0x7ff);
-	isp3_param_write(params_vdev, value, ISP32_CCM_ENHANCE0);
+	isp3_param_write(params_vdev, value, ISP32_CCM_ENHANCE0, id);
 
 	value = (arg->color_enh_rat_max & 0x3fff) << 16 |
 		(arg->color_coef2_b2y & 0x7ff);
-	isp3_param_write(params_vdev, value, ISP32_CCM_ENHANCE1);
+	isp3_param_write(params_vdev, value, ISP32_CCM_ENHANCE1, id);
 }
 
 static void
-isp_ccm_enable(struct rkisp_isp_params_vdev *params_vdev, bool en)
+isp_ccm_enable(struct rkisp_isp_params_vdev *params_vdev, bool en, u32 id)
 {
 	if (en)
-		isp3_param_set_bits(params_vdev, ISP3X_CCM_CTRL, ISP_CCM_EN);
+		isp3_param_set_bits(params_vdev, ISP3X_CCM_CTRL, ISP_CCM_EN, id);
 	else
-		isp3_param_clear_bits(params_vdev, ISP3X_CCM_CTRL, ISP_CCM_EN);
+		isp3_param_clear_bits(params_vdev, ISP3X_CCM_CTRL, ISP_CCM_EN, id);
 }
 
 static void
 isp_goc_config(struct rkisp_isp_params_vdev *params_vdev,
-	       const struct isp3x_gammaout_cfg *arg)
+	       const struct isp3x_gammaout_cfg *arg, u32 id)
 {
 	int i;
 	u32 value;
 
-	value = isp3_param_read(params_vdev, ISP3X_GAMMA_OUT_CTRL);
+	value = isp3_param_read(params_vdev, ISP3X_GAMMA_OUT_CTRL, id);
 	value &= ISP3X_GAMMA_OUT_EN;
 	value |= !!arg->equ_segm << 1 | !!arg->finalx4_dense_en << 2;
-	isp3_param_write(params_vdev, value, ISP3X_GAMMA_OUT_CTRL);
+	isp3_param_write(params_vdev, value, ISP3X_GAMMA_OUT_CTRL, id);
 
-	isp3_param_write(params_vdev, arg->offset, ISP3X_GAMMA_OUT_OFFSET);
+	isp3_param_write(params_vdev, arg->offset, ISP3X_GAMMA_OUT_OFFSET, id);
 	for (i = 0; i < ISP32_GAMMA_OUT_MAX_SAMPLES / 2; i++) {
 		value = ISP_PACK_2SHORT(arg->gamma_y[2 * i],
 					arg->gamma_y[2 * i + 1]);
-		isp3_param_write(params_vdev, value, ISP3X_GAMMA_OUT_Y0 + i * 4);
+		isp3_param_write(params_vdev, value, ISP3X_GAMMA_OUT_Y0 + i * 4, id);
 	}
-	isp3_param_write(params_vdev, arg->gamma_y[2 * i], ISP3X_GAMMA_OUT_Y0 + i * 4);
+	isp3_param_write(params_vdev, arg->gamma_y[2 * i], ISP3X_GAMMA_OUT_Y0 + i * 4, id);
 }
 
 static void
-isp_goc_enable(struct rkisp_isp_params_vdev *params_vdev, bool en)
+isp_goc_enable(struct rkisp_isp_params_vdev *params_vdev, bool en, u32 id)
 {
 	if (en)
-		isp3_param_set_bits(params_vdev, ISP3X_GAMMA_OUT_CTRL, ISP3X_GAMMA_OUT_EN);
+		isp3_param_set_bits(params_vdev, ISP3X_GAMMA_OUT_CTRL, ISP3X_GAMMA_OUT_EN, id);
 	else
-		isp3_param_clear_bits(params_vdev, ISP3X_GAMMA_OUT_CTRL, ISP3X_GAMMA_OUT_EN);
+		isp3_param_clear_bits(params_vdev, ISP3X_GAMMA_OUT_CTRL, ISP3X_GAMMA_OUT_EN, id);
 }
 
 static void
 isp_cproc_config(struct rkisp_isp_params_vdev *params_vdev,
-		 const struct isp2x_cproc_cfg *arg)
+		 const struct isp2x_cproc_cfg *arg, u32 id)
 {
 	u32 quantization = params_vdev->quantization;
 
-	isp3_param_write(params_vdev, arg->contrast, ISP3X_CPROC_CONTRAST);
-	isp3_param_write(params_vdev, arg->hue, ISP3X_CPROC_HUE);
-	isp3_param_write(params_vdev, arg->sat, ISP3X_CPROC_SATURATION);
-	isp3_param_write(params_vdev, arg->brightness, ISP3X_CPROC_BRIGHTNESS);
+	isp3_param_write(params_vdev, arg->contrast, ISP3X_CPROC_CONTRAST, id);
+	isp3_param_write(params_vdev, arg->hue, ISP3X_CPROC_HUE, id);
+	isp3_param_write(params_vdev, arg->sat, ISP3X_CPROC_SATURATION, id);
+	isp3_param_write(params_vdev, arg->brightness, ISP3X_CPROC_BRIGHTNESS, id);
 
 	if (quantization != V4L2_QUANTIZATION_FULL_RANGE) {
 		isp3_param_clear_bits(params_vdev, ISP3X_CPROC_CTRL,
 				      CIF_C_PROC_YOUT_FULL |
 				      CIF_C_PROC_YIN_FULL |
-				      CIF_C_PROC_COUT_FULL);
+				      CIF_C_PROC_COUT_FULL, id);
 	} else {
 		isp3_param_set_bits(params_vdev, ISP3X_CPROC_CTRL,
 				    CIF_C_PROC_YOUT_FULL |
 				    CIF_C_PROC_YIN_FULL |
-				    CIF_C_PROC_COUT_FULL);
+				    CIF_C_PROC_COUT_FULL, id);
 	}
 }
 
 static void
-isp_cproc_enable(struct rkisp_isp_params_vdev *params_vdev, bool en)
+isp_cproc_enable(struct rkisp_isp_params_vdev *params_vdev, bool en, u32 id)
 {
 	if (en)
 		isp3_param_set_bits(params_vdev, ISP3X_CPROC_CTRL,
-				    CIF_C_PROC_CTR_ENABLE);
+				    CIF_C_PROC_CTR_ENABLE, id);
 	else
 		isp3_param_clear_bits(params_vdev, ISP3X_CPROC_CTRL,
-				      CIF_C_PROC_CTR_ENABLE);
+				      CIF_C_PROC_CTR_ENABLE, id);
 }
 
 static void
 isp_ie_config(struct rkisp_isp_params_vdev *params_vdev,
-	      const struct isp2x_ie_cfg *arg)
+	      const struct isp2x_ie_cfg *arg, u32 id)
 {
 	u32 eff_ctrl;
 
-	eff_ctrl = isp3_param_read(params_vdev, ISP3X_IMG_EFF_CTRL);
+	eff_ctrl = isp3_param_read(params_vdev, ISP3X_IMG_EFF_CTRL, id);
 	eff_ctrl &= ~CIF_IMG_EFF_CTRL_MODE_MASK;
 
 	if (params_vdev->quantization == V4L2_QUANTIZATION_FULL_RANGE)
@@ -979,7 +1000,7 @@
 		eff_ctrl |= CIF_IMG_EFF_CTRL_MODE_SEPIA;
 		break;
 	case V4L2_COLORFX_SET_CBCR:
-		isp3_param_write(params_vdev, arg->eff_tint, ISP3X_IMG_EFF_TINT);
+		isp3_param_write(params_vdev, arg->eff_tint, ISP3X_IMG_EFF_TINT, id);
 		eff_ctrl |= CIF_IMG_EFF_CTRL_MODE_SEPIA;
 		break;
 		/*
@@ -989,25 +1010,25 @@
 	case V4L2_COLORFX_AQUA:
 		eff_ctrl |= CIF_IMG_EFF_CTRL_MODE_COLOR_SEL;
 		isp3_param_write(params_vdev, arg->color_sel,
-				 ISP3X_IMG_EFF_COLOR_SEL);
+				 ISP3X_IMG_EFF_COLOR_SEL, id);
 		break;
 	case V4L2_COLORFX_EMBOSS:
 		eff_ctrl |= CIF_IMG_EFF_CTRL_MODE_EMBOSS;
 		isp3_param_write(params_vdev, arg->eff_mat_1,
-				 CIF_IMG_EFF_MAT_1);
+				 CIF_IMG_EFF_MAT_1, id);
 		isp3_param_write(params_vdev, arg->eff_mat_2,
-				 CIF_IMG_EFF_MAT_2);
+				 CIF_IMG_EFF_MAT_2, id);
 		isp3_param_write(params_vdev, arg->eff_mat_3,
-				 CIF_IMG_EFF_MAT_3);
+				 CIF_IMG_EFF_MAT_3, id);
 		break;
 	case V4L2_COLORFX_SKETCH:
 		eff_ctrl |= CIF_IMG_EFF_CTRL_MODE_SKETCH;
 		isp3_param_write(params_vdev, arg->eff_mat_3,
-				 CIF_IMG_EFF_MAT_3);
+				 CIF_IMG_EFF_MAT_3, id);
 		isp3_param_write(params_vdev, arg->eff_mat_4,
-				 CIF_IMG_EFF_MAT_4);
+				 CIF_IMG_EFF_MAT_4, id);
 		isp3_param_write(params_vdev, arg->eff_mat_5,
-				 CIF_IMG_EFF_MAT_5);
+				 CIF_IMG_EFF_MAT_5, id);
 		break;
 	case V4L2_COLORFX_BW:
 		eff_ctrl |= CIF_IMG_EFF_CTRL_MODE_BLACKWHITE;
@@ -1019,25 +1040,26 @@
 		break;
 	}
 
-	isp3_param_write(params_vdev, eff_ctrl, ISP3X_IMG_EFF_CTRL);
+	isp3_param_write(params_vdev, eff_ctrl, ISP3X_IMG_EFF_CTRL, id);
 }
 
 static void
-isp_ie_enable(struct rkisp_isp_params_vdev *params_vdev, bool en)
+isp_ie_enable(struct rkisp_isp_params_vdev *params_vdev, bool en, u32 id)
 {
 	if (en) {
 		isp3_param_set_bits(params_vdev, ISP3X_IMG_EFF_CTRL,
 				    CIF_IMG_EFF_CTRL_CFG_UPD |
-				    CIF_IMG_EFF_CTRL_ENABLE);
+				    CIF_IMG_EFF_CTRL_ENABLE, id);
 	} else {
 		isp3_param_clear_bits(params_vdev, ISP3X_IMG_EFF_CTRL,
-				      CIF_IMG_EFF_CTRL_ENABLE);
+				      CIF_IMG_EFF_CTRL_ENABLE, id);
 	}
 }
 
 static void
 isp_rawae_config_foraf(struct rkisp_isp_params_vdev *params_vdev,
-		       const struct isp32_rawaf_meas_cfg *arg)
+		       const struct isp32_rawaf_meas_cfg *arg,
+		       struct isp2x_window *win, u32 id)
 {
 	u32 block_hsize, block_vsize;
 	u32 addr, value;
@@ -1052,33 +1074,40 @@
 	} else {
 		addr = ISP3X_RAWAE_BIG1_BASE;
 	}
-	value = isp3_param_read(params_vdev, addr + ISP3X_RAWAE_BIG_CTRL);
+	value = isp3_param_read(params_vdev, addr + ISP3X_RAWAE_BIG_CTRL, id);
 	value &= ISP3X_RAWAE_BIG_EN;
 
 	value |= ISP3X_RAWAE_BIG_WND0_NUM(wnd_num_idx);
-	isp3_param_write(params_vdev, value, addr + ISP3X_RAWAE_BIG_CTRL);
+	isp3_param_write(params_vdev, value, addr + ISP3X_RAWAE_BIG_CTRL, id);
 
 	isp3_param_write(params_vdev,
-			 ISP_PACK_2SHORT(arg->win[0].h_offs, arg->win[0].v_offs),
-			 addr + ISP3X_RAWAE_BIG_OFFSET);
+			 ISP_PACK_2SHORT(win->h_offs, win->v_offs),
+			 addr + ISP3X_RAWAE_BIG_OFFSET, id);
 
-	block_hsize = arg->win[0].h_size / ae_wnd_num[wnd_num_idx];
-	block_vsize = arg->win[0].v_size / ae_wnd_num[wnd_num_idx];
+	block_hsize = win->h_size / ae_wnd_num[wnd_num_idx];
+	block_vsize = win->v_size / ae_wnd_num[wnd_num_idx];
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(block_hsize, block_vsize),
-			 addr + ISP3X_RAWAE_BIG_BLK_SIZE);
+			 addr + ISP3X_RAWAE_BIG_BLK_SIZE, id);
 }
 
 static void
 isp_rawaf_config(struct rkisp_isp_params_vdev *params_vdev,
-		 const struct isp32_rawaf_meas_cfg *arg)
+		 const struct isp32_rawaf_meas_cfg *arg, u32 id)
 {
+	struct rkisp_device *dev = params_vdev->dev;
+	struct v4l2_rect *out_crop = &dev->isp_sdev.out_crop;
+	u32 width = out_crop->width, height = out_crop->height;
 	u32 i, var, ctrl;
 	u16 h_size, v_size;
 	u16 h_offs, v_offs;
 	u8 gaus_en, viir_en, v1_fir_sel;
 	size_t num_of_win = min_t(size_t, ARRAY_SIZE(arg->win),
 				  arg->num_afm_win);
+	struct isp2x_window win_ae;
+
+	if (dev->hw_dev->unite)
+		width = width / 2 + RKMOUDLE_UNITE_EXTEND_PIXEL;
 
 	for (i = 0; i < num_of_win; i++) {
 		h_size = arg->win[i].h_size;
@@ -1086,9 +1115,19 @@
 		h_offs = arg->win[i].h_offs < 2 ? 2 : arg->win[i].h_offs;
 		v_offs = arg->win[i].v_offs < 1 ? 1 : arg->win[i].v_offs;
 
+		if (!v_size || v_size + v_offs - 2 > height)
+			v_size = height - v_offs - 2;
+		if (!h_size || h_size + h_offs - 2 > width)
+			h_size = width - h_offs - 2;
+
 		if (i == 0) {
 			h_size = h_size / 15 * 15;
 			v_size = v_size / 15 * 15;
+
+			win_ae.h_size = h_size;
+			win_ae.v_size = v_size;
+			win_ae.h_offs = h_offs;
+			win_ae.v_offs = v_offs;
 		}
 
 		/*
@@ -1097,7 +1136,7 @@
 		 */
 		isp3_param_write(params_vdev,
 				 ISP_PACK_2SHORT(v_offs, h_offs),
-				 ISP3X_RAWAF_OFFSET_WINA + i * 8);
+				 ISP3X_RAWAF_OFFSET_WINA + i * 8, id);
 
 		/*
 		 * value must be smaller than [width of picture -2]
@@ -1105,7 +1144,7 @@
 		 */
 		isp3_param_write(params_vdev,
 				 ISP_PACK_2SHORT(v_size, h_size),
-				 ISP3X_RAWAF_SIZE_WINA + i * 8);
+				 ISP3X_RAWAF_SIZE_WINA + i * 8, id);
 	}
 
 	var = 0;
@@ -1114,60 +1153,60 @@
 			var |= ISP3X_RAWAF_INTLINE0_EN << i;
 		var |= ISP3X_RAWAF_INELINE0(arg->line_num[i]) << 4 * i;
 	}
-	isp3_param_write(params_vdev, var, ISP3X_RAWAF_INT_LINE);
+	isp3_param_write(params_vdev, var, ISP3X_RAWAF_INT_LINE, id);
 
 	if (params_vdev->dev->isp_ver == ISP_V32_L) {
 		var = (arg->hldg_dilate_num & 0x7) << 16 |
 		      !!arg->bls_en << 12 | (arg->bls_offset & 0x1FF);
-		isp3_param_write(params_vdev, var, ISP32L_RAWAF_CTRL1);
+		isp3_param_write(params_vdev, var, ISP32L_RAWAF_CTRL1, id);
 	}
 
-	var = isp3_param_read(params_vdev, ISP3X_RAWAF_THRES);
+	var = isp3_param_read(params_vdev, ISP3X_RAWAF_THRES, id);
 	var &= ~0xFFFF;
 	var |= arg->afm_thres;
-	isp3_param_write(params_vdev, var, ISP3X_RAWAF_THRES);
+	isp3_param_write(params_vdev, var, ISP3X_RAWAF_THRES, id);
 
 	var = (arg->lum_var_shift[1] & 0x7) << 20 | (arg->lum_var_shift[0] & 0x7) << 16 |
 		(arg->afm_var_shift[1] & 0x7) << 4 | (arg->afm_var_shift[0] & 0x7);
 	if (params_vdev->dev->isp_ver == ISP_V32_L)
 		var |= (arg->tnrin_shift & 0xf) << 8;
-	isp3_param_write(params_vdev, var, ISP3X_RAWAF_VAR_SHIFT);
+	isp3_param_write(params_vdev, var, ISP3X_RAWAF_VAR_SHIFT, id);
 
 	for (i = 0; i < ISP32_RAWAF_GAMMA_NUM / 2; i++) {
 		var = ISP_PACK_2SHORT(arg->gamma_y[2 * i], arg->gamma_y[2 * i + 1]);
-		isp3_param_write(params_vdev, var, ISP3X_RAWAF_GAMMA_Y0 + i * 4);
+		isp3_param_write(params_vdev, var, ISP3X_RAWAF_GAMMA_Y0 + i * 4, id);
 	}
 	var = ISP_PACK_2SHORT(arg->gamma_y[16], 0);
-	isp3_param_write(params_vdev, var, ISP3X_RAWAF_GAMMA_Y8);
+	isp3_param_write(params_vdev, var, ISP3X_RAWAF_GAMMA_Y8, id);
 
 	var = (arg->v1iir_var_shift & 0x7) << 8 | (arg->h1iir_var_shift & 0x7);
 	if (params_vdev->dev->isp_ver == ISP_V32)
 		var |= (arg->v2iir_var_shift & 0x7) << 12 | (arg->h2iir_var_shift & 0x7) << 4;
-	isp3_param_write(params_vdev, var, ISP3X_RAWAF_HVIIR_VAR_SHIFT);
+	isp3_param_write(params_vdev, var, ISP3X_RAWAF_HVIIR_VAR_SHIFT, id);
 
 	var = ISP_PACK_2SHORT(arg->h_fv_thresh, arg->v_fv_thresh);
-	isp3_param_write(params_vdev, var, ISP3X_RAWAF_HIIR_THRESH);
+	isp3_param_write(params_vdev, var, ISP3X_RAWAF_HIIR_THRESH, id);
 
 	for (i = 0; i < ISP32_RAWAF_VFIR_COE_NUM; i++) {
 		var = ISP_PACK_2SHORT(arg->v1fir_coe[i], arg->v2fir_coe[i]);
-		isp3_param_write(params_vdev, var, ISP32_RAWAF_V_FIR_COE0 + i * 4);
+		isp3_param_write(params_vdev, var, ISP32_RAWAF_V_FIR_COE0 + i * 4, id);
 	}
 
 	for (i = 0; i < ISP32_RAWAF_GAUS_COE_NUM / 4; i++) {
 		var = ISP_PACK_4BYTE(arg->gaus_coe[i * 4], arg->gaus_coe[i * 4 + 1],
 				     arg->gaus_coe[i * 4 + 2], arg->gaus_coe[i * 4 + 3]);
-		isp3_param_write(params_vdev, var, ISP32_RAWAF_GAUS_COE03 + i * 4);
+		isp3_param_write(params_vdev, var, ISP32_RAWAF_GAUS_COE03 + i * 4, id);
 	}
 	var = ISP_PACK_4BYTE(arg->gaus_coe[ISP32_RAWAF_GAUS_COE_NUM - 1], 0, 0, 0);
-	isp3_param_write(params_vdev, var, ISP32_RAWAF_GAUS_COE8);
+	isp3_param_write(params_vdev, var, ISP32_RAWAF_GAUS_COE8, id);
 
-	isp3_param_write(params_vdev, arg->highlit_thresh, ISP3X_RAWAF_HIGHLIT_THRESH);
+	isp3_param_write(params_vdev, arg->highlit_thresh, ISP3X_RAWAF_HIGHLIT_THRESH, id);
 
 	if (params_vdev->dev->isp_ver == ISP_V32_L) {
 		var = ISP_PACK_2SHORT(arg->h_fv_limit, arg->h_fv_slope);
-		isp3_param_write(params_vdev, var, ISP32L_RAWAF_CORING_H);
+		isp3_param_write(params_vdev, var, ISP32L_RAWAF_CORING_H, id);
 		var = ISP_PACK_2SHORT(arg->v_fv_limit, arg->v_fv_slope);
-		isp3_param_write(params_vdev, var, ISP32L_RAWAF_CORING_V);
+		isp3_param_write(params_vdev, var, ISP32L_RAWAF_CORING_V, id);
 	}
 
 	viir_en = arg->viir_en;
@@ -1176,23 +1215,23 @@
 	if (viir_en == 0)
 		v1_fir_sel = 0;
 
-	ctrl = isp3_param_read(params_vdev, ISP3X_RAWAF_CTRL);
+	ctrl = isp3_param_read(params_vdev, ISP3X_RAWAF_CTRL, id);
 	ctrl &= ISP3X_RAWAF_EN;
 	if (arg->hiir_en) {
 		ctrl |= ISP3X_RAWAF_HIIR_EN;
 		for (i = 0; i < ISP32_RAWAF_HIIR_COE_NUM / 2; i++) {
 			var = ISP_PACK_2SHORT(arg->h1iir1_coe[i * 2], arg->h1iir1_coe[i * 2 + 1]);
-			isp3_param_write(params_vdev, var, ISP3X_RAWAF_H1_IIR1_COE01 + i * 4);
+			isp3_param_write(params_vdev, var, ISP3X_RAWAF_H1_IIR1_COE01 + i * 4, id);
 			var = ISP_PACK_2SHORT(arg->h1iir2_coe[i * 2], arg->h1iir2_coe[i * 2 + 1]);
-			isp3_param_write(params_vdev, var, ISP3X_RAWAF_H1_IIR2_COE01 + i * 4);
+			isp3_param_write(params_vdev, var, ISP3X_RAWAF_H1_IIR2_COE01 + i * 4, id);
 
 			if (params_vdev->dev->isp_ver == ISP_V32_L)
 				continue;
 
 			var = ISP_PACK_2SHORT(arg->h2iir1_coe[i * 2], arg->h2iir1_coe[i * 2 + 1]);
-			isp3_param_write(params_vdev, var, ISP3X_RAWAF_H2_IIR1_COE01 + i * 4);
+			isp3_param_write(params_vdev, var, ISP3X_RAWAF_H2_IIR1_COE01 + i * 4, id);
 			var = ISP_PACK_2SHORT(arg->h2iir2_coe[i * 2], arg->h2iir2_coe[i * 2 + 1]);
-			isp3_param_write(params_vdev, var, ISP3X_RAWAF_H2_IIR2_COE01 + i * 4);
+			isp3_param_write(params_vdev, var, ISP3X_RAWAF_H2_IIR2_COE01 + i * 4, id);
 		}
 	}
 	if (viir_en) {
@@ -1202,7 +1241,7 @@
 				var = ISP_PACK_2SHORT(arg->v1iir_coe[i], arg->v2iir_coe[i]);
 			else
 				var = ISP_PACK_2SHORT(arg->v1iir_coe[i], 0);
-			isp3_param_write(params_vdev, var, ISP3X_RAWAF_V_IIR_COE0 + i * 4);
+			isp3_param_write(params_vdev, var, ISP3X_RAWAF_V_IIR_COE0 + i * 4, id);
 		}
 	}
 	if (arg->ldg_en) {
@@ -1212,12 +1251,12 @@
 					 arg->curve_h[i].ldg_lumth |
 					 arg->curve_h[i].ldg_gain << 8 |
 					 arg->curve_h[i].ldg_gslp << 16,
-					 ISP3X_RAWAF_H_CURVEL + i * 16);
+					 ISP3X_RAWAF_H_CURVEL + i * 16, id);
 			isp3_param_write(params_vdev,
 					 arg->curve_v[i].ldg_lumth |
 					 arg->curve_v[i].ldg_gain << 8 |
 					 arg->curve_v[i].ldg_gslp << 16,
-					 ISP3X_RAWAF_V_CURVEL + i * 16);
+					 ISP3X_RAWAF_V_CURVEL + i * 16, id);
 		}
 	}
 
@@ -1239,9 +1278,9 @@
 			!!arg->v1_acc_mode << 26 |
 			!!arg->v2_acc_mode << 27 |
 			!!arg->ae_sel << 29;
-	isp3_param_write(params_vdev, ctrl, ISP3X_RAWAF_CTRL);
+	isp3_param_write(params_vdev, ctrl, ISP3X_RAWAF_CTRL, id);
 
-	ctrl = isp3_param_read(params_vdev, ISP3X_VI_ISP_PATH);
+	ctrl = isp3_param_read(params_vdev, ISP3X_VI_ISP_PATH, id);
 	if (((ctrl & ISP3X_RAWAF_SEL(3)) != ISP3X_RAWAF_SEL(arg->rawaf_sel)) ||
 	    (((!!(ctrl & ISP32L_BNR2AF_SEL)) != arg->bnr2af_sel) &&
 	     (params_vdev->dev->isp_ver == ISP_V32_L))) {
@@ -1254,16 +1293,16 @@
 			else
 				ctrl &= ~ISP32L_BNR2AF_SEL;
 		}
-		isp3_param_write(params_vdev, ctrl, ISP3X_VI_ISP_PATH);
+		isp3_param_write(params_vdev, ctrl, ISP3X_VI_ISP_PATH, id);
 	}
 
 	params_vdev->afaemode_en = arg->ae_mode;
 	if (params_vdev->afaemode_en)
-		isp_rawae_config_foraf(params_vdev, arg);
+		isp_rawae_config_foraf(params_vdev, arg, &win_ae, id);
 }
 
 static void
-isp_rawae_enable_foraf(struct rkisp_isp_params_vdev *params_vdev, bool en)
+isp_rawae_enable_foraf(struct rkisp_isp_params_vdev *params_vdev, bool en, u32 id)
 {
 	u32 exp_ctrl;
 	u32 addr = ISP3X_RAWAE_BIG1_BASE;
@@ -1271,20 +1310,20 @@
 	if (params_vdev->dev->isp_ver == ISP_V32_L)
 		addr = ISP3X_RAWAE_LITE_BASE;
 
-	exp_ctrl = isp3_param_read(params_vdev, addr + ISP3X_RAWAE_BIG_CTRL);
+	exp_ctrl = isp3_param_read(params_vdev, addr + ISP3X_RAWAE_BIG_CTRL, id);
 	exp_ctrl &= ~ISP32_REG_WR_MASK;
 	if (en)
 		exp_ctrl |= ISP32_MODULE_EN;
 	else
 		exp_ctrl &= ~ISP32_MODULE_EN;
 
-	isp3_param_write(params_vdev, exp_ctrl, addr + ISP3X_RAWAE_BIG_CTRL);
+	isp3_param_write(params_vdev, exp_ctrl, addr + ISP3X_RAWAE_BIG_CTRL, id);
 }
 
 static void
-isp_rawaf_enable(struct rkisp_isp_params_vdev *params_vdev, bool en)
+isp_rawaf_enable(struct rkisp_isp_params_vdev *params_vdev, bool en, u32 id)
 {
-	u32 afm_ctrl = isp3_param_read(params_vdev, ISP3X_RAWAF_CTRL);
+	u32 afm_ctrl = isp3_param_read(params_vdev, ISP3X_RAWAF_CTRL, id);
 
 	afm_ctrl &= ~ISP32_REG_WR_MASK;
 	if (en)
@@ -1292,9 +1331,9 @@
 	else
 		afm_ctrl &= ~ISP3X_RAWAF_EN;
 
-	isp3_param_write(params_vdev, afm_ctrl, ISP3X_RAWAF_CTRL);
+	isp3_param_write(params_vdev, afm_ctrl, ISP3X_RAWAF_CTRL, id);
 	if (params_vdev->afaemode_en) {
-		isp_rawae_enable_foraf(params_vdev, en);
+		isp_rawae_enable_foraf(params_vdev, en, id);
 		if (!en)
 			params_vdev->afaemode_en = false;
 	}
@@ -1302,78 +1341,78 @@
 
 static void
 isp_rawaelite_config(struct rkisp_isp_params_vdev *params_vdev,
-		     const struct isp2x_rawaelite_meas_cfg *arg)
+		     const struct isp2x_rawaelite_meas_cfg *arg, u32 id)
 {
 	struct rkisp_device *ispdev = params_vdev->dev;
 	struct v4l2_rect *out_crop = &ispdev->isp_sdev.out_crop;
-	u32 width = out_crop->width;
+	u32 width = out_crop->width, height = out_crop->height;
+	u32 h_size, v_size, h_offs, v_offs;
 	u32 block_hsize, block_vsize, value;
 	u32 wnd_num_idx = 0;
 	const u32 ae_wnd_num[] = {1, 5};
 
-	value = isp3_param_read(params_vdev, ISP3X_RAWAE_LITE_CTRL);
+	value = isp3_param_read(params_vdev, ISP3X_RAWAE_LITE_CTRL, id);
 	value &= ~(ISP3X_RAWAE_LITE_WNDNUM);
 	if (arg->wnd_num) {
 		value |= ISP3X_RAWAE_LITE_WNDNUM;
 		wnd_num_idx = 1;
 	}
 	value &= ~ISP32_REG_WR_MASK;
-	isp3_param_write(params_vdev, value, ISP3X_RAWAE_LITE_CTRL);
+	isp3_param_write(params_vdev, value, ISP3X_RAWAE_LITE_CTRL, id);
 
+	h_offs = arg->win.h_offs & ~0x1;
+	v_offs = arg->win.v_offs & ~0x1;
 	isp3_param_write(params_vdev,
-			 ISP_PACK_2SHORT(arg->win.h_offs, arg->win.v_offs),
-			 ISP3X_RAWAE_LITE_OFFSET);
+			 ISP_PACK_2SHORT(h_offs, v_offs),
+			 ISP3X_RAWAE_LITE_OFFSET, id);
 
-	block_hsize = arg->win.h_size / ae_wnd_num[wnd_num_idx];
-	value = block_hsize * ae_wnd_num[wnd_num_idx] + arg->win.h_offs;
-	if (ispdev->hw_dev->is_unite)
+	if (ispdev->hw_dev->unite)
 		width = width / 2 + RKMOUDLE_UNITE_EXTEND_PIXEL;
-	if (value + 1 > width)
-		block_hsize -= 1;
-	block_vsize = arg->win.v_size / ae_wnd_num[wnd_num_idx];
-	value = block_vsize * ae_wnd_num[wnd_num_idx] + arg->win.v_offs;
-	if (value + 2 > out_crop->height)
-		block_vsize -= 1;
-	if (block_vsize % 2)
-		block_vsize -= 1;
+	h_size = arg->win.h_size;
+	v_size = arg->win.v_size;
+	if (!h_size || h_size + h_offs + 1 > width)
+		h_size = width - h_offs - 1;
+	if (!v_size || v_size + v_offs + 2 > height)
+		v_size = height - v_offs - 2;
+	block_hsize = (h_size / ae_wnd_num[wnd_num_idx]) & ~0x1;
+	block_vsize = (v_size / ae_wnd_num[wnd_num_idx]) & ~0x1;
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(block_hsize, block_vsize),
-			 ISP3X_RAWAE_LITE_BLK_SIZ);
+			 ISP3X_RAWAE_LITE_BLK_SIZ, id);
 
-	value = isp3_param_read(params_vdev, ISP3X_VI_ISP_PATH);
+	value = isp3_param_read(params_vdev, ISP3X_VI_ISP_PATH, id);
 	if ((value & ISP3X_RAWAE012_SEL(3)) != ISP3X_RAWAE012_SEL(arg->rawae_sel)) {
 		value &= ~(ISP3X_RAWAE012_SEL(3));
 		value |= ISP3X_RAWAE012_SEL(arg->rawae_sel);
-		isp3_param_write(params_vdev, value, ISP3X_VI_ISP_PATH);
+		isp3_param_write(params_vdev, value, ISP3X_VI_ISP_PATH, id);
 	}
 }
 
 static void
-isp_rawaelite_enable(struct rkisp_isp_params_vdev *params_vdev, bool en)
+isp_rawaelite_enable(struct rkisp_isp_params_vdev *params_vdev, bool en, u32 id)
 {
 	u32 exp_ctrl;
 
-	exp_ctrl = isp3_param_read(params_vdev, ISP3X_RAWAE_LITE_CTRL);
+	exp_ctrl = isp3_param_read(params_vdev, ISP3X_RAWAE_LITE_CTRL, id);
 	exp_ctrl &= ~ISP32_REG_WR_MASK;
 	if (en)
 		exp_ctrl |= ISP3X_RAWAE_LITE_EN;
 	else
 		exp_ctrl &= ~ISP3X_RAWAE_LITE_EN;
 
-	isp3_param_write(params_vdev, exp_ctrl, ISP3X_RAWAE_LITE_CTRL);
+	isp3_param_write(params_vdev, exp_ctrl, ISP3X_RAWAE_LITE_CTRL, id);
 }
 
 static void
 isp_rawaebig_config(struct rkisp_isp_params_vdev *params_vdev,
 		    const struct isp2x_rawaebig_meas_cfg *arg,
-		    u32 blk_no)
+		    u32 blk_no, u32 id)
 {
 	struct rkisp_device *ispdev = params_vdev->dev;
 	struct v4l2_rect *out_crop = &ispdev->isp_sdev.out_crop;
-	u32 width = out_crop->width;
-	u32 block_hsize, block_vsize;
-	u32 addr, i, value, h_size, v_size;
-	u32 wnd_num_idx = 0;
+	u32 width = out_crop->width, height = out_crop->height;
+	u32 addr, i, value, h_size, v_size, h_offs, v_offs;
+	u32 block_hsize, block_vsize, wnd_num_idx = 0;
 	const u32 ae_wnd_num[] = {
 		1, 5, 15, 15
 	};
@@ -1392,7 +1431,7 @@
 	}
 
 	/* avoid to override the old enable value */
-	value = isp3_param_read(params_vdev, addr + ISP3X_RAWAE_BIG_CTRL);
+	value = isp3_param_read(params_vdev, addr + ISP3X_RAWAE_BIG_CTRL, id);
 	value &= ISP3X_RAWAE_BIG_EN;
 
 	wnd_num_idx = arg->wnd_num;
@@ -1408,59 +1447,65 @@
 		if (arg->subwin_en[3])
 			value |= ISP3X_RAWAE_BIG_WND4_EN;
 	}
-	isp3_param_write(params_vdev, value, addr + ISP3X_RAWAE_BIG_CTRL);
+	isp3_param_write(params_vdev, value, addr + ISP3X_RAWAE_BIG_CTRL, id);
 
+	h_offs = arg->win.h_offs & ~0x1;
+	v_offs = arg->win.v_offs & ~0x1;
 	isp3_param_write(params_vdev,
-			 ISP_PACK_2SHORT(arg->win.h_offs, arg->win.v_offs),
-			 addr + ISP3X_RAWAE_BIG_OFFSET);
+			 ISP_PACK_2SHORT(h_offs, v_offs),
+			 addr + ISP3X_RAWAE_BIG_OFFSET, id);
 
-	block_hsize = arg->win.h_size / ae_wnd_num[wnd_num_idx];
-	value = block_hsize * ae_wnd_num[wnd_num_idx] + arg->win.h_offs;
-	if (ispdev->hw_dev->is_unite)
+	if (ispdev->hw_dev->unite)
 		width = width / 2 + RKMOUDLE_UNITE_EXTEND_PIXEL;
-	if (value + 1 > width)
-		block_hsize -= 1;
-	block_vsize = arg->win.v_size / ae_wnd_num[wnd_num_idx];
-	value = block_vsize * ae_wnd_num[wnd_num_idx] + arg->win.v_offs;
-	if (value + 2 > out_crop->height)
-		block_vsize -= 1;
-	if (block_vsize % 2)
-		block_vsize -= 1;
+	h_size = arg->win.h_size;
+	v_size = arg->win.v_size;
+	if (!h_size || h_size + h_offs + 1 > width)
+		h_size = width - h_offs - 1;
+	if (!v_size || v_size + v_offs + 2 > height)
+		v_size = height - v_offs - 2;
+	block_hsize = (h_size / ae_wnd_num[wnd_num_idx]) & ~0x1;
+	block_vsize = (v_size / ae_wnd_num[wnd_num_idx]) & ~0x1;
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(block_hsize, block_vsize),
-			 addr + ISP3X_RAWAE_BIG_BLK_SIZE);
+			 addr + ISP3X_RAWAE_BIG_BLK_SIZE, id);
 
 	for (i = 0; i < ISP32_RAWAEBIG_SUBWIN_NUM; i++) {
-		isp3_param_write(params_vdev,
-			ISP_PACK_2SHORT(arg->subwin[i].h_offs, arg->subwin[i].v_offs),
-			addr + ISP3X_RAWAE_BIG_WND1_OFFSET + 8 * i);
+		h_offs = arg->subwin[i].h_offs & ~0x1;
+		v_offs = arg->subwin[i].v_offs & ~0x1;
+		isp3_param_write(params_vdev, ISP_PACK_2SHORT(h_offs, v_offs),
+				 addr + ISP3X_RAWAE_BIG_WND1_OFFSET + 8 * i, id);
 
-		v_size = arg->subwin[i].v_size + arg->subwin[i].v_offs;
-		h_size = arg->subwin[i].h_size + arg->subwin[i].h_offs;
-		isp3_param_write(params_vdev,
-			ISP_PACK_2SHORT(h_size, v_size),
-			addr + ISP3X_RAWAE_BIG_WND1_SIZE + 8 * i);
+		v_size = arg->subwin[i].v_size;
+		h_size = arg->subwin[i].h_size;
+		if (!h_size || h_size + h_offs > width)
+			h_size = width - h_offs;
+		if (!v_size || v_size + v_offs > height)
+			v_size = height - v_offs;
+		h_size = (h_size + h_offs) & ~0x1;
+		v_size = (v_size + v_offs) & ~0x1;
+		isp3_param_write(params_vdev, ISP_PACK_2SHORT(h_size, v_size),
+				 addr + ISP3X_RAWAE_BIG_WND1_SIZE + 8 * i, id);
 	}
 
-	value = isp3_param_read(params_vdev, ISP3X_VI_ISP_PATH);
+	value = isp3_param_read(params_vdev, ISP3X_VI_ISP_PATH, id);
 	if (blk_no == 0) {
 		if ((value & ISP3X_RAWAE3_SEL(3)) != ISP3X_RAWAE3_SEL(arg->rawae_sel)) {
 			value &= ~(ISP3X_RAWAE3_SEL(3));
 			value |= ISP3X_RAWAE3_SEL(arg->rawae_sel);
-			isp3_param_write(params_vdev, value, ISP3X_VI_ISP_PATH);
+			isp3_param_write(params_vdev, value, ISP3X_VI_ISP_PATH, id);
 		}
 	} else {
 		if ((value & ISP3X_RAWAE012_SEL(3)) != ISP3X_RAWAE012_SEL(arg->rawae_sel)) {
 			value &= ~(ISP3X_RAWAE012_SEL(3));
 			value |= ISP3X_RAWAE012_SEL(arg->rawae_sel);
-			isp3_param_write(params_vdev, value, ISP3X_VI_ISP_PATH);
+			isp3_param_write(params_vdev, value, ISP3X_VI_ISP_PATH, id);
 		}
 	}
 }
 
 static void
 isp_rawaebig_enable(struct rkisp_isp_params_vdev *params_vdev,
-		    bool en, u32 blk_no)
+		    bool en, u32 blk_no, u32 id)
 {
 	u32 exp_ctrl;
 	u32 addr;
@@ -1478,117 +1523,120 @@
 		break;
 	}
 
-	exp_ctrl = isp3_param_read(params_vdev, addr + ISP3X_RAWAE_BIG_CTRL);
+	exp_ctrl = isp3_param_read(params_vdev, addr + ISP3X_RAWAE_BIG_CTRL, id);
 	exp_ctrl &= ~ISP32_REG_WR_MASK;
 	if (en)
 		exp_ctrl |= ISP32_MODULE_EN;
 	else
 		exp_ctrl &= ~ISP32_MODULE_EN;
 
-	isp3_param_write(params_vdev, exp_ctrl, addr + ISP3X_RAWAE_BIG_CTRL);
+	isp3_param_write(params_vdev, exp_ctrl, addr + ISP3X_RAWAE_BIG_CTRL, id);
 }
 
 static void
 isp_rawae1_config(struct rkisp_isp_params_vdev *params_vdev,
-		  const struct isp2x_rawaebig_meas_cfg *arg)
+		  const struct isp2x_rawaebig_meas_cfg *arg, u32 id)
 {
-	isp_rawaebig_config(params_vdev, arg, 1);
+	isp_rawaebig_config(params_vdev, arg, 1, id);
 }
 
 static void
-isp_rawae1_enable(struct rkisp_isp_params_vdev *params_vdev, bool en)
+isp_rawae1_enable(struct rkisp_isp_params_vdev *params_vdev, bool en, u32 id)
 {
-	isp_rawaebig_enable(params_vdev, en, 1);
+	isp_rawaebig_enable(params_vdev, en, 1, id);
 }
 
 static void
 isp_rawae2_config(struct rkisp_isp_params_vdev *params_vdev,
-		  const struct isp2x_rawaebig_meas_cfg *arg)
+		  const struct isp2x_rawaebig_meas_cfg *arg, u32 id)
 {
-	isp_rawaebig_config(params_vdev, arg, 2);
+	isp_rawaebig_config(params_vdev, arg, 2, id);
 }
 
 static void
-isp_rawae2_enable(struct rkisp_isp_params_vdev *params_vdev, bool en)
+isp_rawae2_enable(struct rkisp_isp_params_vdev *params_vdev, bool en, u32 id)
 {
-	isp_rawaebig_enable(params_vdev, en, 2);
+	isp_rawaebig_enable(params_vdev, en, 2, id);
 }
 
 static void
 isp_rawae3_config(struct rkisp_isp_params_vdev *params_vdev,
-		  const struct isp2x_rawaebig_meas_cfg *arg)
+		  const struct isp2x_rawaebig_meas_cfg *arg, u32 id)
 {
-	isp_rawaebig_config(params_vdev, arg, 0);
+	isp_rawaebig_config(params_vdev, arg, 0, id);
 }
 
 static void
-isp_rawae3_enable(struct rkisp_isp_params_vdev *params_vdev, bool en)
+isp_rawae3_enable(struct rkisp_isp_params_vdev *params_vdev, bool en, u32 id)
 {
-	isp_rawaebig_enable(params_vdev, en, 0);
+	isp_rawaebig_enable(params_vdev, en, 0, id);
 }
 
 static void
 isp_rawawb_cfg_sram(struct rkisp_isp_params_vdev *params_vdev,
-		    const struct isp32_rawawb_meas_cfg *arg, bool is_check)
+		    const struct isp32_rawawb_meas_cfg *arg, bool is_check, u32 id)
 {
 	u32 i, val = ISP32_MODULE_EN;
 
 	if (params_vdev->dev->isp_ver == ISP_V32 && is_check &&
-	    !(isp3_param_read(params_vdev, ISP3X_RAWAWB_CTRL) & val))
+	    !(isp3_param_read(params_vdev, ISP3X_RAWAWB_CTRL, id) & val))
 		return;
 
 	for (i = 0; i < ISP32_RAWAWB_WEIGHT_NUM / 5; i++) {
-		isp3_param_write(params_vdev,
-				 (arg->wp_blk_wei_w[5 * i] & 0x3f) |
-				 (arg->wp_blk_wei_w[5 * i + 1] & 0x3f) << 6 |
-				 (arg->wp_blk_wei_w[5 * i + 2] & 0x3f) << 12 |
-				 (arg->wp_blk_wei_w[5 * i + 3] & 0x3f) << 18 |
-				 (arg->wp_blk_wei_w[5 * i + 4] & 0x3f) << 24,
-				 ISP3X_RAWAWB_WRAM_DATA_BASE);
+		isp3_param_write_direct(params_vdev,
+					(arg->wp_blk_wei_w[5 * i] & 0x3f) |
+					(arg->wp_blk_wei_w[5 * i + 1] & 0x3f) << 6 |
+					(arg->wp_blk_wei_w[5 * i + 2] & 0x3f) << 12 |
+					(arg->wp_blk_wei_w[5 * i + 3] & 0x3f) << 18 |
+					(arg->wp_blk_wei_w[5 * i + 4] & 0x3f) << 24,
+					ISP3X_RAWAWB_WRAM_DATA_BASE);
 	}
 }
 
 static void
 isp_rawawb_config(struct rkisp_isp_params_vdev *params_vdev,
-		  const struct isp32_rawawb_meas_cfg *arg)
+		  const struct isp32_rawawb_meas_cfg *arg, u32 id)
 {
-	struct isp32_isp_params_cfg *params_rec = params_vdev->isp32_params;
+	struct rkisp_device *dev = params_vdev->dev;
+	struct v4l2_rect *out_crop = &dev->isp_sdev.out_crop;
+	struct isp32_isp_params_cfg *params_rec = params_vdev->isp32_params + id;
 	struct isp32_rawawb_meas_cfg *arg_rec = &params_rec->meas.rawawb;
 	const struct isp2x_bls_fixed_val *pval = &arg->bls2_val;
-	u32 value, val, mask, i;
+	u32 width = out_crop->width, height = out_crop->height;
+	u32 value, val, mask, i, h_size, v_size, h_offs, v_offs;
 
-	value = isp3_param_read(params_vdev, ISP3X_BLS_CTRL);
+	value = isp3_param_read(params_vdev, ISP3X_BLS_CTRL, id);
 	value &= ~ISP32_BLS_BLS2_EN;
 	if (arg->bls2_en) {
 		switch (params_vdev->raw_type) {
 		case RAW_BGGR:
-			isp3_param_write(params_vdev, pval->r, ISP32_BLS2_D_FIXED);
-			isp3_param_write(params_vdev, pval->gr, ISP32_BLS2_C_FIXED);
-			isp3_param_write(params_vdev, pval->gb, ISP32_BLS2_B_FIXED);
-			isp3_param_write(params_vdev, pval->b, ISP32_BLS2_A_FIXED);
+			isp3_param_write(params_vdev, pval->r, ISP32_BLS2_D_FIXED, id);
+			isp3_param_write(params_vdev, pval->gr, ISP32_BLS2_C_FIXED, id);
+			isp3_param_write(params_vdev, pval->gb, ISP32_BLS2_B_FIXED, id);
+			isp3_param_write(params_vdev, pval->b, ISP32_BLS2_A_FIXED, id);
 			break;
 		case RAW_GBRG:
-			isp3_param_write(params_vdev, pval->r, ISP32_BLS2_C_FIXED);
-			isp3_param_write(params_vdev, pval->gr, ISP32_BLS2_D_FIXED);
-			isp3_param_write(params_vdev, pval->gb, ISP32_BLS2_A_FIXED);
-			isp3_param_write(params_vdev, pval->b, ISP32_BLS2_B_FIXED);
+			isp3_param_write(params_vdev, pval->r, ISP32_BLS2_C_FIXED, id);
+			isp3_param_write(params_vdev, pval->gr, ISP32_BLS2_D_FIXED, id);
+			isp3_param_write(params_vdev, pval->gb, ISP32_BLS2_A_FIXED, id);
+			isp3_param_write(params_vdev, pval->b, ISP32_BLS2_B_FIXED, id);
 			break;
 		case RAW_GRBG:
-			isp3_param_write(params_vdev, pval->r, ISP32_BLS2_B_FIXED);
-			isp3_param_write(params_vdev, pval->gr, ISP32_BLS2_A_FIXED);
-			isp3_param_write(params_vdev, pval->gb, ISP32_BLS2_D_FIXED);
-			isp3_param_write(params_vdev, pval->b, ISP32_BLS2_C_FIXED);
+			isp3_param_write(params_vdev, pval->r, ISP32_BLS2_B_FIXED, id);
+			isp3_param_write(params_vdev, pval->gr, ISP32_BLS2_A_FIXED, id);
+			isp3_param_write(params_vdev, pval->gb, ISP32_BLS2_D_FIXED, id);
+			isp3_param_write(params_vdev, pval->b, ISP32_BLS2_C_FIXED, id);
 			break;
 		case RAW_RGGB:
 		default:
-			isp3_param_write(params_vdev, pval->r, ISP32_BLS2_A_FIXED);
-			isp3_param_write(params_vdev, pval->gr, ISP32_BLS2_B_FIXED);
-			isp3_param_write(params_vdev, pval->gb, ISP32_BLS2_C_FIXED);
-			isp3_param_write(params_vdev, pval->b, ISP32_BLS2_D_FIXED);
+			isp3_param_write(params_vdev, pval->r, ISP32_BLS2_A_FIXED, id);
+			isp3_param_write(params_vdev, pval->gr, ISP32_BLS2_B_FIXED, id);
+			isp3_param_write(params_vdev, pval->gb, ISP32_BLS2_C_FIXED, id);
+			isp3_param_write(params_vdev, pval->b, ISP32_BLS2_D_FIXED, id);
 		}
 		value |= ISP32_BLS_BLS2_EN;
 	}
-	isp3_param_write(params_vdev, value, ISP3X_BLS_CTRL);
+	isp3_param_write(params_vdev, value, ISP3X_BLS_CTRL, id);
 
 	value = arg->in_overexposure_threshold << 16 |
 		!!arg->blk_with_luma_wei_en << 8 |
@@ -1599,463 +1647,473 @@
 		!!arg->blk_measure_enable;
 	if (params_vdev->dev->isp_ver == ISP_V32_L)
 		value |= !!arg->ds16x8_mode_en << 7;
-	isp3_param_write(params_vdev, value, ISP3X_RAWAWB_BLK_CTRL);
+	isp3_param_write(params_vdev, value, ISP3X_RAWAWB_BLK_CTRL, id);
 
-	isp3_param_write(params_vdev,
-			 ISP_PACK_2SHORT(arg->h_offs, arg->v_offs),
-			 ISP3X_RAWAWB_WIN_OFFS);
 
+	h_offs = arg->h_offs & ~0x1;
+	v_offs = arg->v_offs & ~0x1;
 	isp3_param_write(params_vdev,
-			 ISP_PACK_2SHORT(arg->h_size, arg->v_size),
-			 ISP3X_RAWAWB_WIN_SIZE);
+			 ISP_PACK_2SHORT(h_offs, v_offs),
+			 ISP3X_RAWAWB_WIN_OFFS, id);
+
+	if (dev->hw_dev->unite)
+		width = width / 2 + RKMOUDLE_UNITE_EXTEND_PIXEL;
+	h_size = arg->h_size;
+	v_size = arg->v_size;
+	if (!h_size || h_size + h_offs > width)
+		h_size = width - h_offs;
+	if (!v_size || v_size + v_offs > height)
+		v_size = height - v_offs;
+	isp3_param_write(params_vdev,
+			 ISP_PACK_2SHORT(h_size, v_size),
+			 ISP3X_RAWAWB_WIN_SIZE, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->r_max, arg->g_max),
-			 ISP3X_RAWAWB_LIMIT_RG_MAX);
+			 ISP3X_RAWAWB_LIMIT_RG_MAX, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->b_max, arg->y_max),
-			 ISP3X_RAWAWB_LIMIT_BY_MAX);
+			 ISP3X_RAWAWB_LIMIT_BY_MAX, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->r_min, arg->g_min),
-			 ISP3X_RAWAWB_LIMIT_RG_MIN);
+			 ISP3X_RAWAWB_LIMIT_RG_MIN, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->b_min, arg->y_min),
-			 ISP3X_RAWAWB_LIMIT_BY_MIN);
+			 ISP3X_RAWAWB_LIMIT_BY_MIN, id);
 
 	value = !!arg->wp_hist_xytype << 4 |
 		!!arg->wp_blk_wei_en1 << 3 |
 		!!arg->wp_blk_wei_en0 << 2 |
 		!!arg->wp_luma_wei_en1 << 1 |
 		!!arg->wp_luma_wei_en0;
-	isp3_param_write(params_vdev, value, ISP3X_RAWAWB_WEIGHT_CURVE_CTRL);
+	isp3_param_write(params_vdev, value, ISP3X_RAWAWB_WEIGHT_CURVE_CTRL, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_4BYTE(arg->wp_luma_weicurve_y0,
 					arg->wp_luma_weicurve_y1,
 					arg->wp_luma_weicurve_y2,
 					arg->wp_luma_weicurve_y3),
-			 ISP3X_RAWAWB_YWEIGHT_CURVE_XCOOR03);
+			 ISP3X_RAWAWB_YWEIGHT_CURVE_XCOOR03, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_4BYTE(arg->wp_luma_weicurve_y4,
 					arg->wp_luma_weicurve_y5,
 					arg->wp_luma_weicurve_y6,
 					arg->wp_luma_weicurve_y7),
-			 ISP3X_RAWAWB_YWEIGHT_CURVE_XCOOR47);
+			 ISP3X_RAWAWB_YWEIGHT_CURVE_XCOOR47, id);
 
 	isp3_param_write(params_vdev,
 			 arg->wp_luma_weicurve_y8,
-			 ISP3X_RAWAWB_YWEIGHT_CURVE_XCOOR8);
+			 ISP3X_RAWAWB_YWEIGHT_CURVE_XCOOR8, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_4BYTE(arg->wp_luma_weicurve_w0,
 					arg->wp_luma_weicurve_w1,
 					arg->wp_luma_weicurve_w2,
 					arg->wp_luma_weicurve_w3),
-			 ISP3X_RAWAWB_YWEIGHT_CURVE_YCOOR03);
+			 ISP3X_RAWAWB_YWEIGHT_CURVE_YCOOR03, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_4BYTE(arg->wp_luma_weicurve_w4,
 					arg->wp_luma_weicurve_w5,
 					arg->wp_luma_weicurve_w6,
 					arg->wp_luma_weicurve_w7),
-			 ISP3X_RAWAWB_YWEIGHT_CURVE_YCOOR47);
+			 ISP3X_RAWAWB_YWEIGHT_CURVE_YCOOR47, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->wp_luma_weicurve_w8,
 					 arg->pre_wbgain_inv_r),
-			 ISP3X_RAWAWB_YWEIGHT_CURVE_YCOOR8);
+			 ISP3X_RAWAWB_YWEIGHT_CURVE_YCOOR8, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->pre_wbgain_inv_g,
 					 arg->pre_wbgain_inv_b),
-			 ISP3X_RAWAWB_PRE_WBGAIN_INV);
+			 ISP3X_RAWAWB_PRE_WBGAIN_INV, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->vertex0_u_0, arg->vertex0_v_0),
-			 ISP3X_RAWAWB_UV_DETC_VERTEX0_0);
+			 ISP3X_RAWAWB_UV_DETC_VERTEX0_0, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->vertex1_u_0, arg->vertex1_v_0),
-			 ISP3X_RAWAWB_UV_DETC_VERTEX1_0);
+			 ISP3X_RAWAWB_UV_DETC_VERTEX1_0, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->vertex2_u_0, arg->vertex2_v_0),
-			 ISP3X_RAWAWB_UV_DETC_VERTEX2_0);
+			 ISP3X_RAWAWB_UV_DETC_VERTEX2_0, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->vertex3_u_0, arg->vertex3_v_0),
-			 ISP3X_RAWAWB_UV_DETC_VERTEX3_0);
+			 ISP3X_RAWAWB_UV_DETC_VERTEX3_0, id);
 
 	isp3_param_write(params_vdev, arg->islope01_0,
-			 ISP3X_RAWAWB_UV_DETC_ISLOPE01_0);
+			 ISP3X_RAWAWB_UV_DETC_ISLOPE01_0, id);
 
 	isp3_param_write(params_vdev, arg->islope12_0,
-			 ISP3X_RAWAWB_UV_DETC_ISLOPE12_0);
+			 ISP3X_RAWAWB_UV_DETC_ISLOPE12_0, id);
 
 	isp3_param_write(params_vdev, arg->islope23_0,
-			 ISP3X_RAWAWB_UV_DETC_ISLOPE23_0);
+			 ISP3X_RAWAWB_UV_DETC_ISLOPE23_0, id);
 
 	isp3_param_write(params_vdev, arg->islope30_0,
-			 ISP3X_RAWAWB_UV_DETC_ISLOPE30_0);
+			 ISP3X_RAWAWB_UV_DETC_ISLOPE30_0, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->vertex0_u_1,
 					 arg->vertex0_v_1),
-			 ISP3X_RAWAWB_UV_DETC_VERTEX0_1);
+			 ISP3X_RAWAWB_UV_DETC_VERTEX0_1, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->vertex1_u_1,
 					 arg->vertex1_v_1),
-			 ISP3X_RAWAWB_UV_DETC_VERTEX1_1);
+			 ISP3X_RAWAWB_UV_DETC_VERTEX1_1, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->vertex2_u_1,
 					 arg->vertex2_v_1),
-			 ISP3X_RAWAWB_UV_DETC_VERTEX2_1);
+			 ISP3X_RAWAWB_UV_DETC_VERTEX2_1, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->vertex3_u_1,
 					 arg->vertex3_v_1),
-			 ISP3X_RAWAWB_UV_DETC_VERTEX3_1);
+			 ISP3X_RAWAWB_UV_DETC_VERTEX3_1, id);
 
 	isp3_param_write(params_vdev, arg->islope01_1,
-			 ISP3X_RAWAWB_UV_DETC_ISLOPE01_1);
+			 ISP3X_RAWAWB_UV_DETC_ISLOPE01_1, id);
 
 	isp3_param_write(params_vdev, arg->islope12_1,
-			 ISP3X_RAWAWB_UV_DETC_ISLOPE12_1);
+			 ISP3X_RAWAWB_UV_DETC_ISLOPE12_1, id);
 
 	isp3_param_write(params_vdev, arg->islope23_1,
-			 ISP3X_RAWAWB_UV_DETC_ISLOPE23_1);
+			 ISP3X_RAWAWB_UV_DETC_ISLOPE23_1, id);
 
 	isp3_param_write(params_vdev, arg->islope30_1,
-			 ISP3X_RAWAWB_UV_DETC_ISLOPE30_1);
+			 ISP3X_RAWAWB_UV_DETC_ISLOPE30_1, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->vertex0_u_2,
 					 arg->vertex0_v_2),
-			 ISP3X_RAWAWB_UV_DETC_VERTEX0_2);
+			 ISP3X_RAWAWB_UV_DETC_VERTEX0_2, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->vertex1_u_2,
 					 arg->vertex1_v_2),
-			 ISP3X_RAWAWB_UV_DETC_VERTEX1_2);
+			 ISP3X_RAWAWB_UV_DETC_VERTEX1_2, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->vertex2_u_2,
 					 arg->vertex2_v_2),
-			 ISP3X_RAWAWB_UV_DETC_VERTEX2_2);
+			 ISP3X_RAWAWB_UV_DETC_VERTEX2_2, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->vertex3_u_2,
 					 arg->vertex3_v_2),
-			 ISP3X_RAWAWB_UV_DETC_VERTEX3_2);
+			 ISP3X_RAWAWB_UV_DETC_VERTEX3_2, id);
 
 	isp3_param_write(params_vdev, arg->islope01_2,
-			 ISP3X_RAWAWB_UV_DETC_ISLOPE01_2);
+			 ISP3X_RAWAWB_UV_DETC_ISLOPE01_2, id);
 
 	isp3_param_write(params_vdev, arg->islope12_2,
-			 ISP3X_RAWAWB_UV_DETC_ISLOPE12_2);
+			 ISP3X_RAWAWB_UV_DETC_ISLOPE12_2, id);
 
 	isp3_param_write(params_vdev, arg->islope23_2,
-			 ISP3X_RAWAWB_UV_DETC_ISLOPE23_2);
+			 ISP3X_RAWAWB_UV_DETC_ISLOPE23_2, id);
 
 	isp3_param_write(params_vdev, arg->islope30_2,
-			 ISP3X_RAWAWB_UV_DETC_ISLOPE30_2);
+			 ISP3X_RAWAWB_UV_DETC_ISLOPE30_2, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->vertex0_u_3,
 					 arg->vertex0_v_3),
-			 ISP3X_RAWAWB_UV_DETC_VERTEX0_3);
+			 ISP3X_RAWAWB_UV_DETC_VERTEX0_3, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->vertex1_u_3,
 					 arg->vertex1_v_3),
-			 ISP3X_RAWAWB_UV_DETC_VERTEX1_3);
+			 ISP3X_RAWAWB_UV_DETC_VERTEX1_3, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->vertex2_u_3,
 					 arg->vertex2_v_3),
-			 ISP3X_RAWAWB_UV_DETC_VERTEX2_3);
+			 ISP3X_RAWAWB_UV_DETC_VERTEX2_3, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->vertex3_u_3,
 					 arg->vertex3_v_3),
-			 ISP3X_RAWAWB_UV_DETC_VERTEX3_3);
+			 ISP3X_RAWAWB_UV_DETC_VERTEX3_3, id);
 
 	isp3_param_write(params_vdev, arg->islope01_3,
-			 ISP3X_RAWAWB_UV_DETC_ISLOPE01_3);
+			 ISP3X_RAWAWB_UV_DETC_ISLOPE01_3, id);
 
 	isp3_param_write(params_vdev, arg->islope12_3,
-			 ISP3X_RAWAWB_UV_DETC_ISLOPE12_3);
+			 ISP3X_RAWAWB_UV_DETC_ISLOPE12_3, id);
 
 	isp3_param_write(params_vdev, arg->islope23_3,
-			 ISP3X_RAWAWB_UV_DETC_ISLOPE23_3);
+			 ISP3X_RAWAWB_UV_DETC_ISLOPE23_3, id);
 
 	isp3_param_write(params_vdev, arg->islope30_3,
-			 ISP3X_RAWAWB_UV_DETC_ISLOPE30_3);
+			 ISP3X_RAWAWB_UV_DETC_ISLOPE30_3, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->rgb2ryuvmat0_y,
 					 arg->rgb2ryuvmat1_y),
-			 ISP3X_RAWAWB_YUV_RGB2ROTY_0);
+			 ISP3X_RAWAWB_YUV_RGB2ROTY_0, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->rgb2ryuvmat2_y,
 					 arg->rgb2ryuvofs_y),
-			 ISP3X_RAWAWB_YUV_RGB2ROTY_1);
+			 ISP3X_RAWAWB_YUV_RGB2ROTY_1, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->rgb2ryuvmat0_u,
 					 arg->rgb2ryuvmat1_u),
-			 ISP3X_RAWAWB_YUV_RGB2ROTU_0);
+			 ISP3X_RAWAWB_YUV_RGB2ROTU_0, id);
 
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->rgb2ryuvmat2_u,
 					 arg->rgb2ryuvofs_u),
-			 ISP3X_RAWAWB_YUV_RGB2ROTU_1);
+			 ISP3X_RAWAWB_YUV_RGB2ROTU_1, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->rgb2ryuvmat0_v,
 					 arg->rgb2ryuvmat1_v),
-			 ISP3X_RAWAWB_YUV_RGB2ROTV_0);
+			 ISP3X_RAWAWB_YUV_RGB2ROTV_0, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->rgb2ryuvmat2_v,
 					 arg->rgb2ryuvofs_v),
-			 ISP3X_RAWAWB_YUV_RGB2ROTV_1);
+			 ISP3X_RAWAWB_YUV_RGB2ROTV_1, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->coor_x1_ls0_y,
 					 arg->vec_x21_ls0_y),
-			 ISP3X_RAWAWB_YUV_X_COOR_Y_0);
+			 ISP3X_RAWAWB_YUV_X_COOR_Y_0, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->coor_x1_ls0_u,
 					 arg->vec_x21_ls0_u),
-			 ISP3X_RAWAWB_YUV_X_COOR_U_0);
+			 ISP3X_RAWAWB_YUV_X_COOR_U_0, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->coor_x1_ls0_v,
 					 arg->vec_x21_ls0_v),
-			 ISP3X_RAWAWB_YUV_X_COOR_V_0);
+			 ISP3X_RAWAWB_YUV_X_COOR_V_0, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_4BYTE(arg->dis_x1x2_ls0, 0,
 					arg->rotu0_ls0, arg->rotu1_ls0),
-			 ISP3X_RAWAWB_YUV_X1X2_DIS_0);
+			 ISP3X_RAWAWB_YUV_X1X2_DIS_0, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_4BYTE(arg->rotu2_ls0, arg->rotu3_ls0,
 					arg->rotu4_ls0, arg->rotu5_ls0),
-			 ISP3X_RAWAWB_YUV_INTERP_CURVE_UCOOR_0);
+			 ISP3X_RAWAWB_YUV_INTERP_CURVE_UCOOR_0, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->th0_ls0, arg->th1_ls0),
-			 ISP3X_RAWAWB_YUV_INTERP_CURVE_TH0_0);
+			 ISP3X_RAWAWB_YUV_INTERP_CURVE_TH0_0, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->th2_ls0, arg->th3_ls0),
-			 ISP3X_RAWAWB_YUV_INTERP_CURVE_TH1_0);
+			 ISP3X_RAWAWB_YUV_INTERP_CURVE_TH1_0, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->th4_ls0, arg->th5_ls0),
-			 ISP3X_RAWAWB_YUV_INTERP_CURVE_TH2_0);
+			 ISP3X_RAWAWB_YUV_INTERP_CURVE_TH2_0, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->coor_x1_ls1_y,
 					 arg->vec_x21_ls1_y),
-			 ISP3X_RAWAWB_YUV_X_COOR_Y_1);
+			 ISP3X_RAWAWB_YUV_X_COOR_Y_1, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->coor_x1_ls1_u,
 					 arg->vec_x21_ls1_u),
-			 ISP3X_RAWAWB_YUV_X_COOR_U_1);
+			 ISP3X_RAWAWB_YUV_X_COOR_U_1, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->coor_x1_ls1_v,
 					 arg->vec_x21_ls1_v),
-			 ISP3X_RAWAWB_YUV_X_COOR_V_1);
+			 ISP3X_RAWAWB_YUV_X_COOR_V_1, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_4BYTE(arg->dis_x1x2_ls1, 0,
 					arg->rotu0_ls1, arg->rotu1_ls1),
-			 ISP3X_RAWAWB_YUV_X1X2_DIS_1);
+			 ISP3X_RAWAWB_YUV_X1X2_DIS_1, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_4BYTE(arg->rotu2_ls1, arg->rotu3_ls1,
 					arg->rotu4_ls1, arg->rotu5_ls1),
-			 ISP3X_RAWAWB_YUV_INTERP_CURVE_UCOOR_1);
+			 ISP3X_RAWAWB_YUV_INTERP_CURVE_UCOOR_1, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->th0_ls1, arg->th1_ls1),
-			 ISP3X_RAWAWB_YUV_INTERP_CURVE_TH0_1);
+			 ISP3X_RAWAWB_YUV_INTERP_CURVE_TH0_1, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->th2_ls1, arg->th3_ls1),
-			 ISP3X_RAWAWB_YUV_INTERP_CURVE_TH1_1);
+			 ISP3X_RAWAWB_YUV_INTERP_CURVE_TH1_1, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->th4_ls1, arg->th5_ls1),
-			 ISP3X_RAWAWB_YUV_INTERP_CURVE_TH2_1);
+			 ISP3X_RAWAWB_YUV_INTERP_CURVE_TH2_1, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->coor_x1_ls2_y, arg->vec_x21_ls2_y),
-			 ISP3X_RAWAWB_YUV_X_COOR_Y_2);
+			 ISP3X_RAWAWB_YUV_X_COOR_Y_2, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->coor_x1_ls2_u, arg->vec_x21_ls2_u),
-			 ISP3X_RAWAWB_YUV_X_COOR_U_2);
+			 ISP3X_RAWAWB_YUV_X_COOR_U_2, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->coor_x1_ls2_v, arg->vec_x21_ls2_v),
-			 ISP3X_RAWAWB_YUV_X_COOR_V_2);
+			 ISP3X_RAWAWB_YUV_X_COOR_V_2, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_4BYTE(arg->dis_x1x2_ls2, 0,
 					arg->rotu0_ls2, arg->rotu1_ls2),
-			 ISP3X_RAWAWB_YUV_X1X2_DIS_2);
+			 ISP3X_RAWAWB_YUV_X1X2_DIS_2, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_4BYTE(arg->rotu2_ls2, arg->rotu3_ls2,
 					arg->rotu4_ls2, arg->rotu5_ls2),
-			 ISP3X_RAWAWB_YUV_INTERP_CURVE_UCOOR_2);
+			 ISP3X_RAWAWB_YUV_INTERP_CURVE_UCOOR_2, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->th0_ls2, arg->th1_ls2),
-			 ISP3X_RAWAWB_YUV_INTERP_CURVE_TH0_2);
+			 ISP3X_RAWAWB_YUV_INTERP_CURVE_TH0_2, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->th2_ls2, arg->th3_ls2),
-			 ISP3X_RAWAWB_YUV_INTERP_CURVE_TH1_2);
+			 ISP3X_RAWAWB_YUV_INTERP_CURVE_TH1_2, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->th4_ls2, arg->th5_ls2),
-			 ISP3X_RAWAWB_YUV_INTERP_CURVE_TH2_2);
+			 ISP3X_RAWAWB_YUV_INTERP_CURVE_TH2_2, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->coor_x1_ls3_y,
 					 arg->vec_x21_ls3_y),
-			 ISP3X_RAWAWB_YUV_X_COOR_Y_3);
+			 ISP3X_RAWAWB_YUV_X_COOR_Y_3, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->coor_x1_ls3_u,
 					 arg->vec_x21_ls3_u),
-			 ISP3X_RAWAWB_YUV_X_COOR_U_3);
+			 ISP3X_RAWAWB_YUV_X_COOR_U_3, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->coor_x1_ls3_v,
 					 arg->vec_x21_ls3_v),
-			 ISP3X_RAWAWB_YUV_X_COOR_V_3);
+			 ISP3X_RAWAWB_YUV_X_COOR_V_3, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_4BYTE(arg->dis_x1x2_ls3, 0,
 					arg->rotu0_ls3, arg->rotu1_ls3),
-			 ISP3X_RAWAWB_YUV_X1X2_DIS_3);
+			 ISP3X_RAWAWB_YUV_X1X2_DIS_3, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_4BYTE(arg->rotu2_ls3, arg->rotu3_ls3,
 					arg->rotu4_ls3, arg->rotu5_ls3),
-			 ISP3X_RAWAWB_YUV_INTERP_CURVE_UCOOR_3);
+			 ISP3X_RAWAWB_YUV_INTERP_CURVE_UCOOR_3, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->th0_ls3, arg->th1_ls3),
-			 ISP3X_RAWAWB_YUV_INTERP_CURVE_TH0_3);
+			 ISP3X_RAWAWB_YUV_INTERP_CURVE_TH0_3, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->th2_ls3, arg->th3_ls3),
-			 ISP3X_RAWAWB_YUV_INTERP_CURVE_TH1_3);
+			 ISP3X_RAWAWB_YUV_INTERP_CURVE_TH1_3, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->th4_ls3, arg->th5_ls3),
-			 ISP3X_RAWAWB_YUV_INTERP_CURVE_TH2_3);
+			 ISP3X_RAWAWB_YUV_INTERP_CURVE_TH2_3, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->wt0, arg->wt1),
-			 ISP3X_RAWAWB_RGB2XY_WT01);
+			 ISP3X_RAWAWB_RGB2XY_WT01, id);
 
-	isp3_param_write(params_vdev, arg->wt2,
-			 ISP3X_RAWAWB_RGB2XY_WT2);
+	isp3_param_write(params_vdev, arg->wt2, ISP3X_RAWAWB_RGB2XY_WT2, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->mat0_x, arg->mat0_y),
-			 ISP3X_RAWAWB_RGB2XY_MAT0_XY);
+			 ISP3X_RAWAWB_RGB2XY_MAT0_XY, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->mat1_x, arg->mat1_y),
-			 ISP3X_RAWAWB_RGB2XY_MAT1_XY);
+			 ISP3X_RAWAWB_RGB2XY_MAT1_XY, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->mat2_x, arg->mat2_y),
-			 ISP3X_RAWAWB_RGB2XY_MAT2_XY);
+			 ISP3X_RAWAWB_RGB2XY_MAT2_XY, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->nor_x0_0, arg->nor_x1_0),
-			 ISP3X_RAWAWB_XY_DETC_NOR_X_0);
+			 ISP3X_RAWAWB_XY_DETC_NOR_X_0, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->nor_y0_0, arg->nor_y1_0),
-			 ISP3X_RAWAWB_XY_DETC_NOR_Y_0);
+			 ISP3X_RAWAWB_XY_DETC_NOR_Y_0, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->big_x0_0, arg->big_x1_0),
-			 ISP3X_RAWAWB_XY_DETC_BIG_X_0);
+			 ISP3X_RAWAWB_XY_DETC_BIG_X_0, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->big_y0_0, arg->big_y1_0),
-			 ISP3X_RAWAWB_XY_DETC_BIG_Y_0);
+			 ISP3X_RAWAWB_XY_DETC_BIG_Y_0, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->nor_x0_1, arg->nor_x1_1),
-			 ISP3X_RAWAWB_XY_DETC_NOR_X_1);
+			 ISP3X_RAWAWB_XY_DETC_NOR_X_1, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->nor_y0_1, arg->nor_y1_1),
-			 ISP3X_RAWAWB_XY_DETC_NOR_Y_1);
+			 ISP3X_RAWAWB_XY_DETC_NOR_Y_1, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->big_x0_1, arg->big_x1_1),
-			 ISP3X_RAWAWB_XY_DETC_BIG_X_1);
+			 ISP3X_RAWAWB_XY_DETC_BIG_X_1, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->big_y0_1, arg->big_y1_1),
-			 ISP3X_RAWAWB_XY_DETC_BIG_Y_1);
+			 ISP3X_RAWAWB_XY_DETC_BIG_Y_1, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->nor_x0_2, arg->nor_x1_2),
-			 ISP3X_RAWAWB_XY_DETC_NOR_X_2);
+			 ISP3X_RAWAWB_XY_DETC_NOR_X_2, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->nor_y0_2, arg->nor_y1_2),
-			 ISP3X_RAWAWB_XY_DETC_NOR_Y_2);
+			 ISP3X_RAWAWB_XY_DETC_NOR_Y_2, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->big_x0_2, arg->big_x1_2),
-			 ISP3X_RAWAWB_XY_DETC_BIG_X_2);
+			 ISP3X_RAWAWB_XY_DETC_BIG_X_2, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->big_y0_2, arg->big_y1_2),
-			 ISP3X_RAWAWB_XY_DETC_BIG_Y_2);
+			 ISP3X_RAWAWB_XY_DETC_BIG_Y_2, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->nor_x0_3, arg->nor_x1_3),
-			 ISP3X_RAWAWB_XY_DETC_NOR_X_3);
+			 ISP3X_RAWAWB_XY_DETC_NOR_X_3, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->nor_y0_3, arg->nor_y1_3),
-			 ISP3X_RAWAWB_XY_DETC_NOR_Y_3);
+			 ISP3X_RAWAWB_XY_DETC_NOR_Y_3, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->big_x0_3, arg->big_x1_3),
-			 ISP3X_RAWAWB_XY_DETC_BIG_X_3);
+			 ISP3X_RAWAWB_XY_DETC_BIG_X_3, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->big_y0_3, arg->big_y1_3),
-			 ISP3X_RAWAWB_XY_DETC_BIG_Y_3);
+			 ISP3X_RAWAWB_XY_DETC_BIG_Y_3, id);
 
 	value = (arg->exc_wp_region0_excen & 0x3) |
 		!!arg->exc_wp_region0_measen << 2 |
@@ -2076,138 +2134,138 @@
 		(arg->exc_wp_region6_excen & 0x3) << 24 |
 		!!arg->exc_wp_region6_domain << 27 |
 		!!arg->multiwindow_en << 31;
-	isp3_param_write(params_vdev, value, ISP3X_RAWAWB_MULTIWINDOW_EXC_CTRL);
+	isp3_param_write(params_vdev, value, ISP3X_RAWAWB_MULTIWINDOW_EXC_CTRL, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->multiwindow0_h_offs,
 					 arg->multiwindow0_v_offs),
-			 ISP3X_RAWAWB_MULTIWINDOW0_OFFS);
+			 ISP3X_RAWAWB_MULTIWINDOW0_OFFS, id);
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->multiwindow0_h_size,
 					 arg->multiwindow0_v_size),
-			 ISP3X_RAWAWB_MULTIWINDOW0_SIZE);
+			 ISP3X_RAWAWB_MULTIWINDOW0_SIZE, id);
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->multiwindow1_h_offs,
 					 arg->multiwindow1_v_offs),
-			 ISP3X_RAWAWB_MULTIWINDOW1_OFFS);
+			 ISP3X_RAWAWB_MULTIWINDOW1_OFFS, id);
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->multiwindow1_h_size,
 					 arg->multiwindow1_v_size),
-			 ISP3X_RAWAWB_MULTIWINDOW1_SIZE);
+			 ISP3X_RAWAWB_MULTIWINDOW1_SIZE, id);
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->multiwindow2_h_offs,
 					 arg->multiwindow2_v_offs),
-			 ISP3X_RAWAWB_MULTIWINDOW2_OFFS);
+			 ISP3X_RAWAWB_MULTIWINDOW2_OFFS, id);
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->multiwindow2_h_size,
 					 arg->multiwindow2_v_size),
-			 ISP3X_RAWAWB_MULTIWINDOW2_SIZE);
+			 ISP3X_RAWAWB_MULTIWINDOW2_SIZE, id);
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->multiwindow3_h_offs,
 					 arg->multiwindow3_v_offs),
-			 ISP3X_RAWAWB_MULTIWINDOW3_OFFS);
+			 ISP3X_RAWAWB_MULTIWINDOW3_OFFS, id);
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->multiwindow3_h_size,
 					 arg->multiwindow3_v_size),
-			 ISP3X_RAWAWB_MULTIWINDOW3_SIZE);
+			 ISP3X_RAWAWB_MULTIWINDOW3_SIZE, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->exc_wp_region0_xu0,
 					 arg->exc_wp_region0_xu1),
-			 ISP3X_RAWAWB_EXC_WP_REGION0_XU);
+			 ISP3X_RAWAWB_EXC_WP_REGION0_XU, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->exc_wp_region0_yv0,
 					 arg->exc_wp_region0_yv1),
-			 ISP3X_RAWAWB_EXC_WP_REGION0_YV);
+			 ISP3X_RAWAWB_EXC_WP_REGION0_YV, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->exc_wp_region1_xu0,
 					 arg->exc_wp_region1_xu1),
-			 ISP3X_RAWAWB_EXC_WP_REGION1_XU);
+			 ISP3X_RAWAWB_EXC_WP_REGION1_XU, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->exc_wp_region1_yv0,
 					 arg->exc_wp_region1_yv1),
-			 ISP3X_RAWAWB_EXC_WP_REGION1_YV);
+			 ISP3X_RAWAWB_EXC_WP_REGION1_YV, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->exc_wp_region2_xu0,
 					 arg->exc_wp_region2_xu1),
-			 ISP3X_RAWAWB_EXC_WP_REGION2_XU);
+			 ISP3X_RAWAWB_EXC_WP_REGION2_XU, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->exc_wp_region2_yv0,
 					 arg->exc_wp_region2_yv1),
-			 ISP3X_RAWAWB_EXC_WP_REGION2_YV);
+			 ISP3X_RAWAWB_EXC_WP_REGION2_YV, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->exc_wp_region3_xu0,
 					 arg->exc_wp_region3_xu1),
-			 ISP3X_RAWAWB_EXC_WP_REGION3_XU);
+			 ISP3X_RAWAWB_EXC_WP_REGION3_XU, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->exc_wp_region3_yv0,
 					 arg->exc_wp_region3_yv1),
-			 ISP3X_RAWAWB_EXC_WP_REGION3_YV);
+			 ISP3X_RAWAWB_EXC_WP_REGION3_YV, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->exc_wp_region4_xu0,
 					 arg->exc_wp_region4_xu1),
-			 ISP3X_RAWAWB_EXC_WP_REGION4_XU);
+			 ISP3X_RAWAWB_EXC_WP_REGION4_XU, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->exc_wp_region4_yv0,
 					 arg->exc_wp_region4_yv1),
-			 ISP3X_RAWAWB_EXC_WP_REGION4_YV);
+			 ISP3X_RAWAWB_EXC_WP_REGION4_YV, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->exc_wp_region5_xu0,
 					 arg->exc_wp_region5_xu1),
-			 ISP3X_RAWAWB_EXC_WP_REGION5_XU);
+			 ISP3X_RAWAWB_EXC_WP_REGION5_XU, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->exc_wp_region5_yv0,
 					 arg->exc_wp_region5_yv1),
-			 ISP3X_RAWAWB_EXC_WP_REGION5_YV);
+			 ISP3X_RAWAWB_EXC_WP_REGION5_YV, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->exc_wp_region6_xu0,
 					 arg->exc_wp_region6_xu1),
-			 ISP3X_RAWAWB_EXC_WP_REGION6_XU);
+			 ISP3X_RAWAWB_EXC_WP_REGION6_XU, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(arg->exc_wp_region6_yv0,
 					 arg->exc_wp_region6_yv1),
-			 ISP3X_RAWAWB_EXC_WP_REGION6_YV);
+			 ISP3X_RAWAWB_EXC_WP_REGION6_YV, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_4BYTE(arg->exc_wp_region0_weight,
 					arg->exc_wp_region1_weight,
 					arg->exc_wp_region2_weight,
 					arg->exc_wp_region3_weight),
-			 ISP32_RAWAWB_EXC_WP_WEIGHT0_3);
+			 ISP32_RAWAWB_EXC_WP_WEIGHT0_3, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_4BYTE(arg->exc_wp_region4_weight,
 					arg->exc_wp_region5_weight,
 					arg->exc_wp_region6_weight, 0),
-			 ISP32_RAWAWB_EXC_WP_WEIGHT4_6);
+			 ISP32_RAWAWB_EXC_WP_WEIGHT4_6, id);
 
 	if (params_vdev->dev->isp_ver == ISP_V32) {
 		if (params_vdev->dev->hw_dev->is_single)
-			isp_rawawb_cfg_sram(params_vdev, arg, false);
+			isp_rawawb_cfg_sram(params_vdev, arg, false, id);
 		else
 			memcpy(arg_rec->wp_blk_wei_w, arg->wp_blk_wei_w,
 			       ISP32_RAWAWB_WEIGHT_NUM);
 	} else {
 		for (i = 0; i < ISP32L_RAWAWB_WEIGHT_NUM; i++)
 			isp3_param_write(params_vdev, arg->win_weight[i],
-					 ISP32L_RAWAWB_WIN_WEIGHT_0 + i * 4);
+					 ISP32L_RAWAWB_WIN_WEIGHT_0 + i * 4, id);
 	}
 
 	/* avoid to override the old enable value */
-	value = isp3_param_read_cache(params_vdev, ISP3X_RAWAWB_CTRL);
+	value = isp3_param_read_cache(params_vdev, ISP3X_RAWAWB_CTRL, id);
 	value &= (ISP32_MODULE_EN |
 		  ISP32_RAWAWB_2DDR_PATH_EN |
 		  ISP32_RAWAWB_2DDR_PATH_DS);
@@ -2227,45 +2285,46 @@
 		 !!arg->yuv3d_en0 << 3 |
 		 !!arg->xy_en0 << 2 |
 		 !!arg->uv_en0 << 1;
-	isp3_param_write(params_vdev, value, ISP3X_RAWAWB_CTRL);
+	isp3_param_write(params_vdev, value, ISP3X_RAWAWB_CTRL, id);
 
 	mask = ISP32_DRC2AWB_SEL | ISP32_BNR2AWB_SEL | ISP3X_RAWAWB_SEL(3);
 	val = ISP3X_RAWAWB_SEL(arg->rawawb_sel) |
 	      (arg->bnr2awb_sel & 0x1) << 26 | (arg->drc2awb_sel & 0x1) << 27;
-	value = isp3_param_read(params_vdev, ISP3X_VI_ISP_PATH);
+	value = isp3_param_read(params_vdev, ISP3X_VI_ISP_PATH, id);
 	if ((value & mask) != val) {
 		value &= ~mask;
 		value |= val;
-		isp3_param_write(params_vdev, value, ISP3X_VI_ISP_PATH);
+		isp3_param_write(params_vdev, value, ISP3X_VI_ISP_PATH, id);
 	}
 }
 
 static void
-isp_rawawb_enable(struct rkisp_isp_params_vdev *params_vdev, bool en)
+isp_rawawb_enable(struct rkisp_isp_params_vdev *params_vdev, bool en, u32 id)
 {
 	u32 awb_ctrl;
 
-	awb_ctrl = isp3_param_read_cache(params_vdev, ISP3X_RAWAWB_CTRL);
+	awb_ctrl = isp3_param_read_cache(params_vdev, ISP3X_RAWAWB_CTRL, id);
 	awb_ctrl &= ~ISP32_REG_WR_MASK;
 	if (en)
 		awb_ctrl |= ISP32_MODULE_EN;
 	else
 		awb_ctrl &= ~ISP32_MODULE_EN;
 
-	isp3_param_write(params_vdev, awb_ctrl, ISP3X_RAWAWB_CTRL);
+	isp3_param_write(params_vdev, awb_ctrl, ISP3X_RAWAWB_CTRL, id);
 }
 
 static void
 isp_rawhstlite_config(struct rkisp_isp_params_vdev *params_vdev,
-		      const struct isp2x_rawhistlite_cfg *arg)
+		      const struct isp2x_rawhistlite_cfg *arg, u32 id)
 {
-	u32 i;
-	u32 value;
-	u32 hist_ctrl;
-	u32 block_hsize, block_vsize;
+	struct rkisp_device *ispdev = params_vdev->dev;
+	struct v4l2_rect *out_crop = &ispdev->isp_sdev.out_crop;
+	u32 width = out_crop->width, height = out_crop->height;
+	u32 i, value, hist_ctrl, block_hsize, block_vsize;
+	u32 h_size, v_size, h_offs, v_offs;
 
 	/* avoid to override the old enable value */
-	hist_ctrl = isp3_param_read(params_vdev, ISP3X_RAWHIST_LITE_CTRL);
+	hist_ctrl = isp3_param_read(params_vdev, ISP3X_RAWHIST_LITE_CTRL, id);
 	hist_ctrl &= ISP3X_RAWHIST_EN;
 	hist_ctrl = hist_ctrl |
 		    ISP3X_RAWHIST_MODE(arg->mode) |
@@ -2273,23 +2332,31 @@
 	if (params_vdev->dev->isp_ver == ISP_V32)
 		hist_ctrl |= ISP3X_RAWHIST_DATASEL(arg->data_sel) |
 			     ISP3X_RAWHIST_WATERLINE(arg->waterline);
-	isp3_param_write(params_vdev, hist_ctrl, ISP3X_RAWHIST_LITE_CTRL);
+	isp3_param_write(params_vdev, hist_ctrl, ISP3X_RAWHIST_LITE_CTRL, id);
 
+	h_offs = arg->win.h_offs & ~0x1;
+	v_offs = arg->win.v_offs & ~0x1;
 	isp3_param_write(params_vdev,
-			 ISP_PACK_2SHORT(arg->win.h_offs, arg->win.v_offs),
-			 ISP3X_RAWHIST_LITE_OFFS);
+			 ISP_PACK_2SHORT(h_offs, v_offs),
+			 ISP3X_RAWHIST_LITE_OFFS, id);
 
-	block_hsize = arg->win.h_size / ISP32_RAWHISTLITE_ROW_NUM - 1;
-	block_vsize = arg->win.v_size / ISP32_RAWHISTLITE_COLUMN_NUM - 1;
-	block_hsize &= 0xFFFE;
-	block_vsize &= 0xFFFE;
+	if (ispdev->hw_dev->unite)
+		width = width / 2 + RKMOUDLE_UNITE_EXTEND_PIXEL;
+	h_size = arg->win.h_size;
+	v_size = arg->win.v_size;
+	if (!h_size || h_size + h_offs + 1 > width)
+		h_size = width - h_offs - 1;
+	if (!v_size || v_size + v_offs + 1 > height)
+		v_size = height - v_offs - 1;
+	block_hsize = (h_size / ISP32_RAWHISTLITE_ROW_NUM) & ~0x1;
+	block_vsize = (v_size / ISP32_RAWHISTLITE_COLUMN_NUM) & ~0x1;
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(block_hsize, block_vsize),
-			 ISP3X_RAWHIST_LITE_SIZE);
+			 ISP3X_RAWHIST_LITE_SIZE, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_4BYTE(arg->rcc, arg->gcc, arg->bcc, arg->off),
-			 ISP3X_RAWHIST_LITE_RAW2Y_CC);
+			 ISP3X_RAWHIST_LITE_RAW2Y_CC, id);
 
 	for (i = 0; i < (ISP32_RAWHISTLITE_WEIGHT_REG_SIZE / 4); i++) {
 		value = ISP_PACK_4BYTE(arg->weight[4 * i + 0],
@@ -2297,32 +2364,32 @@
 				       arg->weight[4 * i + 2],
 				       arg->weight[4 * i + 3]);
 		isp3_param_write(params_vdev, value,
-				 ISP3X_RAWHIST_LITE_WEIGHT + 4 * i);
+				 ISP3X_RAWHIST_LITE_WEIGHT + 4 * i, id);
 	}
 
 	value = ISP_PACK_4BYTE(arg->weight[4 * i + 0], 0, 0, 0);
 	isp3_param_write(params_vdev, value,
-			 ISP3X_RAWHIST_LITE_WEIGHT + 4 * i);
+			 ISP3X_RAWHIST_LITE_WEIGHT + 4 * i, id);
 }
 
 static void
-isp_rawhstlite_enable(struct rkisp_isp_params_vdev *params_vdev, bool en)
+isp_rawhstlite_enable(struct rkisp_isp_params_vdev *params_vdev, bool en, u32 id)
 {
 	u32 hist_ctrl;
 
-	hist_ctrl = isp3_param_read(params_vdev, ISP3X_RAWHIST_LITE_CTRL);
+	hist_ctrl = isp3_param_read(params_vdev, ISP3X_RAWHIST_LITE_CTRL, id);
 	hist_ctrl &= ~(ISP32_MODULE_EN | ISP32_REG_WR_MASK);
 
 	if (en)
 		hist_ctrl |= ISP32_MODULE_EN;
 
-	isp3_param_write(params_vdev, hist_ctrl, ISP3X_RAWHIST_LITE_CTRL);
+	isp3_param_write(params_vdev, hist_ctrl, ISP3X_RAWHIST_LITE_CTRL, id);
 }
 
 static void
 isp_rawhstbig_cfg_sram(struct rkisp_isp_params_vdev *params_vdev,
 		       const struct isp2x_rawhistbig_cfg *arg,
-		       u32 blk_no, bool is_check)
+		       u32 blk_no, bool is_check, u32 id)
 {
 	u32 i, j, wnd_num_idx, value;
 	u8 weight15x15[ISP32_RAWHISTBIG_WEIGHT_REG_SIZE];
@@ -2344,7 +2411,7 @@
 
 	value = ISP3X_RAWHIST_EN;
 	if (is_check &&
-	    !(isp3_param_read(params_vdev, addr + ISP3X_RAWHIST_BIG_CTRL) & value))
+	    !(isp3_param_read(params_vdev, addr + ISP3X_RAWHIST_BIG_CTRL, id) & value))
 		return;
 
 	wnd_num_idx = arg->wnd_num;
@@ -2369,13 +2436,16 @@
 
 static void
 isp_rawhstbig_config(struct rkisp_isp_params_vdev *params_vdev,
-		     const struct isp2x_rawhistbig_cfg *arg, u32 blk_no)
+		     const struct isp2x_rawhistbig_cfg *arg, u32 blk_no, u32 id)
 {
-	struct isp32_isp_params_cfg *params_rec = params_vdev->isp32_params;
+	struct isp32_isp_params_cfg *params_rec = params_vdev->isp32_params + id;
 	struct rkisp_device *dev = params_vdev->dev;
+	struct v4l2_rect *out_crop = &dev->isp_sdev.out_crop;
+	u32 width = out_crop->width, height = out_crop->height;
 	struct isp2x_rawhistbig_cfg *arg_rec;
 	u32 hist_ctrl, block_hsize, block_vsize, wnd_num_idx;
 	const u32 hist_wnd_num[] = {5, 5, 15, 15};
+	u32 h_size, v_size, h_offs, v_offs;
 	u32 addr;
 
 	switch (blk_no) {
@@ -2396,7 +2466,7 @@
 
 	wnd_num_idx = arg->wnd_num;
 	/* avoid to override the old enable value */
-	hist_ctrl = isp3_param_read(params_vdev, addr + ISP3X_RAWHIST_BIG_CTRL);
+	hist_ctrl = isp3_param_read(params_vdev, addr + ISP3X_RAWHIST_BIG_CTRL, id);
 	hist_ctrl &= ISP3X_RAWHIST_EN;
 	hist_ctrl = hist_ctrl |
 		    ISP3X_RAWHIST_MODE(arg->mode) |
@@ -2405,33 +2475,41 @@
 	if (params_vdev->dev->isp_ver == ISP_V32)
 		hist_ctrl |= ISP3X_RAWHIST_DATASEL(arg->data_sel) |
 			     ISP3X_RAWHIST_WATERLINE(arg->waterline);
-	isp3_param_write(params_vdev, hist_ctrl, addr + ISP3X_RAWHIST_BIG_CTRL);
+	isp3_param_write(params_vdev, hist_ctrl, addr + ISP3X_RAWHIST_BIG_CTRL, id);
 
+	h_offs = arg->win.h_offs & ~0x1;
+	v_offs = arg->win.v_offs & ~0x1;
 	isp3_param_write(params_vdev,
-			 ISP_PACK_2SHORT(arg->win.h_offs, arg->win.v_offs),
-			 addr + ISP3X_RAWHIST_BIG_OFFS);
+			 ISP_PACK_2SHORT(h_offs, v_offs),
+			 addr + ISP3X_RAWHIST_BIG_OFFS, id);
 
-	block_hsize = arg->win.h_size / hist_wnd_num[wnd_num_idx] - 1;
-	block_vsize = arg->win.v_size / hist_wnd_num[wnd_num_idx] - 1;
-	block_hsize &= 0xFFFE;
-	block_vsize &= 0xFFFE;
+	if (dev->hw_dev->unite)
+		width = width / 2 + RKMOUDLE_UNITE_EXTEND_PIXEL;
+	h_size = arg->win.h_size;
+	v_size = arg->win.v_size;
+	if (!h_size || h_size + h_offs + 1 > width)
+		h_size = width - h_offs - 1;
+	if (!v_size || v_size + v_offs + 1 > height)
+		v_size = height - v_offs - 1;
+	block_hsize = (h_size / hist_wnd_num[wnd_num_idx]) & ~0x1;
+	block_vsize = (v_size / hist_wnd_num[wnd_num_idx]) & ~0x1;
 	isp3_param_write(params_vdev,
 			 ISP_PACK_2SHORT(block_hsize, block_vsize),
-			 addr + ISP3X_RAWHIST_BIG_SIZE);
+			 addr + ISP3X_RAWHIST_BIG_SIZE, id);
 
 	isp3_param_write(params_vdev,
 			 ISP_PACK_4BYTE(arg->rcc, arg->gcc, arg->bcc, arg->off),
-			 addr + ISP3X_RAWHIST_BIG_RAW2Y_CC);
+			 addr + ISP3X_RAWHIST_BIG_RAW2Y_CC, id);
 
 	if (dev->hw_dev->is_single)
-		isp_rawhstbig_cfg_sram(params_vdev, arg, blk_no, false);
+		isp_rawhstbig_cfg_sram(params_vdev, arg, blk_no, false, id);
 	else
 		*arg_rec = *arg;
 }
 
 static void
 isp_rawhstbig_enable(struct rkisp_isp_params_vdev *params_vdev,
-		     bool en, u32 blk_no)
+		     bool en, u32 blk_no, u32 id)
 {
 	u32 hist_ctrl;
 	u32 addr;
@@ -2449,72 +2527,72 @@
 		break;
 	}
 
-	hist_ctrl = isp3_param_read(params_vdev, addr + ISP3X_RAWHIST_BIG_CTRL);
+	hist_ctrl = isp3_param_read(params_vdev, addr + ISP3X_RAWHIST_BIG_CTRL, id);
 	hist_ctrl &= ~(ISP3X_RAWHIST_EN | ISP32_REG_WR_MASK);
 	if (en)
 		hist_ctrl |= ISP3X_RAWHIST_EN;
 
-	isp3_param_write(params_vdev, hist_ctrl, addr + ISP3X_RAWHIST_BIG_CTRL);
+	isp3_param_write(params_vdev, hist_ctrl, addr + ISP3X_RAWHIST_BIG_CTRL, id);
 }
 
 static void
 isp_rawhst1_config(struct rkisp_isp_params_vdev *params_vdev,
-		   const struct isp2x_rawhistbig_cfg *arg)
+		   const struct isp2x_rawhistbig_cfg *arg, u32 id)
 {
-	isp_rawhstbig_config(params_vdev, arg, 1);
+	isp_rawhstbig_config(params_vdev, arg, 1, id);
 }
 
 static void
-isp_rawhst1_enable(struct rkisp_isp_params_vdev *params_vdev, bool en)
+isp_rawhst1_enable(struct rkisp_isp_params_vdev *params_vdev, bool en, u32 id)
 {
-	isp_rawhstbig_enable(params_vdev, en, 1);
+	isp_rawhstbig_enable(params_vdev, en, 1, id);
 }
 
 static void
 isp_rawhst2_config(struct rkisp_isp_params_vdev *params_vdev,
-		   const struct isp2x_rawhistbig_cfg *arg)
+		   const struct isp2x_rawhistbig_cfg *arg, u32 id)
 {
-	isp_rawhstbig_config(params_vdev, arg, 2);
+	isp_rawhstbig_config(params_vdev, arg, 2, id);
 }
 
 static void
-isp_rawhst2_enable(struct rkisp_isp_params_vdev *params_vdev, bool en)
+isp_rawhst2_enable(struct rkisp_isp_params_vdev *params_vdev, bool en, u32 id)
 {
-	isp_rawhstbig_enable(params_vdev, en, 2);
+	isp_rawhstbig_enable(params_vdev, en, 2, id);
 }
 
 static void
 isp_rawhst3_config(struct rkisp_isp_params_vdev *params_vdev,
-		   const struct isp2x_rawhistbig_cfg *arg)
+		   const struct isp2x_rawhistbig_cfg *arg, u32 id)
 {
-	isp_rawhstbig_config(params_vdev, arg, 0);
+	isp_rawhstbig_config(params_vdev, arg, 0, id);
 }
 
 static void
-isp_rawhst3_enable(struct rkisp_isp_params_vdev *params_vdev, bool en)
+isp_rawhst3_enable(struct rkisp_isp_params_vdev *params_vdev, bool en, u32 id)
 {
-	isp_rawhstbig_enable(params_vdev, en, 0);
+	isp_rawhstbig_enable(params_vdev, en, 0, id);
 }
 
 static void
 isp_hdrmge_config(struct rkisp_isp_params_vdev *params_vdev,
 		  const struct isp32_hdrmge_cfg *arg,
-		  enum rkisp_params_type type)
+		  enum rkisp_params_type type, u32 id)
 {
 	u32 value;
 	int i;
 
 	if (type == RKISP_PARAMS_SHD || type == RKISP_PARAMS_ALL) {
 		value = ISP_PACK_2SHORT(arg->gain0, arg->gain0_inv);
-		isp3_param_write(params_vdev, value, ISP3X_HDRMGE_GAIN0);
+		isp3_param_write(params_vdev, value, ISP3X_HDRMGE_GAIN0, id);
 
 		value = ISP_PACK_2SHORT(arg->gain1, arg->gain1_inv);
-		isp3_param_write(params_vdev, value, ISP3X_HDRMGE_GAIN1);
+		isp3_param_write(params_vdev, value, ISP3X_HDRMGE_GAIN1, id);
 
 		value = arg->gain2;
-		isp3_param_write(params_vdev, value, ISP3X_HDRMGE_GAIN2);
+		isp3_param_write(params_vdev, value, ISP3X_HDRMGE_GAIN2, id);
 
-		value = isp3_param_read_cache(params_vdev, ISP3X_HDRMGE_CTRL);
+		value = isp3_param_read_cache(params_vdev, ISP3X_HDRMGE_CTRL, id);
 		if (arg->s_base)
 			value |= BIT(1);
 		else
@@ -2523,48 +2601,48 @@
 			value |= BIT(6);
 		else
 			value &= ~BIT(6);
-		isp3_param_write(params_vdev, value, ISP3X_HDRMGE_CTRL);
+		isp3_param_write(params_vdev, value, ISP3X_HDRMGE_CTRL, id);
 	}
 
 	if (type == RKISP_PARAMS_IMD || type == RKISP_PARAMS_ALL) {
 		value = ISP_PACK_4BYTE(arg->ms_dif_0p8, arg->ms_diff_0p15,
 				       arg->lm_dif_0p9, arg->lm_dif_0p15);
-		isp3_param_write(params_vdev, value, ISP3X_HDRMGE_LIGHTZ);
+		isp3_param_write(params_vdev, value, ISP3X_HDRMGE_LIGHTZ, id);
 		value = (arg->ms_scl & 0x7ff) |
 			(arg->ms_thd0 & 0x3ff) << 12 |
 			(arg->ms_thd1 & 0x3ff) << 22;
-		isp3_param_write(params_vdev, value, ISP3X_HDRMGE_MS_DIFF);
+		isp3_param_write(params_vdev, value, ISP3X_HDRMGE_MS_DIFF, id);
 		value = (arg->lm_scl & 0x7ff) |
 			(arg->lm_thd0 & 0x3ff) << 12 |
 			(arg->lm_thd1 & 0x3ff) << 22;
-		isp3_param_write(params_vdev, value, ISP3X_HDRMGE_LM_DIFF);
+		isp3_param_write(params_vdev, value, ISP3X_HDRMGE_LM_DIFF, id);
 
 		for (i = 0; i < ISP32_HDRMGE_L_CURVE_NUM; i++) {
 			value = ISP_PACK_2SHORT(arg->curve.curve_0[i], arg->curve.curve_1[i]);
-			isp3_param_write(params_vdev, value, ISP3X_HDRMGE_DIFF_Y0 + 4 * i);
+			isp3_param_write(params_vdev, value, ISP3X_HDRMGE_DIFF_Y0 + 4 * i, id);
 		}
 
 		for (i = 0; i < ISP32_HDRMGE_E_CURVE_NUM; i++) {
 			value = (arg->l_raw1[i] & 0x3ff) << 20 |
 				(arg->l_raw0[i] & 0x3ff) << 10 |
 				(arg->e_y[i] & 0x3ff);
-			isp3_param_write(params_vdev, value, ISP3X_HDRMGE_OVER_Y0 + 4 * i);
+			isp3_param_write(params_vdev, value, ISP3X_HDRMGE_OVER_Y0 + 4 * i, id);
 		}
 
 		value = ISP_PACK_2SHORT(arg->each_raw_gain0, arg->each_raw_gain1);
-		isp3_param_write(params_vdev, value, ISP32_HDRMGE_EACH_GAIN);
+		isp3_param_write(params_vdev, value, ISP32_HDRMGE_EACH_GAIN, id);
 	}
 }
 
 static void
-isp_hdrmge_enable(struct rkisp_isp_params_vdev *params_vdev, bool en)
+isp_hdrmge_enable(struct rkisp_isp_params_vdev *params_vdev, bool en, u32 id)
 {
 }
 
 static void
 isp_hdrdrc_config(struct rkisp_isp_params_vdev *params_vdev,
 		  const struct isp32_drc_cfg *arg,
-		  enum rkisp_params_type type)
+		  enum rkisp_params_type type, u32 id)
 {
 	u32 i, value;
 
@@ -2574,76 +2652,76 @@
 	value = (arg->offset_pow2 & 0x0F) << 28 |
 		(arg->compres_scl & 0x1FFF) << 14 |
 		(arg->position & 0x03FFF);
-	isp3_param_write(params_vdev, value, ISP3X_DRC_CTRL1);
+	isp3_param_write(params_vdev, value, ISP3X_DRC_CTRL1, id);
 
 	value = arg->delta_scalein << 24 |
 		(arg->hpdetail_ratio & 0xFFF) << 12 |
 		(arg->lpdetail_ratio & 0xFFF);
-	isp3_param_write(params_vdev, value, ISP3X_DRC_LPRATIO);
+	isp3_param_write(params_vdev, value, ISP3X_DRC_LPRATIO, id);
 
 	value = ISP_PACK_4BYTE(arg->bilat_wt_off, 0, arg->weipre_frame, arg->weicur_pix);
-	isp3_param_write(params_vdev, value, ISP3X_DRC_EXPLRATIO);
+	isp3_param_write(params_vdev, value, ISP3X_DRC_EXPLRATIO, id);
 
 	value = (arg->force_sgm_inv0 & 0xFFFF) << 16 |
 		arg->motion_scl << 8 | arg->edge_scl;
-	isp3_param_write(params_vdev, value, ISP3X_DRC_SIGMA);
+	isp3_param_write(params_vdev, value, ISP3X_DRC_SIGMA, id);
 
 	value = ISP_PACK_2SHORT(arg->space_sgm_inv0, arg->space_sgm_inv1);
-	isp3_param_write(params_vdev, value, ISP3X_DRC_SPACESGM);
+	isp3_param_write(params_vdev, value, ISP3X_DRC_SPACESGM, id);
 
 	value = ISP_PACK_2SHORT(arg->range_sgm_inv0, arg->range_sgm_inv1);
-	isp3_param_write(params_vdev, value, ISP3X_DRC_RANESGM);
+	isp3_param_write(params_vdev, value, ISP3X_DRC_RANESGM, id);
 
 	value = (arg->weig_bilat & 0x1f) | (arg->weig_maxl & 0x1f) << 8 |
 		(arg->bilat_soft_thd & 0x3fff) << 16;
 	if (arg->enable_soft_thd)
 		value |= BIT(31);
-	isp3_param_write(params_vdev, value, ISP3X_DRC_BILAT);
+	isp3_param_write(params_vdev, value, ISP3X_DRC_BILAT, id);
 
 	for (i = 0; i < ISP32_DRC_Y_NUM / 2; i++) {
 		value = ISP_PACK_2SHORT(arg->gain_y[2 * i],
 					arg->gain_y[2 * i + 1]);
-		isp3_param_write(params_vdev, value, ISP3X_DRC_GAIN_Y0 + 4 * i);
+		isp3_param_write(params_vdev, value, ISP3X_DRC_GAIN_Y0 + 4 * i, id);
 	}
 	value = ISP_PACK_2SHORT(arg->gain_y[2 * i], 0);
-	isp3_param_write(params_vdev, value, ISP3X_DRC_GAIN_Y0 + 4 * i);
+	isp3_param_write(params_vdev, value, ISP3X_DRC_GAIN_Y0 + 4 * i, id);
 
 	for (i = 0; i < ISP32_DRC_Y_NUM / 2; i++) {
 		value = ISP_PACK_2SHORT(arg->compres_y[2 * i],
 					arg->compres_y[2 * i + 1]);
-		isp3_param_write(params_vdev, value, ISP3X_DRC_COMPRES_Y0 + 4 * i);
+		isp3_param_write(params_vdev, value, ISP3X_DRC_COMPRES_Y0 + 4 * i, id);
 	}
 	value = ISP_PACK_2SHORT(arg->compres_y[2 * i], 0);
-	isp3_param_write(params_vdev, value, ISP3X_DRC_COMPRES_Y0 + 4 * i);
+	isp3_param_write(params_vdev, value, ISP3X_DRC_COMPRES_Y0 + 4 * i, id);
 
 	for (i = 0; i < ISP32_DRC_Y_NUM / 2; i++) {
 		value = ISP_PACK_2SHORT(arg->scale_y[2 * i],
 					arg->scale_y[2 * i + 1]);
-		isp3_param_write(params_vdev, value, ISP3X_DRC_SCALE_Y0 + 4 * i);
+		isp3_param_write(params_vdev, value, ISP3X_DRC_SCALE_Y0 + 4 * i, id);
 	}
 	value = ISP_PACK_2SHORT(arg->scale_y[2 * i], 0);
-	isp3_param_write(params_vdev, value, ISP3X_DRC_SCALE_Y0 + 4 * i);
+	isp3_param_write(params_vdev, value, ISP3X_DRC_SCALE_Y0 + 4 * i, id);
 
 	if (params_vdev->dev->isp_ver == ISP_V32)
 		value = ISP_PACK_2SHORT(arg->min_ogain, arg->iir_weight);
 	else
 		value = ISP_PACK_2SHORT(arg->min_ogain, 0);
-	isp3_param_write(params_vdev, value, ISP3X_DRC_IIRWG_GAIN);
+	isp3_param_write(params_vdev, value, ISP3X_DRC_IIRWG_GAIN, id);
 
 	value = arg->gas_t & 0x1fff;
-	isp3_param_write(params_vdev, value, ISP32_DRC_LUM3X2_CTRL);
+	isp3_param_write(params_vdev, value, ISP32_DRC_LUM3X2_CTRL, id);
 
 	value = ISP_PACK_4BYTE(arg->gas_l0, arg->gas_l1, arg->gas_l2, arg->gas_l3);
-	isp3_param_write(params_vdev, value, ISP32_DRC_LUM3X2_GAS);
+	isp3_param_write(params_vdev, value, ISP32_DRC_LUM3X2_GAS, id);
 }
 
 static void
-isp_hdrdrc_enable(struct rkisp_isp_params_vdev *params_vdev, bool en)
+isp_hdrdrc_enable(struct rkisp_isp_params_vdev *params_vdev, bool en, u32 id)
 {
 	u32 value;
 	bool real_en;
 
-	value = isp3_param_read(params_vdev, ISP3X_DRC_CTRL0);
+	value = isp3_param_read(params_vdev, ISP3X_DRC_CTRL0, id);
 	real_en = !!(value & ISP32_MODULE_EN);
 	if ((en && real_en) || (!en && !real_en))
 		return;
@@ -2651,17 +2729,17 @@
 	if (en) {
 		value |= ISP32_MODULE_EN;
 		isp3_param_set_bits(params_vdev, ISP3X_ISP_CTRL1,
-				    ISP3X_ADRC_FST_FRAME);
+				    ISP3X_ADRC_FST_FRAME, id);
 	} else {
 		value = 0;
-		isp3_param_clear_bits(params_vdev, ISP3X_GAIN_CTRL, BIT(12));
+		isp3_param_clear_bits(params_vdev, ISP3X_GAIN_CTRL, BIT(12), id);
 	}
-	isp3_param_write(params_vdev, value, ISP3X_DRC_CTRL0);
+	isp3_param_write(params_vdev, value, ISP3X_DRC_CTRL0, id);
 }
 
 static void
 isp_gic_config(struct rkisp_isp_params_vdev *params_vdev,
-	       const struct isp21_gic_cfg *arg)
+	       const struct isp21_gic_cfg *arg, u32 id)
 {
 	u32 value;
 	s32 i;
@@ -2669,12 +2747,12 @@
 	value = (arg->regmingradthrdark2 & 0x03FF) << 20 |
 		(arg->regmingradthrdark1 & 0x03FF) << 10 |
 		(arg->regminbusythre & 0x03FF);
-	isp3_param_write(params_vdev, value, ISP3X_GIC_DIFF_PARA1);
+	isp3_param_write(params_vdev, value, ISP3X_GIC_DIFF_PARA1, id);
 
 	value = (arg->regdarkthre & 0x07FF) << 21 |
 		(arg->regmaxcorvboth & 0x03FF) << 11 |
 		(arg->regdarktthrehi & 0x07FF);
-	isp3_param_write(params_vdev, value, ISP3X_GIC_DIFF_PARA2);
+	isp3_param_write(params_vdev, value, ISP3X_GIC_DIFF_PARA2, id);
 
 	value = (arg->regkgrad2dark & 0x0F) << 28 |
 		(arg->regkgrad1dark & 0x0F) << 24 |
@@ -2683,46 +2761,46 @@
 		(arg->regkgrad2 & 0x0F) << 8 |
 		(arg->regkgrad1 & 0x0F) << 4 |
 		(arg->reggbthre & 0x0F);
-	isp3_param_write(params_vdev, value, ISP3X_GIC_DIFF_PARA3);
+	isp3_param_write(params_vdev, value, ISP3X_GIC_DIFF_PARA3, id);
 
 	value = (arg->regmaxcorv & 0x03FF) << 20 |
 		(arg->regmingradthr2 & 0x03FF) << 10 |
 		(arg->regmingradthr1 & 0x03FF);
-	isp3_param_write(params_vdev, value, ISP3X_GIC_DIFF_PARA4);
+	isp3_param_write(params_vdev, value, ISP3X_GIC_DIFF_PARA4, id);
 
 	value = (arg->gr_ratio & 0x03) << 28 |
 		(arg->noise_scale & 0x7F) << 12 |
 		(arg->noise_base & 0xFFF);
-	isp3_param_write(params_vdev, value, ISP3X_GIC_NOISE_PARA1);
+	isp3_param_write(params_vdev, value, ISP3X_GIC_NOISE_PARA1, id);
 
 	value = arg->diff_clip & 0x7fff;
-	isp3_param_write(params_vdev, value, ISP3X_GIC_NOISE_PARA2);
+	isp3_param_write(params_vdev, value, ISP3X_GIC_NOISE_PARA2, id);
 
 	for (i = 0; i < ISP32_GIC_SIGMA_Y_NUM / 2; i++) {
 		value = ISP_PACK_2SHORT(arg->sigma_y[2 * i], arg->sigma_y[2 * i + 1]);
-		isp3_param_write(params_vdev, value, ISP3X_GIC_SIGMA_VALUE0 + 4 * i);
+		isp3_param_write(params_vdev, value, ISP3X_GIC_SIGMA_VALUE0 + 4 * i, id);
 	}
 	value = ISP_PACK_2SHORT(arg->sigma_y[2 * i], 0);
-	isp3_param_write(params_vdev, value, ISP3X_GIC_SIGMA_VALUE0 + 4 * i);
+	isp3_param_write(params_vdev, value, ISP3X_GIC_SIGMA_VALUE0 + 4 * i, id);
 }
 
 static void
-isp_gic_enable(struct rkisp_isp_params_vdev *params_vdev, bool en)
+isp_gic_enable(struct rkisp_isp_params_vdev *params_vdev, bool en, u32 id)
 {
 	u32 value = 0;
 
 	if (en)
 		value |= ISP32_MODULE_EN;
-	isp3_param_write(params_vdev, value, ISP3X_GIC_CONTROL);
+	isp3_param_write(params_vdev, value, ISP3X_GIC_CONTROL, id);
 }
 
 static void
 isp_dhaz_config(struct rkisp_isp_params_vdev *params_vdev,
-		const struct isp32_dhaz_cfg *arg)
+		const struct isp32_dhaz_cfg *arg, u32 id)
 {
 	u32 i, value, ctrl;
 
-	ctrl = isp3_param_read(params_vdev, ISP3X_DHAZ_CTRL);
+	ctrl = isp3_param_read(params_vdev, ISP3X_DHAZ_CTRL, id);
 	ctrl &= ISP3X_DHAZ_ENMUX;
 
 	ctrl |= !!arg->enh_luma_en << 28 | !!arg->color_deviate_en << 27 |
@@ -2736,109 +2814,109 @@
 			value = (arg->hist_wr[i * 3] & 0x3ff) |
 				(arg->hist_wr[i * 3 + 1] & 0x3ff) << 10 |
 				(arg->hist_wr[i * 3 + 2] & 0x3ff) << 20;
-			isp3_param_write(params_vdev, value, ISP3X_DHAZ_HIST_WR0 + i * 4);
+			isp3_param_write(params_vdev, value, ISP3X_DHAZ_HIST_WR0 + i * 4, id);
 		}
 		value = arg->hist_wr[i * 3] & 0x3ff;
-		isp3_param_write(params_vdev, value, ISP3X_DHAZ_HIST_WR0 + i * 4);
+		isp3_param_write(params_vdev, value, ISP3X_DHAZ_HIST_WR0 + i * 4, id);
 	}
-	isp3_param_write(params_vdev, ctrl, ISP3X_DHAZ_CTRL);
+	isp3_param_write(params_vdev, ctrl, ISP3X_DHAZ_CTRL, id);
 
 	value = ISP_PACK_4BYTE(arg->dc_min_th, arg->dc_max_th,
 			       arg->yhist_th, arg->yblk_th);
-	isp3_param_write(params_vdev, value, ISP3X_DHAZ_ADP0);
+	isp3_param_write(params_vdev, value, ISP3X_DHAZ_ADP0, id);
 
 	value = ISP_PACK_4BYTE(arg->bright_min, arg->bright_max,
 			       arg->wt_max, 0);
-	isp3_param_write(params_vdev, value, ISP3X_DHAZ_ADP1);
+	isp3_param_write(params_vdev, value, ISP3X_DHAZ_ADP1, id);
 
 	value = ISP_PACK_4BYTE(arg->air_min, arg->air_max,
 			       arg->dark_th, arg->tmax_base);
-	isp3_param_write(params_vdev, value, ISP3X_DHAZ_ADP2);
+	isp3_param_write(params_vdev, value, ISP3X_DHAZ_ADP2, id);
 
 	value = ISP_PACK_2SHORT(arg->tmax_off, arg->tmax_max);
-	isp3_param_write(params_vdev, value, ISP3X_DHAZ_ADP_TMAX);
+	isp3_param_write(params_vdev, value, ISP3X_DHAZ_ADP_TMAX, id);
 
 	value = (arg->hist_min & 0xFFFF) << 16 |
 		(arg->hist_th_off & 0xFF) << 8 |
 		(arg->hist_k & 0x1F);
-	isp3_param_write(params_vdev, value, ISP3X_DHAZ_ADP_HIST0);
+	isp3_param_write(params_vdev, value, ISP3X_DHAZ_ADP_HIST0, id);
 
 	value = ISP_PACK_2SHORT(arg->hist_scale, arg->hist_gratio);
-	isp3_param_write(params_vdev, value, ISP3X_DHAZ_ADP_HIST1);
+	isp3_param_write(params_vdev, value, ISP3X_DHAZ_ADP_HIST1, id);
 
 	value = ISP_PACK_2SHORT(arg->enhance_chroma, arg->enhance_value);
-	isp3_param_write(params_vdev, value, ISP3X_DHAZ_ENHANCE);
+	isp3_param_write(params_vdev, value, ISP3X_DHAZ_ENHANCE, id);
 
 	value = (arg->iir_wt_sigma & 0x07FF) << 16 |
 		(arg->iir_sigma & 0xFF) << 8 |
 		(arg->stab_fnum & 0x1F);
-	isp3_param_write(params_vdev, value, ISP3X_DHAZ_IIR0);
+	isp3_param_write(params_vdev, value, ISP3X_DHAZ_IIR0, id);
 
 	value = (arg->iir_pre_wet & 0x0F) << 24 |
 		(arg->iir_tmax_sigma & 0x7FF) << 8 |
 		(arg->iir_air_sigma & 0xFF);
-	isp3_param_write(params_vdev, value, ISP3X_DHAZ_IIR1);
+	isp3_param_write(params_vdev, value, ISP3X_DHAZ_IIR1, id);
 
 	value = (arg->cfg_wt & 0x01FF) << 16 |
 		(arg->cfg_air & 0xFF) << 8 |
 		(arg->cfg_alpha & 0xFF);
-	isp3_param_write(params_vdev, value, ISP3X_DHAZ_SOFT_CFG0);
+	isp3_param_write(params_vdev, value, ISP3X_DHAZ_SOFT_CFG0, id);
 
 	value = ISP_PACK_2SHORT(arg->cfg_tmax, arg->cfg_gratio);
-	isp3_param_write(params_vdev, value, ISP3X_DHAZ_SOFT_CFG1);
+	isp3_param_write(params_vdev, value, ISP3X_DHAZ_SOFT_CFG1, id);
 
 	value = (arg->range_sima & 0x01FF) << 16 |
 		(arg->space_sigma_pre & 0xFF) << 8 |
 		(arg->space_sigma_cur & 0xFF);
-	isp3_param_write(params_vdev, value, ISP3X_DHAZ_BF_SIGMA);
+	isp3_param_write(params_vdev, value, ISP3X_DHAZ_BF_SIGMA, id);
 
 	value = ISP_PACK_2SHORT(arg->bf_weight, arg->dc_weitcur);
-	isp3_param_write(params_vdev, value, ISP3X_DHAZ_BF_WET);
+	isp3_param_write(params_vdev, value, ISP3X_DHAZ_BF_WET, id);
 
 	for (i = 0; i < ISP32_DHAZ_ENH_CURVE_NUM / 2; i++) {
 		value = ISP_PACK_2SHORT(arg->enh_curve[2 * i], arg->enh_curve[2 * i + 1]);
-		isp3_param_write(params_vdev, value, ISP3X_DHAZ_ENH_CURVE0 + 4 * i);
+		isp3_param_write(params_vdev, value, ISP3X_DHAZ_ENH_CURVE0 + 4 * i, id);
 	}
 	value = ISP_PACK_2SHORT(arg->enh_curve[2 * i], 0);
-	isp3_param_write(params_vdev, value, ISP3X_DHAZ_ENH_CURVE0 + 4 * i);
+	isp3_param_write(params_vdev, value, ISP3X_DHAZ_ENH_CURVE0 + 4 * i, id);
 
 	value = ISP_PACK_4BYTE(arg->gaus_h0, arg->gaus_h1, arg->gaus_h2, 0);
-	isp3_param_write(params_vdev, value, ISP3X_DHAZ_GAUS);
+	isp3_param_write(params_vdev, value, ISP3X_DHAZ_GAUS, id);
 
 	for (i = 0; i < ISP32_DHAZ_SIGMA_IDX_NUM / 4; i++) {
 		value = ISP_PACK_4BYTE(arg->sigma_idx[i * 4], arg->sigma_idx[i * 4 + 1],
 					arg->sigma_idx[i * 4 + 2], arg->sigma_idx[i * 4 + 3]);
-		isp3_param_write(params_vdev, value, ISP3X_DHAZ_GAIN_IDX0 + i * 4);
+		isp3_param_write(params_vdev, value, ISP3X_DHAZ_GAIN_IDX0 + i * 4, id);
 	}
 	value = ISP_PACK_4BYTE(arg->sigma_idx[i * 4], arg->sigma_idx[i * 4 + 1],
 				arg->sigma_idx[i * 4 + 2], 0);
-	isp3_param_write(params_vdev, value, ISP3X_DHAZ_GAIN_IDX0 + i * 4);
+	isp3_param_write(params_vdev, value, ISP3X_DHAZ_GAIN_IDX0 + i * 4, id);
 
 	for (i = 0; i < ISP32_DHAZ_SIGMA_LUT_NUM / 2; i++) {
 		value = ISP_PACK_2SHORT(arg->sigma_lut[i * 2], arg->sigma_lut[i * 2 + 1]);
-		isp3_param_write(params_vdev, value, ISP3X_DHAZ_GAIN_LUT0 + i * 4);
+		isp3_param_write(params_vdev, value, ISP3X_DHAZ_GAIN_LUT0 + i * 4, id);
 	}
 	value = ISP_PACK_2SHORT(arg->sigma_lut[i * 2], 0);
-	isp3_param_write(params_vdev, value, ISP3X_DHAZ_GAIN_LUT0 + i * 4);
+	isp3_param_write(params_vdev, value, ISP3X_DHAZ_GAIN_LUT0 + i * 4, id);
 
 	for (i = 0; i < ISP32_DHAZ_ENH_LUMA_NUM / 3; i++) {
 		value = (arg->enh_luma[i * 3 + 2] & 0x3ff) << 20 |
 			(arg->enh_luma[i * 3 + 1] & 0x3ff) << 10 |
 			(arg->enh_luma[i * 3] & 0x3ff);
-		isp3_param_write(params_vdev, value, ISP32_DHAZ_ENH_LUMA0 + i * 4);
+		isp3_param_write(params_vdev, value, ISP32_DHAZ_ENH_LUMA0 + i * 4, id);
 	}
 	value = (arg->enh_luma[i * 3 + 1] & 0x3ff) << 10 |
 		(arg->enh_luma[i * 3] & 0x3ff);
-	isp3_param_write(params_vdev, value, ISP32_DHAZ_ENH_LUMA0 + i * 4);
+	isp3_param_write(params_vdev, value, ISP32_DHAZ_ENH_LUMA0 + i * 4, id);
 }
 
 static void
-isp_dhaz_enable(struct rkisp_isp_params_vdev *params_vdev, bool en)
+isp_dhaz_enable(struct rkisp_isp_params_vdev *params_vdev, bool en, u32 id)
 {
 	u32 value;
 	bool real_en;
 
-	value = isp3_param_read(params_vdev, ISP3X_DHAZ_CTRL);
+	value = isp3_param_read(params_vdev, ISP3X_DHAZ_CTRL, id);
 	real_en = !!(value & ISP3X_DHAZ_ENMUX);
 	if ((en && real_en) || (!en && !real_en))
 		return;
@@ -2846,17 +2924,17 @@
 	if (en) {
 		value |= ISP32_SELF_FORCE_UPD | ISP3X_DHAZ_ENMUX;
 		isp3_param_set_bits(params_vdev, ISP3X_ISP_CTRL1,
-				    ISP3X_DHAZ_FST_FRAME);
+				    ISP3X_DHAZ_FST_FRAME, id);
 	} else {
 		value &= ~ISP3X_DHAZ_ENMUX;
-		isp3_param_clear_bits(params_vdev, ISP3X_GAIN_CTRL, BIT(16));
+		isp3_param_clear_bits(params_vdev, ISP3X_GAIN_CTRL, BIT(16), id);
 	}
-	isp3_param_write(params_vdev, value, ISP3X_DHAZ_CTRL);
+	isp3_param_write(params_vdev, value, ISP3X_DHAZ_CTRL, id);
 }
 
 static void
 isp_3dlut_config(struct rkisp_isp_params_vdev *params_vdev,
-		 const struct isp2x_3dlut_cfg *arg)
+		 const struct isp2x_3dlut_cfg *arg, u32 id)
 {
 	struct rkisp_device *dev = params_vdev->dev;
 	struct rkisp_isp_params_val_v32 *priv_val;
@@ -2864,58 +2942,58 @@
 	u32 *data;
 
 	priv_val = (struct rkisp_isp_params_val_v32 *)params_vdev->priv_val;
-	buf_idx = (priv_val->buf_3dlut_idx++) % ISP32_3DLUT_BUF_NUM;
+	buf_idx = (priv_val->buf_3dlut_idx[id]++) % ISP32_3DLUT_BUF_NUM;
 
-	if (!priv_val->buf_3dlut[buf_idx].vaddr) {
+	if (!priv_val->buf_3dlut[id][buf_idx].vaddr) {
 		dev_err(dev->dev, "no find 3dlut buf\n");
 		return;
 	}
-	data = (u32 *)priv_val->buf_3dlut[buf_idx].vaddr;
+	data = (u32 *)priv_val->buf_3dlut[id][buf_idx].vaddr;
 	for (i = 0; i < arg->actual_size; i++)
 		data[i] = (arg->lut_b[i] & 0x3FF) |
 			  (arg->lut_g[i] & 0xFFF) << 10 |
 			  (arg->lut_r[i] & 0x3FF) << 22;
-	rkisp_prepare_buffer(params_vdev->dev, &priv_val->buf_3dlut[buf_idx]);
-	value = priv_val->buf_3dlut[buf_idx].dma_addr;
-	isp3_param_write(params_vdev, value, ISP3X_MI_LUT_3D_RD_BASE);
-	isp3_param_write(params_vdev, arg->actual_size, ISP3X_MI_LUT_3D_RD_WSIZE);
+	rkisp_prepare_buffer(params_vdev->dev, &priv_val->buf_3dlut[id][buf_idx]);
+	value = priv_val->buf_3dlut[id][buf_idx].dma_addr;
+	isp3_param_write(params_vdev, value, ISP3X_MI_LUT_3D_RD_BASE, id);
+	isp3_param_write(params_vdev, arg->actual_size, ISP3X_MI_LUT_3D_RD_WSIZE, id);
 
-	value = isp3_param_read(params_vdev, ISP3X_3DLUT_CTRL);
+	value = isp3_param_read(params_vdev, ISP3X_3DLUT_CTRL, id);
 	value &= ISP3X_3DLUT_EN;
 
 	if (value)
-		isp3_param_set_bits(params_vdev, ISP3X_3DLUT_UPDATE, 0x01);
+		isp3_param_set_bits(params_vdev, ISP3X_3DLUT_UPDATE, 0x01, id);
 
-	isp3_param_write(params_vdev, value, ISP3X_3DLUT_CTRL);
+	isp3_param_write(params_vdev, value, ISP3X_3DLUT_CTRL, id);
 }
 
 static void
-isp_3dlut_enable(struct rkisp_isp_params_vdev *params_vdev, bool en)
+isp_3dlut_enable(struct rkisp_isp_params_vdev *params_vdev, bool en, u32 id)
 {
 	u32 value;
 	bool en_state;
 	struct rkisp_isp_params_val_v32 *priv_val;
 
-	value = isp3_param_read(params_vdev, ISP3X_3DLUT_CTRL);
+	value = isp3_param_read(params_vdev, ISP3X_3DLUT_CTRL, id);
 	en_state = (value & ISP3X_3DLUT_EN) ? true : false;
 
 	if (en == en_state)
 		return;
 
 	priv_val = (struct rkisp_isp_params_val_v32 *)params_vdev->priv_val;
-	if (en && priv_val->buf_3dlut[0].vaddr) {
-		isp3_param_set_bits(params_vdev, ISP3X_3DLUT_CTRL, 0x01);
-		isp3_param_set_bits(params_vdev, ISP3X_3DLUT_UPDATE, 0x01);
+	if (en && priv_val->buf_3dlut[id][0].vaddr) {
+		isp3_param_set_bits(params_vdev, ISP3X_3DLUT_CTRL, 0x01, id);
+		isp3_param_set_bits(params_vdev, ISP3X_3DLUT_UPDATE, 0x01, id);
 	} else {
-		isp3_param_clear_bits(params_vdev, ISP3X_3DLUT_CTRL, 0x01);
-		isp3_param_clear_bits(params_vdev, ISP3X_3DLUT_UPDATE, 0x01);
-		isp3_param_clear_bits(params_vdev, ISP3X_GAIN_CTRL, BIT(20));
+		isp3_param_clear_bits(params_vdev, ISP3X_3DLUT_CTRL, 0x01, id);
+		isp3_param_clear_bits(params_vdev, ISP3X_3DLUT_UPDATE, 0x01, id);
+		isp3_param_clear_bits(params_vdev, ISP3X_GAIN_CTRL, BIT(20), id);
 	}
 }
 
 static void
 isp_ldch_config(struct rkisp_isp_params_vdev *params_vdev,
-		const struct isp32_ldch_cfg *arg)
+		const struct isp32_ldch_cfg *arg, u32 id)
 {
 	struct rkisp_device *dev = params_vdev->dev;
 	struct rkisp_isp_params_val_v32 *priv_val;
@@ -2923,7 +3001,7 @@
 	int buf_idx, i;
 	u32 value;
 
-	value = isp3_param_read(params_vdev, ISP3X_LDCH_STS);
+	value = isp3_param_read(params_vdev, ISP3X_LDCH_STS, id);
 	value &= ISP32_MODULE_EN;
 	value |= !!arg->map13p3_en << 7 |
 		 !!arg->force_map_en << 6 |
@@ -2931,20 +3009,20 @@
 		 !!arg->sample_avr_en << 3 |
 		 !!arg->zero_interp_en << 2 |
 		 !!arg->frm_end_dis << 1;
-	isp3_param_write(params_vdev, value, ISP3X_LDCH_STS);
+	isp3_param_write(params_vdev, value, ISP3X_LDCH_STS, id);
 	if (arg->bic_mode_en) {
 		for (i = 0; i < ISP32_LDCH_BIC_NUM / 4; i++) {
 			value = ISP_PACK_4BYTE(arg->bicubic[i * 4], arg->bicubic[i * 4 + 1],
 					arg->bicubic[i * 4 + 2], arg->bicubic[i * 4 + 3]);
-			isp3_param_write(params_vdev, value, ISP32_LDCH_BIC_TABLE0 + i * 4);
+			isp3_param_write(params_vdev, value, ISP32_LDCH_BIC_TABLE0 + i * 4, id);
 		}
 	}
 
 	priv_val = (struct rkisp_isp_params_val_v32 *)params_vdev->priv_val;
 	for (i = 0; i < ISP32_MESH_BUF_NUM; i++) {
-		if (!priv_val->buf_ldch[i].mem_priv)
+		if (!priv_val->buf_ldch[id][i].mem_priv)
 			continue;
-		if (arg->buf_fd == priv_val->buf_ldch[i].dma_fd)
+		if (arg->buf_fd == priv_val->buf_ldch[id][i].dma_fd)
 			break;
 	}
 	if (i == ISP32_MESH_BUF_NUM) {
@@ -2952,28 +3030,28 @@
 		return;
 	}
 
-	if (!priv_val->buf_ldch[i].vaddr) {
+	if (!priv_val->buf_ldch[id][i].vaddr) {
 		dev_err(dev->dev, "no ldch buffer allocated\n");
 		return;
 	}
 
-	buf_idx = priv_val->buf_ldch_idx;
-	head = (struct isp2x_mesh_head *)priv_val->buf_ldch[buf_idx].vaddr;
+	buf_idx = priv_val->buf_ldch_idx[id];
+	head = (struct isp2x_mesh_head *)priv_val->buf_ldch[id][buf_idx].vaddr;
 	head->stat = MESH_BUF_INIT;
 
 	buf_idx = i;
-	head = (struct isp2x_mesh_head *)priv_val->buf_ldch[buf_idx].vaddr;
+	head = (struct isp2x_mesh_head *)priv_val->buf_ldch[id][buf_idx].vaddr;
 	head->stat = MESH_BUF_CHIPINUSE;
-	priv_val->buf_ldch_idx = buf_idx;
-	rkisp_prepare_buffer(dev, &priv_val->buf_ldch[buf_idx]);
-	value = priv_val->buf_ldch[buf_idx].dma_addr + head->data_oft;
-	isp3_param_write(params_vdev, value, ISP3X_MI_LUT_LDCH_RD_BASE);
-	isp3_param_write(params_vdev, arg->hsize, ISP3X_MI_LUT_LDCH_RD_H_WSIZE);
-	isp3_param_write(params_vdev, arg->vsize, ISP3X_MI_LUT_LDCH_RD_V_SIZE);
+	priv_val->buf_ldch_idx[id] = buf_idx;
+	rkisp_prepare_buffer(dev, &priv_val->buf_ldch[id][buf_idx]);
+	value = priv_val->buf_ldch[id][buf_idx].dma_addr + head->data_oft;
+	isp3_param_write(params_vdev, value, ISP3X_MI_LUT_LDCH_RD_BASE, id);
+	isp3_param_write(params_vdev, arg->hsize, ISP3X_MI_LUT_LDCH_RD_H_WSIZE, id);
+	isp3_param_write(params_vdev, arg->vsize, ISP3X_MI_LUT_LDCH_RD_V_SIZE, id);
 }
 
 static void
-isp_ldch_enable(struct rkisp_isp_params_vdev *params_vdev, bool en)
+isp_ldch_enable(struct rkisp_isp_params_vdev *params_vdev, bool en, u32 id)
 {
 	struct rkisp_device *dev = params_vdev->dev;
 	struct rkisp_isp_params_val_v32 *priv_val;
@@ -2981,24 +3059,24 @@
 
 	priv_val = (struct rkisp_isp_params_val_v32 *)params_vdev->priv_val;
 	if (en) {
-		buf_idx = priv_val->buf_ldch_idx;
-		if (!priv_val->buf_ldch[buf_idx].vaddr) {
+		buf_idx = priv_val->buf_ldch_idx[id];
+		if (!priv_val->buf_ldch[id][buf_idx].vaddr) {
 			dev_err(dev->dev, "no ldch buffer allocated\n");
 			return;
 		}
-		isp3_param_set_bits(params_vdev, ISP3X_LDCH_STS, 0x01);
+		isp3_param_set_bits(params_vdev, ISP3X_LDCH_STS, 0x01, id);
 	} else {
-		isp3_param_clear_bits(params_vdev, ISP3X_LDCH_STS, 0x01);
+		isp3_param_clear_bits(params_vdev, ISP3X_LDCH_STS, 0x01, id);
 	}
 }
 
 static void
 isp_ynr_config(struct rkisp_isp_params_vdev *params_vdev,
-	       const struct isp32_ynr_cfg *arg)
+	       const struct isp32_ynr_cfg *arg, u32 id)
 {
 	u32 i, value;
 
-	value = isp3_param_read(params_vdev, ISP3X_YNR_GLOBAL_CTRL);
+	value = isp3_param_read(params_vdev, ISP3X_YNR_GLOBAL_CTRL, id);
 	value &= ISP32_MODULE_EN;
 
 	value |= !!arg->rnr_en << 26 |
@@ -3011,96 +3089,96 @@
 		 !!arg->lgft3x3_bypass << 3 |
 		 !!arg->lbft5x5_bypass << 2 |
 		 !!arg->bft3x3_bypass << 1;
-	isp3_param_write(params_vdev, value, ISP3X_YNR_GLOBAL_CTRL);
+	isp3_param_write(params_vdev, value, ISP3X_YNR_GLOBAL_CTRL, id);
 
 	value = ISP_PACK_2SHORT(arg->rnr_max_r, arg->local_gainscale);
-	isp3_param_write(params_vdev, value, ISP3X_YNR_RNR_MAX_R);
+	isp3_param_write(params_vdev, value, ISP3X_YNR_RNR_MAX_R, id);
 
 	value = ISP_PACK_2SHORT(arg->rnr_center_coorh, arg->rnr_center_coorv);
-	isp3_param_write(params_vdev, value, ISP3X_YNR_RNR_CENTER_COOR);
+	isp3_param_write(params_vdev, value, ISP3X_YNR_RNR_CENTER_COOR, id);
 
 	value = ISP_PACK_2SHORT(arg->loclagain_adj_thresh, arg->localgain_adj);
-	isp3_param_write(params_vdev, value, ISP3X_YNR_LOCAL_GAIN_CTRL);
+	isp3_param_write(params_vdev, value, ISP3X_YNR_LOCAL_GAIN_CTRL, id);
 
 	value = ISP_PACK_2SHORT(arg->low_bf_inv0, arg->low_bf_inv1);
-	isp3_param_write(params_vdev, value, ISP3X_YNR_LOWNR_CTRL0);
+	isp3_param_write(params_vdev, value, ISP3X_YNR_LOWNR_CTRL0, id);
 
 	value = ISP_PACK_2SHORT(arg->low_thred_adj, arg->low_peak_supress);
-	isp3_param_write(params_vdev, value, ISP3X_YNR_LOWNR_CTRL1);
+	isp3_param_write(params_vdev, value, ISP3X_YNR_LOWNR_CTRL1, id);
 
 	value = ISP_PACK_2SHORT(arg->low_edge_adj_thresh, arg->low_dist_adj);
-	isp3_param_write(params_vdev, value, ISP3X_YNR_LOWNR_CTRL2);
+	isp3_param_write(params_vdev, value, ISP3X_YNR_LOWNR_CTRL2, id);
 
 	value = (arg->low_bi_weight & 0xFF) << 24 |
 		(arg->low_weight & 0xFF) << 16 |
 		(arg->low_center_weight & 0xFFFF);
-	isp3_param_write(params_vdev, value, ISP3X_YNR_LOWNR_CTRL3);
+	isp3_param_write(params_vdev, value, ISP3X_YNR_LOWNR_CTRL3, id);
 
 	value = ISP_PACK_2SHORT(arg->lbf_weight_thres, arg->frame_full_size);
 	if (params_vdev->dev->isp_ver == ISP_V32_L)
 		value |= (arg->frame_add4line & 0xf) << 12;
-	isp3_param_write(params_vdev, value, ISP3X_YNR_LOWNR_CTRL4);
+	isp3_param_write(params_vdev, value, ISP3X_YNR_LOWNR_CTRL4, id);
 
 	value = (arg->low_gauss1_coeff2 & 0xFFFF) << 16 |
 		(arg->low_gauss1_coeff1 & 0xFF) << 8 |
 		(arg->low_gauss1_coeff0 & 0xFF);
-	isp3_param_write(params_vdev, value, ISP3X_YNR_GAUSS1_COEFF);
+	isp3_param_write(params_vdev, value, ISP3X_YNR_GAUSS1_COEFF, id);
 
 	value = (arg->low_gauss2_coeff2 & 0xFFFF) << 16 |
 		(arg->low_gauss2_coeff1 & 0xFF) << 8 |
 		(arg->low_gauss2_coeff0 & 0xFF);
-	isp3_param_write(params_vdev, value, ISP3X_YNR_GAUSS2_COEFF);
+	isp3_param_write(params_vdev, value, ISP3X_YNR_GAUSS2_COEFF, id);
 
 	for (i = 0; i < ISP32_YNR_XY_NUM / 2; i++) {
 		value = ISP_PACK_2SHORT(arg->luma_points_x[2 * i],
 					arg->luma_points_x[2 * i + 1]);
-		isp3_param_write(params_vdev, value, ISP3X_YNR_SGM_DX_0_1 + 4 * i);
+		isp3_param_write(params_vdev, value, ISP3X_YNR_SGM_DX_0_1 + 4 * i, id);
 	}
 	value = ISP_PACK_2SHORT(arg->luma_points_x[2 * i], 0);
-	isp3_param_write(params_vdev, value, ISP3X_YNR_SGM_DX_0_1 + 4 * i);
+	isp3_param_write(params_vdev, value, ISP3X_YNR_SGM_DX_0_1 + 4 * i, id);
 
 	for (i = 0; i < ISP32_YNR_XY_NUM / 2; i++) {
 		value = ISP_PACK_2SHORT(arg->lsgm_y[2 * i],
 					arg->lsgm_y[2 * i + 1]);
-		isp3_param_write(params_vdev, value, ISP3X_YNR_LSGM_Y_0_1 + 4 * i);
+		isp3_param_write(params_vdev, value, ISP3X_YNR_LSGM_Y_0_1 + 4 * i, id);
 	}
 	value = ISP_PACK_2SHORT(arg->lsgm_y[2 * i], 0);
-	isp3_param_write(params_vdev, value, ISP3X_YNR_LSGM_Y_0_1 + 4 * i);
+	isp3_param_write(params_vdev, value, ISP3X_YNR_LSGM_Y_0_1 + 4 * i, id);
 
 	for (i = 0; i < ISP32_YNR_XY_NUM / 4; i++) {
 		value = ISP_PACK_4BYTE(arg->rnr_strength3[4 * i],
 					arg->rnr_strength3[4 * i + 1],
 					arg->rnr_strength3[4 * i + 2],
 					arg->rnr_strength3[4 * i + 3]);
-		isp3_param_write(params_vdev, value, ISP3X_YNR_RNR_STRENGTH03 + 4 * i);
+		isp3_param_write(params_vdev, value, ISP3X_YNR_RNR_STRENGTH03 + 4 * i, id);
 	}
 	value = ISP_PACK_4BYTE(arg->rnr_strength3[4 * i], 0, 0, 0);
-	isp3_param_write(params_vdev, value, ISP3X_YNR_RNR_STRENGTH03 + 4 * i);
+	isp3_param_write(params_vdev, value, ISP3X_YNR_RNR_STRENGTH03 + 4 * i, id);
 
 	value = (arg->nlm_hi_bf_scale & 0x3ff) << 16 |
 		(arg->nlm_hi_gain_alpha & 0x1f) << 11 |
 		(arg->nlm_min_sigma & 0x7ff);
-	isp3_param_write(params_vdev, value, ISP32_YNR_NLM_SIGMA_GAIN);
+	isp3_param_write(params_vdev, value, ISP32_YNR_NLM_SIGMA_GAIN, id);
 
 	value = (arg->nlm_coe[5] & 0xf) << 20 | (arg->nlm_coe[4] & 0xf) << 16 |
 		(arg->nlm_coe[3] & 0xf) << 12 | (arg->nlm_coe[2] & 0xf) << 8 |
 		(arg->nlm_coe[1] & 0xf) << 4 | (arg->nlm_coe[0] & 0xf);
-	isp3_param_write(params_vdev, value, ISP32_YNR_NLM_COE);
+	isp3_param_write(params_vdev, value, ISP32_YNR_NLM_COE, id);
 
 	value = (arg->nlm_center_weight & 0x3ffff) << 10 | (arg->nlm_weight_offset & 0x3ff);
-	isp3_param_write(params_vdev, value, ISP32_YNR_NLM_WEIGHT);
+	isp3_param_write(params_vdev, value, ISP32_YNR_NLM_WEIGHT, id);
 
 	value = arg->nlm_nr_weight & 0x7ff;
-	isp3_param_write(params_vdev, value, ISP32_YNR_NLM_NR_WEIGHT);
+	isp3_param_write(params_vdev, value, ISP32_YNR_NLM_NR_WEIGHT, id);
 }
 
 static void
-isp_ynr_enable(struct rkisp_isp_params_vdev *params_vdev, bool en)
+isp_ynr_enable(struct rkisp_isp_params_vdev *params_vdev, bool en, u32 id)
 {
 	u32 ynr_ctrl;
 	bool real_en;
 
-	ynr_ctrl = isp3_param_read_cache(params_vdev, ISP3X_YNR_GLOBAL_CTRL);
+	ynr_ctrl = isp3_param_read_cache(params_vdev, ISP3X_YNR_GLOBAL_CTRL, id);
 	real_en = !!(ynr_ctrl & ISP32_MODULE_EN);
 	if ((en && real_en) || (!en && !real_en))
 		return;
@@ -3108,22 +3186,22 @@
 	if (en) {
 		ynr_ctrl |= ISP32_MODULE_EN;
 		isp3_param_set_bits(params_vdev, ISP3X_ISP_CTRL1,
-				    ISP3X_YNR_FST_FRAME);
+				    ISP3X_YNR_FST_FRAME, id);
 	} else {
 		ynr_ctrl &= ~ISP32_MODULE_EN;
 	}
 
-	isp3_param_write(params_vdev, ynr_ctrl, ISP3X_YNR_GLOBAL_CTRL);
+	isp3_param_write(params_vdev, ynr_ctrl, ISP3X_YNR_GLOBAL_CTRL, id);
 }
 
 static void
 isp_cnr_config(struct rkisp_isp_params_vdev *params_vdev,
-	       const struct isp32_cnr_cfg *arg)
+	       const struct isp32_cnr_cfg *arg, u32 id)
 {
 	u32 i, value, ctrl, gain_ctrl;
 
-	gain_ctrl = isp3_param_read(params_vdev, ISP3X_GAIN_CTRL);
-	ctrl = isp3_param_read(params_vdev, ISP3X_CNR_CTRL);
+	gain_ctrl = isp3_param_read(params_vdev, ISP3X_GAIN_CTRL, id);
+	ctrl = isp3_param_read(params_vdev, ISP3X_CNR_CTRL, id);
 	ctrl &= ISP32_MODULE_EN;
 
 	ctrl |= !!arg->bf3x3_wgt0_sel << 8 |
@@ -3139,63 +3217,63 @@
 		value &= ~ISP3X_CNR_GLOBAL_GAIN_ALPHA_MAX;
 		value |= BIT(15);
 	}
-	isp3_param_write(params_vdev, ctrl, ISP3X_CNR_CTRL);
-	isp3_param_write(params_vdev, value, ISP3X_CNR_EXGAIN);
+	isp3_param_write(params_vdev, ctrl, ISP3X_CNR_CTRL, id);
+	isp3_param_write(params_vdev, value, ISP3X_CNR_EXGAIN, id);
 
 	value = ISP_PACK_2SHORT(arg->thumb_sigma_c, arg->thumb_sigma_y);
-	isp3_param_write(params_vdev, value, ISP32_CNR_THUMB1);
+	isp3_param_write(params_vdev, value, ISP32_CNR_THUMB1, id);
 
 	value = arg->thumb_bf_ratio & 0x7ff;
-	isp3_param_write(params_vdev, value, ISP32_CNR_THUMB_BF_RATIO);
+	isp3_param_write(params_vdev, value, ISP32_CNR_THUMB_BF_RATIO, id);
 
 	value = ISP_PACK_4BYTE(arg->lbf1x7_weit_d0, arg->lbf1x7_weit_d1,
 			       arg->lbf1x7_weit_d2, arg->lbf1x7_weit_d3);
-	isp3_param_write(params_vdev, value, ISP32_CNR_LBF_WEITD);
+	isp3_param_write(params_vdev, value, ISP32_CNR_LBF_WEITD, id);
 
 	value = (arg->wgt_slope & 0x3ff) << 20 | (arg->exp_shift & 0x3f) << 12 |
 		arg->iir_strength << 4 | (arg->iir_uvgain & 0xf);
-	isp3_param_write(params_vdev, value, ISP32_CNR_IIR_PARA1);
+	isp3_param_write(params_vdev, value, ISP32_CNR_IIR_PARA1, id);
 
 	value = ISP_PACK_4BYTE(arg->chroma_ghost, arg->iir_uv_clip, 0, 0);
-	isp3_param_write(params_vdev, value, ISP32_CNR_IIR_PARA2);
+	isp3_param_write(params_vdev, value, ISP32_CNR_IIR_PARA2, id);
 
 	value = ISP_PACK_4BYTE(arg->gaus_coe[0], arg->gaus_coe[1],
 			       arg->gaus_coe[2], arg->gaus_coe[3]);
-	isp3_param_write(params_vdev, value, ISP32_CNR_GAUS_COE1);
+	isp3_param_write(params_vdev, value, ISP32_CNR_GAUS_COE1, id);
 
 	value = ISP_PACK_4BYTE(arg->gaus_coe[4], arg->gaus_coe[5], 0, 0);
-	isp3_param_write(params_vdev, value, ISP32_CNR_GAUS_COE2);
+	isp3_param_write(params_vdev, value, ISP32_CNR_GAUS_COE2, id);
 
 	value = (arg->global_alpha & 0x7ff) << 20 | arg->bf_wgt_clip << 12 |
 		(arg->gaus_ratio & 0x7ff);
-	isp3_param_write(params_vdev, value, ISP32_CNR_GAUS_RATIO);
+	isp3_param_write(params_vdev, value, ISP32_CNR_GAUS_RATIO, id);
 
 	value = arg->bf_ratio << 24 | (arg->sigma_r & 0x3fff) << 8 |
 		(arg->uv_gain & 0x7f);
-	isp3_param_write(params_vdev, value, ISP32_CNR_BF_PARA1);
+	isp3_param_write(params_vdev, value, ISP32_CNR_BF_PARA1, id);
 
 	value = (arg->adj_ratio & 0x7fff) << 16 | (arg->adj_offset & 0x1ff);
-	isp3_param_write(params_vdev, value, ISP32_CNR_BF_PARA2);
+	isp3_param_write(params_vdev, value, ISP32_CNR_BF_PARA2, id);
 
 	for (i = 0; i < ISP32_CNR_SIGMA_Y_NUM / 4; i++) {
 		value = ISP_PACK_4BYTE(arg->sigma_y[i * 4], arg->sigma_y[i * 4 + 1],
 				arg->sigma_y[i * 4 + 2], arg->sigma_y[i * 4 + 3]);
-		isp3_param_write(params_vdev, value, ISP32_CNR_SIGMA0 + i * 4);
+		isp3_param_write(params_vdev, value, ISP32_CNR_SIGMA0 + i * 4, id);
 	}
 	value = arg->sigma_y[i * 4];
-	isp3_param_write(params_vdev, value, ISP32_CNR_SIGMA0 + i * 4);
+	isp3_param_write(params_vdev, value, ISP32_CNR_SIGMA0 + i * 4, id);
 
 	value = (arg->iir_gain_alpha & 0xf) << 8 | arg->iir_global_gain;
-	isp3_param_write(params_vdev, value, ISP32_CNR_IIR_GLOBAL_GAIN);
+	isp3_param_write(params_vdev, value, ISP32_CNR_IIR_GLOBAL_GAIN, id);
 }
 
 static void
-isp_cnr_enable(struct rkisp_isp_params_vdev *params_vdev, bool en)
+isp_cnr_enable(struct rkisp_isp_params_vdev *params_vdev, bool en, u32 id)
 {
 	u32 cnr_ctrl;
 	bool real_en;
 
-	cnr_ctrl = isp3_param_read_cache(params_vdev, ISP3X_CNR_CTRL);
+	cnr_ctrl = isp3_param_read_cache(params_vdev, ISP3X_CNR_CTRL, id);
 	real_en = !!(cnr_ctrl & ISP32_MODULE_EN);
 	if ((en && real_en) || (!en && !real_en))
 		return;
@@ -3203,21 +3281,21 @@
 	if (en) {
 		cnr_ctrl |= ISP32_MODULE_EN;
 		isp3_param_set_bits(params_vdev, ISP3X_ISP_CTRL1,
-				    ISP3X_CNR_FST_FRAME);
+				    ISP3X_CNR_FST_FRAME, id);
 	} else {
 		cnr_ctrl &= ~ISP32_MODULE_EN;
 	}
 
-	isp3_param_write(params_vdev, cnr_ctrl, ISP3X_CNR_CTRL);
+	isp3_param_write(params_vdev, cnr_ctrl, ISP3X_CNR_CTRL, id);
 }
 
 static void
 isp_sharp_config(struct rkisp_isp_params_vdev *params_vdev,
-		 const struct isp32_sharp_cfg *arg)
+		 const struct isp32_sharp_cfg *arg, u32 id)
 {
 	u32 i, value;
 
-	value = isp3_param_read(params_vdev, ISP3X_SHARP_EN);
+	value = isp3_param_read(params_vdev, ISP3X_SHARP_EN, id);
 	value &= ISP32_MODULE_EN;
 
 	value |= !!arg->bypass << 1 |
@@ -3229,11 +3307,11 @@
 	else
 		value |= !!arg->clip_hf_mode << 6 |
 			 !!arg->add_mode << 7;
-	isp3_param_write(params_vdev, value, ISP3X_SHARP_EN);
+	isp3_param_write(params_vdev, value, ISP3X_SHARP_EN, id);
 
 	value = ISP_PACK_4BYTE(arg->pbf_ratio, arg->gaus_ratio,
 				arg->bf_ratio, arg->sharp_ratio);
-	isp3_param_write(params_vdev, value, ISP3X_SHARP_RATIO);
+	isp3_param_write(params_vdev, value, ISP3X_SHARP_RATIO, id);
 
 	value = (arg->luma_dx[6] & 0x0F) << 24 |
 		(arg->luma_dx[5] & 0x0F) << 20 |
@@ -3242,217 +3320,222 @@
 		(arg->luma_dx[2] & 0x0F) << 8 |
 		(arg->luma_dx[1] & 0x0F) << 4 |
 		(arg->luma_dx[0] & 0x0F);
-	isp3_param_write(params_vdev, value, ISP3X_SHARP_LUMA_DX);
+	isp3_param_write(params_vdev, value, ISP3X_SHARP_LUMA_DX, id);
 
 	value = (arg->pbf_sigma_inv[2] & 0x3FF) << 20 |
 		(arg->pbf_sigma_inv[1] & 0x3FF) << 10 |
 		(arg->pbf_sigma_inv[0] & 0x3FF);
-	isp3_param_write(params_vdev, value, ISP3X_SHARP_PBF_SIGMA_INV_0);
+	isp3_param_write(params_vdev, value, ISP3X_SHARP_PBF_SIGMA_INV_0, id);
 
 	value = (arg->pbf_sigma_inv[5] & 0x3FF) << 20 |
 		(arg->pbf_sigma_inv[4] & 0x3FF) << 10 |
 		(arg->pbf_sigma_inv[3] & 0x3FF);
-	isp3_param_write(params_vdev, value, ISP3X_SHARP_PBF_SIGMA_INV_1);
+	isp3_param_write(params_vdev, value, ISP3X_SHARP_PBF_SIGMA_INV_1, id);
 
 	value = (arg->pbf_sigma_inv[7] & 0x3FF) << 10 |
 		(arg->pbf_sigma_inv[6] & 0x3FF);
-	isp3_param_write(params_vdev, value, ISP3X_SHARP_PBF_SIGMA_INV_2);
+	isp3_param_write(params_vdev, value, ISP3X_SHARP_PBF_SIGMA_INV_2, id);
 
 	value = (arg->bf_sigma_inv[2] & 0x3FF) << 20 |
 		(arg->bf_sigma_inv[1] & 0x3FF) << 10 |
 		(arg->bf_sigma_inv[0] & 0x3FF);
-	isp3_param_write(params_vdev, value, ISP3X_SHARP_BF_SIGMA_INV_0);
+	isp3_param_write(params_vdev, value, ISP3X_SHARP_BF_SIGMA_INV_0, id);
 
 	value = (arg->bf_sigma_inv[5] & 0x3FF) << 20 |
 		(arg->bf_sigma_inv[4] & 0x3FF) << 10 |
 		(arg->bf_sigma_inv[3] & 0x3FF);
-	isp3_param_write(params_vdev, value, ISP3X_SHARP_BF_SIGMA_INV_1);
+	isp3_param_write(params_vdev, value, ISP3X_SHARP_BF_SIGMA_INV_1, id);
 
 	value = (arg->bf_sigma_inv[7] & 0x3FF) << 10 |
 		(arg->bf_sigma_inv[6] & 0x3FF);
-	isp3_param_write(params_vdev, value, ISP3X_SHARP_BF_SIGMA_INV_2);
+	isp3_param_write(params_vdev, value, ISP3X_SHARP_BF_SIGMA_INV_2, id);
 
 	value = (arg->bf_sigma_shift & 0x0F) << 4 |
 		(arg->pbf_sigma_shift & 0x0F);
-	isp3_param_write(params_vdev, value, ISP3X_SHARP_SIGMA_SHIFT);
+	isp3_param_write(params_vdev, value, ISP3X_SHARP_SIGMA_SHIFT, id);
 
 	value = (arg->clip_hf[2] & 0x3FF) << 20 |
 		(arg->clip_hf[1] & 0x3FF) << 10 |
 		(arg->clip_hf[0] & 0x3FF);
-	isp3_param_write(params_vdev, value, ISP3X_SHARP_CLIP_HF_0);
+	isp3_param_write(params_vdev, value, ISP3X_SHARP_CLIP_HF_0, id);
 
 	value = (arg->clip_hf[5] & 0x3FF) << 20 |
 		(arg->clip_hf[4] & 0x3FF) << 10 |
 		(arg->clip_hf[3] & 0x3FF);
-	isp3_param_write(params_vdev, value, ISP3X_SHARP_CLIP_HF_1);
+	isp3_param_write(params_vdev, value, ISP3X_SHARP_CLIP_HF_1, id);
 
 	value = (arg->clip_hf[7] & 0x3FF) << 10 |
 		(arg->clip_hf[6] & 0x3FF);
-	isp3_param_write(params_vdev, value, ISP3X_SHARP_CLIP_HF_2);
+	isp3_param_write(params_vdev, value, ISP3X_SHARP_CLIP_HF_2, id);
 
 	value = ISP_PACK_4BYTE(arg->pbf_coef0, arg->pbf_coef1, arg->pbf_coef2, 0);
-	isp3_param_write(params_vdev, value, ISP3X_SHARP_PBF_COEF);
+	isp3_param_write(params_vdev, value, ISP3X_SHARP_PBF_COEF, id);
 
 	value = ISP_PACK_4BYTE(arg->bf_coef0, arg->bf_coef1, arg->bf_coef2, 0);
-	isp3_param_write(params_vdev, value, ISP3X_SHARP_BF_COEF);
+	isp3_param_write(params_vdev, value, ISP3X_SHARP_BF_COEF, id);
 
 	value = ISP_PACK_4BYTE(arg->gaus_coef[0], arg->gaus_coef[1], arg->gaus_coef[2], 0);
-	isp3_param_write(params_vdev, value, ISP3X_SHARP_GAUS_COEF0);
+	isp3_param_write(params_vdev, value, ISP3X_SHARP_GAUS_COEF0, id);
 
 	value = ISP_PACK_4BYTE(arg->gaus_coef[3], arg->gaus_coef[4], arg->gaus_coef[5], 0);
-	isp3_param_write(params_vdev, value, ISP3X_SHARP_GAUS_COEF1);
+	isp3_param_write(params_vdev, value, ISP3X_SHARP_GAUS_COEF1, id);
 
 	value = arg->local_gainscale << 24 | (arg->global_gain_alpha & 0xf) << 16 |
 		(arg->global_gain & 0x3ff);
-	isp3_param_write(params_vdev, value, ISP32_SHARP_GAIN);
+	isp3_param_write(params_vdev, value, ISP32_SHARP_GAIN, id);
 
 	for (i = 0; i < ISP32_SHARP_GAIN_ADJ_NUM / 2; i++) {
 		value = ISP_PACK_2SHORT(arg->gain_adj[i * 2], arg->gain_adj[i * 2 + 1]);
-		isp3_param_write(params_vdev, value, ISP32_SHARP_GAIN_ADJUST0 + i * 4);
+		isp3_param_write(params_vdev, value, ISP32_SHARP_GAIN_ADJUST0 + i * 4, id);
 	}
 
 	value = ISP_PACK_2SHORT(arg->center_wid, arg->center_het);
-	isp3_param_write(params_vdev, value, ISP32_SHARP_CENTER);
+	isp3_param_write(params_vdev, value, ISP32_SHARP_CENTER, id);
 
 	for (i = 0; i < ISP32_SHARP_STRENGTH_NUM / 4; i++) {
 		value = ISP_PACK_4BYTE(arg->strength[i * 4], arg->strength[i * 4 + 1],
 				       arg->strength[i * 4 + 2], arg->strength[i * 4 + 3]);
-		isp3_param_write(params_vdev, value, ISP32_SHARP_GAIN_DIS_STRENGTH0 + i * 4);
+		isp3_param_write(params_vdev, value, ISP32_SHARP_GAIN_DIS_STRENGTH0 + i * 4, id);
 	}
 	value = ISP_PACK_4BYTE(arg->strength[i * 4], arg->strength[i * 4 + 1], 0, 0);
-	isp3_param_write(params_vdev, value, ISP32_SHARP_GAIN_DIS_STRENGTH0 + i * 4);
+	isp3_param_write(params_vdev, value, ISP32_SHARP_GAIN_DIS_STRENGTH0 + i * 4, id);
 
 	if (params_vdev->dev->isp_ver == ISP_V32) {
 		value = (arg->noise_strength & 0x3fff) << 16 | (arg->enhance_bit & 0xf) << 12 |
 			(arg->noise_sigma & 0x3ff);
-		isp3_param_write(params_vdev, value, ISP32_SHARP_TEXTURE);
+		isp3_param_write(params_vdev, value, ISP32_SHARP_TEXTURE, id);
 	} else {
 		value = (arg->ehf_th[2] & 0x3FF) << 20 |
 			(arg->ehf_th[1] & 0x3FF) << 10 |
 			(arg->ehf_th[0] & 0x3FF);
-		isp3_param_write(params_vdev, value, ISP3X_SHARP_EHF_TH_0);
+		isp3_param_write(params_vdev, value, ISP3X_SHARP_EHF_TH_0, id);
 		value = (arg->ehf_th[5] & 0x3FF) << 20 |
 			(arg->ehf_th[4] & 0x3FF) << 10 |
 			(arg->ehf_th[3] & 0x3FF);
-		isp3_param_write(params_vdev, value, ISP3X_SHARP_EHF_TH_1);
+		isp3_param_write(params_vdev, value, ISP3X_SHARP_EHF_TH_1, id);
 		value = (arg->ehf_th[7] & 0x3FF) << 10 |
 			(arg->ehf_th[6] & 0x3FF);
-		isp3_param_write(params_vdev, value, ISP3X_SHARP_EHF_TH_2);
+		isp3_param_write(params_vdev, value, ISP3X_SHARP_EHF_TH_2, id);
 
 		value = (arg->clip_neg[2] & 0x3FF) << 20 |
 			(arg->clip_neg[1] & 0x3FF) << 10 |
 			(arg->clip_neg[0] & 0x3FF);
-		isp3_param_write(params_vdev, value, ISP32L_SHARP_CLIP_NEG_0);
+		isp3_param_write(params_vdev, value, ISP32L_SHARP_CLIP_NEG_0, id);
 		value = (arg->clip_neg[5] & 0x3FF) << 20 |
 			(arg->clip_neg[4] & 0x3FF) << 10 |
 			(arg->clip_neg[3] & 0x3FF);
-		isp3_param_write(params_vdev, value, ISP32L_SHARP_CLIP_NEG_1);
+		isp3_param_write(params_vdev, value, ISP32L_SHARP_CLIP_NEG_1, id);
 		value = (arg->clip_neg[7] & 0x3FF) << 10 |
 			(arg->clip_neg[6] & 0x3FF);
-		isp3_param_write(params_vdev, value, ISP32L_SHARP_CLIP_NEG_2);
+		isp3_param_write(params_vdev, value, ISP32L_SHARP_CLIP_NEG_2, id);
 	}
 }
 
 static void
-isp_sharp_enable(struct rkisp_isp_params_vdev *params_vdev, bool en)
+isp_sharp_enable(struct rkisp_isp_params_vdev *params_vdev, bool en, u32 id)
 {
 	u32 value;
 
-	value = isp3_param_read_cache(params_vdev, ISP3X_SHARP_EN);
+	value = isp3_param_read_cache(params_vdev, ISP3X_SHARP_EN, id);
+	if ((en && (value & ISP32_MODULE_EN)) ||
+	    (!en && !(value & ISP32_MODULE_EN)))
+		return;
+
 	value &= ~ISP32_MODULE_EN;
-
-	if (en)
+	if (en) {
+		isp3_param_set_bits(params_vdev,
+			ISP3X_ISP_CTRL1, ISP32_SHP_FST_FRAME, id);
 		value |= ISP32_MODULE_EN;
-
-	isp3_param_write(params_vdev, value, ISP3X_SHARP_EN);
+	}
+	isp3_param_write(params_vdev, value, ISP3X_SHARP_EN, id);
 }
 
 static void
 isp_baynr_config(struct rkisp_isp_params_vdev *params_vdev,
-		 const struct isp32_baynr_cfg *arg)
+		 const struct isp32_baynr_cfg *arg, u32 id)
 {
 	u32 i, value;
 
-	value = isp3_param_read(params_vdev, ISP3X_BAYNR_CTRL);
+	value = isp3_param_read(params_vdev, ISP3X_BAYNR_CTRL, id);
 	value &= ISP32_MODULE_EN;
 
 	value |= !!arg->bay3d_gain_en << 16 |
 		 (arg->lg2_mode & 0x3) << 12 |
 		 !!arg->gauss_en << 8 |
 		 !!arg->log_bypass << 4;
-	isp3_param_write(params_vdev, value, ISP3X_BAYNR_CTRL);
+	isp3_param_write(params_vdev, value, ISP3X_BAYNR_CTRL, id);
 
 	value = ISP_PACK_2SHORT(arg->dgain0, arg->dgain1);
-	isp3_param_write(params_vdev, value, ISP3X_BAYNR_DGAIN0);
+	isp3_param_write(params_vdev, value, ISP3X_BAYNR_DGAIN0, id);
 
-	isp3_param_write(params_vdev, arg->dgain2, ISP3X_BAYNR_DGAIN1);
-	isp3_param_write(params_vdev, arg->pix_diff, ISP3X_BAYNR_PIXDIFF);
+	isp3_param_write(params_vdev, arg->dgain2, ISP3X_BAYNR_DGAIN1, id);
+	isp3_param_write(params_vdev, arg->pix_diff, ISP3X_BAYNR_PIXDIFF, id);
 
 	value = ISP_PACK_2SHORT(arg->softthld, arg->diff_thld);
-	isp3_param_write(params_vdev, value, ISP3X_BAYNR_THLD);
+	isp3_param_write(params_vdev, value, ISP3X_BAYNR_THLD, id);
 
 	value = ISP_PACK_2SHORT(arg->reg_w1, arg->bltflt_streng);
-	isp3_param_write(params_vdev, value, ISP3X_BAYNR_W1_STRENG);
+	isp3_param_write(params_vdev, value, ISP3X_BAYNR_W1_STRENG, id);
 
 	for (i = 0; i < ISP32_BAYNR_XY_NUM / 2; i++) {
 		value = ISP_PACK_2SHORT(arg->sigma_x[2 * i], arg->sigma_x[2 * i + 1]);
-		isp3_param_write(params_vdev, value, ISP3X_BAYNR_SIGMAX01 + 4 * i);
+		isp3_param_write(params_vdev, value, ISP3X_BAYNR_SIGMAX01 + 4 * i, id);
 	}
 
 	for (i = 0; i < ISP32_BAYNR_XY_NUM / 2; i++) {
 		value = ISP_PACK_2SHORT(arg->sigma_y[2 * i], arg->sigma_y[2 * i + 1]);
-		isp3_param_write(params_vdev, value, ISP3X_BAYNR_SIGMAY01 + 4 * i);
+		isp3_param_write(params_vdev, value, ISP3X_BAYNR_SIGMAY01 + 4 * i, id);
 	}
 
 	value = (arg->weit_d2 & 0x3FF) << 20 |
 		(arg->weit_d1 & 0x3FF) << 10 |
 		(arg->weit_d0 & 0x3FF);
-	isp3_param_write(params_vdev, value, ISP3X_BAYNR_WRIT_D);
+	isp3_param_write(params_vdev, value, ISP3X_BAYNR_WRIT_D, id);
 
 	value = ISP_PACK_2SHORT(arg->lg2_off, arg->lg2_lgoff);
-	isp3_param_write(params_vdev, value, ISP3X_BAYNR_LG_OFF);
+	isp3_param_write(params_vdev, value, ISP3X_BAYNR_LG_OFF, id);
 
 	value = arg->dat_max & 0xfffff;
-	isp3_param_write(params_vdev, value, ISP3X_BAYNR_DAT_MAX);
+	isp3_param_write(params_vdev, value, ISP3X_BAYNR_DAT_MAX, id);
 
 	value = ISP_PACK_2SHORT(arg->rgain_off, arg->bgain_off);
-	isp3_param_write(params_vdev, value, ISP32_BAYNR_SIGOFF);
+	isp3_param_write(params_vdev, value, ISP32_BAYNR_SIGOFF, id);
 
 	for (i = 0; i < ISP32_BAYNR_GAIN_NUM / 4; i++) {
 		value = ISP_PACK_4BYTE(arg->gain_x[i * 4], arg->gain_x[i * 4 + 1],
 				       arg->gain_x[i * 4 + 2], arg->gain_x[i * 4 + 3]);
-		isp3_param_write(params_vdev, value, ISP32_BAYNR_GAINX03 + i * 4);
+		isp3_param_write(params_vdev, value, ISP32_BAYNR_GAINX03 + i * 4, id);
 	}
 
 	for (i = 0; i < ISP32_BAYNR_GAIN_NUM / 2; i++) {
 		value = ISP_PACK_2SHORT(arg->gain_y[i * 2], arg->gain_y[i * 2 + 1]);
-		isp3_param_write(params_vdev, value, ISP32_BAYNR_GAINY01 + i * 4);
+		isp3_param_write(params_vdev, value, ISP32_BAYNR_GAINY01 + i * 4, id);
 	}
 }
 
 static void
-isp_baynr_enable(struct rkisp_isp_params_vdev *params_vdev, bool en)
+isp_baynr_enable(struct rkisp_isp_params_vdev *params_vdev, bool en, u32 id)
 {
 	u32 value;
 
-	value = isp3_param_read_cache(params_vdev, ISP3X_BAYNR_CTRL);
+	value = isp3_param_read_cache(params_vdev, ISP3X_BAYNR_CTRL, id);
 	value &= ~ISP32_MODULE_EN;
 
 	if (en)
 		value |= ISP32_MODULE_EN;
 
-	isp3_param_write(params_vdev, value, ISP3X_BAYNR_CTRL);
+	isp3_param_write(params_vdev, value, ISP3X_BAYNR_CTRL, id);
 }
 
 static void
 isp_bay3d_config(struct rkisp_isp_params_vdev *params_vdev,
-		 const struct isp32_bay3d_cfg *arg)
+		 const struct isp32_bay3d_cfg *arg, u32 id)
 {
 	struct rkisp_isp_params_val_v32 *priv_val;
 	u32 i, value;
 
 	priv_val = (struct rkisp_isp_params_val_v32 *)params_vdev->priv_val;
-	value = isp3_param_read(params_vdev, ISP3X_BAY3D_CTRL);
+	value = isp3_param_read(params_vdev, ISP3X_BAY3D_CTRL, id);
 	value &= (ISP32_MODULE_EN | ISP32_BAY3D_BWSAVING(1));
 
 	value |= !!arg->loswitch_protect << 12 |
@@ -3478,7 +3561,7 @@
 			  "bwsaving to %d no support change for bay3d en\n",
 			  arg->bwsaving_en);
 	}
-	isp3_param_write(params_vdev, value, ISP3X_BAY3D_CTRL);
+	isp3_param_write(params_vdev, value, ISP3X_BAY3D_CTRL, id);
 
 	value = !!arg->wgtmix_opt_en << 12 |
 		!!arg->curds_high_en << 8 |
@@ -3500,72 +3583,72 @@
 		value &= ~(BIT(3) | BIT(4));
 	else if (!(value & (BIT(3) | BIT(4))))
 		value |= BIT(3);
-	isp3_param_write(params_vdev, value, ISP32_BAY3D_CTRL1);
+	isp3_param_write(params_vdev, value, ISP32_BAY3D_CTRL1, id);
 
 	value = ISP_PACK_2SHORT(arg->softwgt, arg->hidif_th);
-	isp3_param_write(params_vdev, value, ISP3X_BAY3D_KALRATIO);
+	isp3_param_write(params_vdev, value, ISP3X_BAY3D_KALRATIO, id);
 
-	isp3_param_write(params_vdev, arg->glbpk2, ISP3X_BAY3D_GLBPK2);
+	isp3_param_write(params_vdev, arg->glbpk2, ISP3X_BAY3D_GLBPK2, id);
 
 	value = ISP_PACK_2SHORT(arg->wgtlmt, arg->wgtratio);
-	isp3_param_write(params_vdev, value, ISP3X_BAY3D_WGTLMT);
+	isp3_param_write(params_vdev, value, ISP3X_BAY3D_WGTLMT, id);
 
 	for (i = 0; i < ISP32_BAY3D_XY_NUM / 2; i++) {
 		value = ISP_PACK_2SHORT(arg->sig0_x[2 * i],
 					arg->sig0_x[2 * i + 1]);
-		isp3_param_write(params_vdev, value, ISP3X_BAY3D_SIG0_X0 + 4 * i);
+		isp3_param_write(params_vdev, value, ISP3X_BAY3D_SIG0_X0 + 4 * i, id);
 
 		value = ISP_PACK_2SHORT(arg->sig1_x[2 * i],
 					arg->sig1_x[2 * i + 1]);
-		isp3_param_write(params_vdev, value, ISP3X_BAY3D_SIG1_X0 + 4 * i);
+		isp3_param_write(params_vdev, value, ISP3X_BAY3D_SIG1_X0 + 4 * i, id);
 	}
 
 	for (i = 0; i < ISP32_BAY3D_XY_NUM / 2; i++) {
 		value = ISP_PACK_2SHORT(arg->sig0_y[2 * i],
 					arg->sig0_y[2 * i + 1]);
-		isp3_param_write(params_vdev, value, ISP3X_BAY3D_SIG0_Y0 + 4 * i);
+		isp3_param_write(params_vdev, value, ISP3X_BAY3D_SIG0_Y0 + 4 * i, id);
 
 		value = ISP_PACK_2SHORT(arg->sig1_y[2 * i],
 					arg->sig1_y[2 * i + 1]);
-		isp3_param_write(params_vdev, value, ISP3X_BAY3D_SIG1_Y0 + 4 * i);
+		isp3_param_write(params_vdev, value, ISP3X_BAY3D_SIG1_Y0 + 4 * i, id);
 
 		value = ISP_PACK_2SHORT(arg->sig2_y[2 * i],
 					arg->sig2_y[2 * i + 1]);
-		isp3_param_write(params_vdev, value, ISP3X_BAY3D_SIG2_Y0 + 4 * i);
+		isp3_param_write(params_vdev, value, ISP3X_BAY3D_SIG2_Y0 + 4 * i, id);
 	}
 
 	if (params_vdev->dev->isp_ver == ISP_V32_L) {
 		value = ISP_PACK_2SHORT(0, arg->wgtmin);
-		isp3_param_write(params_vdev, value, ISP3X_BAY3D_LODIF_STAT1);
+		isp3_param_write(params_vdev, value, ISP3X_BAY3D_LODIF_STAT1, id);
 	}
 
 	value = ISP_PACK_2SHORT(arg->hisigrat0, arg->hisigrat1);
-	isp3_param_write(params_vdev, value, ISP32_BAY3D_HISIGRAT);
+	isp3_param_write(params_vdev, value, ISP32_BAY3D_HISIGRAT, id);
 
 	value = ISP_PACK_2SHORT(arg->hisigoff0, arg->hisigoff1);
-	isp3_param_write(params_vdev, value, ISP32_BAY3D_HISIGOFF);
+	isp3_param_write(params_vdev, value, ISP32_BAY3D_HISIGOFF, id);
 
 	value = ISP_PACK_2SHORT(arg->losigoff, arg->losigrat);
-	isp3_param_write(params_vdev, value, ISP32_BAY3D_LOSIG);
+	isp3_param_write(params_vdev, value, ISP32_BAY3D_LOSIG, id);
 
 	value = ISP_PACK_2SHORT(arg->rgain_off, arg->bgain_off);
-	isp3_param_write(params_vdev, value, ISP32_BAY3D_SIGPK);
+	isp3_param_write(params_vdev, value, ISP32_BAY3D_SIGPK, id);
 
 	value = ISP_PACK_4BYTE(arg->siggaus0, arg->siggaus1, arg->siggaus2, 0);
 	if (params_vdev->dev->isp_ver == ISP_V32)
 		value |= (arg->siggaus3 << 24);
-	isp3_param_write(params_vdev, value, ISP32_BAY3D_SIGGAUS);
+	isp3_param_write(params_vdev, value, ISP32_BAY3D_SIGGAUS, id);
 }
 
 static void
-isp_bay3d_enable(struct rkisp_isp_params_vdev *params_vdev, bool en)
+isp_bay3d_enable(struct rkisp_isp_params_vdev *params_vdev, bool en, u32 id)
 {
 	struct rkisp_device *ispdev = params_vdev->dev;
 	struct rkisp_isp_params_val_v32 *priv_val;
 	u32 value, bay3d_ctrl;
 
 	priv_val = (struct rkisp_isp_params_val_v32 *)params_vdev->priv_val;
-	bay3d_ctrl = isp3_param_read_cache(params_vdev, ISP3X_BAY3D_CTRL);
+	bay3d_ctrl = isp3_param_read_cache(params_vdev, ISP3X_BAY3D_CTRL, id);
 	if ((en && (bay3d_ctrl & ISP32_MODULE_EN)) ||
 	    (!en && !(bay3d_ctrl & ISP32_MODULE_EN)))
 		return;
@@ -3576,85 +3659,85 @@
 			return;
 		}
 
-		value = priv_val->buf_3dnr_iir.size;
-		isp3_param_write(params_vdev, value, ISP3X_MI_BAY3D_IIR_WR_SIZE);
-		value = priv_val->buf_3dnr_iir.dma_addr;
-		isp3_param_write(params_vdev, value, ISP3X_MI_BAY3D_IIR_WR_BASE);
-		isp3_param_write(params_vdev, value, ISP3X_MI_BAY3D_IIR_RD_BASE);
+		value = priv_val->bay3d_iir_size;
+		isp3_param_write(params_vdev, value, ISP3X_MI_BAY3D_IIR_WR_SIZE, id);
+		value = priv_val->buf_3dnr_iir.dma_addr + value * id;
+		isp3_param_write(params_vdev, value, ISP3X_MI_BAY3D_IIR_WR_BASE, id);
+		isp3_param_write(params_vdev, value, ISP3X_MI_BAY3D_IIR_RD_BASE, id);
 
-		value = priv_val->buf_3dnr_ds.size;
-		isp3_param_write(params_vdev, value, ISP3X_MI_BAY3D_DS_WR_SIZE);
-		value = priv_val->buf_3dnr_ds.dma_addr;
-		isp3_param_write(params_vdev, value, ISP3X_MI_BAY3D_DS_WR_BASE);
-		isp3_param_write(params_vdev, value, ISP3X_MI_BAY3D_DS_RD_BASE);
+		value = priv_val->bay3d_ds_size;
+		isp3_param_write(params_vdev, value, ISP3X_MI_BAY3D_DS_WR_SIZE, id);
+		value = priv_val->buf_3dnr_ds.dma_addr + value * id;
+		isp3_param_write(params_vdev, value, ISP3X_MI_BAY3D_DS_WR_BASE, id);
+		isp3_param_write(params_vdev, value, ISP3X_MI_BAY3D_DS_RD_BASE, id);
 
 		value = priv_val->is_sram ?
 			ispdev->hw_dev->sram.dma_addr : priv_val->buf_3dnr_cur.dma_addr;
-		isp3_param_write(params_vdev, value, ISP3X_MI_BAY3D_CUR_WR_BASE);
-		isp3_param_write(params_vdev, value, ISP3X_MI_BAY3D_CUR_RD_BASE);
+		isp3_param_write(params_vdev, value, ISP3X_MI_BAY3D_CUR_WR_BASE, id);
+		isp3_param_write(params_vdev, value, ISP3X_MI_BAY3D_CUR_RD_BASE, id);
 		value = priv_val->bay3d_cur_size;
-		isp3_param_write(params_vdev, value, ISP3X_MI_BAY3D_CUR_WR_SIZE);
-		isp3_param_write(params_vdev, value, ISP32_MI_BAY3D_CUR_RD_SIZE);
+		isp3_param_write(params_vdev, value, ISP3X_MI_BAY3D_CUR_WR_SIZE, id);
+		isp3_param_write(params_vdev, value, ISP32_MI_BAY3D_CUR_RD_SIZE, id);
 		value = priv_val->bay3d_cur_wsize;
-		isp3_param_write(params_vdev, value, ISP3X_MI_BAY3D_CUR_WR_LENGTH);
-		isp3_param_write(params_vdev, value, ISP3X_MI_BAY3D_CUR_RD_LENGTH);
+		isp3_param_write(params_vdev, value, ISP3X_MI_BAY3D_CUR_WR_LENGTH, id);
+		isp3_param_write(params_vdev, value, ISP3X_MI_BAY3D_CUR_RD_LENGTH, id);
 		value = priv_val->bay3d_cur_wrap_line << 16 |
 			(ispdev->isp_ver == ISP_V32 ? 28 : 20);
-		isp3_param_write(params_vdev, value, ISP3X_BAY3D_MI_ST);
+		isp3_param_write(params_vdev, value, ISP3X_BAY3D_MI_ST, id);
 
 		/* mibuf_size for fifo_cur_full, set to max: (3072 - 2) / 2, 2 align */
 		if (ispdev->isp_ver == ISP_V32) {
 			value = 0x5fe << 16;
-			isp3_param_set_bits(params_vdev, ISP3X_BAY3D_IN_IRQ_LINECNT, value);
+			isp3_param_set_bits(params_vdev, ISP3X_BAY3D_IN_IRQ_LINECNT, value, id);
 		}
 
-		value = isp3_param_read_cache(params_vdev, ISP32_BAY3D_CTRL1);
+		value = isp3_param_read_cache(params_vdev, ISP32_BAY3D_CTRL1, id);
 		if (priv_val->is_lo8x8) {
 			if (value & (BIT(3) | BIT(4))) {
 				value &= ~(BIT(3) | BIT(4));
-				isp3_param_write(params_vdev, value, ISP32_BAY3D_CTRL1);
+				isp3_param_write(params_vdev, value, ISP32_BAY3D_CTRL1, id);
 			}
 		} else if (!(value & (BIT(3) | BIT(4)))) {
 			value |= BIT(3);
-			isp3_param_write(params_vdev, value, ISP32_BAY3D_CTRL1);
+			isp3_param_write(params_vdev, value, ISP32_BAY3D_CTRL1, id);
 		}
 		bay3d_ctrl |= ISP32_MODULE_EN;
-		isp3_param_write(params_vdev, bay3d_ctrl, ISP3X_BAY3D_CTRL);
+		isp3_param_write(params_vdev, bay3d_ctrl, ISP3X_BAY3D_CTRL, id);
 
 		value = ISP3X_BAY3D_IIR_WR_AUTO_UPD | ISP3X_BAY3D_CUR_WR_AUTO_UPD |
 			ISP3X_BAY3D_DS_WR_AUTO_UPD | ISP3X_BAY3D_IIRSELF_UPD |
 			ISP3X_BAY3D_CURSELF_UPD | ISP3X_BAY3D_DSSELF_UPD |
 			ISP3X_BAY3D_RDSELF_UPD;
-		isp3_param_set_bits(params_vdev, MI_WR_CTRL2, value);
+		isp3_param_set_bits(params_vdev, MI_WR_CTRL2, value, id);
 
-		isp3_param_set_bits(params_vdev, ISP3X_ISP_CTRL1, ISP3X_RAW3D_FST_FRAME);
+		isp3_param_set_bits(params_vdev, ISP3X_ISP_CTRL1, ISP3X_RAW3D_FST_FRAME, id);
 	} else {
 		bay3d_ctrl &= ~ISP32_MODULE_EN;
-		isp3_param_write(params_vdev, bay3d_ctrl, ISP3X_BAY3D_CTRL);
-		isp3_param_clear_bits(params_vdev, ISP3X_GAIN_CTRL, BIT(4));
+		isp3_param_write(params_vdev, bay3d_ctrl, ISP3X_BAY3D_CTRL, id);
+		isp3_param_clear_bits(params_vdev, ISP3X_GAIN_CTRL, BIT(4), id);
 	}
 }
 
 static void
 isp_gain_config(struct rkisp_isp_params_vdev *params_vdev,
-		const struct isp3x_gain_cfg *arg)
+		const struct isp3x_gain_cfg *arg, u32 id)
 {
 	u32 val;
 
 	val = arg->g0 & 0x3ffff;
-	isp3_param_write(params_vdev, val, ISP3X_GAIN_G0);
+	isp3_param_write(params_vdev, val, ISP3X_GAIN_G0, id);
 	val = ISP_PACK_2SHORT(arg->g1, arg->g2);
-	isp3_param_write(params_vdev, val, ISP3X_GAIN_G1_G2);
+	isp3_param_write(params_vdev, val, ISP3X_GAIN_G1_G2, id);
 }
 
 static void
-isp_gain_enable(struct rkisp_isp_params_vdev *params_vdev, bool en)
+isp_gain_enable(struct rkisp_isp_params_vdev *params_vdev, bool en, u32 id)
 {
 	struct rkisp_isp_params_val_v32 *priv_val =
 		(struct rkisp_isp_params_val_v32 *)params_vdev->priv_val;
 	u32 val = 0;
 
-	val = isp3_param_read_cache(params_vdev, ISP3X_GAIN_CTRL);
+	val = isp3_param_read_cache(params_vdev, ISP3X_GAIN_CTRL, id);
 	if (en) {
 		if (params_vdev->dev->isp_ver == ISP_V32) {
 			val |= priv_val->lut3d_en << 20 |
@@ -3662,7 +3745,7 @@
 				priv_val->drc_en << 12 |
 				priv_val->lsc_en << 8 |
 				priv_val->bay3d_en << 4;
-			if (isp3_param_read(params_vdev, ISP3X_HDRMGE_CTRL) & BIT(0))
+			if (isp3_param_read(params_vdev, ISP3X_HDRMGE_CTRL, id) & BIT(0))
 				val |= BIT(1);
 			if (val)
 				val |= ISP32_MODULE_EN;
@@ -3670,12 +3753,12 @@
 			val |= ISP32_MODULE_EN;
 		}
 	}
-	isp3_param_write(params_vdev, val, ISP3X_GAIN_CTRL);
+	isp3_param_write(params_vdev, val, ISP3X_GAIN_CTRL, id);
 }
 
 static void
 isp_cac_config(struct rkisp_isp_params_vdev *params_vdev,
-	       const struct isp32_cac_cfg *arg)
+	       const struct isp32_cac_cfg *arg, u32 id)
 {
 	struct rkisp_device *dev = params_vdev->dev;
 	struct rkisp_isp_params_val_v32 *priv_val;
@@ -3684,7 +3767,7 @@
 
 	priv_val = (struct rkisp_isp_params_val_v32 *)params_vdev->priv_val;
 
-	ctrl = isp3_param_read(params_vdev, ISP3X_CAC_CTRL);
+	ctrl = isp3_param_read(params_vdev, ISP3X_CAC_CTRL, id);
 	ctrl &= ISP3X_CAC_EN;
 	ctrl |= !!arg->bypass_en << 1 | !!arg->center_en << 3 |
 		(arg->clip_g_mode & 0x3) << 5 | !!arg->edge_detect_en << 7 |
@@ -3692,38 +3775,38 @@
 
 	val = (arg->psf_sft_bit & 0xff) |
 		(arg->cfg_num & 0x7ff) << 8;
-	isp3_param_write(params_vdev, val, ISP3X_CAC_PSF_PARA);
+	isp3_param_write(params_vdev, val, ISP3X_CAC_PSF_PARA, id);
 
 	val = ISP_PACK_2SHORT(arg->center_width, arg->center_height);
-	isp3_param_write(params_vdev, val, ISP3X_CAC_STRENGTH_CENTER);
+	isp3_param_write(params_vdev, val, ISP3X_CAC_STRENGTH_CENTER, id);
 
 	for (i = 0; i < ISP32_CAC_STRENGTH_NUM / 2; i++) {
 		val = ISP_PACK_2SHORT(arg->strength[2 * i], arg->strength[2 * i + 1]);
-		isp3_param_write(params_vdev, val, ISP3X_CAC_STRENGTH0 + i * 4);
+		isp3_param_write(params_vdev, val, ISP3X_CAC_STRENGTH0 + i * 4, id);
 	}
 
 	val = (arg->flat_thed_r & 0x1f) << 8 | (arg->flat_thed_b & 0x1f);
-	isp3_param_write(params_vdev, val, ISP32_CAC_FLAT_THED);
+	isp3_param_write(params_vdev, val, ISP32_CAC_FLAT_THED, id);
 
 	val = ISP_PACK_2SHORT(arg->offset_b, arg->offset_r);
-	isp3_param_write(params_vdev, val, ISP32_CAC_OFFSET);
+	isp3_param_write(params_vdev, val, ISP32_CAC_OFFSET, id);
 
 	val = arg->expo_thed_b & 0x1fffff;
-	isp3_param_write(params_vdev, val, ISP32_CAC_EXPO_THED_B);
+	isp3_param_write(params_vdev, val, ISP32_CAC_EXPO_THED_B, id);
 
 	val = arg->expo_thed_r & 0x1fffff;
-	isp3_param_write(params_vdev, val, ISP32_CAC_EXPO_THED_R);
+	isp3_param_write(params_vdev, val, ISP32_CAC_EXPO_THED_R, id);
 
 	val = arg->expo_adj_b & 0xfffff;
-	isp3_param_write(params_vdev, val, ISP32_CAC_EXPO_ADJ_B);
+	isp3_param_write(params_vdev, val, ISP32_CAC_EXPO_ADJ_B, id);
 
 	val = arg->expo_adj_r & 0xfffff;
-	isp3_param_write(params_vdev, val, ISP32_CAC_EXPO_ADJ_R);
+	isp3_param_write(params_vdev, val, ISP32_CAC_EXPO_ADJ_R, id);
 
 	for (i = 0; i < ISP32_MESH_BUF_NUM; i++) {
-		if (!priv_val->buf_cac[i].mem_priv)
+		if (!priv_val->buf_cac[id][i].mem_priv)
 			continue;
-		if (arg->buf_fd == priv_val->buf_cac[i].dma_fd)
+		if (arg->buf_fd == priv_val->buf_cac[id][i].dma_fd)
 			break;
 	}
 
@@ -3732,44 +3815,53 @@
 		return;
 	}
 
-	if (!priv_val->buf_cac[i].vaddr) {
+	if (!priv_val->buf_cac[id][i].vaddr) {
 		dev_err(dev->dev, "no cac buffer allocated\n");
 		return;
 	}
 
-	val = priv_val->buf_cac_idx;
-	head = (struct isp2x_mesh_head *)priv_val->buf_cac[val].vaddr;
+	val = priv_val->buf_cac_idx[id];
+	head = (struct isp2x_mesh_head *)priv_val->buf_cac[id][val].vaddr;
 	head->stat = MESH_BUF_INIT;
 
-	head = (struct isp2x_mesh_head *)priv_val->buf_cac[i].vaddr;
+	head = (struct isp2x_mesh_head *)priv_val->buf_cac[id][i].vaddr;
 	head->stat = MESH_BUF_CHIPINUSE;
-	priv_val->buf_cac_idx = i;
-	rkisp_prepare_buffer(dev, &priv_val->buf_cac[i]);
-	val = priv_val->buf_cac[i].dma_addr + head->data_oft;
-	isp3_param_write(params_vdev, val, ISP3X_MI_LUT_CAC_RD_BASE);
-	isp3_param_write(params_vdev, arg->hsize, ISP3X_MI_LUT_CAC_RD_H_WSIZE);
-	isp3_param_write(params_vdev, arg->vsize, ISP3X_MI_LUT_CAC_RD_V_SIZE);
+	priv_val->buf_cac_idx[id] = i;
+	rkisp_prepare_buffer(dev, &priv_val->buf_cac[id][i]);
+	val = priv_val->buf_cac[id][i].dma_addr + head->data_oft;
+	isp3_param_write(params_vdev, val, ISP3X_MI_LUT_CAC_RD_BASE, id);
+	isp3_param_write(params_vdev, arg->hsize, ISP3X_MI_LUT_CAC_RD_H_WSIZE, id);
+	isp3_param_write(params_vdev, arg->vsize, ISP3X_MI_LUT_CAC_RD_V_SIZE, id);
 	if (ctrl & ISP3X_CAC_EN)
 		ctrl |= ISP3X_CAC_LUT_EN | ISP32_SELF_FORCE_UPD | ISP3X_CAC_LUT_MODE(3);
-	isp3_param_write(params_vdev, ctrl, ISP3X_CAC_CTRL);
+	isp3_param_write(params_vdev, ctrl, ISP3X_CAC_CTRL, id);
 }
 
 static void
-isp_cac_enable(struct rkisp_isp_params_vdev *params_vdev, bool en)
+isp_cac_enable(struct rkisp_isp_params_vdev *params_vdev, bool en, u32 id)
 {
+	struct rkisp_device *dev = params_vdev->dev;
+	struct rkisp_isp_params_val_v32 *priv_val;
 	u32 val;
 
-	val = isp3_param_read(params_vdev, ISP3X_CAC_CTRL);
+	priv_val = (struct rkisp_isp_params_val_v32 *)params_vdev->priv_val;
+	val = priv_val->buf_cac_idx[id];
+	if (en && !priv_val->buf_cac[id][val].vaddr) {
+		dev_err(dev->dev, "no cac buffer allocated\n");
+		return;
+	}
+
+	val = isp3_param_read(params_vdev, ISP3X_CAC_CTRL, id);
 	val &= ~(ISP3X_CAC_EN | ISP3X_CAC_LUT_EN | ISP32_SELF_FORCE_UPD);
 	if (en)
 		val |= ISP3X_CAC_EN | ISP3X_CAC_LUT_EN |
 		       ISP32_SELF_FORCE_UPD | ISP3X_CAC_LUT_MODE(3);
-	isp3_param_write(params_vdev, val, ISP3X_CAC_CTRL);
+	isp3_param_write(params_vdev, val, ISP3X_CAC_CTRL, id);
 }
 
 static void
 isp_csm_config(struct rkisp_isp_params_vdev *params_vdev,
-	       const struct isp21_csm_cfg *arg)
+	       const struct isp21_csm_cfg *arg, u32 id)
 {
 	u32 i, val;
 
@@ -3780,19 +3872,19 @@
 			      (arg->csm_coeff[i] & 0x1ff);
 		else
 			val = arg->csm_coeff[i] & 0x1ff;
-		isp3_param_write(params_vdev, val, ISP3X_ISP_CC_COEFF_0 + i * 4);
+		isp3_param_write(params_vdev, val, ISP3X_ISP_CC_COEFF_0 + i * 4, id);
 	}
 
-	val = isp3_param_read_cache(params_vdev, ISP3X_ISP_CTRL0);
+	val = isp3_param_read_cache(params_vdev, ISP3X_ISP_CTRL0, id);
 	val |= CIF_ISP_CTRL_ISP_CSM_Y_FULL_ENA | CIF_ISP_CTRL_ISP_CSM_C_FULL_ENA;
-	isp3_param_write(params_vdev, val, ISP3X_ISP_CTRL0);
+	isp3_param_write(params_vdev, val, ISP3X_ISP_CTRL0, id);
 }
 
 static void
 isp_cgc_config(struct rkisp_isp_params_vdev *params_vdev,
-	       const struct isp21_cgc_cfg *arg)
+	       const struct isp21_cgc_cfg *arg, u32 id)
 {
-	u32 val = isp3_param_read_cache(params_vdev, ISP3X_ISP_CTRL0);
+	u32 val = isp3_param_read_cache(params_vdev, ISP3X_ISP_CTRL0, id);
 	u32 eff_ctrl, cproc_ctrl;
 
 	params_vdev->quantization = V4L2_QUANTIZATION_FULL_RANGE;
@@ -3803,31 +3895,31 @@
 	}
 	if (arg->ratio_en)
 		val |= ISP3X_SW_CGC_RATIO_EN;
-	isp3_param_write(params_vdev, val, ISP3X_ISP_CTRL0);
+	isp3_param_write(params_vdev, val, ISP3X_ISP_CTRL0, id);
 
-	cproc_ctrl = isp3_param_read(params_vdev, ISP3X_CPROC_CTRL);
+	cproc_ctrl = isp3_param_read(params_vdev, ISP3X_CPROC_CTRL, id);
 	if (cproc_ctrl & CIF_C_PROC_CTR_ENABLE) {
 		val = CIF_C_PROC_YOUT_FULL | CIF_C_PROC_YIN_FULL | CIF_C_PROC_COUT_FULL;
 		if (arg->yuv_limit)
 			cproc_ctrl &= ~val;
 		else
 			cproc_ctrl |= val;
-		isp3_param_write(params_vdev, cproc_ctrl, ISP3X_CPROC_CTRL);
+		isp3_param_write(params_vdev, cproc_ctrl, ISP3X_CPROC_CTRL, id);
 	}
 
-	eff_ctrl = isp3_param_read(params_vdev, ISP3X_IMG_EFF_CTRL);
+	eff_ctrl = isp3_param_read(params_vdev, ISP3X_IMG_EFF_CTRL, id);
 	if (eff_ctrl & CIF_IMG_EFF_CTRL_ENABLE) {
 		if (arg->yuv_limit)
 			eff_ctrl &= ~CIF_IMG_EFF_CTRL_YCBCR_FULL;
 		else
 			eff_ctrl |= CIF_IMG_EFF_CTRL_YCBCR_FULL;
-		isp3_param_write(params_vdev, eff_ctrl, ISP3X_IMG_EFF_CTRL);
+		isp3_param_write(params_vdev, eff_ctrl, ISP3X_IMG_EFF_CTRL, id);
 	}
 }
 
 static void
 isp_vsm_config(struct rkisp_isp_params_vdev *params_vdev,
-	       const struct isp32_vsm_cfg *arg)
+	       const struct isp32_vsm_cfg *arg, u32 id)
 {
 	struct rkisp_device *ispdev = params_vdev->dev;
 	struct v4l2_rect *out_crop = &ispdev->isp_sdev.out_crop;
@@ -3836,38 +3928,38 @@
 	u32 val, h, v;
 
 	val = arg->h_offs;
-	isp3_param_write(params_vdev, val, ISP32_VSM_H_OFFS);
+	isp3_param_write(params_vdev, val, ISP32_VSM_H_OFFS, id);
 	val = arg->v_offs;
-	isp3_param_write(params_vdev, val, ISP32_VSM_V_OFFS);
+	isp3_param_write(params_vdev, val, ISP32_VSM_V_OFFS, id);
 
 	h = arg->h_size;
 	if (h > width - arg->h_offs)
 		h = width - arg->h_offs;
 	h &= ~1;
-	isp3_param_write(params_vdev, h, ISP32_VSM_H_SIZE);
+	isp3_param_write(params_vdev, h, ISP32_VSM_H_SIZE, id);
 
 	v = arg->v_size;
 	if (v > height - arg->v_offs)
 		v = height - arg->v_offs;
 	v &= ~1;
-	isp3_param_write(params_vdev, v, ISP32_VSM_V_SIZE);
+	isp3_param_write(params_vdev, v, ISP32_VSM_V_SIZE, id);
 
 	val = arg->h_segments;
 	if (val > (h - 48) / 16)
 		val = (h - 48) / 16;
-	isp3_param_write(params_vdev, val, ISP32_VSM_H_SEGMENTS);
+	isp3_param_write(params_vdev, val, ISP32_VSM_H_SEGMENTS, id);
 
 	val = arg->v_segments;
 	if (val > (v - 48) / 16)
 		val = (v - 48) / 16;
-	isp3_param_write(params_vdev, val, ISP32_VSM_V_SEGMENTS);
+	isp3_param_write(params_vdev, val, ISP32_VSM_V_SEGMENTS, id);
 }
 
 static void
 isp_vsm_enable(struct rkisp_isp_params_vdev *params_vdev,
-	       bool en)
+	       bool en, u32 id)
 {
-	isp3_param_write(params_vdev, en, ISP32_VSM_MODE);
+	isp3_param_write(params_vdev, en, ISP32_VSM_MODE, id);
 }
 
 struct rkisp_isp_params_ops_v32 isp_params_ops_v32 = {
@@ -3946,7 +4038,7 @@
 static __maybe_unused
 void __isp_isr_other_config(struct rkisp_isp_params_vdev *params_vdev,
 			    const struct isp32_isp_params_cfg *new_params,
-			    enum rkisp_params_type type)
+			    enum rkisp_params_type type, u32 id)
 {
 	struct rkisp_isp_params_ops_v32 *ops =
 		(struct rkisp_isp_params_ops_v32 *)params_vdev->priv_ops;
@@ -3958,101 +4050,101 @@
 
 	if (type == RKISP_PARAMS_SHD) {
 		if ((module_cfg_update & ISP32_MODULE_HDRMGE))
-			ops->hdrmge_config(params_vdev, &new_params->others.hdrmge_cfg, type);
+			ops->hdrmge_config(params_vdev, &new_params->others.hdrmge_cfg, type, id);
 
 		if ((module_cfg_update & ISP32_MODULE_DRC))
-			ops->hdrdrc_config(params_vdev, &new_params->others.drc_cfg, type);
+			ops->hdrdrc_config(params_vdev, &new_params->others.drc_cfg, type, id);
 		return;
 	}
 
 	v4l2_dbg(4, rkisp_debug, &params_vdev->dev->v4l2_dev,
-		 "%s seq:%d module_cfg_update:0x%llx\n",
-		 __func__, new_params->frame_id, module_cfg_update);
+		 "%s id:%d seq:%d module_cfg_update:0x%llx\n",
+		 __func__, id, new_params->frame_id, module_cfg_update);
 
 	if (module_cfg_update & ISP32_MODULE_LSC)
-		ops->lsc_config(params_vdev, &new_params->others.lsc_cfg);
+		ops->lsc_config(params_vdev, &new_params->others.lsc_cfg, id);
 
 	if (module_cfg_update & ISP32_MODULE_DPCC)
-		ops->dpcc_config(params_vdev, &new_params->others.dpcc_cfg);
+		ops->dpcc_config(params_vdev, &new_params->others.dpcc_cfg, id);
 
 	if (module_cfg_update & ISP32_MODULE_BLS)
-		ops->bls_config(params_vdev, &new_params->others.bls_cfg);
+		ops->bls_config(params_vdev, &new_params->others.bls_cfg, id);
 
 	if (module_cfg_update & ISP32_MODULE_SDG)
-		ops->sdg_config(params_vdev, &new_params->others.sdg_cfg);
+		ops->sdg_config(params_vdev, &new_params->others.sdg_cfg, id);
 
 	if (module_cfg_update & ISP32_MODULE_AWB_GAIN)
-		ops->awbgain_config(params_vdev, &new_params->others.awb_gain_cfg);
+		ops->awbgain_config(params_vdev, &new_params->others.awb_gain_cfg, id);
 
 	if (module_cfg_update & ISP32_MODULE_DEBAYER)
-		ops->debayer_config(params_vdev, &new_params->others.debayer_cfg);
+		ops->debayer_config(params_vdev, &new_params->others.debayer_cfg, id);
 
 	if (module_cfg_update & ISP32_MODULE_CCM)
-		ops->ccm_config(params_vdev, &new_params->others.ccm_cfg);
+		ops->ccm_config(params_vdev, &new_params->others.ccm_cfg, id);
 
 	if (module_cfg_update & ISP32_MODULE_GOC)
-		ops->goc_config(params_vdev, &new_params->others.gammaout_cfg);
+		ops->goc_config(params_vdev, &new_params->others.gammaout_cfg, id);
 
 	/* range csm->cgc->cproc->ie */
 	if (module_cfg_update & ISP3X_MODULE_CSM)
-		ops->csm_config(params_vdev, &new_params->others.csm_cfg);
+		ops->csm_config(params_vdev, &new_params->others.csm_cfg, id);
 
 	if (module_cfg_update & ISP3X_MODULE_CGC)
-		ops->cgc_config(params_vdev, &new_params->others.cgc_cfg);
+		ops->cgc_config(params_vdev, &new_params->others.cgc_cfg, id);
 
 	if (module_cfg_update & ISP32_MODULE_CPROC)
-		ops->cproc_config(params_vdev, &new_params->others.cproc_cfg);
+		ops->cproc_config(params_vdev, &new_params->others.cproc_cfg, id);
 
 	if (module_cfg_update & ISP32_MODULE_IE)
-		ops->ie_config(params_vdev, &new_params->others.ie_cfg);
+		ops->ie_config(params_vdev, &new_params->others.ie_cfg, id);
 
 	if (module_cfg_update & ISP32_MODULE_HDRMGE)
-		ops->hdrmge_config(params_vdev, &new_params->others.hdrmge_cfg, type);
+		ops->hdrmge_config(params_vdev, &new_params->others.hdrmge_cfg, type, id);
 
 	if (module_cfg_update & ISP32_MODULE_DRC)
-		ops->hdrdrc_config(params_vdev, &new_params->others.drc_cfg, type);
+		ops->hdrdrc_config(params_vdev, &new_params->others.drc_cfg, type, id);
 
 	if (module_cfg_update & ISP32_MODULE_GIC)
-		ops->gic_config(params_vdev, &new_params->others.gic_cfg);
+		ops->gic_config(params_vdev, &new_params->others.gic_cfg, id);
 
 	if (module_cfg_update & ISP32_MODULE_DHAZ)
-		ops->dhaz_config(params_vdev, &new_params->others.dhaz_cfg);
+		ops->dhaz_config(params_vdev, &new_params->others.dhaz_cfg, id);
 
 	if (module_cfg_update & ISP32_MODULE_3DLUT)
-		ops->isp3dlut_config(params_vdev, &new_params->others.isp3dlut_cfg);
+		ops->isp3dlut_config(params_vdev, &new_params->others.isp3dlut_cfg, id);
 
 	if (module_cfg_update & ISP32_MODULE_LDCH)
-		ops->ldch_config(params_vdev, &new_params->others.ldch_cfg);
+		ops->ldch_config(params_vdev, &new_params->others.ldch_cfg, id);
 
 	if (module_cfg_update & ISP32_MODULE_YNR)
-		ops->ynr_config(params_vdev, &new_params->others.ynr_cfg);
+		ops->ynr_config(params_vdev, &new_params->others.ynr_cfg, id);
 
 	if (module_cfg_update & ISP32_MODULE_CNR)
-		ops->cnr_config(params_vdev, &new_params->others.cnr_cfg);
+		ops->cnr_config(params_vdev, &new_params->others.cnr_cfg, id);
 
 	if (module_cfg_update & ISP32_MODULE_SHARP)
-		ops->sharp_config(params_vdev, &new_params->others.sharp_cfg);
+		ops->sharp_config(params_vdev, &new_params->others.sharp_cfg, id);
 
 	if (module_cfg_update & ISP32_MODULE_BAYNR)
-		ops->baynr_config(params_vdev, &new_params->others.baynr_cfg);
+		ops->baynr_config(params_vdev, &new_params->others.baynr_cfg, id);
 
 	if (module_cfg_update & ISP32_MODULE_BAY3D)
-		ops->bay3d_config(params_vdev, &new_params->others.bay3d_cfg);
+		ops->bay3d_config(params_vdev, &new_params->others.bay3d_cfg, id);
 
 	if (module_cfg_update & ISP32_MODULE_CAC)
-		ops->cac_config(params_vdev, &new_params->others.cac_cfg);
+		ops->cac_config(params_vdev, &new_params->others.cac_cfg, id);
 
 	if (module_cfg_update & ISP32_MODULE_GAIN)
-		ops->gain_config(params_vdev, &new_params->others.gain_cfg);
+		ops->gain_config(params_vdev, &new_params->others.gain_cfg, id);
 
 	if (module_cfg_update & ISP32_MODULE_VSM)
-		ops->vsm_config(params_vdev, &new_params->others.vsm_cfg);
+		ops->vsm_config(params_vdev, &new_params->others.vsm_cfg, id);
 }
 
 static __maybe_unused
 void __isp_isr_other_en(struct rkisp_isp_params_vdev *params_vdev,
 			const struct isp32_isp_params_cfg *new_params,
-			enum rkisp_params_type type)
+			enum rkisp_params_type type, u32 id)
 {
 	struct rkisp_isp_params_ops_v32 *ops =
 		(struct rkisp_isp_params_ops_v32 *)params_vdev->priv_ops;
@@ -4070,110 +4162,110 @@
 		return;
 
 	v4l2_dbg(4, rkisp_debug, &params_vdev->dev->v4l2_dev,
-		 "%s seq:%d module_en_update:0x%llx module_ens:0x%llx\n",
-		 __func__, new_params->frame_id, module_en_update, module_ens);
+		 "%s id:%d seq:%d module_en_update:0x%llx module_ens:0x%llx\n",
+		 __func__, id, new_params->frame_id, module_en_update, module_ens);
 
 	if (module_en_update & ISP32_MODULE_DPCC)
-		ops->dpcc_enable(params_vdev, !!(module_ens & ISP32_MODULE_DPCC));
+		ops->dpcc_enable(params_vdev, !!(module_ens & ISP32_MODULE_DPCC), id);
 
 	if (module_en_update & ISP32_MODULE_BLS)
-		ops->bls_enable(params_vdev, !!(module_ens & ISP32_MODULE_BLS));
+		ops->bls_enable(params_vdev, !!(module_ens & ISP32_MODULE_BLS), id);
 
 	if (module_en_update & ISP32_MODULE_SDG)
-		ops->sdg_enable(params_vdev, !!(module_ens & ISP32_MODULE_SDG));
+		ops->sdg_enable(params_vdev, !!(module_ens & ISP32_MODULE_SDG), id);
 
 	if (module_en_update & ISP32_MODULE_LSC) {
-		ops->lsc_enable(params_vdev, !!(module_ens & ISP32_MODULE_LSC));
+		ops->lsc_enable(params_vdev, !!(module_ens & ISP32_MODULE_LSC), id);
 		priv_val->lsc_en = !!(module_ens & ISP32_MODULE_LSC);
 	}
 
 	if (module_en_update & ISP32_MODULE_AWB_GAIN)
-		ops->awbgain_enable(params_vdev, !!(module_ens & ISP32_MODULE_AWB_GAIN));
+		ops->awbgain_enable(params_vdev, !!(module_ens & ISP32_MODULE_AWB_GAIN), id);
 
 	if (module_en_update & ISP32_MODULE_DEBAYER)
-		ops->debayer_enable(params_vdev, !!(module_ens & ISP32_MODULE_DEBAYER));
+		ops->debayer_enable(params_vdev, !!(module_ens & ISP32_MODULE_DEBAYER), id);
 
 	if (module_en_update & ISP32_MODULE_CCM)
-		ops->ccm_enable(params_vdev, !!(module_ens & ISP32_MODULE_CCM));
+		ops->ccm_enable(params_vdev, !!(module_ens & ISP32_MODULE_CCM), id);
 
 	if (module_en_update & ISP32_MODULE_GOC)
-		ops->goc_enable(params_vdev, !!(module_ens & ISP32_MODULE_GOC));
+		ops->goc_enable(params_vdev, !!(module_ens & ISP32_MODULE_GOC), id);
 
 	if (module_en_update & ISP32_MODULE_CPROC)
-		ops->cproc_enable(params_vdev, !!(module_ens & ISP32_MODULE_CPROC));
+		ops->cproc_enable(params_vdev, !!(module_ens & ISP32_MODULE_CPROC), id);
 
 	if (module_en_update & ISP32_MODULE_IE)
-		ops->ie_enable(params_vdev, !!(module_ens & ISP32_MODULE_IE));
+		ops->ie_enable(params_vdev, !!(module_ens & ISP32_MODULE_IE), id);
 
 	if (module_en_update & ISP32_MODULE_HDRMGE) {
-		ops->hdrmge_enable(params_vdev, !!(module_ens & ISP32_MODULE_HDRMGE));
+		ops->hdrmge_enable(params_vdev, !!(module_ens & ISP32_MODULE_HDRMGE), id);
 		priv_val->mge_en = !!(module_ens & ISP32_MODULE_HDRMGE);
 	}
 
 	if (module_en_update & ISP32_MODULE_DRC) {
-		ops->hdrdrc_enable(params_vdev, !!(module_ens & ISP32_MODULE_DRC));
+		ops->hdrdrc_enable(params_vdev, !!(module_ens & ISP32_MODULE_DRC), id);
 		priv_val->drc_en = !!(module_ens & ISP32_MODULE_DRC);
 	}
 
 	if (module_en_update & ISP32_MODULE_GIC)
-		ops->gic_enable(params_vdev, !!(module_ens & ISP32_MODULE_GIC));
+		ops->gic_enable(params_vdev, !!(module_ens & ISP32_MODULE_GIC), id);
 
 	if (module_en_update & ISP32_MODULE_DHAZ) {
-		ops->dhaz_enable(params_vdev, !!(module_ens & ISP32_MODULE_DHAZ));
+		ops->dhaz_enable(params_vdev, !!(module_ens & ISP32_MODULE_DHAZ), id);
 		priv_val->dhaz_en = !!(module_ens & ISP32_MODULE_DHAZ);
 	}
 
 	if (module_en_update & ISP32_MODULE_3DLUT) {
-		ops->isp3dlut_enable(params_vdev, !!(module_ens & ISP32_MODULE_3DLUT));
+		ops->isp3dlut_enable(params_vdev, !!(module_ens & ISP32_MODULE_3DLUT), id);
 		priv_val->lut3d_en = !!(module_ens & ISP32_MODULE_3DLUT);
 	}
 
 	if (module_en_update & ISP32_MODULE_LDCH)
-		ops->ldch_enable(params_vdev, !!(module_ens & ISP32_MODULE_LDCH));
+		ops->ldch_enable(params_vdev, !!(module_ens & ISP32_MODULE_LDCH), id);
 
 	if (module_en_update & ISP32_MODULE_YNR)
-		ops->ynr_enable(params_vdev, !!(module_ens & ISP32_MODULE_YNR));
+		ops->ynr_enable(params_vdev, !!(module_ens & ISP32_MODULE_YNR), id);
 
 	if (module_en_update & ISP32_MODULE_CNR)
-		ops->cnr_enable(params_vdev, !!(module_ens & ISP32_MODULE_CNR));
+		ops->cnr_enable(params_vdev, !!(module_ens & ISP32_MODULE_CNR), id);
 
 	if (module_en_update & ISP32_MODULE_SHARP)
-		ops->sharp_enable(params_vdev, !!(module_ens & ISP32_MODULE_SHARP));
+		ops->sharp_enable(params_vdev, !!(module_ens & ISP32_MODULE_SHARP), id);
 
 	if (module_en_update & ISP32_MODULE_BAYNR)
-		ops->baynr_enable(params_vdev, !!(module_ens & ISP32_MODULE_BAYNR));
+		ops->baynr_enable(params_vdev, !!(module_ens & ISP32_MODULE_BAYNR), id);
 
 	if (module_en_update & ISP32_MODULE_BAY3D) {
-		ops->bay3d_enable(params_vdev, !!(module_ens & ISP32_MODULE_BAY3D));
+		ops->bay3d_enable(params_vdev, !!(module_ens & ISP32_MODULE_BAY3D), id);
 		priv_val->bay3d_en = !!(module_ens & ISP32_MODULE_BAY3D);
 	}
 
 	if (module_en_update & ISP32_MODULE_CAC)
-		ops->cac_enable(params_vdev, !!(module_ens & ISP32_MODULE_CAC));
+		ops->cac_enable(params_vdev, !!(module_ens & ISP32_MODULE_CAC), id);
 
 	if (module_en_update & ISP32_MODULE_GAIN ||
 	    ((priv_val->buf_info_owner == RKISP_INFO2DRR_OWNER_GAIN) &&
-	     !(isp3_param_read(params_vdev, ISP3X_GAIN_CTRL) & ISP3X_GAIN_2DDR_EN)))
-		ops->gain_enable(params_vdev, !!(module_ens & ISP32_MODULE_GAIN));
+	     !(isp3_param_read(params_vdev, ISP3X_GAIN_CTRL, id) & ISP3X_GAIN_2DDR_EN)))
+		ops->gain_enable(params_vdev, !!(module_ens & ISP32_MODULE_GAIN), id);
 
 	if (module_en_update & ISP32_MODULE_VSM)
-		ops->vsm_enable(params_vdev, !!(module_ens & ISP32_MODULE_VSM));
+		ops->vsm_enable(params_vdev, !!(module_ens & ISP32_MODULE_VSM), id);
 
 	/* gain disable, using global gain for cnr */
-	gain_ctrl = isp3_param_read_cache(params_vdev, ISP3X_GAIN_CTRL);
-	cnr_ctrl = isp3_param_read_cache(params_vdev, ISP3X_CNR_CTRL);
+	gain_ctrl = isp3_param_read_cache(params_vdev, ISP3X_GAIN_CTRL, id);
+	cnr_ctrl = isp3_param_read_cache(params_vdev, ISP3X_CNR_CTRL, id);
 	if (!(gain_ctrl & ISP32_MODULE_EN) && cnr_ctrl & ISP32_MODULE_EN) {
 		cnr_ctrl |= BIT(1);
-		isp3_param_write(params_vdev, cnr_ctrl, ISP3X_CNR_CTRL);
-		val = isp3_param_read(params_vdev, ISP3X_CNR_EXGAIN) & 0x3ff;
-		isp3_param_write(params_vdev, val | 0x8000, ISP3X_CNR_EXGAIN);
+		isp3_param_write(params_vdev, cnr_ctrl, ISP3X_CNR_CTRL, id);
+		val = isp3_param_read(params_vdev, ISP3X_CNR_EXGAIN, id) & 0x3ff;
+		isp3_param_write(params_vdev, val | 0x8000, ISP3X_CNR_EXGAIN, id);
 	}
 }
 
 static __maybe_unused
 void __isp_isr_meas_config(struct rkisp_isp_params_vdev *params_vdev,
 			   struct isp32_isp_params_cfg *new_params,
-			   enum rkisp_params_type type)
+			   enum rkisp_params_type type, u32 id)
 {
 	struct rkisp_isp_params_ops_v32 *ops =
 		(struct rkisp_isp_params_ops_v32 *)params_vdev->priv_ops;
@@ -4190,48 +4282,48 @@
 		return;
 
 	v4l2_dbg(4, rkisp_debug, &params_vdev->dev->v4l2_dev,
-		 "%s seq:%d module_cfg_update:0x%llx\n",
-		 __func__, new_params->frame_id, module_cfg_update);
+		 "%s id:%d seq:%d module_cfg_update:0x%llx\n",
+		 __func__, id, new_params->frame_id, module_cfg_update);
 
 	if ((module_cfg_update & ISP32_MODULE_RAWAF))
-		ops->rawaf_config(params_vdev, &new_params->meas.rawaf);
+		ops->rawaf_config(params_vdev, &new_params->meas.rawaf, id);
 
 	if ((module_cfg_update & ISP32_MODULE_RAWAE0) &&
 	    !(params_vdev->afaemode_en && params_vdev->dev->isp_ver == ISP_V32_L))
-		ops->rawae0_config(params_vdev, &new_params->meas.rawae0);
+		ops->rawae0_config(params_vdev, &new_params->meas.rawae0, id);
 
 	if ((module_cfg_update & ISP32_MODULE_RAWAE1))
-		ops->rawae1_config(params_vdev, &new_params->meas.rawae1);
+		ops->rawae1_config(params_vdev, &new_params->meas.rawae1, id);
 
 	if ((module_cfg_update & ISP32_MODULE_RAWAE2))
-		ops->rawae2_config(params_vdev, &new_params->meas.rawae2);
+		ops->rawae2_config(params_vdev, &new_params->meas.rawae2, id);
 
 	if ((module_cfg_update & ISP32_MODULE_RAWAE3) &&
 	    !(params_vdev->afaemode_en && params_vdev->dev->isp_ver == ISP_V32))
-		ops->rawae3_config(params_vdev, &new_params->meas.rawae3);
+		ops->rawae3_config(params_vdev, &new_params->meas.rawae3, id);
 
 	if ((module_cfg_update & ISP32_MODULE_RAWHIST0))
-		ops->rawhst0_config(params_vdev, &new_params->meas.rawhist0);
+		ops->rawhst0_config(params_vdev, &new_params->meas.rawhist0, id);
 
 	if ((module_cfg_update & ISP32_MODULE_RAWHIST1))
-		ops->rawhst1_config(params_vdev, &new_params->meas.rawhist1);
+		ops->rawhst1_config(params_vdev, &new_params->meas.rawhist1, id);
 
 	if ((module_cfg_update & ISP32_MODULE_RAWHIST2))
-		ops->rawhst2_config(params_vdev, &new_params->meas.rawhist2);
+		ops->rawhst2_config(params_vdev, &new_params->meas.rawhist2, id);
 
 	if ((module_cfg_update & ISP32_MODULE_RAWHIST3))
-		ops->rawhst3_config(params_vdev, &new_params->meas.rawhist3);
+		ops->rawhst3_config(params_vdev, &new_params->meas.rawhist3, id);
 
 	if ((module_cfg_update & ISP32_MODULE_RAWAWB) ||
 	    ((priv_val->buf_info_owner == RKISP_INFO2DRR_OWNER_AWB) &&
-	     !(isp3_param_read(params_vdev, ISP3X_RAWAWB_CTRL) & ISP32_RAWAWB_2DDR_PATH_EN)))
-		ops->rawawb_config(params_vdev, &new_params->meas.rawawb);
+	     !(isp3_param_read(params_vdev, ISP3X_RAWAWB_CTRL, id) & ISP32_RAWAWB_2DDR_PATH_EN)))
+		ops->rawawb_config(params_vdev, &new_params->meas.rawawb, id);
 }
 
 static __maybe_unused
 void __isp_isr_meas_en(struct rkisp_isp_params_vdev *params_vdev,
 		       struct isp32_isp_params_cfg *new_params,
-		       enum rkisp_params_type type)
+		       enum rkisp_params_type type, u32 id)
 {
 	struct rkisp_isp_params_ops_v32 *ops =
 		(struct rkisp_isp_params_ops_v32 *)params_vdev->priv_ops;
@@ -4245,64 +4337,53 @@
 		return;
 
 	v4l2_dbg(4, rkisp_debug, &params_vdev->dev->v4l2_dev,
-		 "%s seq:%d module_en_update:0x%llx module_ens:0x%llx\n",
-		 __func__, new_params->frame_id, module_en_update, module_ens);
+		 "%s id:%d seq:%d module_en_update:0x%llx module_ens:0x%llx\n",
+		 __func__, id, new_params->frame_id, module_en_update, module_ens);
 
 	if (module_en_update & ISP32_MODULE_RAWAF)
-		ops->rawaf_enable(params_vdev, !!(module_ens & ISP32_MODULE_RAWAF));
+		ops->rawaf_enable(params_vdev, !!(module_ens & ISP32_MODULE_RAWAF), id);
 
 	if ((module_en_update & ISP32_MODULE_RAWAE0) &&
 	    !(params_vdev->afaemode_en && params_vdev->dev->isp_ver == ISP_V32_L))
-		ops->rawae0_enable(params_vdev, !!(module_ens & ISP32_MODULE_RAWAE0));
+		ops->rawae0_enable(params_vdev, !!(module_ens & ISP32_MODULE_RAWAE0), id);
 
 	if (module_en_update & ISP32_MODULE_RAWAE1)
-		ops->rawae1_enable(params_vdev, !!(module_ens & ISP32_MODULE_RAWAE1));
+		ops->rawae1_enable(params_vdev, !!(module_ens & ISP32_MODULE_RAWAE1), id);
 
 	if (module_en_update & ISP32_MODULE_RAWAE2)
-		ops->rawae2_enable(params_vdev, !!(module_ens & ISP32_MODULE_RAWAE2));
+		ops->rawae2_enable(params_vdev, !!(module_ens & ISP32_MODULE_RAWAE2), id);
 
 	if ((module_en_update & ISP32_MODULE_RAWAE3) &&
 	    !(params_vdev->afaemode_en && params_vdev->dev->isp_ver == ISP_V32))
-		ops->rawae3_enable(params_vdev, !!(module_ens & ISP32_MODULE_RAWAE3));
+		ops->rawae3_enable(params_vdev, !!(module_ens & ISP32_MODULE_RAWAE3), id);
 
 	if (module_en_update & ISP32_MODULE_RAWHIST0)
-		ops->rawhst0_enable(params_vdev, !!(module_ens & ISP32_MODULE_RAWHIST0));
+		ops->rawhst0_enable(params_vdev, !!(module_ens & ISP32_MODULE_RAWHIST0), id);
 
 	if (module_en_update & ISP32_MODULE_RAWHIST1)
-		ops->rawhst1_enable(params_vdev, !!(module_ens & ISP32_MODULE_RAWHIST1));
+		ops->rawhst1_enable(params_vdev, !!(module_ens & ISP32_MODULE_RAWHIST1), id);
 
 	if (module_en_update & ISP32_MODULE_RAWHIST2)
-		ops->rawhst2_enable(params_vdev, !!(module_ens & ISP32_MODULE_RAWHIST2));
+		ops->rawhst2_enable(params_vdev, !!(module_ens & ISP32_MODULE_RAWHIST2), id);
 
 	if (module_en_update & ISP32_MODULE_RAWHIST3)
-		ops->rawhst3_enable(params_vdev, !!(module_ens & ISP32_MODULE_RAWHIST3));
+		ops->rawhst3_enable(params_vdev, !!(module_ens & ISP32_MODULE_RAWHIST3), id);
 
 	if (module_en_update & ISP32_MODULE_RAWAWB)
-		ops->rawawb_enable(params_vdev, !!(module_ens & ISP32_MODULE_RAWAWB));
-}
-
-static __maybe_unused
-void __isp_config_hdrshd(struct rkisp_isp_params_vdev *params_vdev)
-{
-	struct rkisp_isp_params_ops_v32 *ops =
-		(struct rkisp_isp_params_ops_v32 *)params_vdev->priv_ops;
-	struct rkisp_isp_params_val_v32 *priv_val =
-		(struct rkisp_isp_params_val_v32 *)params_vdev->priv_val;
-
-	ops->hdrmge_config(params_vdev, &priv_val->last_hdrmge, RKISP_PARAMS_SHD);
-	ops->hdrdrc_config(params_vdev, &priv_val->last_hdrdrc, RKISP_PARAMS_SHD);
+		ops->rawawb_enable(params_vdev, !!(module_ens & ISP32_MODULE_RAWAWB), id);
 }
 
 static
 void rkisp_params_cfgsram_v32(struct rkisp_isp_params_vdev *params_vdev)
 {
-	struct isp32_isp_params_cfg *params = params_vdev->isp32_params;
+	u32 id = params_vdev->dev->unite_index;
+	struct isp32_isp_params_cfg *params = params_vdev->isp32_params + id;
 
-	isp_lsc_matrix_cfg_sram(params_vdev, &params->others.lsc_cfg, true);
-	isp_rawhstbig_cfg_sram(params_vdev, &params->meas.rawhist1, 1, true);
-	isp_rawhstbig_cfg_sram(params_vdev, &params->meas.rawhist2, 2, true);
-	isp_rawhstbig_cfg_sram(params_vdev, &params->meas.rawhist3, 0, true);
-	isp_rawawb_cfg_sram(params_vdev, &params->meas.rawawb, true);
+	isp_lsc_matrix_cfg_sram(params_vdev, &params->others.lsc_cfg, true, id);
+	isp_rawhstbig_cfg_sram(params_vdev, &params->meas.rawhist1, 1, true, id);
+	isp_rawhstbig_cfg_sram(params_vdev, &params->meas.rawhist2, 2, true, id);
+	isp_rawhstbig_cfg_sram(params_vdev, &params->meas.rawhist3, 0, true, id);
+	isp_rawawb_cfg_sram(params_vdev, &params->meas.rawawb, true, id);
 }
 
 static int
@@ -4313,22 +4394,24 @@
 	struct rkisp_isp_subdev *isp_sdev = &dev->isp_sdev;
 	struct rkisp_isp_params_val_v32 *priv_val;
 	u64 module_en_update, module_ens;
-	int ret, i;
+	int ret, i, id;
 
 	priv_val = (struct rkisp_isp_params_val_v32 *)params_vdev->priv_val;
 	module_en_update = new_params->module_en_update;
 	module_ens = new_params->module_ens;
 
-	priv_val->buf_3dlut_idx = 0;
-	for (i = 0; i < ISP32_3DLUT_BUF_NUM; i++) {
-		if (priv_val->buf_3dlut[i].mem_priv)
-			continue;
-		priv_val->buf_3dlut[i].is_need_vaddr = true;
-		priv_val->buf_3dlut[i].size = ISP32_3DLUT_BUF_SIZE;
-		ret = rkisp_alloc_buffer(dev, &priv_val->buf_3dlut[i]);
-		if (ret) {
-			dev_err(dev->dev, "alloc 3dlut buf fail:%d\n", ret);
-			goto err_3dlut;
+	for (id = 0; id <= !!dev->hw_dev->unite; id++) {
+		priv_val->buf_3dlut_idx[id] = 0;
+		for (i = 0; i < ISP32_3DLUT_BUF_NUM; i++) {
+			if (priv_val->buf_3dlut[id][i].mem_priv)
+				continue;
+			priv_val->buf_3dlut[id][i].is_need_vaddr = true;
+			priv_val->buf_3dlut[id][i].size = ISP32_3DLUT_BUF_SIZE;
+			ret = rkisp_alloc_buffer(dev, &priv_val->buf_3dlut[id][i]);
+			if (ret) {
+				dev_err(dev->dev, "alloc 3dlut buf fail:%d\n", ret);
+				goto err_3dlut;
+			}
 		}
 	}
 
@@ -4343,6 +4426,9 @@
 		u32 h = ALIGN(isp_sdev->in_crop.height, 16);
 		u32 val, wrap_line, wsize, div;
 		bool is_alloc;
+
+		if (dev->hw_dev->unite)
+			w = ALIGN(isp_sdev->in_crop.width / 2 + RKMOUDLE_UNITE_EXTEND_PIXEL, 16);
 
 		priv_val->is_lo8x8 = (!new_params->others.bay3d_cfg.lo4x8_en &&
 				      !new_params->others.bay3d_cfg.lo4x4_en);
@@ -4361,6 +4447,9 @@
 		wsize *= 2;
 		div = is_bwopt_dis ? 1 : 2;
 		val = ALIGN(wsize * h / div, 16);
+		priv_val->bay3d_iir_size = val;
+		if (dev->hw_dev->unite)
+			val *= 2;
 		is_alloc = true;
 		if (priv_val->buf_3dnr_iir.mem_priv) {
 			if (val > priv_val->buf_3dnr_iir.size)
@@ -4381,6 +4470,9 @@
 		val = w * h / div;
 		/* pixel to Byte and align */
 		val = ALIGN(val * 2, 16);
+		priv_val->bay3d_ds_size = val;
+		if (dev->hw_dev->unite)
+			val *= 2;
 		is_alloc = true;
 		if (priv_val->buf_3dnr_ds.mem_priv) {
 			if (val > priv_val->buf_3dnr_ds.size)
@@ -4413,8 +4505,7 @@
 		val = ALIGN(wsize * wrap_line / div, 16);
 		is_alloc = true;
 		if (priv_val->buf_3dnr_cur.mem_priv) {
-			if (val > priv_val->buf_3dnr_cur.size ||
-			    val < dev->hw_dev->sram.size)
+			if (val > priv_val->buf_3dnr_cur.size || val < dev->hw_dev->sram.size)
 				rkisp_free_buffer(dev, &priv_val->buf_3dnr_cur);
 			else
 				is_alloc = false;
@@ -4440,6 +4531,8 @@
 	if (dev->isp_ver == ISP_V32_L) {
 		if (dev->hw_dev->is_frm_buf && !priv_val->buf_frm.mem_priv) {
 			priv_val->buf_frm.size = ISP32_LITE_FRM_BUF_SIZE;
+			if (dev->hw_dev->unite)
+				priv_val->buf_frm.size *= 2;
 			ret = rkisp_alloc_buffer(dev, &priv_val->buf_frm);
 			if (ret) {
 				dev_err(dev->dev, "alloc frm buf fail:%d\n", ret);
@@ -4469,10 +4562,14 @@
 	rkisp_free_buffer(dev, &priv_val->buf_3dnr_iir);
 	rkisp_free_buffer(dev, &priv_val->buf_3dnr_ds);
 err_3dnr:
+	id = dev->hw_dev->unite ? 1 : 0;
 	i = ISP32_3DLUT_BUF_NUM;
 err_3dlut:
-	for (i -= 1; i >= 0; i--)
-		rkisp_free_buffer(dev, &priv_val->buf_3dlut[i]);
+	for (; id >= 0; id--) {
+		for (i -= 1; i >= 0; i--)
+			rkisp_free_buffer(dev, &priv_val->buf_3dlut[id][i]);
+		i = ISP32_3DLUT_BUF_NUM;
+	}
 	return ret;
 }
 
@@ -4664,6 +4761,8 @@
 	if (ispdev->isp_ver == ISP_V32_L)
 		return rkisp_params_check_bigmode_v32_lite(params_vdev);
 
+	if (hw->unite == ISP_UNITE_ONE)
+		hw->is_multi_overflow = true;
 multi_overflow:
 	if (hw->is_multi_overflow) {
 		ispdev->multi_index = 0;
@@ -4831,6 +4930,7 @@
 rkisp_params_first_cfg_v32(struct rkisp_isp_params_vdev *params_vdev)
 {
 	struct rkisp_device *dev = params_vdev->dev;
+	struct rkisp_hw_dev *hw = dev->hw_dev;
 	struct rkisp_isp_params_val_v32 *priv_val =
 		(struct rkisp_isp_params_val_v32 *)params_vdev->priv_val;
 
@@ -4847,25 +4947,39 @@
 	priv_val->lsc_en = 0;
 	priv_val->mge_en = 0;
 	priv_val->lut3d_en = 0;
+	if (hw->unite) {
+		if (dev->is_bigmode)
+			rkisp_next_set_bits(dev, ISP3X_ISP_CTRL1, 0,
+					    ISP3X_BIGMODE_MANUAL | ISP3X_BIGMODE_FORCE_EN, false);
+		__isp_isr_meas_config(params_vdev, params_vdev->isp32_params + 1, RKISP_PARAMS_ALL, 1);
+		__isp_isr_other_config(params_vdev, params_vdev->isp32_params + 1, RKISP_PARAMS_ALL, 1);
+		__isp_isr_other_en(params_vdev, params_vdev->isp32_params + 1, RKISP_PARAMS_ALL, 1);
+		__isp_isr_meas_en(params_vdev, params_vdev->isp32_params + 1, RKISP_PARAMS_ALL, 1);
+	}
 	if (dev->is_bigmode)
-		rkisp_set_bits(params_vdev->dev, ISP3X_ISP_CTRL1, 0,
+		rkisp_set_bits(dev, ISP3X_ISP_CTRL1, 0,
 			       ISP3X_BIGMODE_MANUAL | ISP3X_BIGMODE_FORCE_EN, false);
 
-	__isp_isr_meas_config(params_vdev, params_vdev->isp32_params, RKISP_PARAMS_ALL);
-	__isp_isr_other_config(params_vdev, params_vdev->isp32_params, RKISP_PARAMS_ALL);
-	__isp_isr_other_en(params_vdev, params_vdev->isp32_params, RKISP_PARAMS_ALL);
-	__isp_isr_meas_en(params_vdev, params_vdev->isp32_params, RKISP_PARAMS_ALL);
+	__isp_isr_meas_config(params_vdev, params_vdev->isp32_params, RKISP_PARAMS_ALL, 0);
+	__isp_isr_other_config(params_vdev, params_vdev->isp32_params, RKISP_PARAMS_ALL, 0);
+	__isp_isr_other_en(params_vdev, params_vdev->isp32_params, RKISP_PARAMS_ALL, 0);
+	__isp_isr_meas_en(params_vdev, params_vdev->isp32_params, RKISP_PARAMS_ALL, 0);
 
-	priv_val->cur_hdrmge = params_vdev->isp32_params->others.hdrmge_cfg;
-	priv_val->cur_hdrdrc = params_vdev->isp32_params->others.drc_cfg;
-	priv_val->last_hdrmge = priv_val->cur_hdrmge;
-	priv_val->last_hdrdrc = priv_val->cur_hdrdrc;
 	spin_unlock(&params_vdev->config_lock);
 
 	if (dev->hw_dev->is_frm_buf && priv_val->buf_frm.mem_priv) {
-		isp3_param_write(params_vdev, priv_val->buf_frm.size, ISP32L_FRM_BUF_WR_SIZE);
-		isp3_param_write(params_vdev, priv_val->buf_frm.dma_addr, ISP32L_FRM_BUF_WR_BASE);
-		isp3_param_write(params_vdev, priv_val->buf_frm.dma_addr, ISP32L_FRM_BUF_RD_BASE);
+		u32 size = priv_val->buf_frm.size;
+		u32 addr = priv_val->buf_frm.dma_addr;
+
+		if (hw->unite) {
+			size /= 2;
+			isp3_param_write(params_vdev, size, ISP32L_FRM_BUF_WR_SIZE, 1);
+			isp3_param_write(params_vdev, addr + size, ISP32L_FRM_BUF_WR_BASE, 1);
+			isp3_param_write(params_vdev, addr + size, ISP32L_FRM_BUF_RD_BASE, 1);
+		}
+		isp3_param_write(params_vdev, size, ISP32L_FRM_BUF_WR_SIZE, 0);
+		isp3_param_write(params_vdev, addr, ISP32L_FRM_BUF_WR_BASE, 0);
+		isp3_param_write(params_vdev, addr, ISP32L_FRM_BUF_RD_BASE, 0);
 	}
 	if (dev->hw_dev->is_single && (dev->isp_state & ISP_START))
 		rkisp_set_bits(dev, ISP3X_ISP_CTRL0, 0, CIF_ISP_CTRL_ISP_CFG_UPD, true);
@@ -4879,11 +4993,15 @@
 
 static void rkisp_clear_first_param_v32(struct rkisp_isp_params_vdev *params_vdev)
 {
-	memset(params_vdev->isp32_params, 0, sizeof(struct isp32_isp_params_cfg));
+	u32 size = sizeof(struct isp32_isp_params_cfg);
+
+	if (params_vdev->dev->hw_dev->unite)
+		size *= 2;
+	memset(params_vdev->isp32_params, 0, size);
 }
 
 static void rkisp_deinit_mesh_buf(struct rkisp_isp_params_vdev *params_vdev,
-				  u64 module_id)
+				  u64 module_id, u32 id)
 {
 	struct rkisp_isp_params_val_v32 *priv_val;
 	struct rkisp_dummy_buffer *buf;
@@ -4895,11 +5013,11 @@
 
 	switch (module_id) {
 	case ISP32_MODULE_CAC:
-		buf = priv_val->buf_cac;
+		buf = priv_val->buf_cac[id];
 		break;
 	case ISP32_MODULE_LDCH:
 	default:
-		buf = priv_val->buf_ldch;
+		buf = priv_val->buf_ldch[id];
 		break;
 	}
 
@@ -4919,6 +5037,7 @@
 	u32 mesh_h = meshsize->meas_height;
 	u32 mesh_size, buf_size;
 	int i, ret, buf_cnt = meshsize->buf_cnt;
+	int id = meshsize->unite_isp_id;
 	bool is_alloc;
 
 	priv_val = params_vdev->priv_val;
@@ -4929,16 +5048,16 @@
 
 	switch (meshsize->module_id) {
 	case ISP32_MODULE_CAC:
-		priv_val->buf_cac_idx = 0;
-		buf = priv_val->buf_cac;
+		priv_val->buf_cac_idx[id] = 0;
+		buf = priv_val->buf_cac[id];
 		mesh_w = (mesh_w + 62) / 64 * 9;
 		mesh_h = (mesh_h + 62) / 64 * 2;
 		mesh_size = mesh_w * 4 * mesh_h;
 		break;
 	case ISP32_MODULE_LDCH:
 	default:
-		priv_val->buf_ldch_idx = 0;
-		buf = priv_val->buf_ldch;
+		priv_val->buf_ldch_idx[id] = 0;
+		buf = priv_val->buf_ldch[id];
 		mesh_w = ((mesh_w + 15) / 16 + 2) / 2;
 		mesh_h = (mesh_h + 7) / 8 + 1;
 		mesh_size = mesh_w * 4 * mesh_h;
@@ -4979,7 +5098,7 @@
 
 	return 0;
 err:
-	rkisp_deinit_mesh_buf(params_vdev, meshsize->module_id);
+	rkisp_deinit_mesh_buf(params_vdev, meshsize->module_id, id);
 	return -ENOMEM;
 }
 
@@ -4987,7 +5106,9 @@
 rkisp_get_param_size_v32(struct rkisp_isp_params_vdev *params_vdev,
 			 unsigned int sizes[])
 {
-	sizes[0] = sizeof(struct isp32_isp_params_cfg);
+	u32 mult = params_vdev->dev->hw_dev->unite ? 2 : 1;
+
+	sizes[0] = sizeof(struct isp32_isp_params_cfg) * mult;
 }
 
 static void
@@ -4997,18 +5118,18 @@
 	struct rkisp_isp_params_val_v32 *priv_val;
 	struct rkisp_meshbuf_info *meshbuf = meshbuf_inf;
 	struct rkisp_dummy_buffer *buf;
-	int i;
+	int i, id = meshbuf->unite_isp_id;
 
 	priv_val = params_vdev->priv_val;
 	switch (meshbuf->module_id) {
 	case ISP32_MODULE_CAC:
-		priv_val->buf_cac_idx = 0;
-		buf = priv_val->buf_cac;
+		priv_val->buf_cac_idx[id] = 0;
+		buf = priv_val->buf_cac[id];
 		break;
 	case ISP32_MODULE_LDCH:
 	default:
-		priv_val->buf_ldch_idx = 0;
-		buf = priv_val->buf_ldch;
+		priv_val->buf_ldch_idx[id] = 0;
+		buf = priv_val->buf_ldch[id];
 		break;
 	}
 
@@ -5030,6 +5151,8 @@
 {
 	struct rkisp_meshbuf_size *meshsize = size;
 
+	if (!params_vdev->dev->hw_dev->unite)
+		meshsize->unite_isp_id = 0;
 	return rkisp_init_mesh_buf(params_vdev, meshsize);
 }
 
@@ -5037,7 +5160,10 @@
 rkisp_params_free_meshbuf_v32(struct rkisp_isp_params_vdev *params_vdev,
 			      u64 module_id)
 {
-	rkisp_deinit_mesh_buf(params_vdev, module_id);
+	int id;
+
+	for (id = 0; id <= !!params_vdev->dev->hw_dev->unite; id++)
+		rkisp_deinit_mesh_buf(params_vdev, module_id, id);
 }
 
 static int
@@ -5134,9 +5260,9 @@
 		cfg->buf_fd[i] = buf->dma_fd;
 	}
 	buf = &priv_val->buf_info[0];
-	isp3_param_write(params_vdev, buf->dma_addr, ISP3X_MI_GAIN_WR_BASE);
-	isp3_param_write(params_vdev, buf->size, ISP3X_MI_GAIN_WR_SIZE);
-	isp3_param_write(params_vdev, wsize, ISP3X_MI_GAIN_WR_LENGTH);
+	isp3_param_write(params_vdev, buf->dma_addr, ISP3X_MI_GAIN_WR_BASE, 0);
+	isp3_param_write(params_vdev, buf->size, ISP3X_MI_GAIN_WR_SIZE, 0);
+	isp3_param_write(params_vdev, wsize, ISP3X_MI_GAIN_WR_LENGTH, 0);
 	if (dev->hw_dev->is_single)
 		rkisp_write(dev, ISP3X_MI_WR_CTRL2, ISP3X_GAINSELF_UPD, true);
 	rkisp_set_reg_cache_bits(dev, reg, mask, ctrl);
@@ -5164,7 +5290,7 @@
 {
 	struct rkisp_device *ispdev = params_vdev->dev;
 	struct rkisp_isp_params_val_v32 *priv_val;
-	int i;
+	int i, id;
 
 	priv_val = (struct rkisp_isp_params_val_v32 *)params_vdev->priv_val;
 	rkisp_free_buffer(ispdev, &priv_val->buf_frm);
@@ -5173,10 +5299,12 @@
 	rkisp_free_buffer(ispdev, &priv_val->buf_3dnr_ds);
 	for (i = 0; i < ISP32_LSC_LUT_BUF_NUM; i++)
 		rkisp_free_buffer(ispdev, &priv_val->buf_lsclut[i]);
-	for (i = 0; i < ISP32_3DLUT_BUF_NUM; i++)
-		rkisp_free_buffer(ispdev, &priv_val->buf_3dlut[i]);
 	for (i = 0; i < RKISP_STATS_DDR_BUF_NUM; i++)
 		rkisp_free_buffer(ispdev, &ispdev->stats_vdev.stats_buf[i]);
+	for (id = 0; id <= !!ispdev->hw_dev->unite; id++) {
+		for (i = 0; i < ISP32_3DLUT_BUF_NUM; i++)
+			rkisp_free_buffer(ispdev, &priv_val->buf_3dlut[id][i]);
+	}
 	priv_val->buf_info_owner = 0;
 	priv_val->buf_info_cnt = 0;
 	priv_val->buf_info_idx = -1;
@@ -5187,8 +5315,12 @@
 static void
 rkisp_params_fop_release_v32(struct rkisp_isp_params_vdev *params_vdev)
 {
-	rkisp_deinit_mesh_buf(params_vdev, ISP32_MODULE_LDCH);
-	rkisp_deinit_mesh_buf(params_vdev, ISP32_MODULE_CAC);
+	int id;
+
+	for (id = 0; id <= !!params_vdev->dev->hw_dev->unite; id++) {
+		rkisp_deinit_mesh_buf(params_vdev, ISP32_MODULE_LDCH, id);
+		rkisp_deinit_mesh_buf(params_vdev, ISP32_MODULE_CAC, id);
+	}
 }
 
 /* Not called when the camera active, thus not isr protection. */
@@ -5198,13 +5330,17 @@
 	params_vdev->isp32_params->module_ens = 0;
 	params_vdev->isp32_params->module_en_update = 0x7ffffffffff;
 
-	__isp_isr_other_en(params_vdev, params_vdev->isp32_params, RKISP_PARAMS_ALL);
-	__isp_isr_meas_en(params_vdev, params_vdev->isp32_params, RKISP_PARAMS_ALL);
+	__isp_isr_other_en(params_vdev, params_vdev->isp32_params, RKISP_PARAMS_ALL, 0);
+	__isp_isr_meas_en(params_vdev, params_vdev->isp32_params, RKISP_PARAMS_ALL, 0);
+	if (params_vdev->dev->hw_dev->unite) {
+		__isp_isr_other_en(params_vdev, params_vdev->isp32_params, RKISP_PARAMS_ALL, 1);
+		__isp_isr_meas_en(params_vdev, params_vdev->isp32_params, RKISP_PARAMS_ALL, 1);
+	}
 }
 
 static void
 module_data_abandon(struct rkisp_isp_params_vdev *params_vdev,
-		    struct isp32_isp_params_cfg *params)
+		    struct isp32_isp_params_cfg *params, u32 id)
 {
 	struct rkisp_isp_params_val_v32 *priv_val;
 	struct isp2x_mesh_head *mesh_head;
@@ -5215,9 +5351,9 @@
 		const struct isp32_ldch_cfg *arg = &params->others.ldch_cfg;
 
 		for (i = 0; i < ISP32_MESH_BUF_NUM; i++) {
-			if (priv_val->buf_ldch[i].vaddr &&
-			    arg->buf_fd == priv_val->buf_ldch[i].dma_fd) {
-				mesh_head = (struct isp2x_mesh_head *)priv_val->buf_ldch[i].vaddr;
+			if (priv_val->buf_ldch[id][i].vaddr &&
+			    arg->buf_fd == priv_val->buf_ldch[id][i].dma_fd) {
+				mesh_head = (struct isp2x_mesh_head *)priv_val->buf_ldch[id][i].vaddr;
 				mesh_head->stat = MESH_BUF_CHIPINUSE;
 				break;
 			}
@@ -5228,9 +5364,9 @@
 		const struct isp32_cac_cfg *arg = &params->others.cac_cfg;
 
 		for (i = 0; i < ISP32_MESH_BUF_NUM; i++) {
-			if (priv_val->buf_cac[i].vaddr &&
-			    arg->buf_fd == priv_val->buf_cac[i].dma_fd) {
-				mesh_head = (struct isp2x_mesh_head *)priv_val->buf_cac[i].vaddr;
+			if (priv_val->buf_cac[id][i].vaddr &&
+			    arg->buf_fd == priv_val->buf_cac[id][i].dma_fd) {
+				mesh_head = (struct isp2x_mesh_head *)priv_val->buf_cac[id][i].vaddr;
 				mesh_head->stat = MESH_BUF_CHIPINUSE;
 				break;
 			}
@@ -5242,10 +5378,9 @@
 rkisp_params_cfg_v32(struct rkisp_isp_params_vdev *params_vdev,
 		     u32 frame_id, enum rkisp_params_type type)
 {
+	struct rkisp_hw_dev *hw = params_vdev->dev->hw_dev;
 	struct isp32_isp_params_cfg *new_params = NULL;
 	struct rkisp_buffer *cur_buf = params_vdev->cur_buf;
-	struct rkisp_device *dev = params_vdev->dev;
-	struct rkisp_hw_dev *hw_dev = dev->hw_dev;
 
 	spin_lock(&params_vdev->config_lock);
 	if (!params_vdev->streamon)
@@ -5264,15 +5399,27 @@
 			else if (new_params->module_en_update ||
 				 (new_params->module_cfg_update & ISP32_MODULE_FORCE)) {
 				/* update en immediately */
-				__isp_isr_meas_config(params_vdev, new_params, type);
-				__isp_isr_other_config(params_vdev, new_params, type);
-				__isp_isr_other_en(params_vdev, new_params, type);
-				__isp_isr_meas_en(params_vdev, new_params, type);
+				__isp_isr_meas_config(params_vdev, new_params, type, 0);
+				__isp_isr_other_config(params_vdev, new_params, type, 0);
+				__isp_isr_other_en(params_vdev, new_params, type, 0);
+				__isp_isr_meas_en(params_vdev, new_params, type, 0);
 				new_params->module_cfg_update = 0;
+				if (hw->unite) {
+					struct isp32_isp_params_cfg *params = new_params + 1;
+
+					__isp_isr_meas_config(params_vdev, params, type, 1);
+					__isp_isr_other_config(params_vdev, params, type, 1);
+					__isp_isr_other_en(params_vdev, params, type, 1);
+					__isp_isr_meas_en(params_vdev, params, type, 1);
+					params->module_cfg_update = 0;
+				}
 			}
 			if (new_params->module_cfg_update &
 			    (ISP32_MODULE_LDCH | ISP32_MODULE_CAC)) {
-				module_data_abandon(params_vdev, new_params);
+				module_data_abandon(params_vdev, new_params, 0);
+				if (hw->unite)
+					module_data_abandon(params_vdev, new_params, 1);
+
 			}
 			vb2_buffer_done(&cur_buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
 			cur_buf = NULL;
@@ -5289,22 +5436,21 @@
 		goto unlock;
 
 	new_params = (struct isp32_isp_params_cfg *)(cur_buf->vaddr[0]);
-	__isp_isr_meas_config(params_vdev, new_params, type);
-	__isp_isr_other_config(params_vdev, new_params, type);
-	__isp_isr_other_en(params_vdev, new_params, type);
-	__isp_isr_meas_en(params_vdev, new_params, type);
-	if (!hw_dev->is_single && type != RKISP_PARAMS_SHD)
-		__isp_config_hdrshd(params_vdev);
+	if (hw->unite) {
+		__isp_isr_meas_config(params_vdev, new_params + 1, type, 1);
+		__isp_isr_other_config(params_vdev, new_params + 1, type, 1);
+		__isp_isr_other_en(params_vdev, new_params + 1, type, 1);
+		__isp_isr_meas_en(params_vdev, new_params + 1, type, 1);
+	}
+	__isp_isr_meas_config(params_vdev, new_params, type, 0);
+	__isp_isr_other_config(params_vdev, new_params, type, 0);
+	__isp_isr_other_en(params_vdev, new_params, type, 0);
+	__isp_isr_meas_en(params_vdev, new_params, type, 0);
 
 	if (type != RKISP_PARAMS_IMD) {
-		struct rkisp_isp_params_val_v32 *priv_val =
-			(struct rkisp_isp_params_val_v32 *)params_vdev->priv_val;
-
-		priv_val->last_hdrmge = priv_val->cur_hdrmge;
-		priv_val->last_hdrdrc = priv_val->cur_hdrdrc;
-		priv_val->cur_hdrmge = new_params->others.hdrmge_cfg;
-		priv_val->cur_hdrdrc = new_params->others.drc_cfg;
 		new_params->module_cfg_update = 0;
+		if (hw->unite)
+			(new_params++)->module_cfg_update = 0;
 		vb2_buffer_done(&cur_buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
 		cur_buf = NULL;
 	}
@@ -5317,13 +5463,15 @@
 static void
 rkisp_params_clear_fstflg(struct rkisp_isp_params_vdev *params_vdev)
 {
-	u32 value = isp3_param_read(params_vdev, ISP3X_ISP_CTRL1);
+	u32 value = isp3_param_read(params_vdev, ISP3X_ISP_CTRL1, 0);
 
 	value &= (ISP3X_YNR_FST_FRAME | ISP3X_ADRC_FST_FRAME |
 		  ISP3X_DHAZ_FST_FRAME | ISP3X_CNR_FST_FRAME |
-		  ISP3X_RAW3D_FST_FRAME);
+		  ISP3X_RAW3D_FST_FRAME | ISP32_SHP_FST_FRAME);
 	if (value) {
-		isp3_param_clear_bits(params_vdev, ISP3X_ISP_CTRL1, value);
+		isp3_param_clear_bits(params_vdev, ISP3X_ISP_CTRL1, value, 0);
+		if (params_vdev->dev->hw_dev->unite)
+			isp3_param_clear_bits(params_vdev, ISP3X_ISP_CTRL1, value, 1);
 	}
 }
 
@@ -5382,6 +5530,8 @@
 		return -ENOMEM;
 
 	size = sizeof(struct isp32_isp_params_cfg);
+	if (params_vdev->dev->hw_dev->unite)
+		size *= 2;
 	params_vdev->isp32_params = vmalloc(size);
 	if (!params_vdev->isp32_params) {
 		kfree(priv_val);
diff --git a/kernel/drivers/media/platform/rockchip/isp/isp_params_v32.h b/kernel/drivers/media/platform/rockchip/isp/isp_params_v32.h
index fb1f21e..e1d9148 100644
--- a/kernel/drivers/media/platform/rockchip/isp/isp_params_v32.h
+++ b/kernel/drivers/media/platform/rockchip/isp/isp_params_v32.h
@@ -29,169 +29,171 @@
 struct rkisp_isp_params_vdev;
 struct rkisp_isp_params_ops_v32 {
 	void (*dpcc_config)(struct rkisp_isp_params_vdev *params_vdev,
-			    const struct isp2x_dpcc_cfg *arg);
+			    const struct isp2x_dpcc_cfg *arg, u32 id);
 	void (*dpcc_enable)(struct rkisp_isp_params_vdev *params_vdev,
-			    bool en);
+			    bool en, u32 id);
 	void (*bls_config)(struct rkisp_isp_params_vdev *params_vdev,
-			   const struct isp32_bls_cfg *arg);
+			   const struct isp32_bls_cfg *arg, u32 id);
 	void (*bls_enable)(struct rkisp_isp_params_vdev *params_vdev,
-			   bool en);
+			   bool en, u32 id);
 	void (*sdg_config)(struct rkisp_isp_params_vdev *params_vdev,
-			   const struct isp2x_sdg_cfg *arg);
+			   const struct isp2x_sdg_cfg *arg, u32 id);
 	void (*sdg_enable)(struct rkisp_isp_params_vdev *params_vdev,
-			   bool en);
+			   bool en, u32 id);
 	void (*lsc_config)(struct rkisp_isp_params_vdev *params_vdev,
-			   const struct isp3x_lsc_cfg *arg);
+			   const struct isp3x_lsc_cfg *arg, u32 id);
 	void (*lsc_enable)(struct rkisp_isp_params_vdev *params_vdev,
-			   bool en);
+			   bool en, u32 id);
 	void (*awbgain_config)(struct rkisp_isp_params_vdev *params_vdev,
-			       const struct isp32_awb_gain_cfg *arg);
+			       const struct isp32_awb_gain_cfg *arg, u32 id);
 	void (*awbgain_enable)(struct rkisp_isp_params_vdev *params_vdev,
-			       bool en);
+			       bool en, u32 id);
 	void (*debayer_config)(struct rkisp_isp_params_vdev *params_vdev,
-			       const struct isp32_debayer_cfg *arg);
+			       const struct isp32_debayer_cfg *arg, u32 id);
 	void (*debayer_enable)(struct rkisp_isp_params_vdev *params_vdev,
-			       bool en);
+			       bool en, u32 id);
 	void (*ccm_config)(struct rkisp_isp_params_vdev *params_vdev,
-			   const struct isp32_ccm_cfg *arg);
+			   const struct isp32_ccm_cfg *arg, u32 id);
 	void (*ccm_enable)(struct rkisp_isp_params_vdev *params_vdev,
-			   bool en);
+			   bool en, u32 id);
 	void (*goc_config)(struct rkisp_isp_params_vdev *params_vdev,
-			   const struct isp3x_gammaout_cfg *arg);
+			   const struct isp3x_gammaout_cfg *arg, u32 id);
 	void (*goc_enable)(struct rkisp_isp_params_vdev *params_vdev,
-			   bool en);
+			   bool en, u32 id);
 	void (*cproc_config)(struct rkisp_isp_params_vdev *params_vdev,
-			     const struct isp2x_cproc_cfg *arg);
+			     const struct isp2x_cproc_cfg *arg, u32 id);
 	void (*cproc_enable)(struct rkisp_isp_params_vdev *params_vdev,
-			     bool en);
+			     bool en, u32 id);
 	void (*ie_config)(struct rkisp_isp_params_vdev *params_vdev,
-			  const struct isp2x_ie_cfg *arg);
+			  const struct isp2x_ie_cfg *arg, u32 id);
 	void (*ie_enable)(struct rkisp_isp_params_vdev *params_vdev,
-			  bool en);
+			  bool en, u32 id);
 	void (*rawaf_config)(struct rkisp_isp_params_vdev *params_vdev,
-			     const struct isp32_rawaf_meas_cfg *arg);
+			     const struct isp32_rawaf_meas_cfg *arg, u32 id);
 	void (*rawaf_enable)(struct rkisp_isp_params_vdev *params_vdev,
-			     bool en);
+			     bool en, u32 id);
 	void (*rawae0_config)(struct rkisp_isp_params_vdev *params_vdev,
-			      const struct isp2x_rawaelite_meas_cfg *arg);
+			      const struct isp2x_rawaelite_meas_cfg *arg, u32 id);
 	void (*rawae0_enable)(struct rkisp_isp_params_vdev *params_vdev,
-			      bool en);
+			      bool en, u32 id);
 	void (*rawae1_config)(struct rkisp_isp_params_vdev *params_vdev,
-			      const struct isp2x_rawaebig_meas_cfg *arg);
+			      const struct isp2x_rawaebig_meas_cfg *arg, u32 id);
 	void (*rawae1_enable)(struct rkisp_isp_params_vdev *params_vdev,
-			      bool en);
+			      bool en, u32 id);
 	void (*rawae2_config)(struct rkisp_isp_params_vdev *params_vdev,
-			      const struct isp2x_rawaebig_meas_cfg *arg);
+			      const struct isp2x_rawaebig_meas_cfg *arg, u32 id);
 	void (*rawae2_enable)(struct rkisp_isp_params_vdev *params_vdev,
-			      bool en);
+			      bool en, u32 id);
 	void (*rawae3_config)(struct rkisp_isp_params_vdev *params_vdev,
-			      const struct isp2x_rawaebig_meas_cfg *arg);
+			      const struct isp2x_rawaebig_meas_cfg *arg, u32 id);
 	void (*rawae3_enable)(struct rkisp_isp_params_vdev *params_vdev,
-			      bool en);
+			      bool en, u32 id);
 	void (*rawawb_config)(struct rkisp_isp_params_vdev *params_vdev,
-			      const struct isp32_rawawb_meas_cfg *arg);
+			      const struct isp32_rawawb_meas_cfg *arg, u32 id);
 	void (*rawawb_enable)(struct rkisp_isp_params_vdev *params_vdev,
-			      bool en);
+			      bool en, u32 id);
 	void (*rawhst0_config)(struct rkisp_isp_params_vdev *params_vdev,
-			       const struct isp2x_rawhistlite_cfg *arg);
+			       const struct isp2x_rawhistlite_cfg *arg, u32 id);
 	void (*rawhst0_enable)(struct rkisp_isp_params_vdev *params_vdev,
-			       bool en);
+			       bool en, u32 id);
 	void (*rawhst1_config)(struct rkisp_isp_params_vdev *params_vdev,
-			       const struct isp2x_rawhistbig_cfg *arg);
+			       const struct isp2x_rawhistbig_cfg *arg, u32 id);
 	void (*rawhst1_enable)(struct rkisp_isp_params_vdev *params_vdev,
-			       bool en);
+			       bool en, u32 id);
 	void (*rawhst2_config)(struct rkisp_isp_params_vdev *params_vdev,
-			       const struct isp2x_rawhistbig_cfg *arg);
+			       const struct isp2x_rawhistbig_cfg *arg, u32 id);
 	void (*rawhst2_enable)(struct rkisp_isp_params_vdev *params_vdev,
-			       bool en);
+			       bool en, u32 id);
 	void (*rawhst3_config)(struct rkisp_isp_params_vdev *params_vdev,
-			       const struct isp2x_rawhistbig_cfg *arg);
+			       const struct isp2x_rawhistbig_cfg *arg, u32 id);
 	void (*rawhst3_enable)(struct rkisp_isp_params_vdev *params_vdev,
-			       bool en);
+			       bool en, u32 id);
 	void (*hdrdrc_config)(struct rkisp_isp_params_vdev *params_vdev,
 			      const struct isp32_drc_cfg *arg,
-			      enum rkisp_params_type type);
+			      enum rkisp_params_type type, u32 id);
 	void (*hdrdrc_enable)(struct rkisp_isp_params_vdev *params_vdev,
-			      bool en);
+			      bool en, u32 id);
 	void (*hdrmge_config)(struct rkisp_isp_params_vdev *params_vdev,
 			      const struct isp32_hdrmge_cfg *arg,
-			      enum rkisp_params_type type);
+			      enum rkisp_params_type type, u32 id);
 	void (*hdrmge_enable)(struct rkisp_isp_params_vdev *params_vdev,
-			      bool en);
+			      bool en, u32 id);
 	void (*gic_config)(struct rkisp_isp_params_vdev *params_vdev,
-			   const struct isp21_gic_cfg *arg);
+			   const struct isp21_gic_cfg *arg, u32 id);
 	void (*gic_enable)(struct rkisp_isp_params_vdev *params_vdev,
-			   bool en);
+			   bool en, u32 id);
 	void (*dhaz_config)(struct rkisp_isp_params_vdev *params_vdev,
-			    const struct isp32_dhaz_cfg *arg);
+			    const struct isp32_dhaz_cfg *arg, u32 id);
 	void (*dhaz_enable)(struct rkisp_isp_params_vdev *params_vdev,
-			    bool en);
+			    bool en, u32 id);
 	void (*isp3dlut_config)(struct rkisp_isp_params_vdev *params_vdev,
-				const struct isp2x_3dlut_cfg *arg);
+				const struct isp2x_3dlut_cfg *arg, u32 id);
 	void (*isp3dlut_enable)(struct rkisp_isp_params_vdev *params_vdev,
-				bool en);
+				bool en, u32 id);
 	void (*ldch_config)(struct rkisp_isp_params_vdev *params_vdev,
-			    const struct isp32_ldch_cfg *arg);
+			    const struct isp32_ldch_cfg *arg, u32 id);
 	void (*ldch_enable)(struct rkisp_isp_params_vdev *params_vdev,
-			    bool en);
+			    bool en, u32 id);
 	void (*ynr_config)(struct rkisp_isp_params_vdev *params_vdev,
-			   const struct isp32_ynr_cfg *arg);
+			   const struct isp32_ynr_cfg *arg, u32 id);
 	void (*ynr_enable)(struct rkisp_isp_params_vdev *params_vdev,
-			   bool en);
+			   bool en, u32 id);
 	void (*cnr_config)(struct rkisp_isp_params_vdev *params_vdev,
-			   const struct isp32_cnr_cfg *arg);
+			   const struct isp32_cnr_cfg *arg, u32 id);
 	void (*cnr_enable)(struct rkisp_isp_params_vdev *params_vdev,
-			   bool en);
+			   bool en, u32 id);
 	void (*sharp_config)(struct rkisp_isp_params_vdev *params_vdev,
-			     const struct isp32_sharp_cfg *arg);
+			     const struct isp32_sharp_cfg *arg, u32 id);
 	void (*sharp_enable)(struct rkisp_isp_params_vdev *params_vdev,
-			     bool en);
+			     bool en, u32 id);
 	void (*baynr_config)(struct rkisp_isp_params_vdev *params_vdev,
-			     const struct isp32_baynr_cfg *arg);
+			     const struct isp32_baynr_cfg *arg, u32 id);
 	void (*baynr_enable)(struct rkisp_isp_params_vdev *params_vdev,
-			     bool en);
+			     bool en, u32 id);
 	void (*bay3d_config)(struct rkisp_isp_params_vdev *params_vdev,
-			     const struct isp32_bay3d_cfg *arg);
+			     const struct isp32_bay3d_cfg *arg, u32 id);
 	void (*bay3d_enable)(struct rkisp_isp_params_vdev *params_vdev,
-			     bool en);
+			     bool en, u32 id);
 	void (*gain_config)(struct rkisp_isp_params_vdev *params_vdev,
-			     const struct isp3x_gain_cfg *arg);
+			     const struct isp3x_gain_cfg *arg, u32 id);
 	void (*gain_enable)(struct rkisp_isp_params_vdev *params_vdev,
-			     bool en);
+			     bool en, u32 id);
 	void (*cac_config)(struct rkisp_isp_params_vdev *params_vdev,
-			     const struct isp32_cac_cfg *arg);
+			     const struct isp32_cac_cfg *arg, u32 id);
 	void (*cac_enable)(struct rkisp_isp_params_vdev *params_vdev,
-			     bool en);
+			     bool en, u32 id);
 	void (*csm_config)(struct rkisp_isp_params_vdev *params_vdev,
-			   const struct isp21_csm_cfg *arg);
+			   const struct isp21_csm_cfg *arg, u32 id);
 	void (*cgc_config)(struct rkisp_isp_params_vdev *params_vdev,
-			   const struct isp21_cgc_cfg *arg);
+			   const struct isp21_cgc_cfg *arg, u32 id);
 	void (*vsm_config)(struct rkisp_isp_params_vdev *params_vdev,
-			   const struct isp32_vsm_cfg *arg);
+			   const struct isp32_vsm_cfg *arg, u32 id);
 	void (*vsm_enable)(struct rkisp_isp_params_vdev *params_vdev,
-			   bool en);
+			   bool en, u32 id);
 };
 
 struct rkisp_isp_params_val_v32 {
 	struct tasklet_struct lsc_tasklet;
 
-	struct rkisp_dummy_buffer buf_3dlut[ISP32_3DLUT_BUF_NUM];
-	u32 buf_3dlut_idx;
+	struct rkisp_dummy_buffer buf_3dlut[ISP3_UNITE_MAX][ISP32_3DLUT_BUF_NUM];
+	u32 buf_3dlut_idx[ISP3_UNITE_MAX];
+
+	struct rkisp_dummy_buffer buf_ldch[ISP3_UNITE_MAX][ISP3X_MESH_BUF_NUM];
+	u32 buf_ldch_idx[ISP3_UNITE_MAX];
+
+	struct rkisp_dummy_buffer buf_cac[ISP3_UNITE_MAX][ISP3X_MESH_BUF_NUM];
+	u32 buf_cac_idx[ISP3_UNITE_MAX];
 
 	struct rkisp_dummy_buffer buf_lsclut[ISP32_LSC_LUT_BUF_NUM];
 	u32 buf_lsclut_idx;
-
-	struct rkisp_dummy_buffer buf_ldch[ISP3X_MESH_BUF_NUM];
-	u32 buf_ldch_idx;
-
-	struct rkisp_dummy_buffer buf_cac[ISP3X_MESH_BUF_NUM];
-	u32 buf_cac_idx;
 
 	struct rkisp_dummy_buffer buf_info[RKISP_INFO2DDR_BUF_MAX];
 	u32 buf_info_owner;
 	u32 buf_info_cnt;
 	int buf_info_idx;
 
+	u32 bay3d_ds_size;
+	u32 bay3d_iir_size;
 	u32 bay3d_cur_size;
 	u32 bay3d_cur_wsize;
 	u32 bay3d_cur_wrap_line;
@@ -200,11 +202,6 @@
 	struct rkisp_dummy_buffer buf_3dnr_ds;
 
 	struct rkisp_dummy_buffer buf_frm;
-
-	struct isp32_hdrmge_cfg last_hdrmge;
-	struct isp32_drc_cfg last_hdrdrc;
-	struct isp32_hdrmge_cfg cur_hdrmge;
-	struct isp32_drc_cfg cur_hdrdrc;
 
 	bool dhaz_en;
 	bool drc_en;
diff --git a/kernel/drivers/media/platform/rockchip/isp/isp_params_v3x.c b/kernel/drivers/media/platform/rockchip/isp/isp_params_v3x.c
index 87db6aa..bd1e557 100644
--- a/kernel/drivers/media/platform/rockchip/isp/isp_params_v3x.c
+++ b/kernel/drivers/media/platform/rockchip/isp/isp_params_v3x.c
@@ -591,7 +591,7 @@
 	struct isp3x_isp_params_cfg *params = params_vdev->isp3x_params;
 
 	isp_lsc_matrix_cfg_sram(params_vdev, &params->others.lsc_cfg, true, 0);
-	if (params_vdev->dev->hw_dev->is_unite) {
+	if (params_vdev->dev->hw_dev->unite) {
 		params++;
 		isp_lsc_matrix_cfg_sram(params_vdev, &params->others.lsc_cfg, true, 1);
 	}
@@ -1223,7 +1223,7 @@
 
 	block_hsize = arg->win.h_size / ae_wnd_num[wnd_num_idx];
 	value = block_hsize * ae_wnd_num[wnd_num_idx] + arg->win.h_offs;
-	if (ispdev->hw_dev->is_unite)
+	if (ispdev->hw_dev->unite)
 		width = width / 2 + RKMOUDLE_UNITE_EXTEND_PIXEL;
 	if (value + 1 > width)
 		block_hsize -= 1;
@@ -1310,7 +1310,7 @@
 
 	block_hsize = arg->win.h_size / ae_wnd_num[wnd_num_idx];
 	value = block_hsize * ae_wnd_num[wnd_num_idx] + arg->win.h_offs;
-	if (ispdev->hw_dev->is_unite)
+	if (ispdev->hw_dev->unite)
 		width = width / 2 + RKMOUDLE_UNITE_EXTEND_PIXEL;
 	if (value + 1 > width)
 		block_hsize -= 1;
@@ -2887,7 +2887,7 @@
 	value = ISP_PACK_2SHORT(arg->sigma_lut[i * 2], 0);
 	isp3_param_write(params_vdev, value, ISP3X_DHAZ_GAIN_LUT0 + i * 4, id);
 
-	if (dev->hw_dev->is_unite &&
+	if (dev->hw_dev->unite &&
 	    dev->hw_dev->is_single &&
 	    ctrl & ISP3X_DHAZ_ENMUX)
 		ctrl |= ISP3X_SELF_FORCE_UPD;
@@ -4095,22 +4095,6 @@
 		ops->rawawb_enable(params_vdev, !!(module_ens & ISP3X_MODULE_RAWAWB), id);
 }
 
-static __maybe_unused
-void __isp_config_hdrshd(struct rkisp_isp_params_vdev *params_vdev)
-{
-	struct rkisp_isp_params_ops_v3x *ops =
-		(struct rkisp_isp_params_ops_v3x *)params_vdev->priv_ops;
-	struct rkisp_isp_params_val_v3x *priv_val =
-		(struct rkisp_isp_params_val_v3x *)params_vdev->priv_val;
-
-	if (params_vdev->dev->hw_dev->is_unite) {
-		ops->hdrmge_config(params_vdev, &priv_val->last_hdrmge, RKISP_PARAMS_SHD, 1);
-		ops->hdrdrc_config(params_vdev, &priv_val->last_hdrdrc, RKISP_PARAMS_SHD, 1);
-	}
-	ops->hdrmge_config(params_vdev, &priv_val->last_hdrmge, RKISP_PARAMS_SHD, 0);
-	ops->hdrdrc_config(params_vdev, &priv_val->last_hdrdrc, RKISP_PARAMS_SHD, 0);
-}
-
 static
 void rkisp_params_cfgsram_v3x(struct rkisp_isp_params_vdev *params_vdev)
 {
@@ -4121,7 +4105,7 @@
 	isp_rawhstbig_cfg_sram(params_vdev, &params->meas.rawhist2, 2, true, 0);
 	isp_rawhstbig_cfg_sram(params_vdev, &params->meas.rawhist3, 0, true, 0);
 	isp_rawawb_cfg_sram(params_vdev, &params->meas.rawawb, true, 0);
-	if (params_vdev->dev->hw_dev->is_unite) {
+	if (params_vdev->dev->hw_dev->unite) {
 		params++;
 		isp_lsc_matrix_cfg_sram(params_vdev, &params->others.lsc_cfg, true, 1);
 		isp_rawhstbig_cfg_sram(params_vdev, &params->meas.rawhist1, 1, true, 1);
@@ -4145,7 +4129,7 @@
 	module_en_update = new_params->module_en_update;
 	module_ens = new_params->module_ens;
 
-	for (id = 0; id <= ispdev->hw_dev->is_unite; id++) {
+	for (id = 0; id <= !!ispdev->hw_dev->unite; id++) {
 		priv_val->buf_3dlut_idx[id] = 0;
 		for (i = 0; i < ISP3X_3DLUT_BUF_NUM; i++) {
 			priv_val->buf_3dlut[id][i].is_need_vaddr = true;
@@ -4162,10 +4146,10 @@
 	    (module_ens & ISP3X_MODULE_BAY3D)) {
 		w = ALIGN(isp_sdev->in_crop.width, 16);
 		h = ALIGN(isp_sdev->in_crop.height, 16);
-		if (ispdev->hw_dev->is_unite)
+		if (ispdev->hw_dev->unite)
 			w = ALIGN(isp_sdev->in_crop.width / 2 + RKMOUDLE_UNITE_EXTEND_PIXEL, 16);
 
-		for (id = 0; id <= ispdev->hw_dev->is_unite; id++) {
+		for (id = 0; id <= !!ispdev->hw_dev->unite; id++) {
 			size = ALIGN((w + w / 8) * h * 2, 16);
 
 			priv_val->buf_3dnr_iir[id].size = size;
@@ -4201,7 +4185,7 @@
 		rkisp_free_buffer(ispdev, &priv_val->buf_3dnr_cur[id]);
 		rkisp_free_buffer(ispdev, &priv_val->buf_3dnr_ds[id]);
 	}
-	id = ispdev->hw_dev->is_unite ? 1 : 0;
+	id = ispdev->hw_dev->unite ? 1 : 0;
 	i = ISP3X_3DLUT_BUF_NUM;
 err_3dlut:
 	for (; id >= 0; id--) {
@@ -4256,7 +4240,7 @@
 				continue;
 			dev_warn(dev, "isp%d %dx%d over four vir isp max:%dx1536\n",
 				 i, hw->isp_size[i].w, hw->isp_size[i].h,
-				 hw->is_unite ? (2560 - RKMOUDLE_UNITE_EXTEND_PIXEL) * 2 : 2560);
+				 hw->unite ? (2560 - RKMOUDLE_UNITE_EXTEND_PIXEL) * 2 : 2560);
 			hw->is_multi_overflow = true;
 			goto multi_overflow;
 		}
@@ -4298,7 +4282,7 @@
 			    (hw->isp_size[idx1[0]].size > ISP3X_VIR2_MAX_SIZE)) {
 				dev_warn(dev, "isp%d %dx%d over three vir isp max:%dx1536\n",
 					 idx1[0], hw->isp_size[idx1[0]].w, hw->isp_size[idx1[0]].h,
-					 hw->is_unite ? (2560 - RKMOUDLE_UNITE_EXTEND_PIXEL) * 2 : 2560);
+					 hw->unite ? (2560 - RKMOUDLE_UNITE_EXTEND_PIXEL) * 2 : 2560);
 				hw->is_multi_overflow = true;
 				goto multi_overflow;
 			} else {
@@ -4357,7 +4341,7 @@
 			    hw->isp_size[idx1[k - 1]].size > (ISP3X_VIR4_MAX_SIZE + ISP3X_VIR2_MAX_SIZE)) {
 				dev_warn(dev, "isp%d %dx%d over two vir isp max:%dx2160\n",
 					 idx1[k - 1], hw->isp_size[idx1[k - 1]].w, hw->isp_size[idx1[k - 1]].h,
-					 hw->is_unite ? (3840 - RKMOUDLE_UNITE_EXTEND_PIXEL) * 2 : 3840);
+					 hw->unite ? (3840 - RKMOUDLE_UNITE_EXTEND_PIXEL) * 2 : 3840);
 				hw->is_multi_overflow = true;
 				goto multi_overflow;
 			} else {
@@ -4379,7 +4363,7 @@
 		ispdev->multi_mode = 0;
 		ispdev->multi_index = 0;
 		width = crop->width;
-		if (hw->is_unite)
+		if (hw->unite)
 			width = width / 2 + RKMOUDLE_UNITE_EXTEND_PIXEL;
 		height = crop->height;
 		size = width * height;
@@ -4416,7 +4400,7 @@
 	priv_val->lsc_en = 0;
 	priv_val->mge_en = 0;
 	priv_val->lut3d_en = 0;
-	if (hw->is_unite) {
+	if (hw->unite) {
 		if (dev->is_bigmode)
 			rkisp_next_set_bits(params_vdev->dev, ISP3X_ISP_CTRL1, 0,
 					    ISP3X_BIGMODE_MANUAL | ISP3X_BIGMODE_FORCE_EN, false);
@@ -4432,11 +4416,6 @@
 	__isp_isr_other_config(params_vdev, params_vdev->isp3x_params, RKISP_PARAMS_ALL, 0);
 	__isp_isr_other_en(params_vdev, params_vdev->isp3x_params, RKISP_PARAMS_ALL, 0);
 	__isp_isr_meas_en(params_vdev, params_vdev->isp3x_params, RKISP_PARAMS_ALL, 0);
-
-	priv_val->cur_hdrmge = params_vdev->isp3x_params->others.hdrmge_cfg;
-	priv_val->cur_hdrdrc = params_vdev->isp3x_params->others.drc_cfg;
-	priv_val->last_hdrmge = priv_val->cur_hdrmge;
-	priv_val->last_hdrdrc = priv_val->cur_hdrdrc;
 	spin_unlock(&params_vdev->config_lock);
 }
 
@@ -4452,7 +4431,7 @@
 
 static void rkisp_clear_first_param_v3x(struct rkisp_isp_params_vdev *params_vdev)
 {
-	u32 mult = params_vdev->dev->hw_dev->is_unite ? ISP3_UNITE_MAX : 1;
+	u32 mult = params_vdev->dev->hw_dev->unite ? ISP3_UNITE_MAX : 1;
 	u32 size = sizeof(struct isp3x_isp_params_cfg) * mult;
 
 	memset(params_vdev->isp3x_params, 0, size);
@@ -4551,7 +4530,7 @@
 rkisp_get_param_size_v3x(struct rkisp_isp_params_vdev *params_vdev,
 			 unsigned int sizes[])
 {
-	u32 mult = params_vdev->dev->hw_dev->is_unite ? ISP3_UNITE_MAX : 1;
+	u32 mult = params_vdev->dev->hw_dev->unite ? ISP3_UNITE_MAX : 1;
 
 	sizes[0] = sizeof(struct isp3x_isp_params_cfg) * mult;
 }
@@ -4596,7 +4575,7 @@
 {
 	struct rkisp_meshbuf_size *meshsize = size;
 
-	if (!params_vdev->dev->hw_dev->is_unite)
+	if (!params_vdev->dev->hw_dev->unite)
 		meshsize->unite_isp_id = 0;
 	rkisp_deinit_mesh_buf(params_vdev, meshsize->module_id, meshsize->unite_isp_id);
 	return rkisp_init_mesh_buf(params_vdev, meshsize);
@@ -4608,7 +4587,7 @@
 {
 	int id;
 
-	for (id = 0; id <= params_vdev->dev->hw_dev->is_unite; id++)
+	for (id = 0; id <= !!params_vdev->dev->hw_dev->unite; id++)
 		rkisp_deinit_mesh_buf(params_vdev, module_id, id);
 }
 
@@ -4621,7 +4600,7 @@
 
 	priv_val = (struct rkisp_isp_params_val_v3x *)params_vdev->priv_val;
 	tasklet_disable(&priv_val->lsc_tasklet);
-	for (id = 0; id <= ispdev->hw_dev->is_unite; id++) {
+	for (id = 0; id <= !!ispdev->hw_dev->unite; id++) {
 		rkisp_free_buffer(ispdev, &priv_val->buf_3dnr_iir[id]);
 		rkisp_free_buffer(ispdev, &priv_val->buf_3dnr_cur[id]);
 		rkisp_free_buffer(ispdev, &priv_val->buf_3dnr_ds[id]);
@@ -4637,7 +4616,7 @@
 {
 	int id;
 
-	for (id = 0; id <= params_vdev->dev->hw_dev->is_unite; id++) {
+	for (id = 0; id <= !!params_vdev->dev->hw_dev->unite; id++) {
 		rkisp_deinit_mesh_buf(params_vdev, ISP3X_MODULE_LDCH, id);
 		rkisp_deinit_mesh_buf(params_vdev, ISP3X_MODULE_CAC, id);
 	}
@@ -4652,7 +4631,7 @@
 
 	__isp_isr_other_en(params_vdev, params_vdev->isp3x_params, RKISP_PARAMS_ALL, 0);
 	__isp_isr_meas_en(params_vdev, params_vdev->isp3x_params, RKISP_PARAMS_ALL, 0);
-	if (params_vdev->dev->hw_dev->is_unite) {
+	if (params_vdev->dev->hw_dev->unite) {
 		__isp_isr_other_en(params_vdev, params_vdev->isp3x_params, RKISP_PARAMS_ALL, 1);
 		__isp_isr_meas_en(params_vdev, params_vdev->isp3x_params, RKISP_PARAMS_ALL, 1);
 	}
@@ -4725,7 +4704,7 @@
 				__isp_isr_other_en(params_vdev, new_params, type, 0);
 				__isp_isr_meas_en(params_vdev, new_params, type, 0);
 				new_params->module_cfg_update = 0;
-				if (hw_dev->is_unite) {
+				if (hw_dev->unite) {
 					struct isp3x_isp_params_cfg *params = new_params + 1;
 
 					__isp_isr_meas_config(params_vdev, params, type, 1);
@@ -4738,7 +4717,7 @@
 			if (new_params->module_cfg_update &
 			    (ISP3X_MODULE_LDCH | ISP3X_MODULE_CAC)) {
 				module_data_abandon(params_vdev, new_params, 0);
-				if (hw_dev->is_unite)
+				if (hw_dev->unite)
 					module_data_abandon(params_vdev, new_params, 1);
 			}
 			vb2_buffer_done(&cur_buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
@@ -4756,7 +4735,7 @@
 		goto unlock;
 
 	new_params = (struct isp3x_isp_params_cfg *)(cur_buf->vaddr[0]);
-	if (hw_dev->is_unite) {
+	if (hw_dev->unite) {
 		__isp_isr_meas_config(params_vdev, new_params + 1, type, 1);
 		__isp_isr_other_config(params_vdev, new_params + 1, type, 1);
 		__isp_isr_other_en(params_vdev, new_params + 1, type, 1);
@@ -4766,19 +4745,10 @@
 	__isp_isr_other_config(params_vdev, new_params, type, 0);
 	__isp_isr_other_en(params_vdev, new_params, type, 0);
 	__isp_isr_meas_en(params_vdev, new_params, type, 0);
-	if (!hw_dev->is_single && type != RKISP_PARAMS_SHD)
-		__isp_config_hdrshd(params_vdev);
 
 	if (type != RKISP_PARAMS_IMD) {
-		struct rkisp_isp_params_val_v3x *priv_val =
-			(struct rkisp_isp_params_val_v3x *)params_vdev->priv_val;
-
-		priv_val->last_hdrmge = priv_val->cur_hdrmge;
-		priv_val->last_hdrdrc = priv_val->cur_hdrdrc;
-		priv_val->cur_hdrmge = new_params->others.hdrmge_cfg;
-		priv_val->cur_hdrdrc = new_params->others.drc_cfg;
 		new_params->module_cfg_update = 0;
-		if (hw_dev->is_unite)
+		if (hw_dev->unite)
 			(new_params++)->module_cfg_update = 0;
 		vb2_buffer_done(&cur_buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
 		cur_buf = NULL;
@@ -4812,7 +4782,7 @@
 	if (value & ISP3X_RAW3D_FST_FRAME)
 		rkisp_clear_bits(params_vdev->dev, ISP3X_ISP_CTRL1,
 				 ISP3X_RAW3D_FST_FRAME, false);
-	if (hw_dev->is_unite) {
+	if (hw_dev->unite) {
 		value = rkisp_next_read(dev, ISP3X_ISP_CTRL1, false);
 		if (value & ISP3X_YNR_FST_FRAME)
 			rkisp_next_clear_bits(params_vdev->dev, ISP3X_ISP_CTRL1,
@@ -4887,7 +4857,7 @@
 		return -ENOMEM;
 
 	size = sizeof(struct isp3x_isp_params_cfg);
-	if (ispdev->hw_dev->is_unite)
+	if (ispdev->hw_dev->unite)
 		size *= 2;
 	params_vdev->isp3x_params = vmalloc(size);
 	if (!params_vdev->isp3x_params) {
diff --git a/kernel/drivers/media/platform/rockchip/isp/isp_params_v3x.h b/kernel/drivers/media/platform/rockchip/isp/isp_params_v3x.h
index 31c2241..0d5041d 100644
--- a/kernel/drivers/media/platform/rockchip/isp/isp_params_v3x.h
+++ b/kernel/drivers/media/platform/rockchip/isp/isp_params_v3x.h
@@ -187,11 +187,6 @@
 	struct rkisp_dummy_buffer buf_3dnr_cur[ISP3_UNITE_MAX];
 	struct rkisp_dummy_buffer buf_3dnr_ds[ISP3_UNITE_MAX];
 
-	struct isp3x_hdrmge_cfg last_hdrmge;
-	struct isp3x_drc_cfg last_hdrdrc;
-	struct isp3x_hdrmge_cfg cur_hdrmge;
-	struct isp3x_drc_cfg cur_hdrdrc;
-
 	bool dhaz_en;
 	bool drc_en;
 	bool lsc_en;
diff --git a/kernel/drivers/media/platform/rockchip/isp/isp_rockit.c b/kernel/drivers/media/platform/rockchip/isp/isp_rockit.c
index 0ba72aa..d97cc27 100644
--- a/kernel/drivers/media/platform/rockchip/isp/isp_rockit.c
+++ b/kernel/drivers/media/platform/rockchip/isp/isp_rockit.c
@@ -657,3 +657,37 @@
 		return 0;
 }
 EXPORT_SYMBOL(rkisp_rockit_get_ispdev);
+
+int rkisp_rockit_get_isp_mode(const char *name)
+{
+	struct rkisp_device *ispdev = NULL;
+	int i, ret = -EINVAL;
+
+	if (rockit_cfg == NULL)
+		goto end;
+
+	for (i = 0; i < rockit_cfg->isp_num; i++) {
+		if (!strcmp(rockit_cfg->rkisp_dev_cfg[i].isp_name, name)) {
+			ispdev = rockit_cfg->rkisp_dev_cfg[i].isp_dev;
+			break;
+		}
+	}
+	if (!ispdev)
+		goto end;
+
+	if (ispdev->is_pre_on) {
+		if (IS_HDR_RDBK(ispdev->rd_mode))
+			ret = RKISP_FAST_OFFLINE;
+		else
+			ret = RKISP_FAST_ONLINE;
+	} else {
+		if (IS_HDR_RDBK(ispdev->rd_mode))
+			ret = RKISP_NORMAL_OFFLINE;
+		else
+			ret = RKISP_NORMAL_ONLINE;
+	}
+
+end:
+	return ret;
+}
+EXPORT_SYMBOL(rkisp_rockit_get_isp_mode);
diff --git a/kernel/drivers/media/platform/rockchip/isp/isp_stats.c b/kernel/drivers/media/platform/rockchip/isp/isp_stats.c
index 372e4d5..d95315e 100644
--- a/kernel/drivers/media/platform/rockchip/isp/isp_stats.c
+++ b/kernel/drivers/media/platform/rockchip/isp/isp_stats.c
@@ -222,7 +222,6 @@
 {
 	struct rkisp_isp_stats_vdev *stats_vdev = queue->drv_priv;
 
-	stats_vdev->rdbk_drop = false;
 	stats_vdev->cur_buf = NULL;
 	stats_vdev->ops->rdbk_enable(stats_vdev, false);
 	stats_vdev->streamon = true;
diff --git a/kernel/drivers/media/platform/rockchip/isp/isp_stats_v32.c b/kernel/drivers/media/platform/rockchip/isp/isp_stats_v32.c
index bfc9624..cd0a490 100644
--- a/kernel/drivers/media/platform/rockchip/isp/isp_stats_v32.c
+++ b/kernel/drivers/media/platform/rockchip/isp/isp_stats_v32.c
@@ -428,6 +428,8 @@
 	struct rkisp_device *dev = stats_vdev->dev;
 	struct rkisp_buffer *buf;
 	unsigned long flags;
+	u32 size = stats_vdev->vdev_fmt.fmt.meta.buffersize;
+	u32 val = 0;
 
 	spin_lock_irqsave(&stats_vdev->rd_lock, flags);
 	if (!stats_vdev->nxt_buf && !list_empty(&stats_vdev->stat)) {
@@ -439,18 +441,23 @@
 	spin_unlock_irqrestore(&stats_vdev->rd_lock, flags);
 
 	if (stats_vdev->nxt_buf) {
-		rkisp_write(dev, ISP3X_MI_3A_WR_BASE, stats_vdev->nxt_buf->buff_addr[0], false);
+		val = stats_vdev->nxt_buf->buff_addr[0];
 		v4l2_dbg(2, rkisp_debug, &dev->v4l2_dev,
 			 "%s BASE:0x%x SHD:0x%x\n",
-			 __func__, stats_vdev->nxt_buf->buff_addr[0],
+			 __func__, val,
 			 isp3_stats_read(stats_vdev, ISP3X_MI_3A_WR_BASE));
 		if (!dev->hw_dev->is_single) {
 			stats_vdev->cur_buf = stats_vdev->nxt_buf;
 			stats_vdev->nxt_buf = NULL;
 		}
 	} else if (stats_vdev->stats_buf[0].mem_priv) {
-		rkisp_write(dev, ISP3X_MI_3A_WR_BASE,
-			    stats_vdev->stats_buf[0].dma_addr, false);
+		val = stats_vdev->stats_buf[0].dma_addr;
+	}
+
+	if (val) {
+		rkisp_write(dev, ISP3X_MI_3A_WR_BASE, val, false);
+		if (dev->hw_dev->unite)
+			rkisp_next_write(dev, ISP3X_MI_3A_WR_BASE, val + size / 2, false);
 	}
 }
 
@@ -533,14 +540,15 @@
 rkisp_stats_send_meas(struct rkisp_isp_stats_vdev *stats_vdev,
 		      struct rkisp_isp_readout_work *meas_work)
 {
-	unsigned int cur_frame_id = -1;
+	struct rkisp_device *dev = stats_vdev->dev;
+	struct rkisp_hw_dev *hw = dev->hw_dev;
+	struct rkisp_isp_params_vdev *params_vdev = &dev->params_vdev;
 	struct rkisp_buffer *cur_buf = stats_vdev->cur_buf;
 	struct rkisp32_isp_stat_buffer *cur_stat_buf = NULL;
 	struct rkisp_stats_ops_v32 *ops =
 		(struct rkisp_stats_ops_v32 *)stats_vdev->priv_ops;
-	struct rkisp_isp_params_vdev *params_vdev = &stats_vdev->dev->params_vdev;
-	u32 size = sizeof(struct rkisp32_isp_stat_buffer);
-	int ret = 0;
+	u32 size = stats_vdev->vdev_fmt.fmt.meta.buffersize;
+	u32 cur_frame_id = meas_work->frame_id;
 	bool is_dummy = false;
 	unsigned long flags;
 
@@ -548,97 +556,107 @@
 		if (!cur_buf && stats_vdev->stats_buf[0].mem_priv) {
 			rkisp_finish_buffer(stats_vdev->dev, &stats_vdev->stats_buf[0]);
 			cur_stat_buf = stats_vdev->stats_buf[0].vaddr;
-			cur_stat_buf->frame_id = -1;
+			cur_stat_buf->frame_id = cur_frame_id;
+			cur_stat_buf->params_id = params_vdev->cur_frame_id;
 			is_dummy = true;
 		} else if (cur_buf) {
 			cur_stat_buf = cur_buf->vaddr[0];
+			cur_stat_buf->frame_id = cur_frame_id;
+			cur_stat_buf->params_id = params_vdev->cur_frame_id;
 		}
-		/* config buf for next frame */
-		stats_vdev->cur_buf = NULL;
-		if (stats_vdev->nxt_buf) {
-			stats_vdev->cur_buf = stats_vdev->nxt_buf;
-			stats_vdev->nxt_buf = NULL;
-		}
-		rkisp_stats_update_buf(stats_vdev);
 
-		cur_frame_id = meas_work->frame_id;
+		/* buffer done when frame of right handle */
+		if (hw->unite == ISP_UNITE_ONE) {
+			if (dev->unite_index == ISP_UNITE_LEFT) {
+				cur_buf = NULL;
+				is_dummy = false;
+			} else if (cur_stat_buf) {
+				cur_stat_buf = (void *)cur_stat_buf + size / 2;
+				cur_stat_buf->frame_id = cur_frame_id;
+				cur_stat_buf->params_id = params_vdev->cur_frame_id;
+			}
+		}
+
+		if (hw->unite != ISP_UNITE_ONE || dev->unite_index == ISP_UNITE_RIGHT) {
+			/* config buf for next frame */
+			stats_vdev->cur_buf = NULL;
+			if (stats_vdev->nxt_buf) {
+				stats_vdev->cur_buf = stats_vdev->nxt_buf;
+				stats_vdev->nxt_buf = NULL;
+			}
+			rkisp_stats_update_buf(stats_vdev);
+		}
 	} else {
 		cur_buf = NULL;
 	}
 
 	if (meas_work->isp3a_ris & ISP3X_3A_RAWAWB)
-		ret |= ops->get_rawawb_meas(stats_vdev, cur_stat_buf);
+		ops->get_rawawb_meas(stats_vdev, cur_stat_buf);
 
 	if (meas_work->isp3a_ris & ISP3X_3A_RAWAF ||
 	    stats_vdev->af_meas_done_next)
-		ret |= ops->get_rawaf_meas(stats_vdev, cur_stat_buf);
+		ops->get_rawaf_meas(stats_vdev, cur_stat_buf);
 
 	if (meas_work->isp3a_ris & ISP3X_3A_RAWAE_BIG ||
 	    stats_vdev->ae_meas_done_next)
-		ret |= ops->get_rawae3_meas(stats_vdev, cur_stat_buf);
+		ops->get_rawae3_meas(stats_vdev, cur_stat_buf);
 
 	if (meas_work->isp3a_ris & ISP3X_3A_RAWHIST_BIG)
-		ret |= ops->get_rawhst3_meas(stats_vdev, cur_stat_buf);
+		ops->get_rawhst3_meas(stats_vdev, cur_stat_buf);
 
 	if (meas_work->isp3a_ris & ISP3X_3A_RAWAE_CH0)
-		ret |= ops->get_rawae0_meas(stats_vdev, cur_stat_buf);
+		ops->get_rawae0_meas(stats_vdev, cur_stat_buf);
 
 	if (meas_work->isp3a_ris & ISP3X_3A_RAWAE_CH1)
-		ret |= ops->get_rawae1_meas(stats_vdev, cur_stat_buf);
+		ops->get_rawae1_meas(stats_vdev, cur_stat_buf);
 
 	if (meas_work->isp3a_ris & ISP3X_3A_RAWAE_CH2)
-		ret |= ops->get_rawae2_meas(stats_vdev, cur_stat_buf);
+		ops->get_rawae2_meas(stats_vdev, cur_stat_buf);
 
 	if (meas_work->isp3a_ris & ISP3X_3A_RAWHIST_CH0)
-		ret |= ops->get_rawhst0_meas(stats_vdev, cur_stat_buf);
+		ops->get_rawhst0_meas(stats_vdev, cur_stat_buf);
 
 	if (meas_work->isp3a_ris & ISP3X_3A_RAWHIST_CH1)
-		ret |= ops->get_rawhst1_meas(stats_vdev, cur_stat_buf);
+		ops->get_rawhst1_meas(stats_vdev, cur_stat_buf);
 
 	if (meas_work->isp3a_ris & ISP3X_3A_RAWHIST_CH2)
-		ret |= ops->get_rawhst2_meas(stats_vdev, cur_stat_buf);
+		ops->get_rawhst2_meas(stats_vdev, cur_stat_buf);
 
 	if (meas_work->isp_ris & ISP3X_FRAME) {
-		ret |= ops->get_bls_stats(stats_vdev, cur_stat_buf);
-		ret |= ops->get_dhaz_stats(stats_vdev, cur_stat_buf);
-		ret |= ops->get_vsm_stats(stats_vdev, cur_stat_buf);
+		ops->get_bls_stats(stats_vdev, cur_stat_buf);
+		ops->get_dhaz_stats(stats_vdev, cur_stat_buf);
+		ops->get_vsm_stats(stats_vdev, cur_stat_buf);
 	}
 
-	if (ret || (cur_stat_buf && !cur_stat_buf->meas_type)) {
+	if (is_dummy) {
+		spin_lock_irqsave(&stats_vdev->rd_lock, flags);
+		if (!list_empty(&stats_vdev->stat)) {
+			cur_buf = list_first_entry(&stats_vdev->stat, struct rkisp_buffer, queue);
+			list_del(&cur_buf->queue);
+		}
+		spin_unlock_irqrestore(&stats_vdev->rd_lock, flags);
 		if (cur_buf) {
-			spin_lock_irqsave(&stats_vdev->rd_lock, flags);
-			list_add_tail(&cur_buf->queue, &stats_vdev->stat);
-			spin_unlock_irqrestore(&stats_vdev->rd_lock, flags);
-		}
-	} else {
-		if (is_dummy) {
-			spin_lock_irqsave(&stats_vdev->rd_lock, flags);
-			if (!list_empty(&stats_vdev->stat)) {
-				cur_buf = list_first_entry(&stats_vdev->stat, struct rkisp_buffer, queue);
-				list_del(&cur_buf->queue);
-			} else {
-				cur_stat_buf->frame_id = cur_frame_id;
-				cur_stat_buf->params_id = params_vdev->cur_frame_id;
-			}
-			spin_unlock_irqrestore(&stats_vdev->rd_lock, flags);
-			if (cur_buf) {
-				memcpy(cur_buf->vaddr[0], cur_stat_buf, size);
-				cur_stat_buf = cur_buf->vaddr[0];
-			}
-		}
-		if (cur_buf && cur_stat_buf) {
-			cur_stat_buf->frame_id = cur_frame_id;
-			cur_stat_buf->params_id = params_vdev->cur_frame_id;
-			cur_stat_buf->params.info2ddr.buf_fd = -1;
-			cur_stat_buf->params.info2ddr.owner = 0;
-			rkisp_stats_info2ddr(stats_vdev, cur_stat_buf);
-
-			vb2_set_plane_payload(&cur_buf->vb.vb2_buf, 0, size);
-			cur_buf->vb.sequence = cur_frame_id;
-			cur_buf->vb.vb2_buf.timestamp = meas_work->timestamp;
-			vb2_buffer_done(&cur_buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
+			memcpy(cur_buf->vaddr[0], stats_vdev->stats_buf[0].vaddr, size);
+			cur_stat_buf = cur_buf->vaddr[0];
 		}
 	}
+	if (cur_buf && cur_stat_buf) {
+		cur_stat_buf->frame_id = cur_frame_id;
+		cur_stat_buf->params_id = params_vdev->cur_frame_id;
+		cur_stat_buf->params.info2ddr.buf_fd = -1;
+		cur_stat_buf->params.info2ddr.owner = 0;
+		rkisp_stats_info2ddr(stats_vdev, cur_stat_buf);
+
+		vb2_set_plane_payload(&cur_buf->vb.vb2_buf, 0, size);
+		cur_buf->vb.sequence = cur_frame_id;
+		cur_buf->vb.vb2_buf.timestamp = meas_work->timestamp;
+		vb2_buffer_done(&cur_buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
+	}
+	v4l2_dbg(4, rkisp_debug, &dev->v4l2_dev,
+		 "%s id:%d seq:%d params_id:%d ris:0x%x buf:%p meas_type:0x%x\n",
+		 __func__, dev->unite_index,
+		 cur_frame_id, params_vdev->cur_frame_id, meas_work->isp3a_ris,
+		 cur_buf, !cur_stat_buf ? 0 : cur_stat_buf->meas_type);
 }
 
 static int
@@ -890,12 +908,12 @@
 rkisp_stats_send_meas_lite(struct rkisp_isp_stats_vdev *stats_vdev,
 			   struct rkisp_isp_readout_work *meas_work)
 {
-	struct rkisp_isp_params_vdev *params_vdev = &stats_vdev->dev->params_vdev;
+	struct rkisp_device *dev = stats_vdev->dev;
+	struct rkisp_isp_params_vdev *params_vdev = &dev->params_vdev;
 	unsigned int cur_frame_id = meas_work->frame_id;
 	struct rkisp_buffer *cur_buf = NULL;
 	struct rkisp32_lite_stat_buffer *cur_stat_buf = NULL;
 	u32 size = sizeof(struct rkisp32_lite_stat_buffer);
-	int ret = 0;
 
 	spin_lock(&stats_vdev->rd_lock);
 	if (!list_empty(&stats_vdev->stat)) {
@@ -913,45 +931,42 @@
 	}
 
 	if (meas_work->isp3a_ris & ISP3X_3A_RAWAWB)
-		ret |= rkisp_stats_get_rawawb_meas_lite(stats_vdev, cur_stat_buf);
+		rkisp_stats_get_rawawb_meas_lite(stats_vdev, cur_stat_buf);
 
 	if (meas_work->isp3a_ris & ISP3X_3A_RAWAF ||
 	    stats_vdev->af_meas_done_next)
-		ret |= rkisp_stats_get_rawaf_meas_lite(stats_vdev, cur_stat_buf);
+		rkisp_stats_get_rawaf_meas_lite(stats_vdev, cur_stat_buf);
 
 	if (meas_work->isp3a_ris & ISP3X_3A_RAWAE_BIG ||
 	    stats_vdev->ae_meas_done_next)
-		ret |= rkisp_stats_get_rawae3_meas_lite(stats_vdev, cur_stat_buf);
+		rkisp_stats_get_rawae3_meas_lite(stats_vdev, cur_stat_buf);
 
 	if (meas_work->isp3a_ris & ISP3X_3A_RAWHIST_BIG)
-		ret |= rkisp_stats_get_rawhst3_meas_lite(stats_vdev, cur_stat_buf);
+		rkisp_stats_get_rawhst3_meas_lite(stats_vdev, cur_stat_buf);
 
 	if (meas_work->isp3a_ris & ISP3X_3A_RAWAE_CH0)
-		ret |= rkisp_stats_get_rawaelite_meas_lite(stats_vdev, cur_stat_buf);
+		rkisp_stats_get_rawaelite_meas_lite(stats_vdev, cur_stat_buf);
 
 	if (meas_work->isp3a_ris & ISP3X_3A_RAWHIST_CH0)
-		ret |= rkisp_stats_get_rawhstlite_meas_lite(stats_vdev, cur_stat_buf);
+		rkisp_stats_get_rawhstlite_meas_lite(stats_vdev, cur_stat_buf);
 
 	if (meas_work->isp_ris & ISP3X_FRAME) {
-		ret |= rkisp_stats_get_bls_stats(stats_vdev, cur_stat_buf);
-		ret |= rkisp_stats_get_dhaz_stats(stats_vdev, cur_stat_buf);
+		rkisp_stats_get_bls_stats(stats_vdev, cur_stat_buf);
+		rkisp_stats_get_dhaz_stats(stats_vdev, cur_stat_buf);
 	}
 
 	if (cur_buf) {
-		if (ret || !cur_stat_buf->meas_type) {
-			unsigned long flags;
-
-			spin_lock_irqsave(&stats_vdev->rd_lock, flags);
-			list_add_tail(&cur_buf->queue, &stats_vdev->stat);
-			spin_unlock_irqrestore(&stats_vdev->rd_lock, flags);
-		} else {
-			rkisp_stats_info2ddr(stats_vdev, cur_stat_buf);
-			vb2_set_plane_payload(&cur_buf->vb.vb2_buf, 0, size);
-			cur_buf->vb.sequence = cur_frame_id;
-			cur_buf->vb.vb2_buf.timestamp = meas_work->timestamp;
-			vb2_buffer_done(&cur_buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
-		}
+		rkisp_stats_info2ddr(stats_vdev, cur_stat_buf);
+		vb2_set_plane_payload(&cur_buf->vb.vb2_buf, 0, size);
+		cur_buf->vb.sequence = cur_frame_id;
+		cur_buf->vb.vb2_buf.timestamp = meas_work->timestamp;
+		vb2_buffer_done(&cur_buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
 	}
+	v4l2_dbg(4, rkisp_debug, &dev->v4l2_dev,
+		 "%s seq:%d params_id:%d ris:0x%x buf:%p meas_type:0x%x\n",
+		 __func__,
+		 cur_frame_id, params_vdev->cur_frame_id, meas_work->isp_ris,
+		 cur_buf, !cur_stat_buf ? 0 : cur_stat_buf->meas_type);
 }
 
 static void
@@ -1048,6 +1063,7 @@
 {
 	struct rkisp_device *dev = stats_vdev->dev;
 	u32 size = stats_vdev->vdev_fmt.fmt.meta.buffersize;
+	u32 div = dev->hw_dev->unite ? 2 : 1;
 
 	if (dev->isp_sdev.in_fmt.fmt_type == FMT_YUV)
 		return;
@@ -1059,8 +1075,8 @@
 	else
 		memset(stats_vdev->stats_buf[0].vaddr, 0, size);
 	rkisp_stats_update_buf(stats_vdev);
-	rkisp_write(dev, ISP3X_MI_DBR_WR_SIZE, size, false);
-	rkisp_set_bits(dev, ISP3X_SWS_CFG, 0, ISP3X_3A_DDR_WRITE_EN, false);
+	rkisp_unite_write(dev, ISP3X_MI_DBR_WR_SIZE, size / div, false);
+	rkisp_unite_set_bits(dev, ISP3X_SWS_CFG, 0, ISP3X_3A_DDR_WRITE_EN, false);
 	if (stats_vdev->nxt_buf) {
 		stats_vdev->cur_buf = stats_vdev->nxt_buf;
 		stats_vdev->nxt_buf = NULL;
@@ -1080,6 +1096,7 @@
 
 void rkisp_init_stats_vdev_v32(struct rkisp_isp_stats_vdev *stats_vdev)
 {
+	int mult = stats_vdev->dev->hw_dev->unite ? 2 : 1;
 	u32 size;
 
 	stats_vdev->vdev_fmt.fmt.meta.dataformat =
@@ -1087,13 +1104,13 @@
 	if (stats_vdev->dev->isp_ver == ISP_V32) {
 		stats_vdev->priv_ops = &stats_ddr_ops_v32;
 		stats_vdev->rd_stats_from_ddr = true;
-		size = sizeof(struct rkisp32_isp_stat_buffer);
+		size = ALIGN(sizeof(struct rkisp32_isp_stat_buffer), 16);
 	} else {
 		stats_vdev->priv_ops = NULL;
 		stats_vdev->rd_stats_from_ddr = false;
 		size = sizeof(struct rkisp32_lite_stat_buffer);
 	}
-	stats_vdev->vdev_fmt.fmt.meta.buffersize = size;
+	stats_vdev->vdev_fmt.fmt.meta.buffersize = size * mult;
 	stats_vdev->ops = &rkisp_isp_stats_ops_tbl;
 }
 
diff --git a/kernel/drivers/media/platform/rockchip/isp/isp_stats_v3x.c b/kernel/drivers/media/platform/rockchip/isp/isp_stats_v3x.c
index 7b21a80..12bac61 100644
--- a/kernel/drivers/media/platform/rockchip/isp/isp_stats_v3x.c
+++ b/kernel/drivers/media/platform/rockchip/isp/isp_stats_v3x.c
@@ -1038,7 +1038,7 @@
 		ret |= ops->get_dhaz_stats(stats_vdev, cur_stat_buf, 0);
 	}
 
-	if (stats_vdev->dev->hw_dev->is_unite) {
+	if (stats_vdev->dev->hw_dev->unite) {
 		size *= 2;
 		if (cur_buf) {
 			cur_stat_buf++;
@@ -1086,7 +1086,7 @@
 {
 	struct rkisp_device *dev = stats_vdev->dev;
 	struct rkisp_hw_dev *hw = dev->hw_dev;
-	void __iomem *base = !hw->is_unite ?
+	void __iomem *base = hw->unite != ISP_UNITE_TWO ?
 		hw->base_addr : hw->base_next_addr;
 	struct rkisp_isp_readout_work work;
 	u32 iq_isr_mask = ISP3X_SIAWB_DONE | ISP3X_SIAF_FIN |
@@ -1138,7 +1138,7 @@
 
 		rkisp_write(dev, ISP3X_MI_3A_WR_BASE,
 			    stats_vdev->stats_buf[wr_buf_idx].dma_addr, false);
-		if (dev->hw_dev->is_unite)
+		if (dev->hw_dev->unite)
 			rkisp_next_write(dev, ISP3X_MI_3A_WR_BASE,
 					 stats_vdev->stats_buf[wr_buf_idx].dma_addr +
 					 ISP3X_RD_STATS_BUF_SIZE, false);
@@ -1178,7 +1178,7 @@
 void rkisp_stats_first_ddr_config_v3x(struct rkisp_isp_stats_vdev *stats_vdev)
 {
 	struct rkisp_device *dev = stats_vdev->dev;
-	int i, mult = dev->hw_dev->is_unite ? 2 : 1;
+	int i, mult = dev->hw_dev->unite ? 2 : 1;
 
 	if (dev->isp_sdev.in_fmt.fmt_type == FMT_YUV)
 		return;
@@ -1199,14 +1199,12 @@
 	stats_vdev->wr_buf_idx = 0;
 
 	rkisp_unite_write(dev, ISP3X_MI_DBR_WR_SIZE,
-			  ISP3X_RD_STATS_BUF_SIZE,
-			  false, dev->hw_dev->is_unite);
+			  ISP3X_RD_STATS_BUF_SIZE, false);
 	rkisp_unite_set_bits(dev, ISP3X_SWS_CFG, 0,
-			     ISP3X_3A_DDR_WRITE_EN, false,
-			     dev->hw_dev->is_unite);
+			     ISP3X_3A_DDR_WRITE_EN, false);
 	rkisp_write(dev, ISP3X_MI_3A_WR_BASE,
 		    stats_vdev->stats_buf[0].dma_addr, false);
-	if (dev->hw_dev->is_unite)
+	if (dev->hw_dev->unite)
 		rkisp_next_write(dev, ISP3X_MI_3A_WR_BASE,
 				 stats_vdev->stats_buf[0].dma_addr +
 				 ISP3X_RD_STATS_BUF_SIZE, false);
@@ -1220,7 +1218,7 @@
 
 void rkisp_init_stats_vdev_v3x(struct rkisp_isp_stats_vdev *stats_vdev)
 {
-	int mult = stats_vdev->dev->hw_dev->is_unite ? 2 : 1;
+	int mult = stats_vdev->dev->hw_dev->unite ? 2 : 1;
 
 	stats_vdev->vdev_fmt.fmt.meta.dataformat =
 		V4L2_META_FMT_RK_ISP1_STAT_3A;
diff --git a/kernel/drivers/media/platform/rockchip/isp/procfs.c b/kernel/drivers/media/platform/rockchip/isp/procfs.c
index 6a8e9b2..a0b38ff 100644
--- a/kernel/drivers/media/platform/rockchip/isp/procfs.c
+++ b/kernel/drivers/media/platform/rockchip/isp/procfs.c
@@ -933,7 +933,7 @@
 		break;
 	case ISP_V30:
 		if (IS_ENABLED(CONFIG_VIDEO_ROCKCHIP_ISP_VERSION_V30)) {
-			if (dev->hw_dev->is_unite)
+			if (dev->hw_dev->unite)
 				isp30_unite_show(dev, p);
 			else
 				isp30_show(dev, p);
@@ -963,7 +963,7 @@
 					 msecs_to_jiffies(1000));
 		seq_printf(p, "****************HW REG*Ret:%d**************\n", ret);
 		for (i = 0; i < ISP3X_RAWAWB_RAM_DATA_BASE; i += 16) {
-			if (!dev->hw_dev->is_unite) {
+			if (dev->hw_dev->unite != ISP_UNITE_TWO) {
 				seq_printf(p, "%04x:  %08x %08x %08x %08x\n", i,
 					   rkisp_read(dev, i, true),
 					   rkisp_read(dev, i + 4, true),
diff --git a/kernel/drivers/media/platform/rockchip/isp/regs.c b/kernel/drivers/media/platform/rockchip/isp/regs.c
index a808d34..17af5ef 100644
--- a/kernel/drivers/media/platform/rockchip/isp/regs.c
+++ b/kernel/drivers/media/platform/rockchip/isp/regs.c
@@ -45,8 +45,7 @@
 
 	if (async && dev->hw_dev->is_single)
 		val = CIF_DUAL_CROP_GEN_CFG_UPD;
-	rkisp_unite_set_bits(dev, stream->config->dual_crop.ctrl,
-			     mask, val, false, dev->hw_dev->is_unite);
+	rkisp_unite_set_bits(dev, stream->config->dual_crop.ctrl, mask, val, false);
 }
 
 void rkisp_config_dcrop(struct rkisp_stream *stream,
@@ -54,7 +53,7 @@
 {
 	struct rkisp_device *dev = stream->ispdev;
 	u32 val = stream->config->dual_crop.yuvmode_mask;
-	bool is_unite = dev->hw_dev->is_unite;
+	bool is_unite = !!dev->hw_dev->unite;
 	struct v4l2_rect tmp = *rect;
 	u32 reg;
 
@@ -69,9 +68,9 @@
 	rkisp_write(dev, reg, tmp.width, false);
 
 	reg = stream->config->dual_crop.v_offset;
-	rkisp_unite_write(dev, reg, tmp.top, false, is_unite);
+	rkisp_unite_write(dev, reg, tmp.top, false);
 	reg = stream->config->dual_crop.v_size;
-	rkisp_unite_write(dev, reg, tmp.height, false, is_unite);
+	rkisp_unite_write(dev, reg, tmp.height, false);
 
 	if (async && dev->hw_dev->is_single)
 		val |= CIF_DUAL_CROP_GEN_CFG_UPD;
@@ -149,8 +148,7 @@
 
 	if (async && dev->hw_dev->is_single)
 		val = CIF_RSZ_CTRL_CFG_UPD_AUTO;
-	rkisp_unite_set_bits(dev, stream->config->rsz.ctrl, 0,
-			     val, false, dev->hw_dev->is_unite);
+	rkisp_unite_set_bits(dev, stream->config->rsz.ctrl, 0, val, false);
 }
 
 static void set_scale(struct rkisp_stream *stream, struct v4l2_rect *in_y,
@@ -220,17 +218,8 @@
 		rkisp_write(dev, scale_vc_addr, scale_vc, false);
 	}
 
-	if (dev->hw_dev->is_unite) {
-		u32 hy_size_reg = stream->id == RKISP_STREAM_MP ?
-			ISP3X_MAIN_RESIZE_HY_SIZE : ISP3X_SELF_RESIZE_HY_SIZE;
-		u32 hc_size_reg = stream->id == RKISP_STREAM_MP ?
-			ISP3X_MAIN_RESIZE_HC_SIZE : ISP3X_SELF_RESIZE_HC_SIZE;
-		u32 hy_offs_mi_reg = stream->id == RKISP_STREAM_MP ?
-			ISP3X_MAIN_RESIZE_HY_OFFS_MI : ISP3X_SELF_RESIZE_HY_OFFS_MI;
-		u32 hc_offs_mi_reg = stream->id == RKISP_STREAM_MP ?
-			ISP3X_MAIN_RESIZE_HC_OFFS_MI : ISP3X_SELF_RESIZE_HC_OFFS_MI;
-		u32 in_crop_offs_reg = stream->id == RKISP_STREAM_MP ?
-			ISP3X_MAIN_RESIZE_IN_CROP_OFFSET : ISP3X_SELF_RESIZE_IN_CROP_OFFSET;
+	if (dev->hw_dev->unite) {
+		u32 hy_size_reg, hc_size_reg, hy_offs_mi_reg, hc_offs_mi_reg, in_crop_offs_reg;
 		u32 isp_in_w = in_y->width / 2 + RKMOUDLE_UNITE_EXTEND_PIXEL;
 		u32 scl_w = out_y->width / 2;
 		u32 left_y = scale_hy == 1 ? scl_w : DIV_ROUND_UP(scl_w * 65536, scale_hy);
@@ -248,6 +237,34 @@
 		u32 extend = RKMOUDLE_UNITE_EXTEND_PIXEL;
 		u32 right_scl_in_y;
 		u32 right_scl_in_c;
+
+		switch (stream->id) {
+		case RKISP_STREAM_MP:
+			hy_size_reg = ISP3X_MAIN_RESIZE_HY_SIZE;
+			hc_size_reg = ISP3X_MAIN_RESIZE_HC_SIZE;
+			hy_offs_mi_reg = ISP3X_MAIN_RESIZE_HY_OFFS_MI;
+			hc_offs_mi_reg = ISP3X_MAIN_RESIZE_HC_OFFS_MI;
+			in_crop_offs_reg = ISP3X_MAIN_RESIZE_IN_CROP_OFFSET;
+			break;
+		case RKISP_STREAM_SP:
+			hy_size_reg = ISP3X_SELF_RESIZE_HY_SIZE;
+			hc_size_reg = ISP3X_SELF_RESIZE_HC_SIZE;
+			hy_offs_mi_reg = ISP3X_SELF_RESIZE_HY_OFFS_MI;
+			hc_offs_mi_reg = ISP3X_SELF_RESIZE_HC_OFFS_MI;
+			in_crop_offs_reg = ISP3X_SELF_RESIZE_IN_CROP_OFFSET;
+			break;
+		case RKISP_STREAM_BP:
+			hy_size_reg = ISP32_BP_RESIZE_HY_SIZE;
+			hc_size_reg = ISP32_BP_RESIZE_HC_SIZE;
+			hy_offs_mi_reg = ISP32_BP_RESIZE_HY_OFFS_MI;
+			hc_offs_mi_reg = ISP32_BP_RESIZE_HC_OFFS_MI;
+			in_crop_offs_reg = ISP32_BP_RESIZE_IN_CROP_OFFSET;
+			break;
+		default:
+			v4l2_warn(&dev->v4l2_dev, "%s no support unite for stream:%d\n",
+				  __func__, stream->id);
+			return;
+		}
 
 		if (right_crop_y < RKMOUDLE_UNITE_EXTEND_PIXEL) {
 			u32 reg;
@@ -362,7 +379,6 @@
 {
 	struct rkisp_device *dev = stream->ispdev;
 	int i = 0;
-	bool is_unite = dev->hw_dev->is_unite;
 
 	if (dev->isp_ver == ISP_V32_L && stream->id == RKISP_STREAM_SP) {
 		set_bilinear_scale(stream, in_y, in_c, out_y, out_c, async);
@@ -377,8 +393,8 @@
 
 	/* Linear interpolation */
 	for (i = 0; i < 64; i++) {
-		rkisp_unite_write(dev, stream->config->rsz.scale_lut_addr, i, true, is_unite);
-		rkisp_unite_write(dev, stream->config->rsz.scale_lut, i, true, is_unite);
+		rkisp_unite_write(dev, stream->config->rsz.scale_lut_addr, i, true);
+		rkisp_unite_write(dev, stream->config->rsz.scale_lut, i, true);
 	}
 
 	set_scale(stream, in_y, in_c, out_y, out_c);
@@ -388,9 +404,7 @@
 
 void rkisp_disable_rsz(struct rkisp_stream *stream, bool async)
 {
-	bool is_unite = stream->ispdev->hw_dev->is_unite;
-
-	rkisp_unite_write(stream->ispdev, stream->config->rsz.ctrl, 0, false, is_unite);
+	rkisp_unite_write(stream->ispdev, stream->config->rsz.ctrl, 0, false);
 	if (stream->ispdev->isp_ver == ISP_V32_L && stream->id == RKISP_STREAM_SP)
 		return;
 	update_rsz_shadow(stream, async);
diff --git a/kernel/drivers/media/platform/rockchip/isp/regs.h b/kernel/drivers/media/platform/rockchip/isp/regs.h
index b24eba0..d263a64 100644
--- a/kernel/drivers/media/platform/rockchip/isp/regs.h
+++ b/kernel/drivers/media/platform/rockchip/isp/regs.h
@@ -1753,7 +1753,7 @@
 static inline void mi_frame_end_int_enable(struct rkisp_stream *stream)
 {
 	struct rkisp_hw_dev *hw = stream->ispdev->hw_dev;
-	void __iomem *base = !hw->is_unite ?
+	void __iomem *base = hw->unite != ISP_UNITE_TWO ?
 		hw->base_addr : hw->base_next_addr;
 	void __iomem *addr = base + CIF_MI_IMSC;
 
@@ -1763,7 +1763,7 @@
 static inline void mi_frame_end_int_disable(struct rkisp_stream *stream)
 {
 	struct rkisp_hw_dev *hw = stream->ispdev->hw_dev;
-	void __iomem *base = !hw->is_unite ?
+	void __iomem *base = hw->unite != ISP_UNITE_TWO ?
 		hw->base_addr : hw->base_next_addr;
 	void __iomem *addr = base + CIF_MI_IMSC;
 
@@ -1773,7 +1773,7 @@
 static inline void mi_frame_end_int_clear(struct rkisp_stream *stream)
 {
 	struct rkisp_hw_dev *hw = stream->ispdev->hw_dev;
-	void __iomem *base = !hw->is_unite ?
+	void __iomem *base = hw->unite != ISP_UNITE_TWO ?
 		hw->base_addr : hw->base_next_addr;
 	void __iomem *addr = base + CIF_MI_ICR;
 
@@ -1783,7 +1783,6 @@
 static inline void stream_data_path(struct rkisp_stream *stream)
 {
 	struct rkisp_device *dev = stream->ispdev;
-	bool is_unite = dev->hw_dev->is_unite;
 	u32 dpcl = 0;
 
 	if (stream->id == RKISP_STREAM_MP)
@@ -1792,7 +1791,7 @@
 		dpcl |= CIF_VI_DPCL_CHAN_MODE_SP;
 
 	if (dpcl)
-		rkisp_unite_set_bits(dev, CIF_VI_DPCL, 0, dpcl, true, is_unite);
+		rkisp_unite_set_bits(dev, CIF_VI_DPCL, 0, dpcl, true);
 }
 
 static inline void mp_set_uv_swap(void __iomem *base)
@@ -1914,16 +1913,15 @@
 static inline void force_cfg_update(struct rkisp_device *dev)
 {
 	u32 val = CIF_MI_CTRL_INIT_OFFSET_EN | CIF_MI_CTRL_INIT_BASE_EN;
-	bool is_unite = dev->hw_dev->is_unite;
 
 	if (dev->isp_ver == ISP_V21) {
 		val |= rkisp_read_reg_cache(dev, CIF_MI_CTRL);
 		rkisp_write(dev, CIF_MI_CTRL, val, true);
 	}
 	dev->hw_dev->is_mi_update = true;
-	rkisp_unite_set_bits(dev, CIF_MI_CTRL, 0, val, false, is_unite);
+	rkisp_unite_set_bits(dev, CIF_MI_CTRL, 0, val, false);
 	val = CIF_MI_INIT_SOFT_UPD;
-	rkisp_unite_write(dev, CIF_MI_INIT, val, true, is_unite);
+	rkisp_unite_write(dev, CIF_MI_INIT, val, true);
 }
 
 static inline void dmatx0_ctrl(void __iomem *base, u32 val)
diff --git a/kernel/drivers/media/platform/rockchip/isp/regs_v2x.h b/kernel/drivers/media/platform/rockchip/isp/regs_v2x.h
index e480063..2f3c100 100644
--- a/kernel/drivers/media/platform/rockchip/isp/regs_v2x.h
+++ b/kernel/drivers/media/platform/rockchip/isp/regs_v2x.h
@@ -2700,7 +2700,7 @@
 		    stream->out_fmt.plane_fmt[0].bytesperline, is_direct);
 	if (stream->ispdev->isp_ver == ISP_V21 || stream->ispdev->isp_ver == ISP_V30)
 		rkisp_set_bits(stream->ispdev, MI_RD_CTRL2, 0, BIT(30), false);
-	if (stream->ispdev->hw_dev->is_unite) {
+	if (stream->ispdev->hw_dev->unite) {
 		rkisp_next_write(stream->ispdev, stream->config->mi.length,
 				 stream->out_fmt.plane_fmt[0].bytesperline, is_direct);
 		rkisp_next_set_bits(stream->ispdev, MI_RD_CTRL2, 0, BIT(30), false);
diff --git a/kernel/drivers/media/platform/rockchip/isp/regs_v3x.h b/kernel/drivers/media/platform/rockchip/isp/regs_v3x.h
index b9e8b42..5e4c857 100644
--- a/kernel/drivers/media/platform/rockchip/isp/regs_v3x.h
+++ b/kernel/drivers/media/platform/rockchip/isp/regs_v3x.h
@@ -343,6 +343,15 @@
 #define ISP32_BP_RESIZE_PHASE_HC_SHD		(ISP32_BP_RESIZE_BASE + 0x0004c)
 #define ISP32_BP_RESIZE_PHASE_VY_SHD		(ISP32_BP_RESIZE_BASE + 0x00050)
 #define ISP32_BP_RESIZE_PHASE_VC_SHD		(ISP32_BP_RESIZE_BASE + 0x00054)
+#define ISP32_BP_RESIZE_HY_SIZE			(ISP32_BP_RESIZE_BASE + 0x00058)
+#define ISP32_BP_RESIZE_HC_SIZE			(ISP32_BP_RESIZE_BASE + 0x0005c)
+#define ISP32_BP_RESIZE_HY_OFFS_MI		(ISP32_BP_RESIZE_BASE + 0x00060)
+#define ISP32_BP_RESIZE_HC_OFFS_MI		(ISP32_BP_RESIZE_BASE + 0x00064)
+#define ISP32_BP_RESIZE_HY_SIZE_SHD		(ISP32_BP_RESIZE_BASE + 0x00068)
+#define ISP32_BP_RESIZE_HC_SIZE_SHD		(ISP32_BP_RESIZE_BASE + 0x0006c)
+#define ISP32_BP_RESIZE_HY_OFFS_MI_SHD		(ISP32_BP_RESIZE_BASE + 0x00070)
+#define ISP32_BP_RESIZE_HC_OFFS_MI_SHD		(ISP32_BP_RESIZE_BASE + 0x00074)
+#define ISP32_BP_RESIZE_IN_CROP_OFFSET		(ISP32_BP_RESIZE_BASE + 0x00078)
 
 #define ISP3X_SELF_RESIZE_BASE			0x00001000
 #define ISP3X_SELF_RESIZE_CTRL			(ISP3X_SELF_RESIZE_BASE + 0x00000)
@@ -1898,6 +1907,7 @@
 #define ISP3X_SW_CGC_RATIO_EN		BIT(29)
 
 /* ISP CTRL1 */
+#define ISP32_SHP_FST_FRAME		BIT(19)
 #define ISP3X_YNR_FST_FRAME		BIT(23)
 #define ISP3X_ADRC_FST_FRAME		BIT(24)
 #define ISP3X_DHAZ_FST_FRAME		BIT(25)
@@ -2168,9 +2178,12 @@
 #define ISP3X_CAC_LUT_MODE(x)		(((x) & 0x3) << 24)
 
 /* CNR */
+#define ISP3X_CNR_THUMB_MIX_CUR_EN	BIT(4)
+
 #define ISP3X_CNR_GLOBAL_GAIN_ALPHA_MAX	GENMASK(15, 12)
 
 /* YNR */
+#define ISP3X_YNR_THUMB_MIX_CUR_EN	BIT(24)
 #define ISP3X_YNR_EN_SHD		BIT(31)
 
 /* BLS */
@@ -2200,6 +2213,9 @@
 /* HDRTMO */
 
 /* HDRDRC */
+#define ISP3X_DRC_WEIPRE_FRAME_MASK	GENMASK(23, 16)
+
+#define ISP3X_DRC_IIR_WEIGHT_MASK	GENMASK(22, 16)
 
 /* HDRMGE */
 
diff --git a/kernel/drivers/media/platform/rockchip/isp/rkisp.c b/kernel/drivers/media/platform/rockchip/isp/rkisp.c
index 1d69fab..6bab4e5 100644
--- a/kernel/drivers/media/platform/rockchip/isp/rkisp.c
+++ b/kernel/drivers/media/platform/rockchip/isp/rkisp.c
@@ -213,17 +213,16 @@
 		max_h = CIF_ISP_INPUT_H_MAX_V21;
 		break;
 	case ISP_V30:
-		if (dev->hw_dev->is_unite) {
-			max_w = CIF_ISP_INPUT_W_MAX_V30_UNITE;
-			max_h = CIF_ISP_INPUT_H_MAX_V30_UNITE;
-		} else {
-			max_w = CIF_ISP_INPUT_W_MAX_V30;
-			max_h = CIF_ISP_INPUT_H_MAX_V30;
-		}
+		max_w = dev->hw_dev->unite ?
+			CIF_ISP_INPUT_W_MAX_V30_UNITE : CIF_ISP_INPUT_W_MAX_V30;
+		max_h = dev->hw_dev->unite ?
+			CIF_ISP_INPUT_H_MAX_V30_UNITE : CIF_ISP_INPUT_H_MAX_V30;
 		break;
 	case ISP_V32:
-		max_w = CIF_ISP_INPUT_W_MAX_V32;
-		max_h = CIF_ISP_INPUT_H_MAX_V32;
+		max_w = dev->hw_dev->unite ?
+			CIF_ISP_INPUT_W_MAX_V32_UNITE : CIF_ISP_INPUT_W_MAX_V32;
+		max_h = dev->hw_dev->unite ?
+			CIF_ISP_INPUT_H_MAX_V32_UNITE : CIF_ISP_INPUT_H_MAX_V32;
 		break;
 	case ISP_V32_L:
 		max_w = CIF_ISP_INPUT_W_MAX_V32_L;
@@ -518,7 +517,9 @@
 	do_div(data_rate, 1000 * 1000);
 	/* increase margin: 25% * num */
 	data_rate += (data_rate >> 2) * num;
-
+	/* one frame two-run, data double */
+	if (hw->is_multi_overflow && num > 1)
+		data_rate *= 2;
 	/* compare with isp clock adjustment table */
 	for (i = 0; i < hw->num_clk_rate_tbl; i++)
 		if (data_rate <= hw->clk_rate_tbl[i].clk_rate)
@@ -528,7 +529,7 @@
 
 	/* set isp clock rate */
 	rkisp_set_clk_rate(hw->clks[0], hw->clk_rate_tbl[i].clk_rate * 1000000UL);
-	if (hw->is_unite)
+	if (hw->unite == ISP_UNITE_TWO)
 		rkisp_set_clk_rate(hw->clks[5], hw->clk_rate_tbl[i].clk_rate * 1000000UL);
 	/* aclk equal to core clk */
 	if (dev->isp_ver == ISP_V32)
@@ -541,48 +542,39 @@
 	struct rkisp_hw_dev *hw = dev->hw_dev;
 
 	if (on) {
-		/* enable bay3d and mi */
+		/* enable mi */
 		rkisp_update_regs(dev, ISP3X_MI_WR_CTRL, ISP3X_MI_WR_CTRL);
 		rkisp_update_regs(dev, ISP3X_ISP_CTRL1, ISP3X_ISP_CTRL1);
-		if (dev->isp_ver == ISP_V21) {
-			rkisp_update_regs(dev, ISP21_BAY3D_CTRL, ISP21_BAY3D_CTRL);
-		} else if (dev->isp_ver == ISP_V30) {
+		if (dev->isp_ver == ISP_V30) {
 			rkisp_update_regs(dev, ISP3X_MPFBC_CTRL, ISP3X_MPFBC_CTRL);
 			rkisp_update_regs(dev, ISP3X_MI_BP_WR_CTRL, ISP3X_MI_BP_WR_CTRL);
-			rkisp_update_regs(dev, ISP3X_BAY3D_CTRL, ISP3X_BAY3D_CTRL);
 			rkisp_update_regs(dev, ISP3X_SWS_CFG, ISP3X_SWS_CFG);
 		} else if (dev->isp_ver == ISP_V32) {
 			rkisp_update_regs(dev, ISP3X_MI_BP_WR_CTRL, ISP3X_MI_BP_WR_CTRL);
 			rkisp_update_regs(dev, ISP32_MI_BPDS_WR_CTRL, ISP32_MI_BPDS_WR_CTRL);
 			rkisp_update_regs(dev, ISP32_MI_MPDS_WR_CTRL, ISP32_MI_MPDS_WR_CTRL);
-			rkisp_update_regs(dev, ISP3X_BAY3D_CTRL, ISP3X_BAY3D_CTRL);
 		}
 	} else {
-		/* disabled bay3d and mi. rv1106 sdmmc workaround, 3a_wr no close */
+		/* disabled mi. rv1106 sdmmc workaround, 3a_wr no close */
 		writel(CIF_MI_CTRL_INIT_OFFSET_EN | CIF_MI_CTRL_INIT_BASE_EN,
 		       hw->base_addr + ISP3X_MI_WR_CTRL);
-		if (dev->isp_ver == ISP_V21) {
-			writel(0, hw->base_addr + ISP21_BAY3D_CTRL);
-		} else if (dev->isp_ver == ISP_V30) {
+		if (dev->isp_ver == ISP_V30) {
 			writel(0, hw->base_addr + ISP3X_MPFBC_CTRL);
 			writel(0, hw->base_addr + ISP3X_MI_BP_WR_CTRL);
-			writel(0, hw->base_addr + ISP3X_BAY3D_CTRL);
 			writel(0xc, hw->base_addr + ISP3X_SWS_CFG);
-			if (hw->is_unite) {
+			if (hw->unite == ISP_UNITE_TWO) {
 				writel(0, hw->base_next_addr + ISP3X_MI_WR_CTRL);
 				writel(0, hw->base_next_addr + ISP3X_MPFBC_CTRL);
 				writel(0, hw->base_next_addr + ISP3X_MI_BP_WR_CTRL);
-				writel(0, hw->base_next_addr + ISP3X_BAY3D_CTRL);
 				writel(0xc, hw->base_next_addr + ISP3X_SWS_CFG);
 			}
 		} else if (dev->isp_ver == ISP_V32) {
 			writel(0, hw->base_addr + ISP3X_MI_BP_WR_CTRL);
 			writel(0, hw->base_addr + ISP32_MI_BPDS_WR_CTRL);
 			writel(0, hw->base_addr + ISP32_MI_MPDS_WR_CTRL);
-			writel(0, hw->base_addr + ISP3X_BAY3D_CTRL);
 		}
 	}
-	rkisp_unite_write(dev, ISP3X_MI_WR_INIT, CIF_MI_INIT_SOFT_UPD, true, hw->is_unite);
+	rkisp_unite_write(dev, ISP3X_MI_WR_INIT, CIF_MI_INIT_SOFT_UPD, true);
 }
 
 /*
@@ -602,7 +594,8 @@
 	hw->cur_dev_id = dev->dev_id;
 	rkisp_dmarx_get_frame(dev, &cur_frame_id, NULL, NULL, true);
 
-	if (hw->is_multi_overflow && is_try)
+	/* isp process the same frame */
+	if (is_try)
 		goto run_next;
 
 	val = 0;
@@ -632,13 +625,12 @@
 	}
 
 	if (rd_mode != dev->rd_mode) {
-		rkisp_unite_set_bits(dev, ISP_HDRMGE_BASE, ISP_HDRMGE_MODE_MASK,
-				     val, false, hw->is_unite);
+		rkisp_unite_set_bits(dev, ISP_HDRMGE_BASE, ISP_HDRMGE_MODE_MASK, val, false);
 		dev->skip_frame = 2;
 		is_upd = true;
 	}
 
-	if (dev->isp_ver == ISP_V20 && dev->dmarx_dev.trigger == T_MANUAL && !is_try) {
+	if (dev->isp_ver == ISP_V20 && dev->dmarx_dev.trigger == T_MANUAL) {
 		if (dev->rd_mode != rd_mode && dev->br_dev.en) {
 			tmp = dev->isp_sdev.in_crop.height;
 			val = rkisp_read(dev, CIF_DUAL_CROP_CTRL, false);
@@ -659,12 +651,15 @@
 	}
 	dev->rd_mode = rd_mode;
 
-	rkisp_params_first_cfg(&dev->params_vdev, &dev->isp_sdev.in_fmt,
-			       dev->isp_sdev.quantization);
-	rkisp_params_cfg(params_vdev, cur_frame_id);
-	rkisp_config_cmsk(dev);
-	rkisp_stream_frame_start(dev, 0);
-	if (!hw->is_single && !is_try) {
+	if (hw->unite != ISP_UNITE_ONE || dev->unite_index == ISP_UNITE_LEFT) {
+		rkisp_params_first_cfg(&dev->params_vdev, &dev->isp_sdev.in_fmt,
+				       dev->isp_sdev.quantization);
+		rkisp_params_cfg(params_vdev, cur_frame_id);
+		rkisp_config_cmsk(dev);
+		rkisp_stream_frame_start(dev, 0);
+	}
+
+	if (!hw->is_single) {
 		/* multi sensor need to reset isp resize mode if scale up */
 		val = 0;
 		if (rkisp_read(dev, ISP3X_MAIN_RESIZE_CTRL, true) & 0xf0)
@@ -700,7 +695,7 @@
 		} else {
 			if (dev->isp_ver == ISP_V32_L)
 				rkisp_write(dev, ISP32_SELF_SCALE_UPDATE, ISP32_SCALE_FORCE_UPD, true);
-			rkisp_unite_write(dev, ISP3X_MI_WR_INIT, CIF_MI_INIT_SOFT_UPD, true, hw->is_unite);
+			rkisp_unite_write(dev, ISP3X_MI_WR_INIT, CIF_MI_INIT_SOFT_UPD, true);
 		}
 		/* sensor mode & index */
 		if (dev->isp_ver >= ISP_V21) {
@@ -711,7 +706,7 @@
 			else
 				val |= ISP21_SENSOR_MODE(dev->multi_mode);
 			writel(val, hw->base_addr + ISP_ACQ_H_OFFS);
-			if (hw->is_unite)
+			if (hw->unite == ISP_UNITE_TWO)
 				writel(val, hw->base_next_addr + ISP_ACQ_H_OFFS);
 			v4l2_dbg(2, rkisp_debug, &dev->v4l2_dev,
 				 "sensor mode:%d index:%d | 0x%x\n",
@@ -731,36 +726,88 @@
 	else
 		dev->rdbk_cnt_x1++;
 	dev->rdbk_cnt++;
-
-	rkisp_params_cfgsram(params_vdev);
-	params_vdev->rdbk_times = dma2frm + 1;
+	if (dev->isp_ver == ISP_V20)
+		params_vdev->rdbk_times = dma2frm + 1;
 
 run_next:
-	if (hw->is_multi_overflow && !dev->is_first_double) {
-		stats_vdev->rdbk_drop = false;
-		if (dev->sw_rd_cnt) {
-			rkisp_multi_overflow_hdl(dev, false);
-			params_vdev->rdbk_times += dev->sw_rd_cnt;
-			stats_vdev->rdbk_drop = true;
-			is_upd = true;
-		} else if (is_try) {
+	rkisp_params_cfgsram(params_vdev);
+	stats_vdev->rdbk_drop = false;
+	if (dev->is_frame_double) {
+		is_upd = true;
+		if (is_try) {
+			/* the frame second running to on mi */
 			rkisp_multi_overflow_hdl(dev, true);
-			is_upd = true;
+			rkisp_update_regs(dev, ISP_LDCH_BASE, ISP_LDCH_BASE);
+
+			val = ISP3X_YNR_FST_FRAME | ISP3X_DHAZ_FST_FRAME | ISP3X_CNR_FST_FRAME;
+			if (dev->isp_ver == ISP_V32)
+				val |= ISP32_SHP_FST_FRAME;
+			else
+				val |= ISP3X_CNR_FST_FRAME;
+			rkisp_unite_clear_bits(dev, ISP3X_ISP_CTRL1, val, false);
+			val = rkisp_read_reg_cache(dev, ISP3X_DRC_IIRWG_GAIN);
+			writel(val, hw->base_addr + ISP3X_DRC_IIRWG_GAIN);
+			if (hw->unite == ISP_UNITE_TWO)
+				writel(val, hw->base_next_addr + ISP3X_DRC_IIRWG_GAIN);
+			val = rkisp_read_reg_cache(dev, ISP3X_DRC_EXPLRATIO);
+			writel(val, hw->base_addr + ISP3X_DRC_EXPLRATIO);
+			if (hw->unite == ISP_UNITE_TWO)
+				writel(val, hw->base_next_addr + ISP3X_DRC_EXPLRATIO);
+		} else {
+			/* the frame first running to off mi to save bandwidth */
+			rkisp_multi_overflow_hdl(dev, false);
+
+			/* FST_FRAME no to read sram thumb */
+			val = ISP3X_YNR_FST_FRAME | ISP3X_DHAZ_FST_FRAME;
+			if (dev->isp_ver == ISP_V32)
+				val |= ISP32_SHP_FST_FRAME;
+			else
+				val |= ISP3X_CNR_FST_FRAME;
+			rkisp_unite_set_bits(dev, ISP3X_ISP_CTRL1, 0, val, false);
+			/* ADRC low iir thumb weight for first sensor switch */
+			val = rkisp_read_reg_cache(dev, ISP3X_DRC_IIRWG_GAIN);
+			val &= ~ISP3X_DRC_IIR_WEIGHT_MASK;
+			writel(val, hw->base_addr + ISP3X_DRC_IIRWG_GAIN);
+			if (hw->unite == ISP_UNITE_TWO)
+				writel(val, hw->base_next_addr + ISP3X_DRC_IIRWG_GAIN);
+			/* ADRC iir5x5 and cur3x3 weight */
+			val = rkisp_read_reg_cache(dev, ISP3X_DRC_EXPLRATIO);
+			val &= ~ISP3X_DRC_WEIPRE_FRAME_MASK;
+			writel(val, hw->base_addr + ISP3X_DRC_EXPLRATIO);
+			if (hw->unite == ISP_UNITE_TWO)
+				writel(val, hw->base_next_addr + ISP3X_DRC_EXPLRATIO);
+			/* YNR_THUMB_MIX_CUR_EN for thumb read addr to 0 */
+			val = rkisp_read_reg_cache(dev, ISP3X_YNR_GLOBAL_CTRL);
+			val |= ISP3X_YNR_THUMB_MIX_CUR_EN;
+			writel(val, hw->base_addr + ISP3X_YNR_GLOBAL_CTRL);
+			if (hw->unite == ISP_UNITE_TWO)
+				writel(val, hw->base_next_addr + ISP3X_YNR_GLOBAL_CTRL);
+			if (dev->isp_ver == ISP_V21 || dev->isp_ver == ISP_V30) {
+				/* CNR_THUMB_MIX_CUR_EN for thumb read addr to 0 */
+				val = rkisp_read_reg_cache(dev, ISP3X_CNR_CTRL);
+				val |= ISP3X_CNR_THUMB_MIX_CUR_EN;
+				writel(val, hw->base_addr + ISP3X_CNR_CTRL);
+				if (hw->unite == ISP_UNITE_TWO)
+					writel(val, hw->base_next_addr + ISP3X_CNR_CTRL);
+			}
+			stats_vdev->rdbk_drop = true;
 		}
 	}
 
-	/* read 3d lut at frame end */
+	/* disable isp force update to read 3dlut
+	 * 3dlut auto update at frame end for single sensor
+	 */
 	if (hw->is_single && is_upd &&
 	    rkisp_read_reg_cache(dev, ISP_3DLUT_UPDATE) & 0x1) {
-		rkisp_unite_write(dev, ISP_3DLUT_UPDATE, 0, true, hw->is_unite);
+		rkisp_unite_write(dev, ISP_3DLUT_UPDATE, 0, true);
 		is_3dlut_upd = true;
 	}
 	if (is_upd) {
 		val = rkisp_read(dev, ISP_CTRL, false);
 		val |= CIF_ISP_CTRL_ISP_CFG_UPD;
-		rkisp_unite_write(dev, ISP_CTRL, val, true, hw->is_unite);
+		rkisp_unite_write(dev, ISP_CTRL, val, true);
 		/* bayer pat after ISP_CFG_UPD for multi sensor to read lsc r/g/b table */
-		rkisp_update_regs(dev, ISP_ACQ_PROP, ISP_ACQ_PROP);
+		rkisp_update_regs(dev, ISP3X_ISP_CTRL1, ISP3X_ISP_CTRL1);
 		/* fix ldch multi sensor case:
 		 * ldch will pre-read data when en and isp force upd or frame end,
 		 * udelay for ldch pre-read data.
@@ -771,12 +818,12 @@
 			udelay(50);
 			val &= ~(BIT(0) | BIT(31));
 			writel(val, hw->base_addr + ISP_LDCH_BASE);
-			if (hw->is_unite)
+			if (hw->unite == ISP_UNITE_TWO)
 				writel(val, hw->base_next_addr + ISP_LDCH_BASE);
 		}
 	}
 	if (is_3dlut_upd)
-		rkisp_unite_write(dev, ISP_3DLUT_UPDATE, 1, true, hw->is_unite);
+		rkisp_unite_write(dev, ISP_3DLUT_UPDATE, 1, true);
 
 	/* if output stream enable, wait it end */
 	val = rkisp_read(dev, CIF_MI_CTRL_SHD, true);
@@ -807,11 +854,13 @@
 	val &= ~SW_IBUF_OP_MODE(0xf);
 	tmp = SW_IBUF_OP_MODE(dev->rd_mode);
 	val |= tmp | SW_CSI2RX_EN | SW_DMA_2FRM_MODE(dma2frm);
+	if (dev->isp_ver > ISP_V20)
+		dma2frm = dev->sw_rd_cnt;
 	v4l2_dbg(2, rkisp_debug, &dev->v4l2_dev,
-		 "readback frame:%d time:%d 0x%x\n",
-		 cur_frame_id, dma2frm + 1, val);
+		 "readback frame:%d time:%d 0x%x try:%d\n",
+		 cur_frame_id, dma2frm + 1, val, is_try);
 	if (!hw->is_shutdown)
-		rkisp_unite_write(dev, CSI2RX_CTRL0, val, true, hw->is_unite);
+		rkisp_unite_write(dev, CSI2RX_CTRL0, val, true);
 }
 
 static void rkisp_fast_switch_rx_buf(struct rkisp_device *dev, bool is_current)
@@ -865,6 +914,12 @@
 			isp = dev;
 			is_try = true;
 			times = 0;
+			if (hw->unite == ISP_UNITE_ONE) {
+				if (dev->sw_rd_cnt < 2)
+					isp->unite_index = ISP_UNITE_RIGHT;
+				if (!hw->is_multi_overflow || (dev->sw_rd_cnt & 0x1))
+					is_try = false;
+			}
 			goto end;
 		}
 		hw->is_idle = true;
@@ -914,17 +969,40 @@
 		times = t.times;
 		hw->cur_dev_id = id;
 		hw->is_idle = false;
+		/* this frame will read count by isp */
 		isp->sw_rd_cnt = 0;
-		if (hw->is_multi_overflow && (hw->pre_dev_id != id)) {
+		/* frame double for multi camera resolution out of hardware limit
+		 * first for HW save this camera information, and second to output image
+		 */
+		isp->is_frame_double = false;
+		if (hw->is_multi_overflow &&
+		    (hw->unite == ISP_UNITE_ONE ||
+		     (hw->pre_dev_id != -1 && hw->pre_dev_id != id))) {
+			isp->is_frame_double = true;
 			isp->sw_rd_cnt = 1;
 			times = 0;
 		}
+		/* resolution out of hardware limit
+		 * frame is vertically divided into left and right
+		 */
+		isp->unite_index = ISP_UNITE_LEFT;
+		if (hw->unite == ISP_UNITE_ONE) {
+			isp->sw_rd_cnt *= 2;
+			isp->sw_rd_cnt += 1;
+		}
+		/* first frame handle twice for thunderboot
+		 * first output stats to AIQ and wait new params to run second
+		 */
 		if (isp->is_pre_on && t.frame_id == 0) {
 			isp->is_first_double = true;
 			isp->skip_frame = 1;
-			isp->sw_rd_cnt = 0;
+			if (hw->unite != ISP_UNITE_ONE) {
+				isp->sw_rd_cnt = 0;
+				isp->is_frame_double = false;
+			}
 			rkisp_fast_switch_rx_buf(isp, false);
 		}
+		isp->params_vdev.rdbk_times = isp->sw_rd_cnt + 1;
 	}
 end:
 	spin_unlock_irqrestore(&hw->rdbk_lock, lock_flags);
@@ -982,12 +1060,6 @@
 {
 	u32 val = 0;
 
-	if (dev->hw_dev->is_multi_overflow &&
-	    dev->sw_rd_cnt &&
-	    irq & ISP_FRAME_END &&
-	    !dev->is_first_double)
-		goto end;
-
 	dev->irq_ends |= (irq & dev->irq_ends_mask);
 	v4l2_dbg(3, rkisp_debug, &dev->v4l2_dev,
 		 "%s irq:0x%x ends:0x%x mask:0x%x\n",
@@ -1001,6 +1073,9 @@
 	if ((dev->irq_ends & dev->irq_ends_mask) != dev->irq_ends_mask ||
 	    !IS_HDR_RDBK(dev->rd_mode))
 		return;
+
+	if (dev->sw_rd_cnt)
+		goto end;
 
 	if (dev->is_first_double) {
 		rkisp_fast_switch_rx_buf(dev, true);
@@ -1068,26 +1143,25 @@
 {
 	struct v4l2_rect *out_crop = &dev->isp_sdev.out_crop;
 	u32 width = out_crop->width, mult = 1;
-	bool is_unite = dev->hw_dev->is_unite;
+	u32 unite = dev->hw_dev->unite;
 
 	/* isp2.0 no ism */
 	if (dev->isp_ver == ISP_V20 || dev->isp_ver == ISP_V21 ||
 	    dev->isp_ver == ISP_V32_L)
 		return;
 
-	if (is_unite)
+	if (unite)
 		width = width / 2 + RKMOUDLE_UNITE_EXTEND_PIXEL;
-	rkisp_unite_write(dev, CIF_ISP_IS_RECENTER, 0, false, is_unite);
-	rkisp_unite_write(dev, CIF_ISP_IS_MAX_DX, 0, false, is_unite);
-	rkisp_unite_write(dev, CIF_ISP_IS_MAX_DY, 0, false, is_unite);
-	rkisp_unite_write(dev, CIF_ISP_IS_DISPLACE, 0, false, is_unite);
-	rkisp_unite_write(dev, CIF_ISP_IS_H_OFFS, out_crop->left, false, is_unite);
-	rkisp_unite_write(dev, CIF_ISP_IS_V_OFFS, out_crop->top, false, is_unite);
-	rkisp_unite_write(dev, CIF_ISP_IS_H_SIZE, width, false, is_unite);
+	rkisp_unite_write(dev, CIF_ISP_IS_RECENTER, 0, false);
+	rkisp_unite_write(dev, CIF_ISP_IS_MAX_DX, 0, false);
+	rkisp_unite_write(dev, CIF_ISP_IS_MAX_DY, 0, false);
+	rkisp_unite_write(dev, CIF_ISP_IS_DISPLACE, 0, false);
+	rkisp_unite_write(dev, CIF_ISP_IS_H_OFFS, out_crop->left, false);
+	rkisp_unite_write(dev, CIF_ISP_IS_V_OFFS, out_crop->top, false);
+	rkisp_unite_write(dev, CIF_ISP_IS_H_SIZE, width, false);
 	if (dev->cap_dev.stream[RKISP_STREAM_SP].interlaced)
 		mult = 2;
-	rkisp_unite_write(dev, CIF_ISP_IS_V_SIZE, out_crop->height / mult,
-			  false, is_unite);
+	rkisp_unite_write(dev, CIF_ISP_IS_V_SIZE, out_crop->height / mult, false);
 
 	if (dev->isp_ver == ISP_V30 || dev->isp_ver == ISP_V32)
 		return;
@@ -1389,20 +1463,18 @@
 
 	for (i = 0; i < 9; i++)
 		rkisp_unite_write(dev, CIF_ISP_CC_COEFF_0 + i * 4,
-				  *(coeff + i), false, dev->hw_dev->is_unite);
+				  *(coeff + i), false);
 
 	val = rkisp_read_reg_cache(dev, CIF_ISP_CTRL);
 
 	if (dev->isp_sdev.quantization == V4L2_QUANTIZATION_FULL_RANGE)
 		rkisp_unite_write(dev, CIF_ISP_CTRL, val |
 				  CIF_ISP_CTRL_ISP_CSM_Y_FULL_ENA |
-				  CIF_ISP_CTRL_ISP_CSM_C_FULL_ENA,
-				  false, dev->hw_dev->is_unite);
+				  CIF_ISP_CTRL_ISP_CSM_C_FULL_ENA, false);
 	else
 		rkisp_unite_write(dev, CIF_ISP_CTRL, val &
 				  ~(CIF_ISP_CTRL_ISP_CSM_Y_FULL_ENA |
-				  CIF_ISP_CTRL_ISP_CSM_C_FULL_ENA),
-				  false, dev->hw_dev->is_unite);
+				  CIF_ISP_CTRL_ISP_CSM_C_FULL_ENA), false);
 }
 
 static void rkisp_config_cmsk_single(struct rkisp_device *dev,
@@ -1601,7 +1673,7 @@
 	cfg = dev->cmsk_cfg;
 	spin_unlock_irqrestore(&dev->cmsk_lock, lock_flags);
 
-	if (!dev->hw_dev->is_unite)
+	if (!dev->hw_dev->unite)
 		rkisp_config_cmsk_single(dev, &cfg);
 	else
 		rkisp_config_cmsk_dual(dev, &cfg);
@@ -1616,7 +1688,7 @@
 	struct ispsd_out_fmt *out_fmt;
 	struct v4l2_rect *in_crop;
 	struct rkisp_sensor_info *sensor;
-	bool is_unite = dev->hw_dev->is_unite;
+	bool is_unite = !!dev->hw_dev->unite;
 	u32 isp_ctrl = 0;
 	u32 irq_mask = 0;
 	u32 signal = 0;
@@ -1646,22 +1718,20 @@
 			    in_fmt->mbus_code == MEDIA_BUS_FMT_Y10_1X10 ||
 			    in_fmt->mbus_code == MEDIA_BUS_FMT_Y12_1X12) {
 				if (dev->isp_ver >= ISP_V20)
-					rkisp_unite_write(dev, ISP_DEBAYER_CONTROL,
-							  0, false, is_unite);
+					rkisp_unite_write(dev, ISP_DEBAYER_CONTROL, 0, false);
 				else
 					rkisp_write(dev, CIF_ISP_DEMOSAIC,
-						CIF_ISP_DEMOSAIC_BYPASS |
-						CIF_ISP_DEMOSAIC_TH(0xc), false);
+						    CIF_ISP_DEMOSAIC_BYPASS |
+						    CIF_ISP_DEMOSAIC_TH(0xc), false);
 			} else {
 				if (dev->isp_ver >= ISP_V20)
 					rkisp_unite_write(dev, ISP_DEBAYER_CONTROL,
 							  SW_DEBAYER_EN |
 							  SW_DEBAYER_FILTER_G_EN |
-							  SW_DEBAYER_FILTER_C_EN,
-							  false, is_unite);
+							  SW_DEBAYER_FILTER_C_EN, false);
 				else
 					rkisp_write(dev, CIF_ISP_DEMOSAIC,
-						CIF_ISP_DEMOSAIC_TH(0xc), false);
+						    CIF_ISP_DEMOSAIC_TH(0xc), false);
 			}
 
 			if (sensor && sensor->mbus.type == V4L2_MBUS_BT656)
@@ -1714,38 +1784,31 @@
 	if (rkisp_read_reg_cache(dev, CIF_ISP_CTRL) & ISP32_MIR_ENABLE)
 		isp_ctrl |= ISP32_MIR_ENABLE;
 
-	rkisp_unite_write(dev, CIF_ISP_CTRL, isp_ctrl, false, is_unite);
+	rkisp_unite_write(dev, CIF_ISP_CTRL, isp_ctrl, false);
 	acq_prop |= signal | in_fmt->yuv_seq |
 		CIF_ISP_ACQ_PROP_BAYER_PAT(in_fmt->bayer_pat) |
 		CIF_ISP_ACQ_PROP_FIELD_SEL_ALL;
-	rkisp_unite_write(dev, CIF_ISP_ACQ_PROP, acq_prop, false, is_unite);
-	rkisp_unite_write(dev, CIF_ISP_ACQ_NR_FRAMES, 0, true, is_unite);
+	rkisp_unite_write(dev, CIF_ISP_ACQ_PROP, acq_prop, false);
+	rkisp_unite_write(dev, CIF_ISP_ACQ_NR_FRAMES, 0, true);
 
 	if (is_unite)
 		width = width / 2 + RKMOUDLE_UNITE_EXTEND_PIXEL;
 	/* Acquisition Size */
-	rkisp_unite_write(dev, CIF_ISP_ACQ_H_OFFS, acq_mult * in_crop->left,
-			  false, is_unite);
-	rkisp_unite_write(dev, CIF_ISP_ACQ_V_OFFS, in_crop->top,
-			  false, is_unite);
-	rkisp_unite_write(dev, CIF_ISP_ACQ_H_SIZE, acq_mult * width,
-			  false, is_unite);
+	rkisp_unite_write(dev, CIF_ISP_ACQ_H_OFFS, acq_mult * in_crop->left, false);
+	rkisp_unite_write(dev, CIF_ISP_ACQ_V_OFFS, in_crop->top, false);
+	rkisp_unite_write(dev, CIF_ISP_ACQ_H_SIZE, acq_mult * width, false);
 
 	/* ISP Out Area differ with ACQ is only FIFO, so don't crop in this */
-	rkisp_unite_write(dev, CIF_ISP_OUT_H_OFFS, 0, true, is_unite);
-	rkisp_unite_write(dev, CIF_ISP_OUT_V_OFFS, 0, true, is_unite);
-	rkisp_unite_write(dev, CIF_ISP_OUT_H_SIZE, width, false, is_unite);
+	rkisp_unite_write(dev, CIF_ISP_OUT_H_OFFS, 0, true);
+	rkisp_unite_write(dev, CIF_ISP_OUT_V_OFFS, 0, true);
+	rkisp_unite_write(dev, CIF_ISP_OUT_H_SIZE, width, false);
 
 	if (dev->cap_dev.stream[RKISP_STREAM_SP].interlaced) {
-		rkisp_unite_write(dev, CIF_ISP_ACQ_V_SIZE, in_crop->height / 2,
-				  false, is_unite);
-		rkisp_unite_write(dev, CIF_ISP_OUT_V_SIZE, in_crop->height / 2,
-				  false, is_unite);
+		rkisp_unite_write(dev, CIF_ISP_ACQ_V_SIZE, in_crop->height / 2, false);
+		rkisp_unite_write(dev, CIF_ISP_OUT_V_SIZE, in_crop->height / 2, false);
 	} else {
-		rkisp_unite_write(dev, CIF_ISP_ACQ_V_SIZE, in_crop->height + extend_line,
-				  false, is_unite);
-		rkisp_unite_write(dev, CIF_ISP_OUT_V_SIZE, in_crop->height + extend_line,
-				  false, is_unite);
+		rkisp_unite_write(dev, CIF_ISP_ACQ_V_SIZE, in_crop->height + extend_line, false);
+		rkisp_unite_write(dev, CIF_ISP_OUT_V_SIZE, in_crop->height + extend_line, false);
 	}
 
 	/* interrupt mask */
@@ -1754,7 +1817,7 @@
 		irq_mask |= ISP2X_LSC_LUT_ERR;
 	if (dev->is_pre_on)
 		irq_mask |= CIF_ISP_FRAME_IN;
-	rkisp_unite_write(dev, CIF_ISP_IMSC, irq_mask, true, is_unite);
+	rkisp_unite_write(dev, CIF_ISP_IMSC, irq_mask, true);
 
 	if ((dev->isp_ver == ISP_V20 ||
 	     dev->isp_ver == ISP_V21) &&
@@ -1909,8 +1972,7 @@
 	if (dev->isp_ver == ISP_V32)
 		dpcl |= BIT(0);
 
-	rkisp_unite_set_bits(dev, CIF_VI_DPCL, 0, dpcl, true,
-			     dev->hw_dev->is_unite);
+	rkisp_unite_set_bits(dev, CIF_VI_DPCL, 0, dpcl, true);
 	return ret;
 }
 
@@ -2007,9 +2069,9 @@
 
 	v4l2_dbg(1, rkisp_debug, &dev->v4l2_dev,
 		 "%s refcnt:%d\n", __func__,
-		 atomic_read(&dev->hw_dev->refcnt));
+		 atomic_read(&hw->refcnt));
 
-	if (atomic_read(&dev->hw_dev->refcnt) > 1)
+	if (atomic_read(&hw->refcnt) > 1)
 		goto end;
 	/*
 	 * ISP(mi) stop in mi frame end -> Stop ISP(mipi) ->
@@ -2065,7 +2127,7 @@
 
 	val = readl(base + CIF_ISP_CTRL);
 	writel(val | CIF_ISP_CTRL_ISP_CFG_UPD, base + CIF_ISP_CTRL);
-	if (hw->is_unite)
+	if (hw->unite == ISP_UNITE_TWO)
 		rkisp_next_write(dev, CIF_ISP_CTRL,
 				 val | CIF_ISP_CTRL_ISP_CFG_UPD, true);
 
@@ -2082,11 +2144,11 @@
 		safe_rate = hw->clk_rate_tbl[0].clk_rate * 1000000UL;
 		if (old_rate > safe_rate) {
 			rkisp_set_clk_rate(hw->clks[0], safe_rate);
-			if (hw->is_unite)
+			if (hw->unite == ISP_UNITE_TWO)
 				rkisp_set_clk_rate(hw->clks[5], safe_rate);
 			udelay(100);
 		}
-		rkisp_soft_reset(dev->hw_dev, false);
+		rkisp_soft_reset(hw, false);
 	}
 
 	if (dev->isp_ver == ISP_V12 || dev->isp_ver == ISP_V13) {
@@ -2097,18 +2159,20 @@
 		writel(0, base + CIF_ISP_CSI0_MASK3);
 	} else if (dev->isp_ver >= ISP_V20) {
 		writel(0, base + CSI2RX_CSI2_RESETN);
-		if (hw->is_unite)
+		if (hw->unite == ISP_UNITE_TWO)
 			rkisp_next_write(dev, CSI2RX_CSI2_RESETN, 0, true);
 	}
 
 	hw->is_dvfs = false;
 	hw->is_runing = false;
-	dev->hw_dev->is_idle = true;
-	dev->hw_dev->is_mi_update = false;
+	hw->is_idle = true;
+	hw->is_mi_update = false;
+	hw->pre_dev_id = -1;
 end:
 	dev->irq_ends_mask = 0;
 	dev->hdr.op_mode = 0;
 	dev->sw_rd_cnt = 0;
+	dev->stats_vdev.rdbk_drop = false;
 	rkisp_set_state(&dev->isp_state, ISP_STOP);
 
 	if (dev->isp_ver >= ISP_V20)
@@ -2154,12 +2218,9 @@
 			val = dev->isp_sdev.out_crop.height / 15;
 			val = dev->cap_dev.wait_line / val;
 			val = ISP3X_RAWAF_INELINE0(val) | ISP3X_RAWAF_INTLINE0_EN;
-			rkisp_unite_write(dev, ISP3X_RAWAF_INT_LINE,
-				val, false, dev->hw_dev->is_unite);
-			rkisp_unite_set_bits(dev, ISP_ISP3A_IMSC, 0,
-				ISP2X_3A_RAWAF, false, dev->hw_dev->is_unite);
-			rkisp_unite_clear_bits(dev, CIF_ISP_IMSC,
-				ISP2X_LSC_LUT_ERR, false, dev->hw_dev->is_unite);
+			rkisp_unite_write(dev, ISP3X_RAWAF_INT_LINE, val, false);
+			rkisp_unite_set_bits(dev, ISP_ISP3A_IMSC, 0, ISP2X_3A_RAWAF, false);
+			rkisp_unite_clear_bits(dev, CIF_ISP_IMSC, ISP2X_LSC_LUT_ERR, false);
 			dev->rawaf_irq_cnt = 0;
 		}
 	}
@@ -2187,12 +2248,11 @@
 		val |= NOC_HURRY_PRIORITY(2) | NOC_HURRY_W_MODE(2) | NOC_HURRY_R_MODE(1);
 	if (atomic_read(&dev->hw_dev->refcnt) > 1)
 		is_direct = false;
-	rkisp_unite_write(dev, CIF_ISP_CTRL, val, is_direct, dev->hw_dev->is_unite);
+	rkisp_unite_write(dev, CIF_ISP_CTRL, val, is_direct);
 	rkisp_clear_reg_cache_bits(dev, CIF_ISP_CTRL, CIF_ISP_CTRL_ISP_CFG_UPD);
 
 	dev->isp_err_cnt = 0;
 	dev->isp_isr_cnt = 0;
-	dev->isp_state = ISP_START | ISP_FRAME_END;
 	dev->irq_ends_mask |= ISP_FRAME_END;
 	dev->irq_ends = 0;
 
@@ -2668,14 +2728,16 @@
 				max_h = CIF_ISP_INPUT_H_MAX_V21;
 				break;
 			case ISP_V30:
-				max_w = dev->hw_dev->is_unite ?
+				max_w = dev->hw_dev->unite ?
 					CIF_ISP_INPUT_W_MAX_V30_UNITE : CIF_ISP_INPUT_W_MAX_V30;
-				max_h = dev->hw_dev->is_unite ?
+				max_h = dev->hw_dev->unite ?
 					CIF_ISP_INPUT_H_MAX_V30_UNITE : CIF_ISP_INPUT_H_MAX_V30;
 				break;
 			case ISP_V32:
-				max_w = CIF_ISP_INPUT_W_MAX_V32;
-				max_h = CIF_ISP_INPUT_H_MAX_V32;
+				max_w = dev->hw_dev->unite ?
+					CIF_ISP_INPUT_W_MAX_V32_UNITE : CIF_ISP_INPUT_W_MAX_V32;
+				max_h = dev->hw_dev->unite ?
+					CIF_ISP_INPUT_H_MAX_V32_UNITE : CIF_ISP_INPUT_H_MAX_V32;
 				break;
 			case ISP_V32_L:
 				max_w = CIF_ISP_INPUT_W_MAX_V32_L;
@@ -2901,6 +2963,7 @@
 	rkisp_config_cif(isp_dev);
 	rkisp_isp_start(isp_dev);
 	rkisp_global_update_mi(isp_dev);
+	isp_dev->isp_state = ISP_START | ISP_FRAME_END;
 	rkisp_rdbk_trigger_event(isp_dev, T_CMD_QUEUE, NULL);
 	return 0;
 }
@@ -2936,7 +2999,7 @@
 	u32 val = pool->buf.buff_addr[RKISP_PLANE_Y];
 
 	rkisp_write(dev, stream->config->mi.y_base_ad_init, val, false);
-	if (dev->hw_dev->is_unite) {
+	if (dev->hw_dev->unite == ISP_UNITE_TWO) {
 		u32 offs = stream->out_fmt.width / 2 - RKMOUDLE_UNITE_EXTEND_PIXEL;
 
 		if (stream->memory)
@@ -3412,7 +3475,7 @@
 	if (dev->is_bigmode)
 		mode |= RKISP_ISP_BIGMODE;
 	info->mode = mode;
-	if (dev->hw_dev->is_unite)
+	if (dev->hw_dev->unite)
 		info->act_width = in_crop->width / 2 + RKMOUDLE_UNITE_EXTEND_PIXEL;
 	else
 		info->act_width = in_crop->width;
@@ -3699,7 +3762,7 @@
 	struct ispsd_in_fmt *in_fmt = &isp_sd->in_fmt;
 	struct ispsd_out_fmt *out_fmt = &isp_sd->out_fmt;
 
-	*in_fmt = rkisp_isp_input_formats[0];
+	*in_fmt = rkisp_isp_input_formats[8];
 	in_frm->width = RKISP_DEFAULT_WIDTH;
 	in_frm->height = RKISP_DEFAULT_HEIGHT;
 	in_frm->code = in_fmt->mbus_code;
@@ -3805,8 +3868,52 @@
 })
 
 #ifdef CONFIG_VIDEO_ROCKCHIP_THUNDER_BOOT_ISP
+static void rkisp_save_tb_info(struct rkisp_device *isp_dev)
+{
+	struct rkisp_isp_params_vdev *params_vdev = &isp_dev->params_vdev;
+	void *resmem_va = phys_to_virt(isp_dev->resmem_pa);
+	struct rkisp_thunderboot_resmem_head *head = resmem_va;
+	int size = 0, offset = 0;
+	void *param = NULL;
+
+	switch (isp_dev->isp_ver) {
+	case ISP_V32:
+		size = sizeof(struct rkisp32_thunderboot_resmem_head);
+		offset = size * isp_dev->dev_id;
+		break;
+	default:
+		break;
+	}
+
+	if (size && size < isp_dev->resmem_size) {
+		dma_sync_single_for_cpu(isp_dev->dev, isp_dev->resmem_addr + offset,
+					size, DMA_FROM_DEVICE);
+		params_vdev->is_first_cfg = true;
+		if (isp_dev->isp_ver == ISP_V32) {
+			struct rkisp32_thunderboot_resmem_head *tmp = resmem_va + offset;
+
+			param = &tmp->cfg;
+			head = &tmp->head;
+			v4l2_info(&isp_dev->v4l2_dev,
+				  "tb param module en:0x%llx upd:0x%llx cfg upd:0x%llx\n",
+				  tmp->cfg.module_en_update,
+				  tmp->cfg.module_ens,
+				  tmp->cfg.module_cfg_update);
+		}
+		if (param)
+			params_vdev->ops->save_first_param(params_vdev, param);
+	} else if (size > isp_dev->resmem_size) {
+		v4l2_err(&isp_dev->v4l2_dev,
+			 "resmem size:%zu no enough for head:%d\n",
+			 isp_dev->resmem_size, size);
+		head->complete = RKISP_TB_NG;
+	}
+	memcpy(&isp_dev->tb_head, head, sizeof(*head));
+}
+
 void rkisp_chk_tb_over(struct rkisp_device *isp_dev)
 {
+	struct rkisp_isp_params_vdev *params_vdev = &isp_dev->params_vdev;
 	struct rkisp_hw_dev *hw = isp_dev->hw_dev;
 	struct rkisp_thunderboot_resmem_head *head;
 	enum rkisp_tb_state tb_state;
@@ -3815,19 +3922,20 @@
 	if (!isp_dev->is_thunderboot)
 		return;
 
+	if (isp_dev->isp_ver == ISP_V32 && params_vdev->is_first_cfg)
+		goto end;
+
 	resmem_va = phys_to_virt(isp_dev->resmem_pa);
 	head = (struct rkisp_thunderboot_resmem_head *)resmem_va;
 	dma_sync_single_for_cpu(isp_dev->dev, isp_dev->resmem_addr,
 				sizeof(struct rkisp_thunderboot_resmem_head),
 				DMA_FROM_DEVICE);
 
-	shm_head_poll_timeout(isp_dev, !!head->complete, 5000, 200 * USEC_PER_MSEC);
+	shm_head_poll_timeout(isp_dev, !!head->complete, 5000, 400 * USEC_PER_MSEC);
 	if (head->complete != RKISP_TB_OK) {
 		v4l2_err(&isp_dev->v4l2_dev, "wait thunderboot over timeout\n");
 	} else {
-		struct rkisp_isp_params_vdev *params_vdev = &isp_dev->params_vdev;
-		void *param = NULL;
-		u32 size = 0, offset = 0, timeout = 50;
+		int i, timeout = 50;
 
 		/* wait for all isp dev to register */
 		if (head->camera_num > 1) {
@@ -3837,42 +3945,18 @@
 					break;
 				usleep_range(200, 210);
 			}
-		}
-
-		switch (isp_dev->isp_ver) {
-		case ISP_V32:
-			size = sizeof(struct rkisp32_thunderboot_resmem_head);
-			offset = size * isp_dev->dev_id;
-			break;
-		default:
-			break;
-		}
-
-		if (size && size < isp_dev->resmem_size) {
-			dma_sync_single_for_cpu(isp_dev->dev, isp_dev->resmem_addr + offset,
-						size, DMA_FROM_DEVICE);
-			params_vdev->is_first_cfg = true;
-			if (isp_dev->isp_ver == ISP_V32) {
-				struct rkisp32_thunderboot_resmem_head *tmp = resmem_va + offset;
-
-				param = &tmp->cfg;
-				head = &tmp->head;
-				v4l2_info(&isp_dev->v4l2_dev,
-					  "tb param module en:0x%llx upd:0x%llx cfg upd:0x%llx\n",
-					  tmp->cfg.module_en_update,
-					  tmp->cfg.module_ens,
-					  tmp->cfg.module_cfg_update);
+			if (head->camera_num > hw->dev_num) {
+				v4l2_err(&isp_dev->v4l2_dev,
+					 "thunderboot invalid camera num:%d, dev num:%d\n",
+					 head->camera_num, hw->dev_num);
+				goto end;
 			}
-			if (param)
-				params_vdev->ops->save_first_param(params_vdev, param);
-		} else if (size > isp_dev->resmem_size) {
-			v4l2_err(&isp_dev->v4l2_dev,
-				 "resmem size:%zu no enough for head:%d\n",
-				 isp_dev->resmem_size, size);
-			head->complete = RKISP_TB_NG;
 		}
+		for (i = 0; i < head->camera_num; i++)
+			rkisp_save_tb_info(hw->isp[i]);
 	}
-	memcpy(&isp_dev->tb_head, head, sizeof(*head));
+end:
+	head = &isp_dev->tb_head;
 	v4l2_info(&isp_dev->v4l2_dev,
 		  "thunderboot info: %d, %d, %d, %d, %d, %d | %d %d\n",
 		  head->enable,
@@ -4005,7 +4089,7 @@
 		   struct rkisp_device *dev)
 {
 	struct rkisp_hw_dev *hw = dev->hw_dev;
-	void __iomem *base = !hw->is_unite ?
+	void __iomem *base = hw->unite != ISP_UNITE_TWO ?
 		hw->base_addr : hw->base_next_addr;
 	unsigned int isp_mis_tmp = 0;
 	unsigned int isp_err = 0;
@@ -4026,7 +4110,7 @@
 	if (isp3a_mis & ISP2X_3A_RAWAE_BIG && dev->params_vdev.rdbk_times > 0)
 		writel(BIT(31), base + RAWAE_BIG1_BASE + RAWAE_BIG_CTRL);
 
-	if (hw->is_unite) {
+	if (hw->unite == ISP_UNITE_TWO) {
 		u32 val = rkisp_read(dev, ISP3X_ISP_RIS, true);
 
 		if (val) {
@@ -4057,9 +4141,14 @@
 		}
 
 		if (IS_HDR_RDBK(dev->hdr.op_mode)) {
-			/* read 3d lut at isp readback */
-			if (!dev->hw_dev->is_single)
-				rkisp_write(dev, ISP_3DLUT_UPDATE, 0, true);
+			/* disabled frame end to read 3dlut for multi sensor
+			 * 3dlut will update at isp readback
+			 */
+			if (!dev->hw_dev->is_single) {
+				writel(0, hw->base_addr + ISP_3DLUT_UPDATE);
+				if (hw->unite == ISP_UNITE_TWO)
+					writel(0, hw->base_next_addr + ISP_3DLUT_UPDATE);
+			}
 			rkisp_stats_rdbk_enable(&dev->stats_vdev, true);
 			goto vs_skip;
 		}
diff --git a/kernel/drivers/media/platform/rockchip/isp/rkisp.h b/kernel/drivers/media/platform/rockchip/isp/rkisp.h
index 0522163..54f32b9 100644
--- a/kernel/drivers/media/platform/rockchip/isp/rkisp.h
+++ b/kernel/drivers/media/platform/rockchip/isp/rkisp.h
@@ -57,6 +57,8 @@
 #define CIF_ISP_INPUT_H_MAX_V30_UNITE	6144
 #define CIF_ISP_INPUT_W_MAX_V32		3072
 #define CIF_ISP_INPUT_H_MAX_V32		1728
+#define CIF_ISP_INPUT_W_MAX_V32_UNITE	3840
+#define CIF_ISP_INPUT_H_MAX_V32_UNITE	2160
 #define CIF_ISP_INPUT_W_MAX_V32_L	4224
 #define CIF_ISP_INPUT_H_MAX_V32_L	3136
 #define CIF_ISP_INPUT_W_MIN		272
diff --git a/kernel/drivers/media/platform/rockchip/isp/version.h b/kernel/drivers/media/platform/rockchip/isp/version.h
index c19a9f9..c4c7254 100644
--- a/kernel/drivers/media/platform/rockchip/isp/version.h
+++ b/kernel/drivers/media/platform/rockchip/isp/version.h
@@ -427,6 +427,38 @@
  * 2.lock for rockit qbuf
  * 3.fix open video during device register
  * 4.sync dev register and fast_work
+ *
+ * v2.2.2 (AIQ v5.1.3)
+ * 1.fixed framerate ctl invalid issue
+ * 2.fix rockit uv offset if switch resolution
+ * 3.fix isp rockit frame rate err
+ * 4.fix error for multi sensor with scale up case
+ * 5.force offset to 0 when frame end for wrap mode
+ * 6.fix sync with 3a_server
+ * 7.fix isp32 and lite buf output err due to mi on/off
+ * 8.fix uyvy format for isp32
+ * 9.wait RISC-V with 400ms timeout
+ * 10.fix uyvy format for unite mode
+ * 11.fix ldch for multiple read back
+ * 12.sync isp stream_on end then to start working
+ * 13.no set clk if assigned-clock-rates in dts
+ * 14.distinguish buf done or subscribed event for param poll
+ * 15.fix repeated reporting statistics if stats video on/off
+ *
+ * v2.3.0 (AIQ v5.3.0)
+ * 1.fix drc and hdrmge err for multi sensor
+ * 2.fix 3dlut for multi sensor
+ * 3.fix stream init pause state
+ * 4.fix refer to sram info for multi sensor
+ * 5.add api get isp work mode for rockit
+ * 6.remove __isp_config_hdrshd
+ * 7.add lock to save tb info
+ * 8.fix list buf delete err
+ * 9.fix get tb info
+ * 10.add iqtool video for isp21
+ * 11.fix image effect for frame two-run
+ * 12.fix underperformance for frame two-run
+ * 13.support unite mode for isp32
  */
 
 #define RKISP_DRIVER_VERSION RKISP_API_VERSION
diff --git a/kernel/drivers/mfd/Kconfig b/kernel/drivers/mfd/Kconfig
index 1ec588c..aa1fe3b 100644
--- a/kernel/drivers/mfd/Kconfig
+++ b/kernel/drivers/mfd/Kconfig
@@ -1218,6 +1218,9 @@
 	  if you say yes here you get support for the RK1000, with func as
 	  TVEncoder or CODEC.
 
+source "drivers/mfd/display-serdes/Kconfig"
+source "drivers/mfd/rkx110_x120/Kconfig"
+
 config MFD_RN5T618
 	tristate "Ricoh RN5T567/618 PMIC"
 	depends on I2C
diff --git a/kernel/drivers/mfd/Makefile b/kernel/drivers/mfd/Makefile
index 1b45f97..17c7220 100644
--- a/kernel/drivers/mfd/Makefile
+++ b/kernel/drivers/mfd/Makefile
@@ -230,6 +230,8 @@
 obj-$(CONFIG_MFD_RK806_SPI)	+= rk806-spi.o
 obj-$(CONFIG_MFD_RK808)		+= rk808.o
 obj-$(CONFIG_MFD_RK1000)	+= rk1000-core.o
+obj-$(CONFIG_MFD_SERDES_DISPLAY)	+= display-serdes/
+obj-y				+= rkx110_x120/
 obj-$(CONFIG_MFD_RN5T618)	+= rn5t618.o
 obj-$(CONFIG_MFD_SEC_CORE)	+= sec-core.o sec-irq.o
 obj-$(CONFIG_MFD_SYSCON)	+= syscon.o
diff --git a/kernel/drivers/mfd/display-serdes/Kconfig b/kernel/drivers/mfd/display-serdes/Kconfig
new file mode 100644
index 0000000..18855fe
--- /dev/null
+++ b/kernel/drivers/mfd/display-serdes/Kconfig
@@ -0,0 +1,31 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Multifunction miscellaneous devices
+#
+comment "driver for different display serdes"
+
+menuconfig MFD_SERDES_DISPLAY
+	tristate "rockchip display serdes drivers support"
+	select DRM_PANEL_BRIDGE
+	select DRM_KMS_HELPER
+	select DRM_MIPI_DSI
+	select SERDES_DISPLAY_CHIP_ROHM
+	select SERDES_DISPLAY_CHIP_MAXIM
+	select SERDES_DISPLAY_CHIP_ROCKCHIP
+	select SERDES_DISPLAY_CHIP_NOVO
+	select MFD_CORE
+	select REGMAP_I2C
+	select GENERIC_PINCTRL_GROUPS
+	select GENERIC_PINMUX_FUNCTIONS
+	default n
+	help
+	  This driver supports different serdes devices from different vendor such as
+	  maxim, rohm, rockchip etc.
+
+if MFD_SERDES_DISPLAY
+source "drivers/mfd/display-serdes/maxim/Kconfig"
+source "drivers/mfd/display-serdes/rohm/Kconfig"
+source "drivers/mfd/display-serdes/rockchip/Kconfig"
+source "drivers/mfd/display-serdes/novo/Kconfig"
+endif
+
diff --git a/kernel/drivers/mfd/display-serdes/Makefile b/kernel/drivers/mfd/display-serdes/Makefile
new file mode 100644
index 0000000..420076c
--- /dev/null
+++ b/kernel/drivers/mfd/display-serdes/Makefile
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for multifunction miscellaneous devices that just used for display
+#
+
+obj-$(CONFIG_SERDES_DISPLAY_CHIP_MAXIM)		+= maxim/
+obj-$(CONFIG_SERDES_DISPLAY_CHIP_ROHM)		+= rohm/
+obj-$(CONFIG_SERDES_DISPLAY_CHIP_ROCKCHIP)	+= rockchip/
+obj-$(CONFIG_SERDES_DISPLAY_CHIP_NOVO)		+= novo/
+
+serdes-mfd-display-$(CONFIG_MFD_SERDES_DISPLAY) += serdes-core.o serdes-irq.o
+
+obj-$(CONFIG_MFD_SERDES_DISPLAY) += serdes-mfd-display.o serdes-i2c.o serdes-bridge.o serdes-panel.o serdes-gpio.o serdes-pinctrl.o
diff --git a/kernel/drivers/mfd/display-serdes/core.h b/kernel/drivers/mfd/display-serdes/core.h
new file mode 100644
index 0000000..28f9945
--- /dev/null
+++ b/kernel/drivers/mfd/display-serdes/core.h
@@ -0,0 +1,353 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * core.h -- core define for mfd display arch
+ *
+ * Copyright (c) 2023-2028 Rockchip Electronics Co. Ltd.
+ *
+ * Author: luowei <lw@rock-chips.com>
+ *
+ */
+
+#ifndef __MFD_SERDES_CORE_H__
+#define __MFD_SERDES_CORE_H__
+
+#include <linux/kernel.h>
+#include <linux/export.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/mfd/core.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/seq_file.h>
+#include <linux/regulator/consumer.h>
+#include <linux/backlight.h>
+#include <linux/module.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/interrupt.h>
+
+#include <linux/completion.h>
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <linux/regmap.h>
+#include <linux/of.h>
+#include <linux/gpio/driver.h>
+#include <linux/gpio/consumer.h>
+#include <linux/extcon-provider.h>
+#include <linux/bitfield.h>
+
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_bridge.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_print.h>
+#include <drm/drm_of.h>
+#include <drm/drm_connector.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_device.h>
+#include <drm/drm_modes.h>
+#include <drm/drm_atomic_state_helper.h>
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_modeset_helper_vtables.h>
+
+#include <video/videomode.h>
+#include <video/of_display_timing.h>
+#include <video/display_timing.h>
+#include <uapi/linux/media-bus-format.h>
+
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinconf-generic.h>
+#include <linux/pinctrl/pinconf.h>
+#include <linux/pinctrl/pinmux.h>
+
+#include <asm/unaligned.h>
+#include "gpio.h"
+
+#include "../../../../drivers/pinctrl/core.h"
+#include "../../../../drivers/pinctrl/pinconf.h"
+#include "../../../../drivers/pinctrl/pinmux.h"
+#include "../../../../drivers/gpio/gpiolib.h"
+
+/*
+* if enable all the debug information,
+* there will be much log.
+*
+* so suggest set CONFIG_LOG_BUF_SHIFT to 18
+*/
+//#define SERDES_DEBUG_MFD
+//#define SERDES_DEBUG_I2C
+//#define SERDES_DEBUG_CHIP
+
+#ifdef SERDES_DEBUG_MFD
+#define SERDES_DBG_MFD(x...) pr_info(x)
+#else
+#define SERDES_DBG_MFD(x...) no_printk(x)
+#endif
+
+#ifdef SERDES_DEBUG_I2C
+#define SERDES_DBG_I2C(x...) pr_info(x)
+#else
+#define SERDES_DBG_I2C(x...) no_printk(x)
+#endif
+
+#ifdef SERDES_DEBUG_CHIP
+#define SERDES_DBG_CHIP(x...) pr_info(x)
+#else
+#define SERDES_DBG_CHIP(x...) no_printk(x)
+#endif
+
+#define MFD_SERDES_DISPLAY_VERSION "serdes-mfd-displaly-v10-230901"
+
+struct serdes;
+struct serdes_chip_pinctrl_info {
+	struct pinctrl_pin_desc *pins;
+	unsigned int num_pins;
+	struct group_desc *groups;
+	unsigned int num_groups;
+	struct function_desc *functions;
+	unsigned int num_functions;
+};
+
+struct serdes_chip_bridge_ops {
+	/* serdes chip function for bridge */
+	int (*power_on)(struct serdes *serdes);
+	int (*init)(struct serdes *serdes);
+	int (*attach)(struct serdes *serdes);
+	enum drm_connector_status (*detect)(struct serdes *serdes);
+	int (*get_modes)(struct serdes *serdes);
+	int (*pre_enable)(struct serdes *serdes);
+	int (*enable)(struct serdes *serdes);
+	int (*disable)(struct serdes *serdes);
+	int (*post_disable)(struct serdes *serdes);
+};
+
+struct serdes_chip_panel_ops {
+	/*serdes chip function for bridge*/
+	int (*power_on)(struct serdes *serdes);
+	int (*init)(struct serdes *serdes);
+	int (*disable)(struct serdes *serdes);
+	int (*unprepare)(struct serdes *serdes);
+	int (*prepare)(struct serdes *serdes);
+	int (*enable)(struct serdes *serdes);
+	int (*get_modes)(struct serdes *serdes);
+	int (*backlight_enable)(struct serdes *serdes);
+	int (*backlight_disable)(struct serdes *serdes);
+};
+
+struct serdes_chip_pinctrl_ops {
+	/* serdes chip pinctrl function */
+	int (*pin_config_get)(struct serdes *serdes,
+			      unsigned int pin,
+			      unsigned long *config);
+	int (*pin_config_set)(struct serdes *serdes,
+			      unsigned int pin,
+			      unsigned long *configs,
+			      unsigned int num_configs);
+
+	int (*set_mux)(struct serdes *serdes, unsigned int func_selector,
+		       unsigned int group_selector);
+};
+
+struct serdes_chip_gpio_ops {
+	/* serdes chip gpio function */
+	int (*direction_input)(struct serdes *serdes, int gpio);
+	int (*direction_output)(struct serdes *serdes, int gpio, int value);
+	int (*get_level)(struct serdes *serdes, int gpio);
+	int (*set_level)(struct serdes *serdes, int gpio, int value);
+	int (*set_config)(struct serdes *serdes, int gpio, unsigned long config);
+	int (*to_irq)(struct serdes *serdes, int gpio);
+};
+
+struct serdes_chip_pm_ops {
+	/* serdes chip function for suspend and resume */
+	int (*suspend)(struct serdes *serdes);
+	int (*resume)(struct serdes *serdes);
+};
+
+struct serdes_chip_irq_ops {
+	/* serdes chip function for lock and err irq */
+	int (*lock_handle)(struct serdes *serdes);
+	int (*err_handle)(struct serdes *serdes);
+};
+
+struct serdes_chip_data {
+	const char *name;
+	enum serdes_type serdes_type;
+	enum serdes_id serdes_id;
+	enum serdes_bridge_type bridge_type;
+	int sequence_init;
+	int connector_type;
+	int reg_id;
+	int id_data;
+	int int_status_reg;
+	int int_trig;
+	int num_gpio;
+	int gpio_base;
+	int same_chip_count;
+	u8 bank_num;
+
+	struct regmap_config *regmap_config;
+	struct serdes_chip_pinctrl_info *pinctrl_info;
+	struct serdes_chip_bridge_ops *bridge_ops;
+	struct serdes_chip_panel_ops *panel_ops;
+	struct serdes_chip_pinctrl_ops *pinctrl_ops;
+	struct serdes_chip_gpio_ops *gpio_ops;
+	struct serdes_chip_pm_ops *pm_ops;
+	struct serdes_chip_irq_ops *irq_ops;
+};
+
+struct serdes_init_seq {
+	struct reg_sequence *reg_sequence;
+	unsigned int reg_seq_cnt;
+};
+
+struct serdes_gpio {
+	struct device *dev;
+	struct serdes_pinctrl *parent;
+	struct regmap *regmap;
+	struct gpio_chip gpio_chip;
+};
+
+struct serdes_pinctrl {
+	struct device *dev;
+	struct serdes *parent;
+	struct pinctrl_dev *pctl;
+	struct pinctrl_pin_desc *pdesc;
+	struct regmap *regmap;
+	struct pinctrl_desc *pinctrl_desc;
+	struct serdes_gpio *gpio;
+	int pin_base;
+};
+
+struct serdes_panel {
+	struct drm_panel panel;
+	enum drm_connector_status status;
+	struct drm_connector connector;
+
+	const char *name;
+	u32 width_mm;
+	u32 height_mm;
+	u32 link_rate;
+	u32 lane_count;
+	bool ssc;
+
+	struct device *dev;
+	struct serdes *parent;
+	struct regmap *regmap;
+	struct mipi_dsi_device *dsi;
+	struct device_node *dsi_node;
+	struct drm_display_mode mode;
+	struct backlight_device *backlight;
+	struct serdes_init_seq *serdes_init_seq;
+	bool sel_mipi;
+	bool dv_swp_ab;
+	bool dpi_deskew_en;
+	bool split_mode;
+	u32 num_lanes;
+	u32 dsi_lane_map[4];
+};
+
+struct serdes_bridge {
+	struct drm_bridge base_bridge;
+	struct drm_bridge *next_bridge;
+	enum drm_connector_status status;
+	atomic_t triggered;
+	struct drm_connector connector;
+	struct drm_panel *panel;
+
+	struct device *dev;
+	struct serdes *parent;
+	struct regmap *regmap;
+	struct mipi_dsi_device *dsi;
+	struct device_node *dsi_node;
+	struct drm_display_mode mode;
+	struct backlight_device *backlight;
+
+	bool sel_mipi;
+	bool dv_swp_ab;
+	bool dpi_deskew_en;
+	bool split_mode;
+	u32 num_lanes;
+	u32 dsi_lane_map[4];
+};
+
+struct serdes {
+	int num_gpio;
+	struct mutex io_lock;
+	struct mutex irq_lock;
+	struct mutex wq_lock;
+	struct device *dev;
+	enum serdes_type type;
+	struct regmap *regmap;
+	struct regulator *supply;
+	struct extcon_dev *extcon;
+	atomic_t conn_trigger;
+
+	/* serdes power and reset pin */
+	struct gpio_desc *reset_gpio;
+	struct gpio_desc *enable_gpio;
+	struct regulator *vpower;
+
+	/* serdes irq pin */
+	struct gpio_desc *lock_gpio;
+	struct gpio_desc *err_gpio;
+	int lock_irq;
+	int err_irq;
+	int lock_irq_trig;
+	int err_irq_trig;
+	atomic_t flag_ser_init;
+
+	struct workqueue_struct *mfd_wq;
+	struct delayed_work mfd_delay_work;
+	bool route_enable;
+	bool use_delay_work;
+	struct pinctrl *pinctrl_node;
+	struct pinctrl_state *pins_default;
+	struct pinctrl_state *pins_sleep;
+
+	struct serdes_init_seq *serdes_init_seq;
+	struct serdes_bridge *serdes_bridge;
+	struct serdes_panel *serdes_panel;
+	struct serdes_pinctrl *pinctrl;
+	struct serdes_chip_data *chip_data;
+};
+
+/* Device I/O API */
+int serdes_reg_read(struct serdes *serdes, unsigned int reg, unsigned int *val);
+int serdes_reg_write(struct serdes *serdes, unsigned int reg, unsigned int val);
+void serdes_reg_lock(struct serdes *serdes);
+int serdes_reg_unlock(struct serdes *serdes);
+int serdes_set_bits(struct serdes *serdes, unsigned int reg,
+		    unsigned int mask, unsigned int val);
+int serdes_bulk_read(struct serdes *serdes, unsigned int reg,
+		     int count, u16 *buf);
+int serdes_bulk_write(struct serdes *serdes, unsigned int reg,
+		      int count, void *src);
+int serdes_multi_reg_write(struct serdes *serdes, const struct reg_sequence *regs,
+			   int num_regs);
+int serdes_i2c_set_sequence(struct serdes *serdes);
+
+int serdes_device_init(struct serdes *serdes);
+int serdes_set_pinctrl_default(struct serdes *serdes);
+int serdes_set_pinctrl_sleep(struct serdes *serdes);
+int serdes_device_suspend(struct serdes *serdes);
+int serdes_device_resume(struct serdes *serdes);
+void serdes_device_shutdown(struct serdes *serdes);
+int serdes_irq_init(struct serdes *serdes);
+void serdes_irq_exit(struct serdes *serdes);
+void serdes_auxadc_init(struct serdes *serdes);
+
+extern struct serdes_chip_data serdes_bu18tl82_data;
+extern struct serdes_chip_data serdes_bu18rl82_data;
+extern struct serdes_chip_data serdes_max96745_data;
+extern struct serdes_chip_data serdes_max96752_data;
+extern struct serdes_chip_data serdes_max96755_data;
+extern struct serdes_chip_data serdes_max96772_data;
+extern struct serdes_chip_data serdes_max96789_data;
+extern struct serdes_chip_data serdes_rkx111_data;
+extern struct serdes_chip_data serdes_rkx121_data;
+extern struct serdes_chip_data serdes_nca9539_data;
+
+#endif
diff --git a/kernel/drivers/mfd/display-serdes/gpio.h b/kernel/drivers/mfd/display-serdes/gpio.h
new file mode 100644
index 0000000..7a9d630
--- /dev/null
+++ b/kernel/drivers/mfd/display-serdes/gpio.h
@@ -0,0 +1,248 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * gpio.h -- GPIO for different serdes chip
+ *
+ * Copyright (c) 2023-2028 Rockchip Electronics Co. Ltd.
+ *
+ * Author: luowei <lw@rock-chips.com>
+ *
+ */
+
+#ifndef __MFD_SERDES_GPIO_H__
+#define __MFD_SERDES_GPIO_H__
+
+#include "core.h"
+enum serdes_parent {
+	BU18TL82 = 0x1000,
+	BU18RL82 = 0x1001,
+	MAX96745 = 0x2000,
+	MAX96752 = 0x2001,
+	MAX96755 = 0x2003,
+	MAX96722 = 0x2004,
+	MAX96789 = 0x2005,
+	RKX111 = 0x3000,
+	RKX121 = 0x3001,
+};
+
+enum serdes_type {
+	TYPE_ID_INVALID = 0,
+	TYPE_SER,
+	TYPE_DES,
+	TYPE_OTHER,
+};
+
+enum serdes_bridge_type {
+	TYPE_BRIDGE_PANEL = 0,
+	TYPE_BRIDGE_BRIDGE,
+};
+
+enum serdes_id {
+	SERDES_ID_INVALID = 0,
+
+	ROHM_ID_BU18TL82,
+	ROHM_ID_BU18RL82,
+
+	MAXIM_ID_MAX96745,
+	MAXIM_ID_MAX96752,
+	MAXIM_ID_MAX96755,
+	MAXIM_ID_MAX96772,
+	MAXIM_ID_MAX96789,
+
+	ROCKCHIP_ID_RKX111,
+	ROCKCHIP_ID_RKX121,
+
+	NOVO_ID_NCA9539,
+
+	SERDES_NUM_ID,
+};
+
+enum bu18tl82_gpio_list {
+	ROHM_BU18TL82_GPIO0 = 0,
+	ROHM_BU18TL82_GPIO1,
+	ROHM_BU18TL82_GPIO2,
+	ROHM_BU18TL82_GPIO3,
+	ROHM_BU18TL82_GPIO4,
+	ROHM_BU18TL82_GPIO5,
+	ROHM_BU18TL82_GPIO6,
+	ROHM_BU18TL82_GPIO7,
+};
+
+enum bu18rl82_gpio_list {
+	ROHM_BU18RL82_GPIO0 = 0,
+	ROHM_BU18RL82_GPIO1,
+	ROHM_BU18RL82_GPIO2,
+	ROHM_BU18RL82_GPIO3,
+	ROHM_BU18RL82_GPIO4,
+	ROHM_BU18RL82_GPIO5,
+	ROHM_BU18RL82_GPIO6,
+	ROHM_BU18RL82_GPIO7,
+};
+
+enum max96745_gpio_list {
+	MAXIM_MAX96745_MFP0 = 0,
+	MAXIM_MAX96745_MFP1,
+	MAXIM_MAX96745_MFP2,
+	MAXIM_MAX96745_MFP3,
+	MAXIM_MAX96745_MFP4,
+	MAXIM_MAX96745_MFP5,
+	MAXIM_MAX96745_MFP6,
+	MAXIM_MAX96745_MFP7,
+	MAXIM_MAX96745_MFP8,
+	MAXIM_MAX96745_MFP9,
+	MAXIM_MAX96745_MFP10,
+	MAXIM_MAX96745_MFP11,
+	MAXIM_MAX96745_MFP12,
+	MAXIM_MAX96745_MFP13,
+	MAXIM_MAX96745_MFP14,
+	MAXIM_MAX96745_MFP15,
+	MAXIM_MAX96745_MFP16,
+	MAXIM_MAX96745_MFP17,
+	MAXIM_MAX96745_MFP18,
+	MAXIM_MAX96745_MFP19,
+	MAXIM_MAX96745_MFP20,
+	MAXIM_MAX96745_MFP21,
+	MAXIM_MAX96745_MFP22,
+	MAXIM_MAX96745_MFP23,
+	MAXIM_MAX96745_MFP24,
+	MAXIM_MAX96745_MFP25,
+};
+
+enum max96752_gpio_list {
+	MAXIM_MAX96752_MFP0 = 0,
+	MAXIM_MAX96752_MFP1,
+	MAXIM_MAX96752_MFP2,
+	MAXIM_MAX96752_MFP3,
+	MAXIM_MAX96752_MFP4,
+	MAXIM_MAX96752_MFP5,
+	MAXIM_MAX96752_MFP6,
+	MAXIM_MAX96752_MFP7,
+	MAXIM_MAX96752_MFP8,
+	MAXIM_MAX96752_MFP9,
+	MAXIM_MAX96752_MFP10,
+	MAXIM_MAX96752_MFP11,
+	MAXIM_MAX96752_MFP12,
+	MAXIM_MAX96752_MFP13,
+	MAXIM_MAX96752_MFP14,
+	MAXIM_MAX96752_MFP15,
+};
+
+enum max96755_gpio_list {
+	MAXIM_MAX96755_MFP0 = 0,
+	MAXIM_MAX96755_MFP1,
+	MAXIM_MAX96755_MFP2,
+	MAXIM_MAX96755_MFP3,
+	MAXIM_MAX96755_MFP4,
+	MAXIM_MAX96755_MFP5,
+	MAXIM_MAX96755_MFP6,
+	MAXIM_MAX96755_MFP7,
+	MAXIM_MAX96755_MFP8,
+	MAXIM_MAX96755_MFP9,
+	MAXIM_MAX96755_MFP10,
+	MAXIM_MAX96755_MFP11,
+	MAXIM_MAX96755_MFP12,
+	MAXIM_MAX96755_MFP13,
+	MAXIM_MAX96755_MFP14,
+	MAXIM_MAX96755_MFP15,
+	MAXIM_MAX96755_MFP16,
+	MAXIM_MAX96755_MFP17,
+	MAXIM_MAX96755_MFP18,
+	MAXIM_MAX96755_MFP19,
+	MAXIM_MAX96755_MFP20,
+};
+
+enum max96722_gpio_list {
+	MAXIM_MAX96772_MFP0 = 0,
+	MAXIM_MAX96772_MFP1,
+	MAXIM_MAX96772_MFP2,
+	MAXIM_MAX96772_MFP3,
+	MAXIM_MAX96772_MFP4,
+	MAXIM_MAX96772_MFP5,
+	MAXIM_MAX96772_MFP6,
+	MAXIM_MAX96772_MFP7,
+	MAXIM_MAX96772_MFP8,
+	MAXIM_MAX96772_MFP9,
+	MAXIM_MAX96772_MFP10,
+	MAXIM_MAX96772_MFP11,
+	MAXIM_MAX96772_MFP12,
+	MAXIM_MAX96772_MFP13,
+	MAXIM_MAX96772_MFP14,
+	MAXIM_MAX96772_MFP15,
+};
+
+enum max96789_gpio_list {
+	MAXIM_MAX96789_MFP0 = 0,
+	MAXIM_MAX96789_MFP1,
+	MAXIM_MAX96789_MFP2,
+	MAXIM_MAX96789_MFP3,
+	MAXIM_MAX96789_MFP4,
+	MAXIM_MAX96789_MFP5,
+	MAXIM_MAX96789_MFP6,
+	MAXIM_MAX96789_MFP7,
+	MAXIM_MAX96789_MFP8,
+	MAXIM_MAX96789_MFP9,
+	MAXIM_MAX96789_MFP10,
+	MAXIM_MAX96789_MFP11,
+	MAXIM_MAX96789_MFP12,
+	MAXIM_MAX96789_MFP13,
+	MAXIM_MAX96789_MFP14,
+	MAXIM_MAX96789_MFP15,
+	MAXIM_MAX96789_MFP16,
+	MAXIM_MAX96789_MFP17,
+	MAXIM_MAX96789_MFP18,
+	MAXIM_MAX96789_MFP19,
+	MAXIM_MAX96789_MFP20,
+};
+
+enum rkx111_gpio_list {
+	ROCKCHIP_RKX111_GPIO0 = 0,
+	ROCKCHIP_RKX111_GPIO1,
+	ROCKCHIP_RKX111_GPIO2,
+	ROCKCHIP_RKX111_GPIO3,
+	ROCKCHIP_RKX111_GPIO4,
+	ROCKCHIP_RKX111_GPIO5,
+	ROCKCHIP_RKX111_GPIO6,
+	ROCKCHIP_RKX111_GPIO7,
+};
+
+enum rkx121_gpio_list {
+	ROCKCHIP_RKX121_GPIO0 = 0,
+	ROCKCHIP_RKX121_GPIO1,
+	ROCKCHIP_RKX121_GPIO2,
+	ROCKCHIP_RKX121_GPIO3,
+	ROCKCHIP_RKX121_GPIO4,
+	ROCKCHIP_RKX121_GPIO5,
+	ROCKCHIP_RKX121_GPIO6,
+	ROCKCHIP_RKX121_GPIO7,
+};
+
+enum serdes_gpio_state {
+	SERDES_GPIO_PULL_NONE = 0,
+	SERDES_GPIO_PULL_DOWN,
+	SERDES_GPIO_PULL_UP,
+	SERDES_GPIO_DIR_IN,
+	SERDES_GPIO_DIR_OUT,
+	SERDES_GPIO_LEVEL_HIGH,
+	SERDES_GPIO_LEVEL_LOW,
+};
+
+enum nca9539_gpio_list {
+	NOVO_NCA9539_GPIO0 = 0,
+	NOVO_NCA9539_GPIO1,
+	NOVO_NCA9539_GPIO2,
+	NOVO_NCA9539_GPIO3,
+	NOVO_NCA9539_GPIO4,
+	NOVO_NCA9539_GPIO5,
+	NOVO_NCA9539_GPIO6,
+	NOVO_NCA9539_GPIO7,
+
+	NOVO_NCA9539_GPIO8,
+	NOVO_NCA9539_GPIO9,
+	NOVO_NCA9539_GPIO10,
+	NOVO_NCA9539_GPIO11,
+	NOVO_NCA9539_GPIO12,
+	NOVO_NCA9539_GPIO13,
+	NOVO_NCA9539_GPIO14,
+	NOVO_NCA9539_GPIO15,
+};
+
+#endif
diff --git a/kernel/drivers/mfd/display-serdes/maxim/Kconfig b/kernel/drivers/mfd/display-serdes/maxim/Kconfig
new file mode 100644
index 0000000..e889dcd
--- /dev/null
+++ b/kernel/drivers/mfd/display-serdes/maxim/Kconfig
@@ -0,0 +1,44 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# maxim display serdes drivers configuration
+#
+
+menuconfig SERDES_DISPLAY_CHIP_MAXIM
+	tristate "maxim serdes device support"
+	default y
+	help
+	  Enable this to be able to choose the drivers for controlling the
+	  maxim serdes.
+
+if SERDES_DISPLAY_CHIP_MAXIM
+config SERDES_DISPLAY_CHIP_MAXIM_MAX96745
+	tristate "maxim max96745 serdes"
+	default y
+	help
+	  To support maxim max96745 display serdes.
+
+config SERDES_DISPLAY_CHIP_MAXIM_MAX96752
+	tristate "maxim max96752 serdes"
+	default y
+	help
+	  To support maxim max96752 display serdes.
+
+config SERDES_DISPLAY_CHIP_MAXIM_MAX96755
+	tristate "maxim max96755 serdes"
+	default y
+	help
+	  To support maxim max96755 display serdes.
+
+config SERDES_DISPLAY_CHIP_MAXIM_MAX96772
+	tristate "maxim max96772 serdes"
+	default y
+	help
+	  To support maxim max96772 display serdes.
+
+config SERDES_DISPLAY_CHIP_MAXIM_MAX96789
+	tristate "maxim max96789 serdes"
+	default y
+	help
+	  To support maxim max96789 display serdes.
+
+endif
diff --git a/kernel/drivers/mfd/display-serdes/maxim/Makefile b/kernel/drivers/mfd/display-serdes/maxim/Makefile
new file mode 100644
index 0000000..349233d
--- /dev/null
+++ b/kernel/drivers/mfd/display-serdes/maxim/Makefile
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# maxim display serdes drivers configuration
+#
+obj-$(CONFIG_SERDES_DISPLAY_CHIP_MAXIM_MAX96745)	+= maxim-max96745.o
+obj-$(CONFIG_SERDES_DISPLAY_CHIP_MAXIM_MAX96752)	+= maxim-max96752.o
+obj-$(CONFIG_SERDES_DISPLAY_CHIP_MAXIM_MAX96755)	+= maxim-max96755.o
+obj-$(CONFIG_SERDES_DISPLAY_CHIP_MAXIM_MAX96772)	+= maxim-max96772.o
+obj-$(CONFIG_SERDES_DISPLAY_CHIP_MAXIM_MAX96789)	+= maxim-max96789.o
diff --git a/kernel/drivers/mfd/display-serdes/maxim/maxim-max96745.c b/kernel/drivers/mfd/display-serdes/maxim/maxim-max96745.c
new file mode 100644
index 0000000..2736936
--- /dev/null
+++ b/kernel/drivers/mfd/display-serdes/maxim/maxim-max96745.c
@@ -0,0 +1,781 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * maxim-max96745.c  --  I2C register interface access for max96745 serdes chip
+ *
+ * Copyright (c) 2023-2028 Rockchip Electronics Co. Ltd.
+ *
+ * Author:
+ */
+
+#include "../core.h"
+#include "maxim-max96745.h"
+
+static bool max96745_volatile_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case 0x0076:
+	case 0x0086:
+	case 0x0100:
+	case 0x0200 ... 0x02ce:
+	case 0x7000:
+	case 0x7070:
+	case 0x7074:
+		return false;
+	default:
+		return true;
+	}
+}
+
+static struct regmap_config max96745_regmap_config = {
+	.name = "max96745",
+	.reg_bits = 16,
+	.val_bits = 8,
+	.max_register = 0x8000,
+	.volatile_reg = max96745_volatile_reg,
+	.cache_type = REGCACHE_RBTREE,
+};
+
+struct serdes_function_data {
+	u8 gpio_out_dis:1;
+	u8 gpio_io_rx_en:1;
+	u8 gpio_tx_en_a:1;
+	u8 gpio_tx_en_b:1;
+	u8 gpio_rx_en_a:1;
+	u8 gpio_rx_en_b:1;
+	u8 gpio_tx_id;
+	u8 gpio_rx_id;
+};
+
+struct config_desc {
+	u16 reg;
+	u8 mask;
+	u8 val;
+};
+
+struct serdes_group_data {
+	const struct config_desc *configs;
+	int num_configs;
+};
+
+static int MAX96745_MFP0_pins[] = {0};
+static int MAX96745_MFP1_pins[] = {1};
+static int MAX96745_MFP2_pins[] = {2};
+static int MAX96745_MFP3_pins[] = {3};
+static int MAX96745_MFP4_pins[] = {4};
+static int MAX96745_MFP5_pins[] = {5};
+static int MAX96745_MFP6_pins[] = {6};
+static int MAX96745_MFP7_pins[] = {7};
+
+static int MAX96745_MFP8_pins[] = {8};
+static int MAX96745_MFP9_pins[] = {9};
+static int MAX96745_MFP10_pins[] = {10};
+static int MAX96745_MFP11_pins[] = {11};
+static int MAX96745_MFP12_pins[] = {12};
+static int MAX96745_MFP13_pins[] = {13};
+static int MAX96745_MFP14_pins[] = {14};
+static int MAX96745_MFP15_pins[] = {15};
+
+static int MAX96745_MFP16_pins[] = {16};
+static int MAX96745_MFP17_pins[] = {17};
+static int MAX96745_MFP18_pins[] = {18};
+static int MAX96745_MFP19_pins[] = {19};
+static int MAX96745_MFP20_pins[] = {20};
+static int MAX96745_MFP21_pins[] = {21};
+static int MAX96745_MFP22_pins[] = {22};
+static int MAX96745_MFP23_pins[] = {23};
+
+static int MAX96745_MFP24_pins[] = {24};
+static int MAX96745_MFP25_pins[] = {25};
+static int MAX96745_I2C_pins[] = {3, 7};
+static int MAX96745_UART_pins[] = {3, 7};
+
+#define GROUP_DESC(nm) \
+{ \
+	.name = #nm, \
+	.pins = nm ## _pins, \
+	.num_pins = ARRAY_SIZE(nm ## _pins), \
+}
+
+static const char *serdes_gpio_groups[] = {
+	"MAX96745_MFP0", "MAX96745_MFP1", "MAX96745_MFP2", "MAX96745_MFP3",
+	"MAX96745_MFP4", "MAX96745_MFP5", "MAX96745_MFP6", "MAX96745_MFP7",
+
+	"MAX96745_MFP8", "MAX96745_MFP9", "MAX96745_MFP10", "MAX96745_MFP11",
+	"MAX96745_MFP12", "MAX96745_MFP13", "MAX96745_MFP14", "MAX96745_MFP15",
+
+	"MAX96745_MFP16", "MAX96745_MFP17", "MAX96745_MFP18", "MAX96745_MFP19",
+	"MAX96745_MFP20", "MAX96745_MFP21", "MAX96745_MFP22", "MAX96745_MFP23",
+
+	"MAX96745_MFP24", "MAX96745_MFP25",
+};
+
+static const char *MAX96745_I2C_groups[] = { "MAX96745_I2C" };
+static const char *MAX96745_UART_groups[] = { "MAX96745_UART" };
+
+#define FUNCTION_DESC(nm) \
+{ \
+	.name = #nm, \
+	.group_names = nm##_groups, \
+	.num_group_names = ARRAY_SIZE(nm##_groups), \
+} \
+
+#define FUNCTION_DESC_GPIO_OUTPUT_A(id) \
+{ \
+	.name = "DES_GPIO"#id"_OUTPUT_A", \
+	.group_names = serdes_gpio_groups, \
+	.num_group_names = ARRAY_SIZE(serdes_gpio_groups), \
+	.data = (void *)(const struct serdes_function_data []) { \
+		{ .gpio_out_dis = 1, .gpio_tx_en_a = 1, \
+		  .gpio_io_rx_en = 1, .gpio_tx_id = id } \
+	}, \
+} \
+
+#define FUNCTION_DESC_GPIO_OUTPUT_B(id) \
+{ \
+	.name = "DES_GPIO"#id"_OUTPUT_B", \
+	.group_names = serdes_gpio_groups, \
+	.num_group_names = ARRAY_SIZE(serdes_gpio_groups), \
+	.data = (void *)(const struct serdes_function_data []) { \
+		{ .gpio_out_dis = 1, .gpio_tx_en_b = 1, \
+		  .gpio_io_rx_en = 1, .gpio_tx_id = id } \
+	}, \
+} \
+
+#define FUNCTION_DESC_GPIO_INPUT_A(id) \
+{ \
+	.name = "DES_GPIO"#id"_INPUT_A", \
+	.group_names = serdes_gpio_groups, \
+	.num_group_names = ARRAY_SIZE(serdes_gpio_groups), \
+	.data = (void *)(const struct serdes_function_data []) { \
+		{ .gpio_rx_en_a = 1, .gpio_rx_id = id } \
+	}, \
+} \
+
+#define FUNCTION_DESC_GPIO_INPUT_B(id) \
+{ \
+	.name = "DES_GPIO"#id"_INPUT_B", \
+	.group_names = serdes_gpio_groups, \
+	.num_group_names = ARRAY_SIZE(serdes_gpio_groups), \
+	.data = (void *)(const struct serdes_function_data []) { \
+		{ .gpio_rx_en_b = 1, .gpio_rx_id = id } \
+	}, \
+} \
+
+#define FUNCTION_DESC_GPIO() \
+{ \
+	.name = "MAX96745_GPIO", \
+	.group_names = serdes_gpio_groups, \
+	.num_group_names = ARRAY_SIZE(serdes_gpio_groups), \
+	.data = (void *)(const struct serdes_function_data []) { \
+		{ } \
+	}, \
+} \
+
+static struct pinctrl_pin_desc max96745_pins_desc[] = {
+	PINCTRL_PIN(MAXIM_MAX96745_MFP0, "MAX96745_MFP0"),
+	PINCTRL_PIN(MAXIM_MAX96745_MFP1, "MAX96745_MFP1"),
+	PINCTRL_PIN(MAXIM_MAX96745_MFP2, "MAX96745_MFP2"),
+	PINCTRL_PIN(MAXIM_MAX96745_MFP3, "MAX96745_MFP3"),
+	PINCTRL_PIN(MAXIM_MAX96745_MFP4, "MAX96745_MFP4"),
+	PINCTRL_PIN(MAXIM_MAX96745_MFP5, "MAX96745_MFP5"),
+	PINCTRL_PIN(MAXIM_MAX96745_MFP6, "MAX96745_MFP6"),
+	PINCTRL_PIN(MAXIM_MAX96745_MFP7, "MAX96745_MFP7"),
+
+	PINCTRL_PIN(MAXIM_MAX96745_MFP8, "MAX96745_MFP8"),
+	PINCTRL_PIN(MAXIM_MAX96745_MFP9, "MAX96745_MFP9"),
+	PINCTRL_PIN(MAXIM_MAX96745_MFP10, "MAX96745_MFP10"),
+	PINCTRL_PIN(MAXIM_MAX96745_MFP11, "MAX96745_MFP11"),
+	PINCTRL_PIN(MAXIM_MAX96745_MFP12, "MAX96745_MFP12"),
+	PINCTRL_PIN(MAXIM_MAX96745_MFP13, "MAX96745_MFP13"),
+	PINCTRL_PIN(MAXIM_MAX96745_MFP14, "MAX96745_MFP14"),
+	PINCTRL_PIN(MAXIM_MAX96745_MFP15, "MAX96745_MFP15"),
+
+	PINCTRL_PIN(MAXIM_MAX96745_MFP16, "MAX96745_MFP16"),
+	PINCTRL_PIN(MAXIM_MAX96745_MFP17, "MAX96745_MFP17"),
+	PINCTRL_PIN(MAXIM_MAX96745_MFP18, "MAX96745_MFP18"),
+	PINCTRL_PIN(MAXIM_MAX96745_MFP19, "MAX96745_MFP19"),
+	PINCTRL_PIN(MAXIM_MAX96745_MFP20, "MAX96745_MFP20"),
+	PINCTRL_PIN(MAXIM_MAX96745_MFP21, "MAX96745_MFP21"),
+	PINCTRL_PIN(MAXIM_MAX96745_MFP22, "MAX96745_MFP22"),
+	PINCTRL_PIN(MAXIM_MAX96745_MFP23, "MAX96745_MFP23"),
+
+	PINCTRL_PIN(MAXIM_MAX96745_MFP24, "MAX96745_MFP24"),
+	PINCTRL_PIN(MAXIM_MAX96745_MFP25, "MAX96745_MFP25"),
+};
+
+static struct group_desc max96745_groups_desc[] = {
+	GROUP_DESC(MAX96745_MFP0),
+	GROUP_DESC(MAX96745_MFP1),
+	GROUP_DESC(MAX96745_MFP2),
+	GROUP_DESC(MAX96745_MFP3),
+	GROUP_DESC(MAX96745_MFP4),
+	GROUP_DESC(MAX96745_MFP5),
+	GROUP_DESC(MAX96745_MFP6),
+	GROUP_DESC(MAX96745_MFP7),
+
+	GROUP_DESC(MAX96745_MFP8),
+	GROUP_DESC(MAX96745_MFP9),
+	GROUP_DESC(MAX96745_MFP10),
+	GROUP_DESC(MAX96745_MFP11),
+	GROUP_DESC(MAX96745_MFP12),
+	GROUP_DESC(MAX96745_MFP13),
+	GROUP_DESC(MAX96745_MFP14),
+	GROUP_DESC(MAX96745_MFP15),
+
+	GROUP_DESC(MAX96745_MFP16),
+	GROUP_DESC(MAX96745_MFP17),
+	GROUP_DESC(MAX96745_MFP18),
+	GROUP_DESC(MAX96745_MFP19),
+	GROUP_DESC(MAX96745_MFP20),
+	GROUP_DESC(MAX96745_MFP21),
+	GROUP_DESC(MAX96745_MFP22),
+	GROUP_DESC(MAX96745_MFP23),
+
+	GROUP_DESC(MAX96745_MFP24),
+	GROUP_DESC(MAX96745_MFP25),
+
+	GROUP_DESC(MAX96745_I2C),
+	GROUP_DESC(MAX96745_UART),
+};
+
+static struct function_desc max96745_functions_desc[] = {
+	FUNCTION_DESC_GPIO_INPUT_A(0),
+	FUNCTION_DESC_GPIO_INPUT_A(1),
+	FUNCTION_DESC_GPIO_INPUT_A(2),
+	FUNCTION_DESC_GPIO_INPUT_A(3),
+	FUNCTION_DESC_GPIO_INPUT_A(4),
+	FUNCTION_DESC_GPIO_INPUT_A(5),
+	FUNCTION_DESC_GPIO_INPUT_A(6),
+	FUNCTION_DESC_GPIO_INPUT_A(7),
+
+	FUNCTION_DESC_GPIO_INPUT_A(8),
+	FUNCTION_DESC_GPIO_INPUT_A(9),
+	FUNCTION_DESC_GPIO_INPUT_A(10),
+	FUNCTION_DESC_GPIO_INPUT_A(11),
+	FUNCTION_DESC_GPIO_INPUT_A(12),
+	FUNCTION_DESC_GPIO_INPUT_A(13),
+	FUNCTION_DESC_GPIO_INPUT_A(14),
+	FUNCTION_DESC_GPIO_INPUT_A(15),
+
+	FUNCTION_DESC_GPIO_INPUT_A(16),
+	FUNCTION_DESC_GPIO_INPUT_A(17),
+	FUNCTION_DESC_GPIO_INPUT_A(18),
+	FUNCTION_DESC_GPIO_INPUT_A(19),
+	FUNCTION_DESC_GPIO_INPUT_A(20),
+	FUNCTION_DESC_GPIO_INPUT_A(21),
+	FUNCTION_DESC_GPIO_INPUT_A(22),
+	FUNCTION_DESC_GPIO_INPUT_A(23),
+
+	FUNCTION_DESC_GPIO_INPUT_A(24),
+	FUNCTION_DESC_GPIO_INPUT_A(25),
+
+	FUNCTION_DESC_GPIO_OUTPUT_A(0),
+	FUNCTION_DESC_GPIO_OUTPUT_A(1),
+	FUNCTION_DESC_GPIO_OUTPUT_A(2),
+	FUNCTION_DESC_GPIO_OUTPUT_A(3),
+	FUNCTION_DESC_GPIO_OUTPUT_A(4),
+	FUNCTION_DESC_GPIO_OUTPUT_A(5),
+	FUNCTION_DESC_GPIO_OUTPUT_A(6),
+	FUNCTION_DESC_GPIO_OUTPUT_A(7),
+
+	FUNCTION_DESC_GPIO_OUTPUT_A(8),
+	FUNCTION_DESC_GPIO_OUTPUT_A(9),
+	FUNCTION_DESC_GPIO_OUTPUT_A(10),
+	FUNCTION_DESC_GPIO_OUTPUT_A(11),
+	FUNCTION_DESC_GPIO_OUTPUT_A(12),
+	FUNCTION_DESC_GPIO_OUTPUT_A(13),
+	FUNCTION_DESC_GPIO_OUTPUT_A(14),
+	FUNCTION_DESC_GPIO_OUTPUT_A(15),
+
+	FUNCTION_DESC_GPIO_OUTPUT_A(16),
+	FUNCTION_DESC_GPIO_OUTPUT_A(17),
+	FUNCTION_DESC_GPIO_OUTPUT_A(18),
+	FUNCTION_DESC_GPIO_OUTPUT_A(19),
+	FUNCTION_DESC_GPIO_OUTPUT_A(20),
+	FUNCTION_DESC_GPIO_OUTPUT_A(21),
+	FUNCTION_DESC_GPIO_OUTPUT_A(22),
+	FUNCTION_DESC_GPIO_OUTPUT_A(23),
+
+	FUNCTION_DESC_GPIO_OUTPUT_A(24),
+	FUNCTION_DESC_GPIO_OUTPUT_A(25),
+
+	FUNCTION_DESC_GPIO_INPUT_B(0),
+	FUNCTION_DESC_GPIO_INPUT_B(1),
+	FUNCTION_DESC_GPIO_INPUT_B(2),
+	FUNCTION_DESC_GPIO_INPUT_B(3),
+	FUNCTION_DESC_GPIO_INPUT_B(4),
+	FUNCTION_DESC_GPIO_INPUT_B(5),
+	FUNCTION_DESC_GPIO_INPUT_B(6),
+	FUNCTION_DESC_GPIO_INPUT_B(7),
+
+	FUNCTION_DESC_GPIO_INPUT_B(8),
+	FUNCTION_DESC_GPIO_INPUT_B(9),
+	FUNCTION_DESC_GPIO_INPUT_B(10),
+	FUNCTION_DESC_GPIO_INPUT_B(11),
+	FUNCTION_DESC_GPIO_INPUT_B(12),
+	FUNCTION_DESC_GPIO_INPUT_B(13),
+	FUNCTION_DESC_GPIO_INPUT_B(14),
+	FUNCTION_DESC_GPIO_INPUT_B(15),
+
+	FUNCTION_DESC_GPIO_INPUT_B(16),
+	FUNCTION_DESC_GPIO_INPUT_B(17),
+	FUNCTION_DESC_GPIO_INPUT_B(18),
+	FUNCTION_DESC_GPIO_INPUT_B(19),
+	FUNCTION_DESC_GPIO_INPUT_B(20),
+	FUNCTION_DESC_GPIO_INPUT_B(21),
+	FUNCTION_DESC_GPIO_INPUT_B(22),
+	FUNCTION_DESC_GPIO_INPUT_B(23),
+
+	FUNCTION_DESC_GPIO_INPUT_B(24),
+	FUNCTION_DESC_GPIO_INPUT_B(25),
+
+	FUNCTION_DESC_GPIO_OUTPUT_B(0),
+	FUNCTION_DESC_GPIO_OUTPUT_B(1),
+	FUNCTION_DESC_GPIO_OUTPUT_B(2),
+	FUNCTION_DESC_GPIO_OUTPUT_B(3),
+	FUNCTION_DESC_GPIO_OUTPUT_B(4),
+	FUNCTION_DESC_GPIO_OUTPUT_B(5),
+	FUNCTION_DESC_GPIO_OUTPUT_B(6),
+	FUNCTION_DESC_GPIO_OUTPUT_B(7),
+
+	FUNCTION_DESC_GPIO_OUTPUT_B(8),
+	FUNCTION_DESC_GPIO_OUTPUT_B(9),
+	FUNCTION_DESC_GPIO_OUTPUT_B(10),
+	FUNCTION_DESC_GPIO_OUTPUT_B(11),
+	FUNCTION_DESC_GPIO_OUTPUT_B(12),
+	FUNCTION_DESC_GPIO_OUTPUT_B(13),
+	FUNCTION_DESC_GPIO_OUTPUT_B(14),
+	FUNCTION_DESC_GPIO_OUTPUT_B(15),
+
+	FUNCTION_DESC_GPIO_OUTPUT_B(16),
+	FUNCTION_DESC_GPIO_OUTPUT_B(17),
+	FUNCTION_DESC_GPIO_OUTPUT_B(18),
+	FUNCTION_DESC_GPIO_OUTPUT_B(19),
+	FUNCTION_DESC_GPIO_OUTPUT_B(20),
+	FUNCTION_DESC_GPIO_OUTPUT_B(21),
+	FUNCTION_DESC_GPIO_OUTPUT_B(22),
+	FUNCTION_DESC_GPIO_OUTPUT_B(23),
+
+	FUNCTION_DESC_GPIO_OUTPUT_B(24),
+	FUNCTION_DESC_GPIO_OUTPUT_B(25),
+
+	FUNCTION_DESC_GPIO(),
+
+	FUNCTION_DESC(MAX96745_I2C),
+	FUNCTION_DESC(MAX96745_UART),
+};
+
+static struct serdes_chip_pinctrl_info max96745_pinctrl_info = {
+	.pins = max96745_pins_desc,
+	.num_pins = ARRAY_SIZE(max96745_pins_desc),
+	.groups = max96745_groups_desc,
+	.num_groups = ARRAY_SIZE(max96745_groups_desc),
+	.functions = max96745_functions_desc,
+	.num_functions = ARRAY_SIZE(max96745_functions_desc),
+};
+
+static bool max96745_vid_tx_active(struct serdes *serdes)
+{
+	u32 val;
+
+	if (serdes_reg_read(serdes, 0x0107, &val))
+		return false;
+
+	if (!FIELD_GET(VID_TX_ACTIVE_A | VID_TX_ACTIVE_B, val))
+		return false;
+
+	return true;
+}
+
+static int max96745_bridge_init(struct serdes *serdes)
+{
+	if (max96745_vid_tx_active(serdes)) {
+		extcon_set_state(serdes->extcon, EXTCON_JACK_VIDEO_OUT, true);
+		pr_info("%s, extcon is true\n", __func__);
+	} else {
+		pr_info("%s, extcon is false\n", __func__);
+	}
+
+	return 0;
+}
+
+static bool max96745_bridge_link_locked(struct serdes *serdes)
+{
+	u32 val;
+
+	if (serdes->lock_gpio)
+		return gpiod_get_value_cansleep(serdes->lock_gpio);
+
+	if (serdes_reg_read(serdes, 0x002a, &val))
+		return false;
+
+	if (!FIELD_GET(LINK_LOCKED, val))
+		return false;
+
+	return true;
+}
+
+static int max96745_bridge_attach(struct serdes *serdes)
+{
+	if (max96745_bridge_link_locked(serdes))
+		serdes->serdes_bridge->status = connector_status_connected;
+	else
+		serdes->serdes_bridge->status = connector_status_disconnected;
+
+	return 0;
+}
+
+static enum drm_connector_status
+max96745_bridge_detect(struct serdes *serdes)
+{
+	struct serdes_bridge *serdes_bridge = serdes->serdes_bridge;
+	enum drm_connector_status status = connector_status_connected;
+
+	if (!drm_kms_helper_is_poll_worker())
+		return serdes_bridge->status;
+
+	if (!max96745_bridge_link_locked(serdes)) {
+		status = connector_status_disconnected;
+		goto out;
+	}
+
+	if (extcon_get_state(serdes->extcon, EXTCON_JACK_VIDEO_OUT)) {
+		u32 dprx_trn_status2;
+
+		if (atomic_cmpxchg(&serdes_bridge->triggered, 1, 0)) {
+			status = connector_status_disconnected;
+			goto out;
+		}
+
+		if (serdes_reg_read(serdes, 0x641a, &dprx_trn_status2)) {
+			status = connector_status_disconnected;
+			goto out;
+		}
+
+		if ((dprx_trn_status2 & DPRX_TRAIN_STATE) != DPRX_TRAIN_STATE) {
+			dev_err(serdes->dev, "Training State: 0x%lx\n",
+				FIELD_GET(DPRX_TRAIN_STATE, dprx_trn_status2));
+			status = connector_status_disconnected;
+			goto out;
+		}
+	} else {
+		atomic_set(&serdes_bridge->triggered, 0);
+	}
+
+out:
+	serdes_bridge->status = status;
+	SERDES_DBG_MFD("%s: status=%d\n", __func__, status);
+	return status;
+}
+
+static int max96745_bridge_enable(struct serdes *serdes)
+{
+	int ret = 0;
+
+	SERDES_DBG_CHIP("%s: serdes chip %s ret=%d\n", __func__, serdes->chip_data->name, ret);
+	return ret;
+}
+
+static int max96745_bridge_disable(struct serdes *serdes)
+{
+	int ret = 0;
+
+	return ret;
+}
+
+static struct serdes_chip_bridge_ops max96745_bridge_ops = {
+	.init = max96745_bridge_init,
+	.attach = max96745_bridge_attach,
+	.detect = max96745_bridge_detect,
+	.enable = max96745_bridge_enable,
+	.disable = max96745_bridge_disable,
+};
+
+static int max96745_pinctrl_config_get(struct serdes *serdes,
+				       unsigned int pin, unsigned long *config)
+{
+	enum pin_config_param param = pinconf_to_config_param(*config);
+	unsigned int gpio_a_reg, gpio_b_reg;
+	u16 arg = 0;
+
+	serdes_reg_read(serdes, GPIO_A_REG(pin), &gpio_a_reg);
+	serdes_reg_read(serdes, GPIO_B_REG(pin), &gpio_b_reg);
+
+	SERDES_DBG_CHIP("%s: serdes chip %s pin=%d param=%d\n", __func__, serdes->chip_data->name, pin, param);
+
+	switch (param) {
+	case PIN_CONFIG_DRIVE_OPEN_DRAIN:
+		if (FIELD_GET(OUT_TYPE, gpio_b_reg))
+			return -EINVAL;
+		break;
+	case PIN_CONFIG_DRIVE_PUSH_PULL:
+		if (!FIELD_GET(OUT_TYPE, gpio_b_reg))
+			return -EINVAL;
+		break;
+	case PIN_CONFIG_BIAS_DISABLE:
+		if (FIELD_GET(PULL_UPDN_SEL, gpio_b_reg) != 0)
+			return -EINVAL;
+		break;
+	case PIN_CONFIG_BIAS_PULL_UP:
+		if (FIELD_GET(PULL_UPDN_SEL, gpio_b_reg) != 1)
+			return -EINVAL;
+		switch (FIELD_GET(RES_CFG, gpio_a_reg)) {
+		case 0:
+			arg = 40000;
+			break;
+		case 1:
+			arg = 10000;
+			break;
+		}
+		break;
+	case PIN_CONFIG_BIAS_PULL_DOWN:
+		if (FIELD_GET(PULL_UPDN_SEL, gpio_b_reg) != 2)
+			return -EINVAL;
+		switch (FIELD_GET(RES_CFG, gpio_a_reg)) {
+		case 0:
+			arg = 40000;
+			break;
+		case 1:
+			arg = 10000;
+			break;
+		}
+		break;
+	case PIN_CONFIG_OUTPUT:
+		if (FIELD_GET(GPIO_OUT_DIS, gpio_a_reg))
+			return -EINVAL;
+
+		arg = FIELD_GET(GPIO_OUT, gpio_a_reg);
+		break;
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	*config = pinconf_to_config_packed(param, arg);
+
+	return 0;
+}
+
+static int max96745_pinctrl_config_set(struct serdes *serdes,
+				       unsigned int pin, unsigned long *configs,
+				       unsigned int num_configs)
+{
+	enum pin_config_param param;
+	u32 arg;
+	u8 res_cfg;
+	int i;
+
+	for (i = 0; i < num_configs; i++) {
+		param = pinconf_to_config_param(configs[i]);
+		arg = pinconf_to_config_argument(configs[i]);
+
+		SERDES_DBG_CHIP("%s: serdes chip %s pin=%d param=%d\n", __func__,
+				serdes->chip_data->name, pin, param);
+
+		switch (param) {
+		case PIN_CONFIG_DRIVE_OPEN_DRAIN:
+			serdes_set_bits(serdes, GPIO_B_REG(pin),
+					OUT_TYPE, FIELD_PREP(OUT_TYPE, 0));
+			break;
+		case PIN_CONFIG_DRIVE_PUSH_PULL:
+			serdes_set_bits(serdes, GPIO_B_REG(pin),
+					OUT_TYPE, FIELD_PREP(OUT_TYPE, 1));
+			break;
+		case PIN_CONFIG_BIAS_DISABLE:
+			serdes_set_bits(serdes, GPIO_C_REG(pin),
+					PULL_UPDN_SEL,
+					FIELD_PREP(PULL_UPDN_SEL, 0));
+			break;
+		case PIN_CONFIG_BIAS_PULL_UP:
+			switch (arg) {
+			case 40000:
+				res_cfg = 0;
+				break;
+			case 1000000:
+				res_cfg = 1;
+				break;
+			default:
+				return -EINVAL;
+			}
+
+			serdes_set_bits(serdes, GPIO_A_REG(pin),
+					RES_CFG, FIELD_PREP(RES_CFG, res_cfg));
+			serdes_set_bits(serdes, GPIO_C_REG(pin),
+					PULL_UPDN_SEL,
+					FIELD_PREP(PULL_UPDN_SEL, 1));
+			break;
+		case PIN_CONFIG_BIAS_PULL_DOWN:
+			switch (arg) {
+			case 40000:
+				res_cfg = 0;
+				break;
+			case 1000000:
+				res_cfg = 1;
+				break;
+			default:
+				return -EINVAL;
+			}
+
+			serdes_set_bits(serdes, GPIO_A_REG(pin),
+					RES_CFG, FIELD_PREP(RES_CFG, res_cfg));
+			serdes_set_bits(serdes, GPIO_C_REG(pin),
+					PULL_UPDN_SEL,
+					FIELD_PREP(PULL_UPDN_SEL, 2));
+			break;
+		case PIN_CONFIG_OUTPUT:
+			serdes_set_bits(serdes, GPIO_A_REG(pin),
+					GPIO_OUT_DIS | GPIO_OUT,
+					FIELD_PREP(GPIO_OUT_DIS, 0) |
+					FIELD_PREP(GPIO_OUT, arg));
+			break;
+		default:
+			return -EOPNOTSUPP;
+		}
+	}
+
+	return 0;
+}
+
+static int max96745_pinctrl_set_mux(struct serdes *serdes,
+				    unsigned int function, unsigned int group)
+{
+	struct serdes_pinctrl *pinctrl = serdes->pinctrl;
+	struct function_desc *func;
+	struct group_desc *grp;
+	int i;
+
+	func = pinmux_generic_get_function(pinctrl->pctl, function);
+	if (!func)
+		return -EINVAL;
+
+	grp = pinctrl_generic_get_group(pinctrl->pctl, group);
+	if (!grp)
+		return -EINVAL;
+
+	SERDES_DBG_CHIP("%s: serdes chip %s func=%s data=%p group=%s data=%p, num_pin=%d\n",
+			__func__, serdes->chip_data->name,
+			func->name, func->data, grp->name, grp->data, grp->num_pins);
+
+	if (func->data) {
+		struct serdes_function_data *data = func->data;
+
+		for (i = 0; i < grp->num_pins; i++) {
+			serdes_set_bits(serdes,
+					GPIO_A_REG(grp->pins[i] - pinctrl->pin_base),
+					GPIO_OUT_DIS,
+					FIELD_PREP(GPIO_OUT_DIS, data->gpio_out_dis));
+			if (data->gpio_tx_en_a || data->gpio_tx_en_b)
+				serdes_set_bits(serdes,
+						GPIO_B_REG(grp->pins[i] - pinctrl->pin_base),
+						GPIO_TX_ID,
+						FIELD_PREP(GPIO_TX_ID, data->gpio_tx_id));
+			if (data->gpio_rx_en_a || data->gpio_rx_en_b)
+				serdes_set_bits(serdes,
+						GPIO_C_REG(grp->pins[i] - pinctrl->pin_base),
+						GPIO_RX_ID,
+						FIELD_PREP(GPIO_RX_ID, data->gpio_rx_id));
+			serdes_set_bits(serdes,
+					GPIO_D_REG(grp->pins[i] - pinctrl->pin_base),
+					GPIO_TX_EN_A | GPIO_TX_EN_B | GPIO_IO_RX_EN |
+					GPIO_RX_EN_A | GPIO_RX_EN_B,
+					FIELD_PREP(GPIO_TX_EN_A, data->gpio_tx_en_a) |
+					FIELD_PREP(GPIO_TX_EN_B, data->gpio_tx_en_b) |
+					FIELD_PREP(GPIO_RX_EN_A, data->gpio_rx_en_a) |
+					FIELD_PREP(GPIO_RX_EN_B, data->gpio_rx_en_b) |
+					FIELD_PREP(GPIO_IO_RX_EN, data->gpio_io_rx_en));
+		}
+	}
+
+	return 0;
+}
+
+static struct serdes_chip_pinctrl_ops max96745_pinctrl_ops = {
+	.pin_config_get = max96745_pinctrl_config_get,
+	.pin_config_set = max96745_pinctrl_config_set,
+	.set_mux = max96745_pinctrl_set_mux,
+};
+
+static int max96745_gpio_direction_input(struct serdes *serdes, int gpio)
+{
+	return 0;
+}
+
+static int max96745_gpio_direction_output(struct serdes *serdes, int gpio, int value)
+{
+	return 0;
+}
+
+static int max96745_gpio_get_level(struct serdes *serdes, int gpio)
+{
+	return 0;
+}
+
+static int max96745_gpio_set_level(struct serdes *serdes, int gpio, int value)
+{
+	return 0;
+}
+
+static int max96745_gpio_set_config(struct serdes *serdes, int gpio, unsigned long config)
+{
+	return 0;
+}
+
+static int max96745_gpio_to_irq(struct serdes *serdes, int gpio)
+{
+	return 0;
+}
+
+static struct serdes_chip_gpio_ops max96745_gpio_ops = {
+	.direction_input = max96745_gpio_direction_input,
+	.direction_output = max96745_gpio_direction_output,
+	.get_level = max96745_gpio_get_level,
+	.set_level = max96745_gpio_set_level,
+	.set_config = max96745_gpio_set_config,
+	.to_irq = max96745_gpio_to_irq,
+};
+
+static int max96745_pm_suspend(struct serdes *serdes)
+{
+	return 0;
+}
+
+static int max96745_pm_resume(struct serdes *serdes)
+{
+	return 0;
+}
+
+static struct serdes_chip_pm_ops max96745_pm_ops = {
+	.suspend = max96745_pm_suspend,
+	.resume = max96745_pm_resume,
+};
+
+static int max96745_irq_lock_handle(struct serdes *serdes)
+{
+	return IRQ_HANDLED;
+}
+
+static int max96745_irq_err_handle(struct serdes *serdes)
+{
+	return IRQ_HANDLED;
+}
+
+static struct serdes_chip_irq_ops max96745_irq_ops = {
+	.lock_handle = max96745_irq_lock_handle,
+	.err_handle = max96745_irq_err_handle,
+};
+
+struct serdes_chip_data serdes_max96745_data = {
+	.name		= "max96745",
+	.serdes_type	= TYPE_SER,
+	.serdes_id	= MAXIM_ID_MAX96745,
+	.connector_type	= DRM_MODE_CONNECTOR_eDP,
+	.regmap_config	= &max96745_regmap_config,
+	.pinctrl_info	= &max96745_pinctrl_info,
+	.bridge_ops	= &max96745_bridge_ops,
+	.pinctrl_ops	= &max96745_pinctrl_ops,
+	.gpio_ops	= &max96745_gpio_ops,
+	.pm_ops		= &max96745_pm_ops,
+	.irq_ops	= &max96745_irq_ops,
+};
+EXPORT_SYMBOL_GPL(serdes_max96745_data);
+
+MODULE_LICENSE("GPL");
diff --git a/kernel/drivers/mfd/display-serdes/maxim/maxim-max96745.h b/kernel/drivers/mfd/display-serdes/maxim/maxim-max96745.h
new file mode 100644
index 0000000..031f4f9
--- /dev/null
+++ b/kernel/drivers/mfd/display-serdes/maxim/maxim-max96745.h
@@ -0,0 +1,139 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * maxim-max96745.h -- register define for max96745 chip
+ *
+ * Copyright (c) 2023 Rockchip Electronics Co. Ltd.
+ *
+ * Author: luowei <lw@rock-chips.com>
+ *
+ */
+
+#ifndef __MFD_SERDES_MAXIM_MAX96745_H__
+#define __MFD_SERDES_MAXIM_MAX96745_H__
+
+#include <linux/bitfield.h>
+
+#define GPIO_A_REG(gpio)	(0x0200 + ((gpio) * 8))
+#define GPIO_B_REG(gpio)	(0x0201 + ((gpio) * 8))
+#define GPIO_C_REG(gpio)	(0x0202 + ((gpio) * 8))
+#define GPIO_D_REG(gpio)	(0x0203 + ((gpio) * 8))
+
+/* 0005h */
+#define PU_LF3			BIT(3)
+#define PU_LF2			BIT(2)
+#define PU_LF1			BIT(1)
+#define PU_LF0			BIT(0)
+
+/* 0010h */
+#define RESET_ALL		BIT(7)
+#define SLEEP			BIT(3)
+
+/* 0011h */
+#define CXTP_B			BIT(2)
+#define CXTP_A			BIT(0)
+
+/* 0013h */
+#define LOCKED			BIT(3)
+#define ERROR			BIT(2)
+
+/* 0026h */
+#define LF_0			GENMASK(2, 0)
+#define LF_1			GENMASK(6, 4)
+
+/* 0027h */
+#define LF_2			GENMASK(2, 0)
+#define LF_3			GENMASK(6, 4)
+
+/* 0028h, 0032h */
+#define LINK_EN			BIT(7)
+#define TX_RATE			GENMASK(3, 2)
+
+/* 0029h, 0033h */
+#define RESET_LINK		BIT(0)
+#define RESET_ONESHOT		BIT(1)
+
+/* 002Ah, 0034h */
+#define LINK_LOCKED		BIT(0)
+
+/* 0076h, 0086h */
+#define DIS_REM_CC		BIT(7)
+
+/* 0100h */
+#define VID_LINK_SEL		GENMASK(2, 1)
+#define VID_TX_EN		BIT(0)
+
+/* 0101h */
+#define BPP			GENMASK(5, 0)
+
+/* 0102h */
+#define PCLKDET_A		BIT(7)
+#define DRIFT_ERR_A		BIT(6)
+#define OVERFLOW_A		BIT(5)
+#define FIFO_WARN_A		BIT(4)
+#define LIM_HEART		BIT(2)
+
+/* 0107h */
+#define VID_TX_ACTIVE_B		BIT(7)
+#define VID_TX_ACTIVE_A		BIT(6)
+
+/* 0108h */
+#define PCLKDET_B		BIT(7)
+#define DRIFT_ERR_B		BIT(6)
+#define OVERFLOW_B		BIT(5)
+#define FIFO_WARN_B		BIT(4)
+
+/* 0200h */
+#define RES_CFG			BIT(7)
+#define TX_COM_EN		BIT(5)
+#define GPIO_OUT		BIT(4)
+#define GPIO_IN			BIT(3)
+#define GPIO_OUT_DIS		BIT(0)
+
+/* 0201h */
+#define PULL_UPDN_SEL		GENMASK(7, 6)
+#define OUT_TYPE		BIT(5)
+#define GPIO_TX_ID		GENMASK(4, 0)
+
+/* 0202h */
+#define OVR_RES_CFG		BIT(7)
+#define IO_EDGE_RATE		GENMASK(6, 5)
+#define GPIO_RX_ID		GENMASK(4, 0)
+
+/* 0203h */
+#define GPIO_IO_RX_EN		BIT(5)
+#define GPIO_OUT_LGC		BIT(4)
+#define GPIO_RX_EN_B		BIT(3)
+#define GPIO_TX_EN_B		BIT(2)
+#define GPIO_RX_EN_A		BIT(1)
+#define GPIO_TX_EN_A		BIT(0)
+
+/* 0750h */
+#define FRCZEROPAD		GENMASK(7, 6)
+#define FRCZPEN			BIT(5)
+#define FRCSDGAIN		BIT(4)
+#define FRCSDEN			BIT(3)
+#define FRCGAIN			GENMASK(2, 1)
+#define FRCEN			BIT(0)
+
+/* 0751h */
+#define FRCDATAWIDTH		BIT(3)
+#define FRCASYNCEN		BIT(2)
+#define FRCHSPOL		BIT(1)
+#define FRCVSPOL		BIT(0)
+
+/* 0752h */
+#define FRCDCMODE		GENMASK(1, 0)
+
+/* 641Ah */
+#define DPRX_TRAIN_STATE	GENMASK(7, 4)
+
+/* 7000h */
+#define LINK_ENABLE		BIT(0)
+
+/* 7070h */
+#define MAX_LANE_COUNT		GENMASK(7, 0)
+
+/* 7074h */
+#define MAX_LINK_RATE		GENMASK(7, 0)
+
+#endif
diff --git a/kernel/drivers/mfd/display-serdes/maxim/maxim-max96752.c b/kernel/drivers/mfd/display-serdes/maxim/maxim-max96752.c
new file mode 100644
index 0000000..2eddb39
--- /dev/null
+++ b/kernel/drivers/mfd/display-serdes/maxim/maxim-max96752.c
@@ -0,0 +1,319 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * maxim-max96752.c  --  I2C register interface access for max96752 serdes chip
+ *
+ * Copyright (c) 2023-2028 Rockchip Electronics Co. Ltd.
+ *
+ * Author:
+ */
+
+#include "../core.h"
+#include "maxim-max96752.h"
+
+static const struct regmap_range max96752_readable_ranges[] = {
+	regmap_reg_range(0x0000, 0x0600),
+};
+
+static const struct regmap_access_table max96752_readable_table = {
+	.yes_ranges = max96752_readable_ranges,
+	.n_yes_ranges = ARRAY_SIZE(max96752_readable_ranges),
+};
+
+static struct regmap_config max96752_regmap_config = {
+	.name = "max96752",
+	.reg_bits = 16,
+	.val_bits = 8,
+	.max_register = 0xffff,
+	.rd_table = &max96752_readable_table,
+};
+
+static int MAX96752_MFP0_pins[] = {0};
+static int MAX96752_MFP1_pins[] = {1};
+static int MAX96752_MFP2_pins[] = {2};
+static int MAX96752_MFP3_pins[] = {3};
+static int MAX96752_MFP4_pins[] = {4};
+static int MAX96752_MFP5_pins[] = {5};
+static int MAX96752_MFP6_pins[] = {6};
+static int MAX96752_MFP7_pins[] = {7};
+
+static int MAX96752_MFP8_pins[] = {8};
+static int MAX96752_MFP9_pins[] = {9};
+static int MAX96752_MFP10_pins[] = {10};
+static int MAX96752_MFP11_pins[] = {11};
+static int MAX96752_MFP12_pins[] = {12};
+static int MAX96752_MFP13_pins[] = {13};
+static int MAX96752_MFP14_pins[] = {14};
+static int MAX96752_MFP15_pins[] = {15};
+
+#define GROUP_DESC(nm) \
+{ \
+	.name = #nm, \
+	.pins = nm ## _pins, \
+	.num_pins = ARRAY_SIZE(nm ## _pins), \
+}
+
+struct serdes_function_data {
+	u8 gpio_out_dis:1;
+	u8 gpio_tx_en:1;
+	u8 gpio_rx_en:1;
+	u8 gpio_tx_id;
+	u8 gpio_rx_id;
+};
+
+static const char *serdes_gpio_groups[] = {
+	"MAX96752_MFP0", "MAX96752_MFP1", "MAX96752_MFP2", "MAX96752_MFP3",
+	"MAX96752_MFP4", "MAX96752_MFP5", "MAX96752_MFP6", "MAX96752_MFP7",
+
+	"MAX96752_MFP8", "MAX96752_MFP9", "MAX96752_MFP10", "MAX96752_MFP11",
+	"MAX96752_MFP12", "MAX96752_MFP13", "MAX96752_MFP14", "MAX96752_MFP15",
+};
+
+#define FUNCTION_DESC_GPIO_INPUT(id) \
+{ \
+	.name = "MFP"#id"_INPUT", \
+	.group_names = serdes_gpio_groups, \
+	.num_group_names = ARRAY_SIZE(serdes_gpio_groups), \
+	.data = (void *)(const struct serdes_function_data []) { \
+		{ .gpio_rx_en = 1, .gpio_rx_id = id } \
+	}, \
+} \
+
+#define FUNCTION_DESC_GPIO_OUTPUT(id) \
+{ \
+	.name = "MFP"#id"_OUTPUT", \
+	.group_names = serdes_gpio_groups, \
+	.num_group_names = ARRAY_SIZE(serdes_gpio_groups), \
+	.data = (void *)(const struct serdes_function_data []) { \
+		{ .gpio_out_dis = 1, .gpio_tx_en = 1, .gpio_tx_id = id } \
+	}, \
+} \
+
+static struct pinctrl_pin_desc max96752_pins_desc[] = {
+	PINCTRL_PIN(MAXIM_MAX96752_MFP0, "MAX96752_MFP0"),
+	PINCTRL_PIN(MAXIM_MAX96752_MFP1, "MAX96752_MFP1"),
+	PINCTRL_PIN(MAXIM_MAX96752_MFP2, "MAX96752_MFP2"),
+	PINCTRL_PIN(MAXIM_MAX96752_MFP3, "MAX96752_MFP3"),
+	PINCTRL_PIN(MAXIM_MAX96752_MFP4, "MAX96752_MFP4"),
+	PINCTRL_PIN(MAXIM_MAX96752_MFP5, "MAX96752_MFP5"),
+	PINCTRL_PIN(MAXIM_MAX96752_MFP6, "MAX96752_MFP6"),
+	PINCTRL_PIN(MAXIM_MAX96752_MFP7, "MAX96752_MFP7"),
+
+	PINCTRL_PIN(MAXIM_MAX96752_MFP8, "MAX96752_MFP8"),
+	PINCTRL_PIN(MAXIM_MAX96752_MFP9, "MAX96752_MFP9"),
+	PINCTRL_PIN(MAXIM_MAX96752_MFP10, "MAX96752_MFP10"),
+	PINCTRL_PIN(MAXIM_MAX96752_MFP11, "MAX96752_MFP11"),
+	PINCTRL_PIN(MAXIM_MAX96752_MFP12, "MAX96752_MFP12"),
+	PINCTRL_PIN(MAXIM_MAX96752_MFP13, "MAX96752_MFP13"),
+	PINCTRL_PIN(MAXIM_MAX96752_MFP14, "MAX96752_MFP14"),
+	PINCTRL_PIN(MAXIM_MAX96752_MFP15, "MAX96752_MFP15"),
+};
+
+static struct group_desc max96752_groups_desc[] = {
+	GROUP_DESC(MAX96752_MFP0),
+	GROUP_DESC(MAX96752_MFP1),
+	GROUP_DESC(MAX96752_MFP2),
+	GROUP_DESC(MAX96752_MFP3),
+	GROUP_DESC(MAX96752_MFP4),
+	GROUP_DESC(MAX96752_MFP5),
+	GROUP_DESC(MAX96752_MFP6),
+	GROUP_DESC(MAX96752_MFP7),
+
+	GROUP_DESC(MAX96752_MFP8),
+	GROUP_DESC(MAX96752_MFP9),
+	GROUP_DESC(MAX96752_MFP10),
+	GROUP_DESC(MAX96752_MFP11),
+	GROUP_DESC(MAX96752_MFP12),
+	GROUP_DESC(MAX96752_MFP13),
+	GROUP_DESC(MAX96752_MFP14),
+	GROUP_DESC(MAX96752_MFP15),
+};
+
+static struct function_desc max96752_functions_desc[] = {
+	FUNCTION_DESC_GPIO_INPUT(0),
+	FUNCTION_DESC_GPIO_INPUT(1),
+	FUNCTION_DESC_GPIO_INPUT(2),
+	FUNCTION_DESC_GPIO_INPUT(3),
+	FUNCTION_DESC_GPIO_INPUT(4),
+	FUNCTION_DESC_GPIO_INPUT(5),
+	FUNCTION_DESC_GPIO_INPUT(6),
+	FUNCTION_DESC_GPIO_INPUT(7),
+
+	FUNCTION_DESC_GPIO_INPUT(8),
+	FUNCTION_DESC_GPIO_INPUT(9),
+	FUNCTION_DESC_GPIO_INPUT(10),
+	FUNCTION_DESC_GPIO_INPUT(11),
+	FUNCTION_DESC_GPIO_INPUT(12),
+	FUNCTION_DESC_GPIO_INPUT(13),
+	FUNCTION_DESC_GPIO_INPUT(14),
+	FUNCTION_DESC_GPIO_INPUT(15),
+
+	FUNCTION_DESC_GPIO_OUTPUT(0),
+	FUNCTION_DESC_GPIO_OUTPUT(1),
+	FUNCTION_DESC_GPIO_OUTPUT(2),
+	FUNCTION_DESC_GPIO_OUTPUT(3),
+	FUNCTION_DESC_GPIO_OUTPUT(4),
+	FUNCTION_DESC_GPIO_OUTPUT(5),
+	FUNCTION_DESC_GPIO_OUTPUT(6),
+	FUNCTION_DESC_GPIO_OUTPUT(7),
+
+	FUNCTION_DESC_GPIO_OUTPUT(8),
+	FUNCTION_DESC_GPIO_OUTPUT(9),
+	FUNCTION_DESC_GPIO_OUTPUT(10),
+	FUNCTION_DESC_GPIO_OUTPUT(11),
+	FUNCTION_DESC_GPIO_OUTPUT(12),
+	FUNCTION_DESC_GPIO_OUTPUT(13),
+	FUNCTION_DESC_GPIO_OUTPUT(14),
+	FUNCTION_DESC_GPIO_OUTPUT(15),
+
+};
+
+static struct serdes_chip_pinctrl_info max96752_pinctrl_info = {
+	.pins = max96752_pins_desc,
+	.num_pins = ARRAY_SIZE(max96752_pins_desc),
+	.groups = max96752_groups_desc,
+	.num_groups = ARRAY_SIZE(max96752_groups_desc),
+	.functions = max96752_functions_desc,
+	.num_functions = ARRAY_SIZE(max96752_functions_desc),
+};
+
+static int max96752_panel_prepare(struct serdes *serdes)
+{
+	return 0;
+}
+
+static int max96752_panel_unprepare(struct serdes *serdes)
+{
+	//serdes_reg_write(serdes, 0x0215, 0x80);	/* lcd_en */
+
+	return 0;
+}
+
+static int max96752_panel_enable(struct serdes *serdes)
+{
+	return 0;
+}
+
+static int max96752_panel_disable(struct serdes *serdes)
+{
+	return 0;
+}
+
+static struct serdes_chip_panel_ops max96752_panel_ops = {
+	.prepare = max96752_panel_prepare,
+	.unprepare = max96752_panel_unprepare,
+	.enable = max96752_panel_enable,
+	.disable = max96752_panel_disable,
+};
+
+static int max96752_pinctrl_config_get(struct serdes *serdes,
+				       unsigned int pin,
+				       unsigned long *config)
+{
+	return 0;
+}
+
+static int max96752_pinctrl_config_set(struct serdes *serdes,
+				       unsigned int pin,
+				       unsigned long *configs,
+				       unsigned int num_configs)
+{
+	return 0;
+}
+
+static int max96752_pinctrl_set_mux(struct serdes *serdes, unsigned int func_selector,
+				    unsigned int group_selector)
+{
+	return 0;
+}
+
+static struct serdes_chip_pinctrl_ops max96752_pinctrl_ops = {
+	.pin_config_get = max96752_pinctrl_config_get,
+	.pin_config_set = max96752_pinctrl_config_set,
+	.set_mux = max96752_pinctrl_set_mux,
+};
+
+static int max96752_gpio_direction_input(struct serdes *serdes, int gpio)
+{
+	return 0;
+}
+
+static int max96752_gpio_direction_output(struct serdes *serdes, int gpio, int value)
+{
+	return 0;
+}
+
+static int max96752_gpio_get_level(struct serdes *serdes, int gpio)
+{
+	return 0;
+}
+
+static int max96752_gpio_set_level(struct serdes *serdes, int gpio, int value)
+{
+	return 0;
+}
+
+static int max96752_gpio_set_config(struct serdes *serdes, int gpio, unsigned long config)
+{
+	return 0;
+}
+
+static int max96752_gpio_to_irq(struct serdes *serdes, int gpio)
+{
+	return 0;
+}
+
+static struct serdes_chip_gpio_ops max96752_gpio_ops = {
+	.direction_input = max96752_gpio_direction_input,
+	.direction_output = max96752_gpio_direction_output,
+	.get_level = max96752_gpio_get_level,
+	.set_level = max96752_gpio_set_level,
+	.set_config = max96752_gpio_set_config,
+	.to_irq = max96752_gpio_to_irq,
+};
+
+static int max96752_pm_suspend(struct serdes *serdes)
+{
+	return 0;
+}
+
+static int max96752_pm_resume(struct serdes *serdes)
+{
+	return 0;
+}
+
+static struct serdes_chip_pm_ops max96752_pm_ops = {
+	.suspend = max96752_pm_suspend,
+	.resume = max96752_pm_resume,
+};
+
+static int max96752_irq_lock_handle(struct serdes *serdes)
+{
+	return IRQ_HANDLED;
+}
+
+static int max96752_irq_err_handle(struct serdes *serdes)
+{
+	return IRQ_HANDLED;
+}
+
+static struct serdes_chip_irq_ops max96752_irq_ops = {
+	.lock_handle = max96752_irq_lock_handle,
+	.err_handle = max96752_irq_err_handle,
+};
+
+struct serdes_chip_data serdes_max96752_data = {
+	.name		= "max96752",
+	.serdes_type	= TYPE_DES,
+	.serdes_id	= MAXIM_ID_MAX96752,
+	.connector_type	= DRM_MODE_CONNECTOR_LVDS,
+	.regmap_config	= &max96752_regmap_config,
+	.pinctrl_info	= &max96752_pinctrl_info,
+	.panel_ops	= &max96752_panel_ops,
+	.pinctrl_ops	= &max96752_pinctrl_ops,
+	.gpio_ops	= &max96752_gpio_ops,
+	.pm_ops		= &max96752_pm_ops,
+	.irq_ops	= &max96752_irq_ops,
+};
+EXPORT_SYMBOL_GPL(serdes_max96752_data);
+
+MODULE_LICENSE("GPL");
diff --git a/kernel/drivers/mfd/display-serdes/maxim/maxim-max96752.h b/kernel/drivers/mfd/display-serdes/maxim/maxim-max96752.h
new file mode 100644
index 0000000..079be4c
--- /dev/null
+++ b/kernel/drivers/mfd/display-serdes/maxim/maxim-max96752.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * maxim-max96752.h -- register define for max96752 chip
+ *
+ * Copyright (c) 2023-2028 Rockchip Electronics Co. Ltd.
+ *
+ * Author:
+ *
+ */
+
+#ifndef __MFD_SERDES_MAXIM_MAX96752_H__
+#define __MFD_SERDES_MAXIM_MAX96752_H__
+
+#endif
diff --git a/kernel/drivers/mfd/display-serdes/maxim/maxim-max96755.c b/kernel/drivers/mfd/display-serdes/maxim/maxim-max96755.c
new file mode 100644
index 0000000..c042eb3
--- /dev/null
+++ b/kernel/drivers/mfd/display-serdes/maxim/maxim-max96755.c
@@ -0,0 +1,719 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * maxim-max96755.c  --  I2C register interface access for max96755 serdes chip
+ *
+ * Copyright (c) 2023-2028 Rockchip Electronics Co. Ltd.
+ *
+ * Author:
+ */
+
+#include "../core.h"
+#include "maxim-max96755.h"
+
+static bool max96755_volatile_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case 0x0002:
+	case 0x0010:
+	case 0x0013:
+	case 0x0053:
+	case 0x0057:
+	case 0x02be ... 0x02fc:
+	case 0x0311:
+	case 0x032a:
+	case 0x0330 ... 0x0331:
+	case 0x0385 ... 0x0387:
+	case 0x03a4 ... 0x03ae:
+		return false;
+	default:
+		return true;
+	}
+}
+
+static struct regmap_config max96755_regmap_config = {
+	.name = "max96755",
+	.reg_bits = 16,
+	.val_bits = 8,
+	.max_register = 0x8000,
+	.volatile_reg = max96755_volatile_reg,
+	.cache_type = REGCACHE_RBTREE,
+};
+
+struct serdes_function_data {
+	u8 gpio_out_dis:1;
+	u8 gpio_tx_en:1;
+	u8 gpio_rx_en:1;
+	u8 gpio_tx_id;
+	u8 gpio_rx_id;
+};
+
+struct config_desc {
+	u16 reg;
+	u8 mask;
+	u8 val;
+};
+
+struct serdes_group_data {
+	const struct config_desc *configs;
+	int num_configs;
+};
+
+static int MAX96755_MFP0_pins[] = {0};
+static int MAX96755_MFP1_pins[] = {1};
+static int MAX96755_MFP2_pins[] = {2};
+static int MAX96755_MFP3_pins[] = {3};
+static int MAX96755_MFP4_pins[] = {4};
+static int MAX96755_MFP5_pins[] = {5};
+static int MAX96755_MFP6_pins[] = {6};
+static int MAX96755_MFP7_pins[] = {7};
+
+static int MAX96755_MFP8_pins[] = {8};
+static int MAX96755_MFP9_pins[] = {9};
+static int MAX96755_MFP10_pins[] = {10};
+static int MAX96755_MFP11_pins[] = {11};
+static int MAX96755_MFP12_pins[] = {12};
+static int MAX96755_MFP13_pins[] = {13};
+static int MAX96755_MFP14_pins[] = {14};
+static int MAX96755_MFP15_pins[] = {15};
+
+static int MAX96755_MFP16_pins[] = {16};
+static int MAX96755_MFP17_pins[] = {17};
+static int MAX96755_MFP18_pins[] = {18};
+static int MAX96755_MFP19_pins[] = {19};
+static int MAX96755_MFP20_pins[] = {20};
+static int MAX96755_I2C_pins[] = {19, 20};
+static int MAX96755_UART_pins[] = {19, 20};
+
+#define GROUP_DESC(nm) \
+{ \
+	.name = #nm, \
+	.pins = nm ## _pins, \
+	.num_pins = ARRAY_SIZE(nm ## _pins), \
+}
+
+#define GROUP_DESC_CONFIG(nm) \
+{ \
+	.name = #nm, \
+	.pins = nm ## _pins, \
+	.num_pins = ARRAY_SIZE(nm ## _pins), \
+	.data = (void *)(const struct serdes_group_data []) { \
+		{ \
+			.configs = nm ## _configs, \
+			.num_configs = ARRAY_SIZE(nm ## _configs), \
+		} \
+	}, \
+}
+
+static const struct config_desc MAX96755_MFP0_configs[] = {
+	{ 0x0005, LOCK_EN, 0 },
+	{ 0x0048, LOC_MS_EN, 0 },
+};
+
+static const struct config_desc MAX96755_MFP1_configs[] = {
+	{ 0x0005, ERRB_EN, 0 },
+};
+
+static const struct config_desc MAX96755_MFP4_configs[] = {
+	{ 0x070, SPI_EN, 0 },
+};
+
+static const struct config_desc MAX96755_MFP5_configs[] = {
+	{ 0x006, RCLKEN, 0 },
+};
+
+static const struct config_desc MAX96755_MFP7_configs[] = {
+	{ 0x0002, AUD_TX_EN_X, 0 },
+	{ 0x0002, AUD_TX_EN_Y, 0 }
+};
+
+static const struct config_desc MAX96755_MFP8_configs[] = {
+	{ 0x0002, AUD_TX_EN_X, 0 },
+	{ 0x0002, AUD_TX_EN_Y, 0 }
+};
+
+static const struct config_desc MAX96755_MFP9_configs[] = {
+	{ 0x0002, AUD_TX_EN_X, 0 },
+	{ 0x0002, AUD_TX_EN_Y, 0 }
+};
+
+static const struct config_desc MAX96755_MFP10_configs[] = {
+	{ 0x0001, IIC_2_EN, 0 },
+	{ 0x0003, UART_2_EN, 0 },
+	{ 0x0140, AUD_RX_EN, 0 },
+};
+
+static const struct config_desc MAX96755_MFP11_configs[] = {
+	{ 0x0001, IIC_2_EN, 0 },
+	{ 0x0003, UART_2_EN, 0 },
+	{ 0x0140, AUD_RX_EN, 0 },
+};
+
+static const struct config_desc MAX96755_MFP12_configs[] = {
+	{ 0x0140, AUD_RX_EN, 0 },
+};
+
+static const struct config_desc MAX96755_MFP13_configs[] = {
+	{ 0x0005, PU_LF0, 0 },
+};
+
+static const struct config_desc MAX96755_MFP14_configs[] = {
+	{ 0x0005, PU_LF1, 0 },
+};
+
+static const struct config_desc MAX96755_MFP15_configs[] = {
+	{ 0x0005, PU_LF2, 0 },
+};
+
+static const struct config_desc MAX96755_MFP16_configs[] = {
+	{ 0x0005, PU_LF3, 0 },
+};
+
+static const struct config_desc MAX96755_MFP17_configs[] = {
+	{ 0x0001, IIC_1_EN, 0 },
+	{ 0x0003, UART_1_EN, 0 },
+};
+
+static const struct config_desc MAX96755_MFP18_configs[] = {
+	{ 0x0001, IIC_1_EN, 0 },
+	{ 0x0003, UART_1_EN, 0 },
+};
+
+static const char *serdes_gpio_groups[] = {
+	"MAX96755_MFP0", "MAX96755_MFP1", "MAX96755_MFP2", "MAX96755_MFP3",
+	"MAX96755_MFP4", "MAX96755_MFP5", "MAX96755_MFP6", "MAX96755_MFP7",
+
+	"MAX96755_MFP8", "MAX96755_MFP9", "MAX96755_MFP10", "MAX96755_MFP11",
+	"MAX96755_MFP12", "MAX96755_MFP13", "MAX96755_MFP14", "MAX96755_MFP15",
+
+	"MAX96755_MFP16", "MAX96755_MFP17", "MAX96755_MFP18", "MAX96755_MFP19",
+	"MAX96755_MFP20",
+};
+
+static const char *MAX96755_I2C_groups[] = { "MAX96755_I2C" };
+static const char *MAX96755_UART_groups[] = { "MAX96755_UART" };
+
+#define FUNCTION_DESC(nm) \
+{ \
+	.name = #nm, \
+	.group_names = nm##_groups, \
+	.num_group_names = ARRAY_SIZE(nm##_groups), \
+} \
+
+
+#define FUNCTION_DESC_GPIO_INPUT(id) \
+{ \
+	.name = "DES_GPIO"#id"_INPUT", \
+	.group_names = serdes_gpio_groups, \
+	.num_group_names = ARRAY_SIZE(serdes_gpio_groups), \
+	.data = (void *)(const struct serdes_function_data []) { \
+		{ .gpio_rx_en = 1, .gpio_rx_id = id } \
+	}, \
+} \
+
+#define FUNCTION_DESC_GPIO_OUTPUT(id) \
+{ \
+	.name = "DES_GPIO"#id"_OUTPUT", \
+	.group_names = serdes_gpio_groups, \
+	.num_group_names = ARRAY_SIZE(serdes_gpio_groups), \
+	.data = (void *)(const struct serdes_function_data []) { \
+		{ .gpio_out_dis = 1, .gpio_tx_en = 1, .gpio_tx_id = id } \
+	}, \
+} \
+
+static struct pinctrl_pin_desc max96755_pins_desc[] = {
+	PINCTRL_PIN(MAXIM_MAX96755_MFP0, "MAX96755_MFP0"),
+	PINCTRL_PIN(MAXIM_MAX96755_MFP1, "MAX96755_MFP1"),
+	PINCTRL_PIN(MAXIM_MAX96755_MFP2, "MAX96755_MFP2"),
+	PINCTRL_PIN(MAXIM_MAX96755_MFP3, "MAX96755_MFP3"),
+	PINCTRL_PIN(MAXIM_MAX96755_MFP4, "MAX96755_MFP4"),
+	PINCTRL_PIN(MAXIM_MAX96755_MFP5, "MAX96755_MFP5"),
+	PINCTRL_PIN(MAXIM_MAX96755_MFP6, "MAX96755_MFP6"),
+	PINCTRL_PIN(MAXIM_MAX96755_MFP7, "MAX96755_MFP7"),
+
+	PINCTRL_PIN(MAXIM_MAX96755_MFP8, "MAX96755_MFP8"),
+	PINCTRL_PIN(MAXIM_MAX96755_MFP9, "MAX96755_MFP9"),
+	PINCTRL_PIN(MAXIM_MAX96755_MFP10, "MAX96755_MFP10"),
+	PINCTRL_PIN(MAXIM_MAX96755_MFP11, "MAX96755_MFP11"),
+	PINCTRL_PIN(MAXIM_MAX96755_MFP12, "MAX96755_MFP12"),
+	PINCTRL_PIN(MAXIM_MAX96755_MFP13, "MAX96755_MFP13"),
+	PINCTRL_PIN(MAXIM_MAX96755_MFP14, "MAX96755_MFP14"),
+	PINCTRL_PIN(MAXIM_MAX96755_MFP15, "MAX96755_MFP15"),
+
+	PINCTRL_PIN(MAXIM_MAX96755_MFP16, "MAX96755_MFP16"),
+	PINCTRL_PIN(MAXIM_MAX96755_MFP17, "MAX96755_MFP17"),
+	PINCTRL_PIN(MAXIM_MAX96755_MFP18, "MAX96755_MFP18"),
+	PINCTRL_PIN(MAXIM_MAX96755_MFP19, "MAX96755_MFP19"),
+	PINCTRL_PIN(MAXIM_MAX96755_MFP20, "MAX96755_MFP20"),
+};
+
+static struct group_desc max96755_groups_desc[] = {
+	GROUP_DESC_CONFIG(MAX96755_MFP0),
+	GROUP_DESC_CONFIG(MAX96755_MFP1),
+	GROUP_DESC(MAX96755_MFP2),
+	GROUP_DESC(MAX96755_MFP3),
+	GROUP_DESC_CONFIG(MAX96755_MFP4),
+	GROUP_DESC_CONFIG(MAX96755_MFP5),
+	GROUP_DESC(MAX96755_MFP6),
+	GROUP_DESC_CONFIG(MAX96755_MFP7),
+
+	GROUP_DESC_CONFIG(MAX96755_MFP8),
+	GROUP_DESC_CONFIG(MAX96755_MFP9),
+	GROUP_DESC_CONFIG(MAX96755_MFP10),
+	GROUP_DESC_CONFIG(MAX96755_MFP11),
+	GROUP_DESC_CONFIG(MAX96755_MFP12),
+	GROUP_DESC_CONFIG(MAX96755_MFP13),
+	GROUP_DESC_CONFIG(MAX96755_MFP14),
+	GROUP_DESC_CONFIG(MAX96755_MFP15),
+
+	GROUP_DESC_CONFIG(MAX96755_MFP16),
+	GROUP_DESC_CONFIG(MAX96755_MFP17),
+	GROUP_DESC_CONFIG(MAX96755_MFP18),
+	GROUP_DESC(MAX96755_MFP19),
+	GROUP_DESC(MAX96755_MFP20),
+	GROUP_DESC(MAX96755_I2C),
+	GROUP_DESC(MAX96755_UART),
+};
+
+static struct function_desc max96755_functions_desc[] = {
+	FUNCTION_DESC_GPIO_INPUT(0),
+	FUNCTION_DESC_GPIO_INPUT(1),
+	FUNCTION_DESC_GPIO_INPUT(2),
+	FUNCTION_DESC_GPIO_INPUT(3),
+	FUNCTION_DESC_GPIO_INPUT(4),
+	FUNCTION_DESC_GPIO_INPUT(5),
+	FUNCTION_DESC_GPIO_INPUT(6),
+	FUNCTION_DESC_GPIO_INPUT(7),
+
+	FUNCTION_DESC_GPIO_INPUT(8),
+	FUNCTION_DESC_GPIO_INPUT(9),
+	FUNCTION_DESC_GPIO_INPUT(10),
+	FUNCTION_DESC_GPIO_INPUT(11),
+	FUNCTION_DESC_GPIO_INPUT(12),
+	FUNCTION_DESC_GPIO_INPUT(13),
+	FUNCTION_DESC_GPIO_INPUT(14),
+	FUNCTION_DESC_GPIO_INPUT(15),
+
+	FUNCTION_DESC_GPIO_INPUT(16),
+	FUNCTION_DESC_GPIO_INPUT(17),
+	FUNCTION_DESC_GPIO_INPUT(18),
+	FUNCTION_DESC_GPIO_INPUT(19),
+	FUNCTION_DESC_GPIO_INPUT(20),
+
+	FUNCTION_DESC_GPIO_OUTPUT(0),
+	FUNCTION_DESC_GPIO_OUTPUT(1),
+	FUNCTION_DESC_GPIO_OUTPUT(2),
+	FUNCTION_DESC_GPIO_OUTPUT(3),
+	FUNCTION_DESC_GPIO_OUTPUT(4),
+	FUNCTION_DESC_GPIO_OUTPUT(5),
+	FUNCTION_DESC_GPIO_OUTPUT(6),
+	FUNCTION_DESC_GPIO_OUTPUT(7),
+
+	FUNCTION_DESC_GPIO_OUTPUT(8),
+	FUNCTION_DESC_GPIO_OUTPUT(9),
+	FUNCTION_DESC_GPIO_OUTPUT(10),
+	FUNCTION_DESC_GPIO_OUTPUT(11),
+	FUNCTION_DESC_GPIO_OUTPUT(12),
+	FUNCTION_DESC_GPIO_OUTPUT(13),
+	FUNCTION_DESC_GPIO_OUTPUT(14),
+	FUNCTION_DESC_GPIO_OUTPUT(15),
+
+	FUNCTION_DESC_GPIO_OUTPUT(16),
+	FUNCTION_DESC_GPIO_OUTPUT(17),
+	FUNCTION_DESC_GPIO_OUTPUT(18),
+	FUNCTION_DESC_GPIO_OUTPUT(19),
+	FUNCTION_DESC_GPIO_OUTPUT(20),
+
+	FUNCTION_DESC(MAX96755_I2C),
+	FUNCTION_DESC(MAX96755_UART),
+};
+
+static struct serdes_chip_pinctrl_info max96755_pinctrl_info = {
+	.pins = max96755_pins_desc,
+	.num_pins = ARRAY_SIZE(max96755_pins_desc),
+	.groups = max96755_groups_desc,
+	.num_groups = ARRAY_SIZE(max96755_groups_desc),
+	.functions = max96755_functions_desc,
+	.num_functions = ARRAY_SIZE(max96755_functions_desc),
+};
+
+static int max96755_bridge_init(struct serdes *serdes)
+{
+	return 0;
+}
+
+static bool max96755_bridge_link_locked(struct serdes *serdes)
+{
+	u32 val;
+
+	if (serdes->lock_gpio)
+		return gpiod_get_value_cansleep(serdes->lock_gpio);
+
+	if (serdes_reg_read(serdes, 0x0013, &val))
+		return false;
+
+	if (!FIELD_GET(LOCKED, val))
+		return false;
+
+	return true;
+}
+
+static int max96755_bridge_attach(struct serdes *serdes)
+{
+	if (max96755_bridge_link_locked(serdes))
+		serdes->serdes_bridge->status = connector_status_connected;
+	else
+		serdes->serdes_bridge->status = connector_status_disconnected;
+
+	return 0;
+}
+
+static enum drm_connector_status
+max96755_bridge_detect(struct serdes *serdes)
+{
+	struct serdes_bridge *serdes_bridge = serdes->serdes_bridge;
+	enum drm_connector_status status = connector_status_connected;
+
+	if (!drm_kms_helper_is_poll_worker())
+		return serdes_bridge->status;
+
+	if (!max96755_bridge_link_locked(serdes)) {
+		status = connector_status_disconnected;
+		goto out;
+	}
+
+	if (extcon_get_state(serdes->extcon, EXTCON_JACK_VIDEO_OUT)) {
+		if (atomic_cmpxchg(&serdes_bridge->triggered, 1, 0)) {
+			status = connector_status_disconnected;
+			goto out;
+		}
+
+	} else {
+		atomic_set(&serdes_bridge->triggered, 0);
+	}
+
+	if (serdes_bridge->next_bridge && (serdes_bridge->next_bridge->ops & DRM_BRIDGE_OP_DETECT))
+		return drm_bridge_detect(serdes_bridge->next_bridge);
+
+out:
+	serdes_bridge->status = status;
+	SERDES_DBG_MFD("%s: status=%d\n", __func__, status);
+	return status;
+}
+
+static int max96755_bridge_enable(struct serdes *serdes)
+{
+	int ret = 0;
+
+	SERDES_DBG_CHIP("%s: serdes chip %s ret=%d\n", __func__, serdes->chip_data->name, ret);
+	return ret;
+}
+
+static int max96755_bridge_disable(struct serdes *serdes)
+{
+	int ret = 0;
+
+	return ret;
+}
+
+static struct serdes_chip_bridge_ops max96755_bridge_ops = {
+	.init = max96755_bridge_init,
+	.attach = max96755_bridge_attach,
+	.detect = max96755_bridge_detect,
+	.enable = max96755_bridge_enable,
+	.disable = max96755_bridge_disable,
+};
+
+static int max96755_pinctrl_set_mux(struct serdes *serdes,
+				    unsigned int function, unsigned int group)
+{
+	struct serdes_pinctrl *pinctrl = serdes->pinctrl;
+	struct function_desc *func;
+	struct group_desc *grp;
+	int i;
+
+	func = pinmux_generic_get_function(pinctrl->pctl, function);
+	if (!func)
+		return -EINVAL;
+
+	grp = pinctrl_generic_get_group(pinctrl->pctl, group);
+	if (!grp)
+		return -EINVAL;
+
+	SERDES_DBG_CHIP("%s: serdes chip %s func=%s data=%p group=%s data=%p, num_pin=%d\n",
+			__func__, serdes->chip_data->name, func->name,
+			func->data, grp->name, grp->data, grp->num_pins);
+
+	if (func->data) {
+		struct serdes_function_data *fdata = func->data;
+
+		for (i = 0; i < grp->num_pins; i++) {
+			serdes_set_bits(serdes, GPIO_A_REG(grp->pins[i] - pinctrl->pin_base),
+					GPIO_OUT_DIS | GPIO_RX_EN | GPIO_TX_EN,
+					FIELD_PREP(GPIO_OUT_DIS, fdata->gpio_out_dis) |
+					FIELD_PREP(GPIO_RX_EN, fdata->gpio_rx_en) |
+					FIELD_PREP(GPIO_TX_EN, fdata->gpio_tx_en));
+
+			if (fdata->gpio_tx_en)
+				serdes_set_bits(serdes,
+						GPIO_B_REG(grp->pins[i] - pinctrl->pin_base),
+						GPIO_TX_ID,
+						FIELD_PREP(GPIO_TX_ID, fdata->gpio_tx_id));
+
+			if (fdata->gpio_rx_en)
+				serdes_set_bits(serdes,
+						GPIO_C_REG(grp->pins[i] - pinctrl->pin_base),
+						GPIO_RX_ID,
+						FIELD_PREP(GPIO_RX_ID, fdata->gpio_rx_id));
+		}
+	}
+
+	if (grp->data) {
+		struct serdes_group_data *gdata = grp->data;
+
+		for (i = 0; i < gdata->num_configs; i++) {
+			const struct config_desc *config = &gdata->configs[i];
+
+			serdes_set_bits(serdes, config->reg,
+					config->mask, config->val);
+		}
+	}
+
+	return 0;
+}
+
+static int max96755_pinctrl_config_get(struct serdes *serdes,
+				       unsigned int pin, unsigned long *config)
+{
+	enum pin_config_param param = pinconf_to_config_param(*config);
+	unsigned int gpio_a_reg, gpio_b_reg;
+	u16 arg = 0;
+
+	serdes_reg_read(serdes, GPIO_A_REG(pin), &gpio_a_reg);
+	serdes_reg_read(serdes, GPIO_B_REG(pin), &gpio_b_reg);
+
+	SERDES_DBG_CHIP("%s: serdes chip %s pin=%d param=%d\n", __func__,
+			serdes->chip_data->name, pin, param);
+
+	switch (param) {
+	case PIN_CONFIG_DRIVE_OPEN_DRAIN:
+		if (FIELD_GET(OUT_TYPE, gpio_b_reg))
+			return -EINVAL;
+		break;
+	case PIN_CONFIG_DRIVE_PUSH_PULL:
+		if (!FIELD_GET(OUT_TYPE, gpio_b_reg))
+			return -EINVAL;
+		break;
+	case PIN_CONFIG_BIAS_DISABLE:
+		if (FIELD_GET(PULL_UPDN_SEL, gpio_b_reg) != 0)
+			return -EINVAL;
+		break;
+	case PIN_CONFIG_BIAS_PULL_UP:
+		if (FIELD_GET(PULL_UPDN_SEL, gpio_b_reg) != 1)
+			return -EINVAL;
+		switch (FIELD_GET(RES_CFG, gpio_a_reg)) {
+		case 0:
+			arg = 40000;
+			break;
+		case 1:
+			arg = 10000;
+			break;
+		}
+		break;
+	case PIN_CONFIG_BIAS_PULL_DOWN:
+		if (FIELD_GET(PULL_UPDN_SEL, gpio_b_reg) != 2)
+			return -EINVAL;
+		switch (FIELD_GET(RES_CFG, gpio_a_reg)) {
+		case 0:
+			arg = 40000;
+			break;
+		case 1:
+			arg = 10000;
+			break;
+		}
+		break;
+	case PIN_CONFIG_OUTPUT:
+		if (FIELD_GET(GPIO_OUT_DIS, gpio_a_reg))
+			return -EINVAL;
+
+		arg = FIELD_GET(GPIO_OUT, gpio_a_reg);
+		break;
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	*config = pinconf_to_config_packed(param, arg);
+
+	return 0;
+}
+
+static int max96755_pinctrl_config_set(struct serdes *serdes,
+				       unsigned int pin, unsigned long *configs,
+				       unsigned int num_configs)
+{
+	enum pin_config_param param;
+	u32 arg;
+	u8 res_cfg;
+	int i;
+
+	for (i = 0; i < num_configs; i++) {
+		param = pinconf_to_config_param(configs[i]);
+		arg = pinconf_to_config_argument(configs[i]);
+
+		SERDES_DBG_CHIP("%s: serdes chip %s pin=%d param=%d\n", __func__,
+				serdes->chip_data->name, pin, param);
+
+		switch (param) {
+		case PIN_CONFIG_DRIVE_OPEN_DRAIN:
+			serdes_set_bits(serdes, GPIO_B_REG(pin),
+					OUT_TYPE, FIELD_PREP(OUT_TYPE, 0));
+			break;
+		case PIN_CONFIG_DRIVE_PUSH_PULL:
+			serdes_set_bits(serdes, GPIO_B_REG(pin),
+					OUT_TYPE, FIELD_PREP(OUT_TYPE, 1));
+			break;
+		case PIN_CONFIG_BIAS_DISABLE:
+			serdes_set_bits(serdes, GPIO_C_REG(pin),
+					PULL_UPDN_SEL,
+					FIELD_PREP(PULL_UPDN_SEL, 0));
+			break;
+		case PIN_CONFIG_BIAS_PULL_UP:
+			switch (arg) {
+			case 40000:
+				res_cfg = 0;
+				break;
+			case 1000000:
+				res_cfg = 1;
+				break;
+			default:
+				return -EINVAL;
+			}
+
+			serdes_set_bits(serdes, GPIO_A_REG(pin),
+					RES_CFG, FIELD_PREP(RES_CFG, res_cfg));
+			serdes_set_bits(serdes, GPIO_C_REG(pin),
+					PULL_UPDN_SEL,
+					FIELD_PREP(PULL_UPDN_SEL, 1));
+			break;
+		case PIN_CONFIG_BIAS_PULL_DOWN:
+			switch (arg) {
+			case 40000:
+				res_cfg = 0;
+				break;
+			case 1000000:
+				res_cfg = 1;
+				break;
+			default:
+				return -EINVAL;
+			}
+
+			serdes_set_bits(serdes, GPIO_A_REG(pin),
+					RES_CFG, FIELD_PREP(RES_CFG, res_cfg));
+			serdes_set_bits(serdes, GPIO_C_REG(pin),
+					PULL_UPDN_SEL,
+					FIELD_PREP(PULL_UPDN_SEL, 2));
+			break;
+		case PIN_CONFIG_OUTPUT:
+			serdes_set_bits(serdes, GPIO_A_REG(pin),
+					GPIO_OUT_DIS | GPIO_OUT,
+					FIELD_PREP(GPIO_OUT_DIS, 0) |
+					FIELD_PREP(GPIO_OUT, arg));
+			break;
+		default:
+			return -EOPNOTSUPP;
+		}
+	}
+
+	return 0;
+}
+
+static struct serdes_chip_pinctrl_ops max96755_pinctrl_ops = {
+	.pin_config_get = max96755_pinctrl_config_get,
+	.pin_config_set = max96755_pinctrl_config_set,
+	.set_mux = max96755_pinctrl_set_mux,
+};
+
+static int max96755_gpio_direction_input(struct serdes *serdes, int gpio)
+{
+	return 0;
+}
+
+static int max96755_gpio_direction_output(struct serdes *serdes, int gpio, int value)
+{
+	return 0;
+}
+
+static int max96755_gpio_get_level(struct serdes *serdes, int gpio)
+{
+	return 0;
+}
+
+static int max96755_gpio_set_level(struct serdes *serdes, int gpio, int value)
+{
+	return 0;
+}
+
+static int max96755_gpio_set_config(struct serdes *serdes, int gpio, unsigned long config)
+{
+	return 0;
+}
+
+static int max96755_gpio_to_irq(struct serdes *serdes, int gpio)
+{
+	return 0;
+}
+
+static struct serdes_chip_gpio_ops max96755_gpio_ops = {
+	.direction_input = max96755_gpio_direction_input,
+	.direction_output = max96755_gpio_direction_output,
+	.get_level = max96755_gpio_get_level,
+	.set_level = max96755_gpio_set_level,
+	.set_config = max96755_gpio_set_config,
+	.to_irq = max96755_gpio_to_irq,
+};
+
+static int max96755_pm_suspend(struct serdes *serdes)
+{
+	return 0;
+}
+
+static int max96755_pm_resume(struct serdes *serdes)
+{
+	return 0;
+}
+
+static struct serdes_chip_pm_ops max96755_pm_ops = {
+	.suspend = max96755_pm_suspend,
+	.resume = max96755_pm_resume,
+};
+
+static int max96755_irq_lock_handle(struct serdes *serdes)
+{
+	return IRQ_HANDLED;
+}
+
+static int max96755_irq_err_handle(struct serdes *serdes)
+{
+	return IRQ_HANDLED;
+}
+
+static struct serdes_chip_irq_ops max96755_irq_ops = {
+	.lock_handle = max96755_irq_lock_handle,
+	.err_handle = max96755_irq_err_handle,
+};
+
+struct serdes_chip_data serdes_max96755_data = {
+	.name		= "max96755",
+	.serdes_type	= TYPE_SER,
+	.serdes_id	= MAXIM_ID_MAX96755,
+	.connector_type	= DRM_MODE_CONNECTOR_LVDS,
+	.regmap_config	= &max96755_regmap_config,
+	.pinctrl_info	= &max96755_pinctrl_info,
+	.bridge_ops	= &max96755_bridge_ops,
+	.pinctrl_ops	= &max96755_pinctrl_ops,
+	.gpio_ops	= &max96755_gpio_ops,
+	.pm_ops		= &max96755_pm_ops,
+	.irq_ops	= &max96755_irq_ops,
+};
+EXPORT_SYMBOL_GPL(serdes_max96755_data);
+
+MODULE_LICENSE("GPL");
diff --git a/kernel/drivers/mfd/display-serdes/maxim/maxim-max96755.h b/kernel/drivers/mfd/display-serdes/maxim/maxim-max96755.h
new file mode 100644
index 0000000..5153f5d
--- /dev/null
+++ b/kernel/drivers/mfd/display-serdes/maxim/maxim-max96755.h
@@ -0,0 +1,189 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * maxim-max96755.h -- register define for max96755 chip
+ *
+ * Copyright (c) 2023-2028 Rockchip Electronics Co. Ltd.
+ *
+ * Author:
+ *
+ */
+
+#ifndef __MFD_SERDES_MAXIM_MAX96755_H__
+#define __MFD_SERDES_MAXIM_MAX96755_H__
+
+#include <linux/bitfield.h>
+
+#define GPIO_A_REG(gpio)	(0x02be + ((gpio) * 3))
+#define GPIO_B_REG(gpio)	(0x02bf + ((gpio) * 3))
+#define GPIO_C_REG(gpio)	(0x02c0 + ((gpio) * 3))
+
+/* 0000h */
+#define DEV_ADDR		GENMASK(7, 1)
+#define CFG_BLOCK		BIT(0)
+
+/* 0001h */
+#define IIC_2_EN		BIT(7)
+#define IIC_1_EN		BIT(6)
+#define DIS_REM_CC		BIT(4)
+#define TX_RATE			GENMASK(3, 2)
+
+/* 0002h */
+#define VID_TX_EN_U		BIT(7)
+#define VID_TX_EN_Z		BIT(6)
+#define VID_TX_EN_Y		BIT(5)
+#define VID_TX_EN_X		BIT(4)
+#define AUD_TX_EN_Y		BIT(3)
+#define AUD_TX_EN_X		BIT(2)
+
+/* 0003h */
+#define UART_2_EN		BIT(5)
+#define UART_1_EN		BIT(4)
+
+/* 0005h */
+#define LOCK_EN			BIT(7)
+#define ERRB_EN			BIT(6)
+#define PU_LF3			BIT(3)
+#define PU_LF2			BIT(2)
+#define PU_LF1			BIT(1)
+#define PU_LF0			BIT(0)
+
+/* 0006h */
+#define RCLKEN			BIT(5)
+
+/* 0010h */
+#define RESET_ALL		BIT(7)
+#define RESET_LINK		BIT(6)
+#define RESET_ONESHOT		BIT(5)
+#define AUTO_LINK		BIT(4)
+#define SLEEP			BIT(3)
+#define REG_ENABLE		BIT(2)
+#define LINK_CFG		GENMASK(1, 0)
+
+/* 0013h */
+#define LINK_MODE		GENMASK(5, 4)
+#define	LOCKED			BIT(3)
+
+/* 0026h */
+#define LF_1			GENMASK(6, 4)
+#define LF_0			GENMASK(2, 0)
+
+/* 0048h */
+#define REM_MS_EN		BIT(5)
+#define LOC_MS_EN		BIT(4)
+
+/* 0053h */
+#define TX_SPLIT_MASK_B		BIT(5)
+#define TX_SPLIT_MASK_A		BIT(4)
+#define TX_STR_SEL		GENMASK(1, 0)
+
+/* 0140h */
+#define AUD_RX_EN		BIT(0)
+
+/* 0170h */
+#define SPI_EN			BIT(0)
+
+/* 01e5h */
+#define PATGEN_MODE		GENMASK(1, 0)
+
+/* 02beh */
+#define RES_CFG			BIT(7)
+#define TX_PRIO			BIT(6)
+#define TX_COMP_EN		BIT(5)
+#define GPIO_OUT		BIT(4)
+#define GPIO_IN			BIT(3)
+#define GPIO_RX_EN		BIT(2)
+#define GPIO_TX_EN		BIT(1)
+#define GPIO_OUT_DIS		BIT(0)
+
+/* 02bfh */
+#define PULL_UPDN_SEL		GENMASK(7, 6)
+#define OUT_TYPE		BIT(5)
+#define GPIO_TX_ID		GENMASK(4, 0)
+
+/* 02c0h */
+#define OVR_RES_CFG		BIT(7)
+#define GPIO_RX_ID		GENMASK(4, 0)
+
+/* 0311h */
+#define START_PORTBU		BIT(7)
+#define START_PORTBZ		BIT(6)
+#define START_PORTBY		BIT(5)
+#define START_PORTBX		BIT(4)
+#define START_PORTAU		BIT(3)
+#define START_PORTAZ		BIT(2)
+#define START_PORTAY		BIT(1)
+#define START_PORTAX		BIT(0)
+
+/* 032ah */
+#define DV_LOCK			BIT(7)
+#define DV_SWP_AB		BIT(6)
+#define LINE_ALT		BIT(5)
+#define DV_CONV			BIT(2)
+#define DV_SPL			BIT(1)
+#define DV_EN			BIT(0)
+
+/* 0330h */
+#define PHY_CONFIG		GENMASK(2, 0)
+#define MIPI_RX_RESET		BIT(3)
+
+/* 0331h */
+#define NUM_LANES		GENMASK(1, 0)
+
+/* 0385h */
+#define DPI_HSYNC_WIDTH_L	GENMASK(7, 0)
+
+/* 0386h */
+#define DPI_VYSNC_WIDTH_L	GENMASK(7, 0)
+
+/* 0387h */
+#define	DPI_HSYNC_WIDTH_H	GENMASK(3, 0)
+#define DPI_VSYNC_WIDTH_H	GENMASK(7, 4)
+
+/* 03a4h */
+#define DPI_DE_SKEW_SEL		BIT(1)
+#define DPI_DESKEW_EN		BIT(0)
+
+/* 03a5h */
+#define DPI_VFP_L		GENMASK(7, 0)
+
+/* 03a6h */
+#define DPI_VFP_H		GENMASK(3, 0)
+#define DPI_VBP_L		GENMASK(7, 4)
+
+/* 03a7h */
+#define DPI_VBP_H		GENMASK(7, 0)
+
+/* 03a8h */
+#define DPI_VACT_L		GENMASK(7, 0)
+
+/* 03a9h */
+#define DPI_VACT_H		GENMASK(3, 0)
+
+/* 03aah */
+#define DPI_HFP_L		GENMASK(7, 0)
+
+/* 03abh */
+#define DPI_HFP_H		GENMASK(3, 0)
+#define DPI_HBP_L		GENMASK(7, 4)
+
+/* 03ach */
+#define DPI_HBP_H		GENMASK(7, 0)
+
+/* 03adh */
+#define DPI_HACT_L		GENMASK(7, 0)
+
+/* 03aeh */
+#define DPI_HACT_H		GENMASK(4, 0)
+
+/* 055dh */
+#define VS_DET			BIT(5)
+#define HS_DET			BIT(4)
+
+enum link_mode {
+	DUAL_LINK,
+	LINKA,
+	LINKB,
+	SPLITTER_MODE,
+};
+
+#endif
diff --git a/kernel/drivers/mfd/display-serdes/maxim/maxim-max96772.c b/kernel/drivers/mfd/display-serdes/maxim/maxim-max96772.c
new file mode 100644
index 0000000..c9b1394
--- /dev/null
+++ b/kernel/drivers/mfd/display-serdes/maxim/maxim-max96772.c
@@ -0,0 +1,328 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * maxim-max96772.c  --  I2C register interface access for max96772 serdes chip
+ *
+ * Copyright (c) 2023-2028 Rockchip Electronics Co. Ltd.
+ *
+ * Author:
+ */
+
+#include "../core.h"
+#include "maxim-max96772.h"
+
+static const struct regmap_range max96772_readable_ranges[] = {
+	regmap_reg_range(0x0000, 0x0800),
+	regmap_reg_range(0x1700, 0x1700),
+	regmap_reg_range(0x4100, 0x4100),
+	regmap_reg_range(0x6230, 0x6230),
+	regmap_reg_range(0xe75e, 0xe75e),
+	regmap_reg_range(0xe776, 0xe7bf),
+};
+
+static const struct regmap_access_table max96772_readable_table = {
+	.yes_ranges = max96772_readable_ranges,
+	.n_yes_ranges = ARRAY_SIZE(max96772_readable_ranges),
+};
+
+static struct regmap_config max96772_regmap_config = {
+	.name = "max96772",
+	.reg_bits = 16,
+	.val_bits = 8,
+	.max_register = 0xffff,
+	.rd_table = &max96772_readable_table,
+};
+
+static int MAX96772_MFP0_pins[] = {0};
+static int MAX96772_MFP1_pins[] = {1};
+static int MAX96772_MFP2_pins[] = {2};
+static int MAX96772_MFP3_pins[] = {3};
+static int MAX96772_MFP4_pins[] = {4};
+static int MAX96772_MFP5_pins[] = {5};
+static int MAX96772_MFP6_pins[] = {6};
+static int MAX96772_MFP7_pins[] = {7};
+
+static int MAX96772_MFP8_pins[] = {8};
+static int MAX96772_MFP9_pins[] = {9};
+static int MAX96772_MFP10_pins[] = {10};
+static int MAX96772_MFP11_pins[] = {11};
+static int MAX96772_MFP12_pins[] = {12};
+static int MAX96772_MFP13_pins[] = {13};
+static int MAX96772_MFP14_pins[] = {14};
+static int MAX96772_MFP15_pins[] = {15};
+
+#define GROUP_DESC(nm) \
+{ \
+	.name = #nm, \
+	.pins = nm ## _pins, \
+	.num_pins = ARRAY_SIZE(nm ## _pins), \
+}
+
+struct serdes_function_data {
+	u8 gpio_out_dis:1;
+	u8 gpio_tx_en:1;
+	u8 gpio_rx_en:1;
+	u8 gpio_tx_id;
+	u8 gpio_rx_id;
+};
+
+static const char *serdes_gpio_groups[] = {
+	"MAX96772_MFP0", "MAX96772_MFP1", "MAX96772_MFP2", "MAX96772_MFP3",
+	"MAX96772_MFP4", "MAX96772_MFP5", "MAX96772_MFP6", "MAX96772_MFP7",
+
+	"MAX96772_MFP8", "MAX96772_MFP9", "MAX96772_MFP10", "MAX96772_MFP11",
+	"MAX96772_MFP12", "MAX96772_MFP13", "MAX96772_MFP14", "MAX96772_MFP15",
+};
+
+#define FUNCTION_DESC_GPIO_INPUT(id) \
+{ \
+	.name = "MFP"#id"_INPUT", \
+	.group_names = serdes_gpio_groups, \
+	.num_group_names = ARRAY_SIZE(serdes_gpio_groups), \
+	.data = (void *)(const struct serdes_function_data []) { \
+		{ .gpio_rx_en = 1, .gpio_rx_id = id } \
+	}, \
+} \
+
+#define FUNCTION_DESC_GPIO_OUTPUT(id) \
+{ \
+	.name = "MFP"#id"_OUTPUT", \
+	.group_names = serdes_gpio_groups, \
+	.num_group_names = ARRAY_SIZE(serdes_gpio_groups), \
+	.data = (void *)(const struct serdes_function_data []) { \
+		{ .gpio_out_dis = 1, .gpio_tx_en = 1, .gpio_tx_id = id } \
+	}, \
+} \
+
+static struct pinctrl_pin_desc max96772_pins_desc[] = {
+	PINCTRL_PIN(MAXIM_MAX96772_MFP0, "MAX96772_MFP0"),
+	PINCTRL_PIN(MAXIM_MAX96772_MFP1, "MAX96772_MFP1"),
+	PINCTRL_PIN(MAXIM_MAX96772_MFP2, "MAX96772_MFP2"),
+	PINCTRL_PIN(MAXIM_MAX96772_MFP3, "MAX96772_MFP3"),
+	PINCTRL_PIN(MAXIM_MAX96772_MFP4, "MAX96772_MFP4"),
+	PINCTRL_PIN(MAXIM_MAX96772_MFP5, "MAX96772_MFP5"),
+	PINCTRL_PIN(MAXIM_MAX96772_MFP6, "MAX96772_MFP6"),
+	PINCTRL_PIN(MAXIM_MAX96772_MFP7, "MAX96772_MFP7"),
+
+	PINCTRL_PIN(MAXIM_MAX96772_MFP8, "MAX96772_MFP8"),
+	PINCTRL_PIN(MAXIM_MAX96772_MFP9, "MAX96772_MFP9"),
+	PINCTRL_PIN(MAXIM_MAX96772_MFP10, "MAX96772_MFP10"),
+	PINCTRL_PIN(MAXIM_MAX96772_MFP11, "MAX96772_MFP11"),
+	PINCTRL_PIN(MAXIM_MAX96772_MFP12, "MAX96772_MFP12"),
+	PINCTRL_PIN(MAXIM_MAX96772_MFP13, "MAX96772_MFP13"),
+	PINCTRL_PIN(MAXIM_MAX96772_MFP14, "MAX96772_MFP14"),
+	PINCTRL_PIN(MAXIM_MAX96772_MFP15, "MAX96772_MFP15"),
+};
+
+static struct group_desc max96772_groups_desc[] = {
+	GROUP_DESC(MAX96772_MFP0),
+	GROUP_DESC(MAX96772_MFP1),
+	GROUP_DESC(MAX96772_MFP2),
+	GROUP_DESC(MAX96772_MFP3),
+	GROUP_DESC(MAX96772_MFP4),
+	GROUP_DESC(MAX96772_MFP5),
+	GROUP_DESC(MAX96772_MFP6),
+	GROUP_DESC(MAX96772_MFP7),
+
+	GROUP_DESC(MAX96772_MFP8),
+	GROUP_DESC(MAX96772_MFP9),
+	GROUP_DESC(MAX96772_MFP10),
+	GROUP_DESC(MAX96772_MFP11),
+	GROUP_DESC(MAX96772_MFP12),
+	GROUP_DESC(MAX96772_MFP13),
+	GROUP_DESC(MAX96772_MFP14),
+	GROUP_DESC(MAX96772_MFP15),
+};
+
+static struct function_desc max96772_functions_desc[] = {
+	FUNCTION_DESC_GPIO_INPUT(0),
+	FUNCTION_DESC_GPIO_INPUT(1),
+	FUNCTION_DESC_GPIO_INPUT(2),
+	FUNCTION_DESC_GPIO_INPUT(3),
+	FUNCTION_DESC_GPIO_INPUT(4),
+	FUNCTION_DESC_GPIO_INPUT(5),
+	FUNCTION_DESC_GPIO_INPUT(6),
+	FUNCTION_DESC_GPIO_INPUT(7),
+
+	FUNCTION_DESC_GPIO_INPUT(8),
+	FUNCTION_DESC_GPIO_INPUT(9),
+	FUNCTION_DESC_GPIO_INPUT(10),
+	FUNCTION_DESC_GPIO_INPUT(11),
+	FUNCTION_DESC_GPIO_INPUT(12),
+	FUNCTION_DESC_GPIO_INPUT(13),
+	FUNCTION_DESC_GPIO_INPUT(14),
+	FUNCTION_DESC_GPIO_INPUT(15),
+
+	FUNCTION_DESC_GPIO_OUTPUT(0),
+	FUNCTION_DESC_GPIO_OUTPUT(1),
+	FUNCTION_DESC_GPIO_OUTPUT(2),
+	FUNCTION_DESC_GPIO_OUTPUT(3),
+	FUNCTION_DESC_GPIO_OUTPUT(4),
+	FUNCTION_DESC_GPIO_OUTPUT(5),
+	FUNCTION_DESC_GPIO_OUTPUT(6),
+	FUNCTION_DESC_GPIO_OUTPUT(7),
+
+	FUNCTION_DESC_GPIO_OUTPUT(8),
+	FUNCTION_DESC_GPIO_OUTPUT(9),
+	FUNCTION_DESC_GPIO_OUTPUT(10),
+	FUNCTION_DESC_GPIO_OUTPUT(11),
+	FUNCTION_DESC_GPIO_OUTPUT(12),
+	FUNCTION_DESC_GPIO_OUTPUT(13),
+	FUNCTION_DESC_GPIO_OUTPUT(14),
+	FUNCTION_DESC_GPIO_OUTPUT(15),
+
+};
+
+static struct serdes_chip_pinctrl_info max96772_pinctrl_info = {
+	.pins = max96772_pins_desc,
+	.num_pins = ARRAY_SIZE(max96772_pins_desc),
+	.groups = max96772_groups_desc,
+	.num_groups = ARRAY_SIZE(max96772_groups_desc),
+	.functions = max96772_functions_desc,
+	.num_functions = ARRAY_SIZE(max96772_functions_desc),
+};
+
+static int max96772_panel_init(struct serdes *serdes)
+{
+	return 0;
+}
+
+static int max96772_panel_prepare(struct serdes *serdes)
+{
+	return 0;
+}
+
+static int max96772_panel_unprepare(struct serdes *serdes)
+{
+	return 0;
+}
+
+static int max96772_panel_enable(struct serdes *serdes)
+{
+	return 0;
+}
+
+static int max96772_panel_disable(struct serdes *serdes)
+{
+	return 0;
+}
+
+static struct serdes_chip_panel_ops max96772_panel_ops = {
+	.init = max96772_panel_init,
+	.prepare = max96772_panel_prepare,
+	.unprepare = max96772_panel_unprepare,
+	.enable = max96772_panel_enable,
+	.disable = max96772_panel_disable,
+};
+
+static int max96772_pinctrl_config_get(struct serdes *serdes,
+				       unsigned int pin,
+				       unsigned long *config)
+{
+	return 0;
+}
+
+static int max96772_pinctrl_config_set(struct serdes *serdes,
+				       unsigned int pin,
+				       unsigned long *configs,
+				       unsigned int num_configs)
+{
+	return 0;
+}
+
+static int max96772_pinctrl_set_mux(struct serdes *serdes, unsigned int func_selector,
+				    unsigned int group_selector)
+{
+	return 0;
+}
+
+static struct serdes_chip_pinctrl_ops max96772_pinctrl_ops = {
+	.pin_config_get = max96772_pinctrl_config_get,
+	.pin_config_set = max96772_pinctrl_config_set,
+	.set_mux = max96772_pinctrl_set_mux,
+};
+
+static int max96772_gpio_direction_input(struct serdes *serdes, int gpio)
+{
+	return 0;
+}
+
+static int max96772_gpio_direction_output(struct serdes *serdes, int gpio, int value)
+{
+	return 0;
+}
+
+static int max96772_gpio_get_level(struct serdes *serdes, int gpio)
+{
+	return 0;
+}
+
+static int max96772_gpio_set_level(struct serdes *serdes, int gpio, int value)
+{
+	return 0;
+}
+
+static int max96772_gpio_set_config(struct serdes *serdes, int gpio, unsigned long config)
+{
+	return 0;
+}
+
+static int max96772_gpio_to_irq(struct serdes *serdes, int gpio)
+{
+	return 0;
+}
+
+static struct serdes_chip_gpio_ops max96772_gpio_ops = {
+	.direction_input = max96772_gpio_direction_input,
+	.direction_output = max96772_gpio_direction_output,
+	.get_level = max96772_gpio_get_level,
+	.set_level = max96772_gpio_set_level,
+	.set_config = max96772_gpio_set_config,
+	.to_irq = max96772_gpio_to_irq,
+};
+
+static int max96772_pm_suspend(struct serdes *serdes)
+{
+	return 0;
+}
+
+static int max96772_pm_resume(struct serdes *serdes)
+{
+	return 0;
+}
+
+static struct serdes_chip_pm_ops max96772_pm_ops = {
+	.suspend = max96772_pm_suspend,
+	.resume = max96772_pm_resume,
+};
+
+static int max96772_irq_lock_handle(struct serdes *serdes)
+{
+	return IRQ_HANDLED;
+}
+
+static int max96772_irq_err_handle(struct serdes *serdes)
+{
+	return IRQ_HANDLED;
+}
+
+static struct serdes_chip_irq_ops max96772_irq_ops = {
+	.lock_handle = max96772_irq_lock_handle,
+	.err_handle = max96772_irq_err_handle,
+};
+
+struct serdes_chip_data serdes_max96772_data = {
+	.name		= "max96772",
+	.serdes_type	= TYPE_DES,
+	.serdes_id	= MAXIM_ID_MAX96772,
+	.connector_type	= DRM_MODE_CONNECTOR_eDP,
+	.regmap_config	= &max96772_regmap_config,
+	.pinctrl_info	= &max96772_pinctrl_info,
+	.panel_ops	= &max96772_panel_ops,
+	.pinctrl_ops	= &max96772_pinctrl_ops,
+	.gpio_ops	= &max96772_gpio_ops,
+	.pm_ops		= &max96772_pm_ops,
+	.irq_ops	= &max96772_irq_ops,
+};
+EXPORT_SYMBOL_GPL(serdes_max96772_data);
+
+MODULE_LICENSE("GPL");
diff --git a/kernel/drivers/mfd/display-serdes/maxim/maxim-max96772.h b/kernel/drivers/mfd/display-serdes/maxim/maxim-max96772.h
new file mode 100644
index 0000000..1a961e6
--- /dev/null
+++ b/kernel/drivers/mfd/display-serdes/maxim/maxim-max96772.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * include/linux/mfd/serdes/gpio.h -- GPIO for different serdes chip
+ *
+ * Copyright (c) 2023-2028 Rockchip Electronics Co. Ltd.
+ *
+ * Author: luowei <lw@rock-chips.com>
+ *
+ */
+
+#ifndef __MFD_SERDES_MAXIM_MAX96772_H__
+#define __MFD_SERDES_MAXIM_MAX96772_H__
+
+#endif
diff --git a/kernel/drivers/mfd/display-serdes/maxim/maxim-max96789.c b/kernel/drivers/mfd/display-serdes/maxim/maxim-max96789.c
new file mode 100644
index 0000000..494763e
--- /dev/null
+++ b/kernel/drivers/mfd/display-serdes/maxim/maxim-max96789.c
@@ -0,0 +1,352 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * maxim-max96789.c  --  I2C register interface access for max96789 serdes chip
+ *
+ * Copyright (c) 2023-2028 Rockchip Electronics Co. Ltd.
+ *
+ * Author:
+ */
+
+#include "../core.h"
+#include "maxim-max96789.h"
+
+static bool max96789_volatile_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case 0x0076:
+	case 0x0086:
+	case 0x0100:
+	case 0x0200 ... 0x02ce:
+	case 0x7000:
+	case 0x7070:
+	case 0x7074:
+		return false;
+	default:
+		return true;
+	}
+}
+
+static struct regmap_config max96789_regmap_config = {
+	.name = "max96789",
+	.reg_bits = 16,
+	.val_bits = 8,
+	.max_register = 0x8000,
+	.volatile_reg = max96789_volatile_reg,
+	.cache_type = REGCACHE_RBTREE,
+};
+
+static int MAX96789_MFP0_pins[] = {0};
+static int MAX96789_MFP1_pins[] = {1};
+static int MAX96789_MFP2_pins[] = {2};
+static int MAX96789_MFP3_pins[] = {3};
+static int MAX96789_MFP4_pins[] = {4};
+static int MAX96789_MFP5_pins[] = {5};
+static int MAX96789_MFP6_pins[] = {6};
+static int MAX96789_MFP7_pins[] = {7};
+
+static int MAX96789_MFP8_pins[] = {8};
+static int MAX96789_MFP9_pins[] = {9};
+static int MAX96789_MFP10_pins[] = {10};
+static int MAX96789_MFP11_pins[] = {11};
+static int MAX96789_MFP12_pins[] = {12};
+static int MAX96789_MFP13_pins[] = {13};
+static int MAX96789_MFP14_pins[] = {14};
+static int MAX96789_MFP15_pins[] = {15};
+
+static int MAX96789_MFP16_pins[] = {16};
+static int MAX96789_MFP17_pins[] = {17};
+static int MAX96789_MFP18_pins[] = {18};
+static int MAX96789_MFP19_pins[] = {19};
+static int MAX96789_MFP20_pins[] = {20};
+
+#define GROUP_DESC(nm) \
+{ \
+	.name = #nm, \
+	.pins = nm ## _pins, \
+	.num_pins = ARRAY_SIZE(nm ## _pins), \
+}
+
+struct serdes_function_data {
+	u8 gpio_out_dis:1;
+	u8 gpio_tx_en:1;
+	u8 gpio_rx_en:1;
+	u8 gpio_tx_id;
+	u8 gpio_rx_id;
+};
+
+static const char *serdes_gpio_groups[] = {
+	"MAX96789_MFP0", "MAX96789_MFP1", "MAX96789_MFP2", "MAX96789_MFP3",
+	"MAX96789_MFP4", "MAX96789_MFP5", "MAX96789_MFP6", "MAX96789_MFP7",
+
+	"MAX96789_MFP8", "MAX96789_MFP9", "MAX96789_MFP10", "MAX96789_MFP11",
+	"MAX96789_MFP12", "MAX96789_MFP13", "MAX96789_MFP14", "MAX96789_MFP15",
+
+	"MAX96789_MFP16", "MAX96789_MFP17", "MAX96789_MFP18", "MAX96789_MFP19",
+	"MAX96789_MFP20",
+};
+
+#define FUNCTION_DESC_GPIO_INPUT(id) \
+{ \
+	.name = "DES_GPIO"#id"_INPUT", \
+	.group_names = serdes_gpio_groups, \
+	.num_group_names = ARRAY_SIZE(serdes_gpio_groups), \
+	.data = (void *)(const struct serdes_function_data []) { \
+		{ .gpio_rx_en = 1, .gpio_rx_id = id } \
+	}, \
+} \
+
+#define FUNCTION_DESC_GPIO_OUTPUT(id) \
+{ \
+	.name = "DES_GPIO"#id"_OUTPUT", \
+	.group_names = serdes_gpio_groups, \
+	.num_group_names = ARRAY_SIZE(serdes_gpio_groups), \
+	.data = (void *)(const struct serdes_function_data []) { \
+		{ .gpio_out_dis = 1, .gpio_tx_en = 1, .gpio_tx_id = id } \
+	}, \
+} \
+
+static struct pinctrl_pin_desc max96789_pins_desc[] = {
+	PINCTRL_PIN(MAXIM_MAX96789_MFP0, "MAX96789_MFP0"),
+	PINCTRL_PIN(MAXIM_MAX96789_MFP1, "MAX96789_MFP1"),
+	PINCTRL_PIN(MAXIM_MAX96789_MFP2, "MAX96789_MFP2"),
+	PINCTRL_PIN(MAXIM_MAX96789_MFP3, "MAX96789_MFP3"),
+	PINCTRL_PIN(MAXIM_MAX96789_MFP4, "MAX96789_MFP4"),
+	PINCTRL_PIN(MAXIM_MAX96789_MFP5, "MAX96789_MFP5"),
+	PINCTRL_PIN(MAXIM_MAX96789_MFP6, "MAX96789_MFP6"),
+	PINCTRL_PIN(MAXIM_MAX96789_MFP7, "MAX96789_MFP7"),
+
+	PINCTRL_PIN(MAXIM_MAX96789_MFP8, "MAX96789_MFP8"),
+	PINCTRL_PIN(MAXIM_MAX96789_MFP9, "MAX96789_MFP9"),
+	PINCTRL_PIN(MAXIM_MAX96789_MFP10, "MAX96789_MFP10"),
+	PINCTRL_PIN(MAXIM_MAX96789_MFP11, "MAX96789_MFP11"),
+	PINCTRL_PIN(MAXIM_MAX96789_MFP12, "MAX96789_MFP12"),
+	PINCTRL_PIN(MAXIM_MAX96789_MFP13, "MAX96789_MFP13"),
+	PINCTRL_PIN(MAXIM_MAX96789_MFP14, "MAX96789_MFP14"),
+	PINCTRL_PIN(MAXIM_MAX96789_MFP15, "MAX96789_MFP15"),
+
+	PINCTRL_PIN(MAXIM_MAX96789_MFP16, "MAX96789_MFP16"),
+	PINCTRL_PIN(MAXIM_MAX96789_MFP17, "MAX96789_MFP17"),
+	PINCTRL_PIN(MAXIM_MAX96789_MFP18, "MAX96789_MFP18"),
+	PINCTRL_PIN(MAXIM_MAX96789_MFP19, "MAX96789_MFP19"),
+	PINCTRL_PIN(MAXIM_MAX96789_MFP20, "MAX96789_MFP20"),
+};
+
+static struct group_desc max96789_groups_desc[] = {
+	GROUP_DESC(MAX96789_MFP0),
+	GROUP_DESC(MAX96789_MFP1),
+	GROUP_DESC(MAX96789_MFP2),
+	GROUP_DESC(MAX96789_MFP3),
+	GROUP_DESC(MAX96789_MFP4),
+	GROUP_DESC(MAX96789_MFP5),
+	GROUP_DESC(MAX96789_MFP6),
+	GROUP_DESC(MAX96789_MFP7),
+
+	GROUP_DESC(MAX96789_MFP8),
+	GROUP_DESC(MAX96789_MFP9),
+	GROUP_DESC(MAX96789_MFP10),
+	GROUP_DESC(MAX96789_MFP11),
+	GROUP_DESC(MAX96789_MFP12),
+	GROUP_DESC(MAX96789_MFP13),
+	GROUP_DESC(MAX96789_MFP14),
+	GROUP_DESC(MAX96789_MFP15),
+
+	GROUP_DESC(MAX96789_MFP16),
+	GROUP_DESC(MAX96789_MFP17),
+	GROUP_DESC(MAX96789_MFP18),
+	GROUP_DESC(MAX96789_MFP19),
+	GROUP_DESC(MAX96789_MFP20),
+};
+
+static struct function_desc max96789_functions_desc[] = {
+	FUNCTION_DESC_GPIO_INPUT(0),
+	FUNCTION_DESC_GPIO_INPUT(1),
+	FUNCTION_DESC_GPIO_INPUT(2),
+	FUNCTION_DESC_GPIO_INPUT(3),
+	FUNCTION_DESC_GPIO_INPUT(4),
+	FUNCTION_DESC_GPIO_INPUT(5),
+	FUNCTION_DESC_GPIO_INPUT(6),
+	FUNCTION_DESC_GPIO_INPUT(7),
+
+	FUNCTION_DESC_GPIO_INPUT(8),
+	FUNCTION_DESC_GPIO_INPUT(9),
+	FUNCTION_DESC_GPIO_INPUT(10),
+	FUNCTION_DESC_GPIO_INPUT(11),
+	FUNCTION_DESC_GPIO_INPUT(12),
+	FUNCTION_DESC_GPIO_INPUT(13),
+	FUNCTION_DESC_GPIO_INPUT(14),
+	FUNCTION_DESC_GPIO_INPUT(15),
+
+	FUNCTION_DESC_GPIO_INPUT(16),
+	FUNCTION_DESC_GPIO_INPUT(17),
+	FUNCTION_DESC_GPIO_INPUT(18),
+	FUNCTION_DESC_GPIO_INPUT(19),
+	FUNCTION_DESC_GPIO_INPUT(20),
+
+	FUNCTION_DESC_GPIO_OUTPUT(0),
+	FUNCTION_DESC_GPIO_OUTPUT(1),
+	FUNCTION_DESC_GPIO_OUTPUT(2),
+	FUNCTION_DESC_GPIO_OUTPUT(3),
+	FUNCTION_DESC_GPIO_OUTPUT(4),
+	FUNCTION_DESC_GPIO_OUTPUT(5),
+	FUNCTION_DESC_GPIO_OUTPUT(6),
+	FUNCTION_DESC_GPIO_OUTPUT(7),
+
+	FUNCTION_DESC_GPIO_OUTPUT(8),
+	FUNCTION_DESC_GPIO_OUTPUT(9),
+	FUNCTION_DESC_GPIO_OUTPUT(10),
+	FUNCTION_DESC_GPIO_OUTPUT(11),
+	FUNCTION_DESC_GPIO_OUTPUT(12),
+	FUNCTION_DESC_GPIO_OUTPUT(13),
+	FUNCTION_DESC_GPIO_OUTPUT(14),
+	FUNCTION_DESC_GPIO_OUTPUT(15),
+
+	FUNCTION_DESC_GPIO_OUTPUT(16),
+	FUNCTION_DESC_GPIO_OUTPUT(17),
+	FUNCTION_DESC_GPIO_OUTPUT(18),
+	FUNCTION_DESC_GPIO_OUTPUT(19),
+	FUNCTION_DESC_GPIO_OUTPUT(20),
+
+};
+
+static struct serdes_chip_pinctrl_info max96789_pinctrl_info = {
+	.pins = max96789_pins_desc,
+	.num_pins = ARRAY_SIZE(max96789_pins_desc),
+	.groups = max96789_groups_desc,
+	.num_groups = ARRAY_SIZE(max96789_groups_desc),
+	.functions = max96789_functions_desc,
+	.num_functions = ARRAY_SIZE(max96789_functions_desc),
+};
+
+static int max96789_bridge_init(struct serdes *serdes)
+{
+	return 0;
+}
+
+static int max96789_bridge_enable(struct serdes *serdes)
+{
+	return 0;
+}
+
+static int max96789_bridge_disable(struct serdes *serdes)
+{
+	return 0;
+}
+
+static struct serdes_chip_bridge_ops max96789_bridge_ops = {
+	.init = max96789_bridge_init,
+	.enable = max96789_bridge_enable,
+	.disable = max96789_bridge_disable,
+};
+
+static int max96789_pinctrl_config_get(struct serdes *serdes,
+				       unsigned int pin,
+				       unsigned long *config)
+{
+	return 0;
+}
+
+static int max96789_pinctrl_config_set(struct serdes *serdes,
+				       unsigned int pin,
+				       unsigned long *configs,
+				       unsigned int num_configs)
+{
+	return 0;
+}
+
+static int max96789_pinctrl_set_mux(struct serdes *serdes, unsigned int func_selector,
+				    unsigned int group_selector)
+{
+	return 0;
+}
+
+static struct serdes_chip_pinctrl_ops max96789_pinctrl_ops = {
+	.pin_config_get = max96789_pinctrl_config_get,
+	.pin_config_set = max96789_pinctrl_config_set,
+	.set_mux = max96789_pinctrl_set_mux,
+};
+
+static int max96789_gpio_direction_input(struct serdes *serdes, int gpio)
+{
+	return 0;
+}
+
+static int max96789_gpio_direction_output(struct serdes *serdes, int gpio, int value)
+{
+	return 0;
+}
+
+static int max96789_gpio_get_level(struct serdes *serdes, int gpio)
+{
+	return 0;
+}
+
+static int max96789_gpio_set_level(struct serdes *serdes, int gpio, int value)
+{
+	return 0;
+}
+
+static int max96789_gpio_set_config(struct serdes *serdes, int gpio, unsigned long config)
+{
+	return 0;
+}
+
+static int max96789_gpio_to_irq(struct serdes *serdes, int gpio)
+{
+	return 0;
+}
+
+static struct serdes_chip_gpio_ops max96789_gpio_ops = {
+	.direction_input = max96789_gpio_direction_input,
+	.direction_output = max96789_gpio_direction_output,
+	.get_level = max96789_gpio_get_level,
+	.set_level = max96789_gpio_set_level,
+	.set_config = max96789_gpio_set_config,
+	.to_irq = max96789_gpio_to_irq,
+};
+
+static int max96789_pm_suspend(struct serdes *serdes)
+{
+	return 0;
+}
+
+static int max96789_pm_resume(struct serdes *serdes)
+{
+	return 0;
+}
+
+static struct serdes_chip_pm_ops max96789_pm_ops = {
+	.suspend = max96789_pm_suspend,
+	.resume = max96789_pm_resume,
+};
+
+static int max96789_irq_lock_handle(struct serdes *serdes)
+{
+	return IRQ_HANDLED;
+}
+
+static int max96789_irq_err_handle(struct serdes *serdes)
+{
+	return IRQ_HANDLED;
+}
+
+static struct serdes_chip_irq_ops max96789_irq_ops = {
+	.lock_handle = max96789_irq_lock_handle,
+	.err_handle = max96789_irq_err_handle,
+};
+
+struct serdes_chip_data serdes_max96789_data = {
+	.name		= "max96789",
+	.serdes_type	= TYPE_SER,
+	.serdes_id	= MAXIM_ID_MAX96789,
+	.connector_type	= DRM_MODE_CONNECTOR_DSI,
+	.regmap_config	= &max96789_regmap_config,
+	.pinctrl_info	= &max96789_pinctrl_info,
+	.bridge_ops	= &max96789_bridge_ops,
+	.pinctrl_ops	= &max96789_pinctrl_ops,
+	.gpio_ops	= &max96789_gpio_ops,
+	.pm_ops		= &max96789_pm_ops,
+	.irq_ops	= &max96789_irq_ops,
+};
+EXPORT_SYMBOL_GPL(serdes_max96789_data);
+
+MODULE_LICENSE("GPL");
diff --git a/kernel/drivers/mfd/display-serdes/maxim/maxim-max96789.h b/kernel/drivers/mfd/display-serdes/maxim/maxim-max96789.h
new file mode 100644
index 0000000..1988b37
--- /dev/null
+++ b/kernel/drivers/mfd/display-serdes/maxim/maxim-max96789.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * maxim-max96789.h -- register define for max96789 chip
+ *
+ * Copyright (c) 2023-2028 Rockchip Electronics Co. Ltd.
+ *
+ * Author:
+ *
+ */
+
+#ifndef __MFD_SERDES_MAXIM_MAX96789_H__
+#define __MFD_SERDES_MAXIM_MAX96789_H__
+
+#endif
diff --git a/kernel/drivers/mfd/display-serdes/novo/Kconfig b/kernel/drivers/mfd/display-serdes/novo/Kconfig
new file mode 100644
index 0000000..f081a00
--- /dev/null
+++ b/kernel/drivers/mfd/display-serdes/novo/Kconfig
@@ -0,0 +1,20 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# rohm serdes drivers configuration
+#
+
+menuconfig SERDES_DISPLAY_CHIP_NOVO
+	tristate "novo expander device support"
+	default y
+	help
+	  Enable this to be able to choose the drivers for controlling the
+	  rohm serdes.
+
+if SERDES_DISPLAY_CHIP_NOVO
+config SERDES_DISPLAY_CHIP_NOVO_NCA9539
+	tristate "novo nca9539 expander"
+	default y
+	help
+	  To support novo nca9539 expander.
+
+endif
diff --git a/kernel/drivers/mfd/display-serdes/novo/Makefile b/kernel/drivers/mfd/display-serdes/novo/Makefile
new file mode 100644
index 0000000..1555be8
--- /dev/null
+++ b/kernel/drivers/mfd/display-serdes/novo/Makefile
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# novo display serdes drivers configuration
+#
+
+obj-$(CONFIG_SERDES_DISPLAY_CHIP_NOVO_NCA9539)		+= novo-nca9539.o
diff --git a/kernel/drivers/mfd/display-serdes/novo/novo-nca9539.c b/kernel/drivers/mfd/display-serdes/novo/novo-nca9539.c
new file mode 100644
index 0000000..94ae94a
--- /dev/null
+++ b/kernel/drivers/mfd/display-serdes/novo/novo-nca9539.c
@@ -0,0 +1,370 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * maxim-nca9539.c  --  I2C register interface access for nca9539 chip
+ *
+ * Copyright (c) 2023-2028 Rockchip Electronics Co. Ltd.
+ *
+ * Author: luowei <lw@rock-chips.com>
+ */
+
+#include "../core.h"
+
+#define REG_NCA9539_INPUT_PORT0 0x00 //Read only default 1111 1111
+#define REG_NCA9539_INPUT_PORT1 0x01 //Read only default 1111 1111
+#define REG_NCA9539_OUT_LEVEL_PORT0 0x02 //Read/write default 1111 1111
+#define REG_NCA9539_OUT_LEVEL_PORT1 0x03 //Read/write default 1111 1111
+#define REG_NCA9539_INVER_PORT0 0x04 //Read/write default 0000 0000
+#define REG_NCA9539_INVER_PORT1 0x05 //Read/write default 0000 0000
+#define REG_NCA9539_DIR_PORT0 0x06 //Read/write byte 1111 1111
+#define REG_NCA9539_DIR_PORT1 0x07 //Read/write byte 1111 1111
+
+static bool nca9539_volatile_reg(struct device *dev, unsigned int reg)
+{
+	return true;
+}
+
+static struct regmap_config nca9539_regmap_config = {
+	.name = "nca9539",
+	.reg_bits = 8,
+	.val_bits = 8,
+	.max_register = 0x0007,
+	.volatile_reg = nca9539_volatile_reg,
+	.cache_type = REGCACHE_RBTREE,
+};
+
+static int NCA9539_GPIO0_pins[] = {0};
+static int NCA9539_GPIO1_pins[] = {1};
+static int NCA9539_GPIO2_pins[] = {2};
+static int NCA9539_GPIO3_pins[] = {3};
+static int NCA9539_GPIO4_pins[] = {4};
+static int NCA9539_GPIO5_pins[] = {5};
+static int NCA9539_GPIO6_pins[] = {6};
+static int NCA9539_GPIO7_pins[] = {7};
+
+static int NCA9539_GPIO8_pins[] = {8};
+static int NCA9539_GPIO9_pins[] = {9};
+static int NCA9539_GPIO10_pins[] = {10};
+static int NCA9539_GPIO11_pins[] = {11};
+static int NCA9539_GPIO12_pins[] = {12};
+static int NCA9539_GPIO13_pins[] = {13};
+static int NCA9539_GPIO14_pins[] = {14};
+static int NCA9539_GPIO15_pins[] = {15};
+
+
+#define GROUP_DESC(nm) \
+{ \
+	.name = #nm, \
+	.pins = nm ## _pins, \
+	.num_pins = ARRAY_SIZE(nm ## _pins), \
+}
+
+static const char *serdes_gpio_groups[] = {
+	"NCA9539_GPIO0", "NCA9539_GPIO1", "NCA9539_GPIO2", "NCA9539_GPIO3",
+	"NCA9539_GPIO4", "NCA9539_GPIO5", "NCA9539_GPIO6", "NCA9539_GPIO7",
+	"NCA9539_GPIO8", "NCA9539_GPIO9", "NCA9539_GPIO10", "NCA9539_GPIO11",
+	"NCA9539_GPIO12", "NCA9539_GPIO13", "NCA9539_GPIO14", "NCA9539_GPIO15",
+};
+
+/*des -> ser -> soc*/
+#define FUNCTION_DESC_GPIO_INPUT(id) \
+{ \
+	.name = "GPIO"#id"_INPUT", \
+	.group_names = serdes_gpio_groups, \
+	.num_group_names = ARRAY_SIZE(serdes_gpio_groups), \
+} \
+
+/*soc -> ser -> des*/
+#define FUNCTION_DESC_GPIO_OUTPUT(id) \
+{ \
+	.name = "GPIO"#id"_OUTPUT", \
+	.group_names = serdes_gpio_groups, \
+	.num_group_names = ARRAY_SIZE(serdes_gpio_groups), \
+} \
+
+
+static struct pinctrl_pin_desc nca9539_pins_desc[] = {
+	PINCTRL_PIN(NOVO_NCA9539_GPIO0, "NCA9539_GPIO0"),
+	PINCTRL_PIN(NOVO_NCA9539_GPIO1, "NCA9539_GPIO1"),
+	PINCTRL_PIN(NOVO_NCA9539_GPIO2, "NCA9539_GPIO2"),
+	PINCTRL_PIN(NOVO_NCA9539_GPIO3, "NCA9539_GPIO3"),
+	PINCTRL_PIN(NOVO_NCA9539_GPIO4, "NCA9539_GPIO4"),
+	PINCTRL_PIN(NOVO_NCA9539_GPIO5, "NCA9539_GPIO5"),
+	PINCTRL_PIN(NOVO_NCA9539_GPIO6, "NCA9539_GPIO6"),
+	PINCTRL_PIN(NOVO_NCA9539_GPIO7, "NCA9539_GPIO7"),
+
+	PINCTRL_PIN(NOVO_NCA9539_GPIO8, "NCA9539_GPIO8"),
+	PINCTRL_PIN(NOVO_NCA9539_GPIO9, "NCA9539_GPIO9"),
+	PINCTRL_PIN(NOVO_NCA9539_GPIO10, "NCA9539_GPIO10"),
+	PINCTRL_PIN(NOVO_NCA9539_GPIO11, "NCA9539_GPIO11"),
+	PINCTRL_PIN(NOVO_NCA9539_GPIO12, "NCA9539_GPIO12"),
+	PINCTRL_PIN(NOVO_NCA9539_GPIO13, "NCA9539_GPIO13"),
+	PINCTRL_PIN(NOVO_NCA9539_GPIO14, "NCA9539_GPIO14"),
+	PINCTRL_PIN(NOVO_NCA9539_GPIO15, "NCA9539_GPIO15"),
+};
+
+static struct group_desc nca9539_groups_desc[] = {
+	GROUP_DESC(NCA9539_GPIO0),
+	GROUP_DESC(NCA9539_GPIO1),
+	GROUP_DESC(NCA9539_GPIO2),
+	GROUP_DESC(NCA9539_GPIO3),
+	GROUP_DESC(NCA9539_GPIO4),
+	GROUP_DESC(NCA9539_GPIO5),
+	GROUP_DESC(NCA9539_GPIO6),
+	GROUP_DESC(NCA9539_GPIO7),
+
+	GROUP_DESC(NCA9539_GPIO8),
+	GROUP_DESC(NCA9539_GPIO9),
+	GROUP_DESC(NCA9539_GPIO10),
+	GROUP_DESC(NCA9539_GPIO11),
+	GROUP_DESC(NCA9539_GPIO12),
+	GROUP_DESC(NCA9539_GPIO13),
+	GROUP_DESC(NCA9539_GPIO14),
+	GROUP_DESC(NCA9539_GPIO15),
+};
+
+static struct function_desc nca9539_functions_desc[] = {
+	FUNCTION_DESC_GPIO_INPUT(0),
+	FUNCTION_DESC_GPIO_INPUT(1),
+	FUNCTION_DESC_GPIO_INPUT(2),
+	FUNCTION_DESC_GPIO_INPUT(3),
+	FUNCTION_DESC_GPIO_INPUT(4),
+	FUNCTION_DESC_GPIO_INPUT(5),
+	FUNCTION_DESC_GPIO_INPUT(6),
+	FUNCTION_DESC_GPIO_INPUT(7),
+	FUNCTION_DESC_GPIO_INPUT(8),
+	FUNCTION_DESC_GPIO_INPUT(9),
+	FUNCTION_DESC_GPIO_INPUT(10),
+	FUNCTION_DESC_GPIO_INPUT(11),
+	FUNCTION_DESC_GPIO_INPUT(12),
+	FUNCTION_DESC_GPIO_INPUT(13),
+	FUNCTION_DESC_GPIO_INPUT(14),
+	FUNCTION_DESC_GPIO_INPUT(15),
+
+	FUNCTION_DESC_GPIO_OUTPUT(0),
+	FUNCTION_DESC_GPIO_OUTPUT(1),
+	FUNCTION_DESC_GPIO_OUTPUT(2),
+	FUNCTION_DESC_GPIO_OUTPUT(3),
+	FUNCTION_DESC_GPIO_OUTPUT(4),
+	FUNCTION_DESC_GPIO_OUTPUT(5),
+	FUNCTION_DESC_GPIO_OUTPUT(6),
+	FUNCTION_DESC_GPIO_OUTPUT(7),
+	FUNCTION_DESC_GPIO_OUTPUT(8),
+	FUNCTION_DESC_GPIO_OUTPUT(9),
+	FUNCTION_DESC_GPIO_OUTPUT(10),
+	FUNCTION_DESC_GPIO_OUTPUT(11),
+	FUNCTION_DESC_GPIO_OUTPUT(12),
+	FUNCTION_DESC_GPIO_OUTPUT(13),
+	FUNCTION_DESC_GPIO_OUTPUT(14),
+	FUNCTION_DESC_GPIO_OUTPUT(15),
+};
+
+static struct serdes_chip_pinctrl_info nca9539_pinctrl_info = {
+	.pins = nca9539_pins_desc,
+	.num_pins = ARRAY_SIZE(nca9539_pins_desc),
+	.groups = nca9539_groups_desc,
+	.num_groups = ARRAY_SIZE(nca9539_groups_desc),
+	.functions = nca9539_functions_desc,
+	.num_functions = ARRAY_SIZE(nca9539_functions_desc),
+};
+
+static int nca9539_pinctrl_config_get(struct serdes *serdes,
+				      unsigned int pin, unsigned long *config)
+{
+	return 0;
+}
+
+static int nca9539_pinctrl_config_set(struct serdes *serdes,
+				      unsigned int pin, unsigned long *configs,
+				      unsigned int num_configs)
+{
+	return 0;
+}
+
+static int nca9539_pinctrl_set_mux(struct serdes *serdes,
+				   unsigned int function, unsigned int group)
+{
+	return 0;
+}
+
+static struct serdes_chip_pinctrl_ops nca9539_pinctrl_ops = {
+	.pin_config_get = nca9539_pinctrl_config_get,
+	.pin_config_set = nca9539_pinctrl_config_set,
+	.set_mux = nca9539_pinctrl_set_mux,
+};
+
+static int nca9539_gpio_direction_input(struct serdes *serdes, int gpio)
+{
+	return 0;
+}
+
+static int nca9539_gpio_direction_output(struct serdes *serdes, int gpio, int value)
+{
+	struct device *dev = serdes->dev;
+	unsigned char port, pin, dir_reg, level_reg;
+	int ret1 = 0, ret2 = 0;
+
+	port = (gpio / 8) & 0x01;
+	pin = (gpio % 8) & 0x7;
+	dir_reg = (port == 0) ? REG_NCA9539_DIR_PORT0 : REG_NCA9539_DIR_PORT1;
+	level_reg = (port == 0) ? REG_NCA9539_OUT_LEVEL_PORT0 : REG_NCA9539_OUT_LEVEL_PORT1;
+
+	switch (pin) {
+	case 0:
+		ret1 = serdes_set_bits(serdes, dir_reg, BIT(0), FIELD_PREP(BIT(0), 0));
+		ret2 = serdes_set_bits(serdes, level_reg, BIT(0), FIELD_PREP(BIT(0), value & 0x01));
+		break;
+	case 1:
+		ret1 = serdes_set_bits(serdes, dir_reg, BIT(1), FIELD_PREP(BIT(1), 0));
+		ret2 = serdes_set_bits(serdes, level_reg, BIT(1), FIELD_PREP(BIT(1), value & 0x01));
+		break;
+	case 2:
+		ret1 = serdes_set_bits(serdes, dir_reg, BIT(2), FIELD_PREP(BIT(2), 0));
+		ret2 = serdes_set_bits(serdes, level_reg, BIT(2), FIELD_PREP(BIT(2), value & 0x01));
+		break;
+	case 3:
+		ret1 = serdes_set_bits(serdes, dir_reg, BIT(3), FIELD_PREP(BIT(3), 0));
+		ret2 = serdes_set_bits(serdes, level_reg, BIT(3), FIELD_PREP(BIT(3), value & 0x01));
+		break;
+	case 4:
+		ret1 = serdes_set_bits(serdes, dir_reg, BIT(4), FIELD_PREP(BIT(4), 0));
+		ret2 = serdes_set_bits(serdes, level_reg, BIT(4), FIELD_PREP(BIT(4), value & 0x01));
+		break;
+	case 5:
+		ret1 = serdes_set_bits(serdes, dir_reg, BIT(5), FIELD_PREP(BIT(5), 0));
+		ret2 = serdes_set_bits(serdes, level_reg, BIT(5), FIELD_PREP(BIT(5), value & 0x01));
+		break;
+	case 6:
+		ret1 = serdes_set_bits(serdes, dir_reg, BIT(6), FIELD_PREP(BIT(6), 0));
+		ret2 = serdes_set_bits(serdes, level_reg, BIT(6), FIELD_PREP(BIT(6), value & 0x01));
+		break;
+	case 7:
+		ret1 = serdes_set_bits(serdes, dir_reg, BIT(7), FIELD_PREP(BIT(7), 0));
+		ret2 = serdes_set_bits(serdes, level_reg, BIT(7), FIELD_PREP(BIT(7), value & 0x01));
+		break;
+	default:
+		break;
+	}
+
+	if (ret1 || ret2)
+		dev_err(dev, "%s reg 0x%04x write error, pin=%d\n", __func__, level_reg, pin);
+
+	return 0;
+}
+
+static int nca9539_gpio_get_level(struct serdes *serdes, int gpio)
+{
+	return 0;
+}
+
+static int nca9539_gpio_set_level(struct serdes *serdes, int gpio, int value)
+{
+	struct device *dev = serdes->dev;
+	unsigned char port, pin, level_reg;
+	int ret = 0;
+
+	port = (gpio / 8) & 0x01;
+	pin = (gpio % 8) & 0x7;
+	level_reg = (port == 0) ? REG_NCA9539_OUT_LEVEL_PORT0 : REG_NCA9539_OUT_LEVEL_PORT1;
+
+	switch (pin) {
+	case 0:
+		ret = serdes_set_bits(serdes, level_reg, BIT(0), FIELD_PREP(BIT(0), value & 0x01));
+		break;
+	case 1:
+		ret = serdes_set_bits(serdes, level_reg, BIT(1), FIELD_PREP(BIT(1), value & 0x01));
+		break;
+	case 2:
+		ret = serdes_set_bits(serdes, level_reg, BIT(2), FIELD_PREP(BIT(2), value & 0x01));
+		break;
+	case 3:
+		ret = serdes_set_bits(serdes, level_reg, BIT(3), FIELD_PREP(BIT(3), value & 0x01));
+		break;
+	case 4:
+		ret = serdes_set_bits(serdes, level_reg, BIT(4), FIELD_PREP(BIT(4), value & 0x01));
+		break;
+	case 5:
+		ret = serdes_set_bits(serdes, level_reg, BIT(5), FIELD_PREP(BIT(5), value & 0x01));
+		break;
+	case 6:
+		ret = serdes_set_bits(serdes, level_reg, BIT(6), FIELD_PREP(BIT(6), value & 0x01));
+		break;
+	case 7:
+		ret = serdes_set_bits(serdes, level_reg, BIT(7), FIELD_PREP(BIT(7), value & 0x01));
+		break;
+	default:
+		break;
+	}
+
+	if (ret)
+		dev_err(dev, "%s reg 0x%04x write error, pin=%d\n", __func__, level_reg, pin);
+
+	SERDES_DBG_CHIP("%s: serdes chip %s gpio=%d value=%d\n",
+			__func__, serdes->chip_data->name, gpio, value);
+
+	return 0;
+}
+
+static int nca9539_gpio_set_config(struct serdes *serdes, int gpio, unsigned long config)
+{
+	return 0;
+}
+
+static int nca9539_gpio_to_irq(struct serdes *serdes, int gpio)
+{
+	return 0;
+}
+
+static struct serdes_chip_gpio_ops nca9539_gpio_ops = {
+	.direction_input = nca9539_gpio_direction_input,
+	.direction_output = nca9539_gpio_direction_output,
+	.get_level = nca9539_gpio_get_level,
+	.set_level = nca9539_gpio_set_level,
+	.set_config = nca9539_gpio_set_config,
+	.to_irq = nca9539_gpio_to_irq,
+};
+
+static int nca9539_pm_suspend(struct serdes *serdes)
+{
+	return 0;
+}
+
+static int nca9539_pm_resume(struct serdes *serdes)
+{
+	return 0;
+}
+
+static struct serdes_chip_pm_ops nca9539_pm_ops = {
+	.suspend = nca9539_pm_suspend,
+	.resume = nca9539_pm_resume,
+};
+
+static int nca9539_irq_lock_handle(struct serdes *serdes)
+{
+	return IRQ_HANDLED;
+}
+
+static int nca9539_irq_err_handle(struct serdes *serdes)
+{
+	return IRQ_HANDLED;
+}
+
+static struct serdes_chip_irq_ops nca9539_irq_ops = {
+	.lock_handle = nca9539_irq_lock_handle,
+	.err_handle = nca9539_irq_err_handle,
+};
+
+struct serdes_chip_data serdes_nca9539_data = {
+	.name		= "nca9539",
+	.serdes_type	= TYPE_OTHER,
+	.serdes_id	= NOVO_ID_NCA9539,
+	.sequence_init = 1,
+	.regmap_config	= &nca9539_regmap_config,
+	.pinctrl_info	= &nca9539_pinctrl_info,
+	.pinctrl_ops	= &nca9539_pinctrl_ops,
+	.gpio_ops	= &nca9539_gpio_ops,
+	.pm_ops		= &nca9539_pm_ops,
+	.irq_ops	= &nca9539_irq_ops,
+};
+EXPORT_SYMBOL_GPL(serdes_nca9539_data);
+
+MODULE_LICENSE("GPL");
diff --git a/kernel/drivers/mfd/display-serdes/rockchip/Kconfig b/kernel/drivers/mfd/display-serdes/rockchip/Kconfig
new file mode 100644
index 0000000..e40bc27
--- /dev/null
+++ b/kernel/drivers/mfd/display-serdes/rockchip/Kconfig
@@ -0,0 +1,25 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# rockchip display serdes drivers configuration
+#
+
+menuconfig SERDES_DISPLAY_CHIP_ROCKCHIP
+	tristate "rockchip serdes device support"
+	default y
+	help
+	  Enable this to be able to choose the drivers for controlling the
+	  rockchip serdes.
+
+if SERDES_DISPLAY_CHIP_ROCKCHIP
+config SERDES_DISPLAY_CHIP_ROCKCHIP_RKX111
+	tristate "rockchip rkx111 serdes"
+	default y
+	help
+	  To support rockchip rkx111 display serdes.
+
+config SERDES_DISPLAY_CHIP_ROCKCHIP_RKX121
+	tristate "rockchip rkx121 serdes"
+	default y
+	help
+	  To support rockchip rkx121 display serdes.
+endif
diff --git a/kernel/drivers/mfd/display-serdes/rockchip/Makefile b/kernel/drivers/mfd/display-serdes/rockchip/Makefile
new file mode 100644
index 0000000..c75d570
--- /dev/null
+++ b/kernel/drivers/mfd/display-serdes/rockchip/Makefile
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# rockchip display serdes drivers configuration
+#
+obj-$(CONFIG_SERDES_DISPLAY_CHIP_ROCKCHIP_RKX111)		+= rockchip-rkx111.o
+obj-$(CONFIG_SERDES_DISPLAY_CHIP_ROCKCHIP_RKX121)		+= rockchip-rkx121.o
diff --git a/kernel/drivers/mfd/display-serdes/rockchip/rockchip-rkx111.c b/kernel/drivers/mfd/display-serdes/rockchip/rockchip-rkx111.c
new file mode 100644
index 0000000..4de7df7
--- /dev/null
+++ b/kernel/drivers/mfd/display-serdes/rockchip/rockchip-rkx111.c
@@ -0,0 +1,189 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * rockchip-rkx111.c  --  I2C register interface access for rkx111 serdes chip
+ *
+ * Copyright (c) 2023-2028 Rockchip Electronics Co. Ltd.
+ *
+ * Author: luowei <lw@rock-chips.com>
+ */
+
+#include "../core.h"
+
+static bool rkx111_volatile_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case 0x0076:
+	case 0x0086:
+	case 0x0100:
+	case 0x0200 ... 0x02ce:
+	case 0x7000:
+	case 0x7070:
+	case 0x7074:
+		return false;
+	default:
+		return true;
+	}
+}
+
+static struct regmap_config rkx111_regmap_config = {
+	.name = "rkx111",
+	.reg_bits = 16,
+	.val_bits = 8,
+	.max_register = 0x8000,
+	.volatile_reg = rkx111_volatile_reg,
+	.cache_type = REGCACHE_RBTREE,
+};
+
+struct pinctrl_pin_desc rkx111_pins_desc[] = {
+};
+
+struct group_desc rkx111_groups_desc[] = {
+	{ "null", NULL, 1, },
+};
+
+struct function_desc rkx111_functions_desc[] = {
+	{ "null", NULL, 1, },
+};
+
+static struct serdes_chip_pinctrl_info rkx111_pinctrl_info = {
+	.pins = rkx111_pins_desc,
+	.num_pins = ARRAY_SIZE(rkx111_pins_desc),
+	.groups = rkx111_groups_desc,
+	.num_groups = ARRAY_SIZE(rkx111_groups_desc),
+	.functions = rkx111_functions_desc,
+	.num_functions = ARRAY_SIZE(rkx111_functions_desc),
+};
+
+static int rkx111_bridge_init(struct serdes *serdes)
+{
+	return 0;
+}
+
+static int rkx111_bridge_enable(struct serdes *serdes)
+{
+	return 0;
+}
+
+static int rkx111_bridge_disable(struct serdes *serdes)
+{
+	return 0;
+}
+
+static struct serdes_chip_bridge_ops rkx111_bridge_ops = {
+	.init = rkx111_bridge_init,
+	.enable = rkx111_bridge_enable,
+	.disable = rkx111_bridge_disable,
+};
+
+static int rkx111_pinctrl_config_get(struct serdes *serdes,
+				     unsigned int pin,
+				     unsigned long *config)
+{
+	return 0;
+}
+
+static int rkx111_pinctrl_config_set(struct serdes *serdes,
+				     unsigned int pin,
+				     unsigned long *configs,
+				     unsigned int num_configs)
+{
+	return 0;
+}
+
+static int rkx111_pinctrl_set_mux(struct serdes *serdes, unsigned int func_selector,
+				  unsigned int group_selector)
+{
+	return 0;
+}
+
+static struct serdes_chip_pinctrl_ops rkx111_pinctrl_ops = {
+	.pin_config_get = rkx111_pinctrl_config_get,
+	.pin_config_set = rkx111_pinctrl_config_set,
+	.set_mux = rkx111_pinctrl_set_mux,
+};
+
+static int rkx111_gpio_direction_input(struct serdes *serdes, int gpio)
+{
+	return 0;
+}
+
+static int rkx111_gpio_direction_output(struct serdes *serdes, int gpio, int value)
+{
+	return 0;
+}
+
+static int rkx111_gpio_get_level(struct serdes *serdes, int gpio)
+{
+	return 0;
+}
+
+static int rkx111_gpio_set_level(struct serdes *serdes, int gpio, int value)
+{
+	return 0;
+}
+
+static int rkx111_gpio_set_config(struct serdes *serdes, int gpio, unsigned long config)
+{
+	return 0;
+}
+
+static int rkx111_gpio_to_irq(struct serdes *serdes, int gpio)
+{
+	return 0;
+}
+
+static struct serdes_chip_gpio_ops rkx111_gpio_ops = {
+	.direction_input = rkx111_gpio_direction_input,
+	.direction_output = rkx111_gpio_direction_output,
+	.get_level = rkx111_gpio_get_level,
+	.set_level = rkx111_gpio_set_level,
+	.set_config = rkx111_gpio_set_config,
+	.to_irq = rkx111_gpio_to_irq,
+};
+
+static int rkx111_pm_suspend(struct serdes *serdes)
+{
+	return 0;
+}
+
+static int rkx111_pm_resume(struct serdes *serdes)
+{
+	return 0;
+}
+
+static struct serdes_chip_pm_ops rkx111_pm_ops = {
+	.suspend = rkx111_pm_suspend,
+	.resume = rkx111_pm_resume,
+};
+
+static int rkx111_irq_lock_handle(struct serdes *serdes)
+{
+	return IRQ_HANDLED;
+}
+
+static int rkx111_irq_err_handle(struct serdes *serdes)
+{
+	return IRQ_HANDLED;
+}
+
+static struct serdes_chip_irq_ops rkx111_irq_ops = {
+	.lock_handle = rkx111_irq_lock_handle,
+	.err_handle = rkx111_irq_err_handle,
+};
+
+struct serdes_chip_data serdes_rkx111_data = {
+	.name		= "rkx111",
+	.serdes_type	= TYPE_SER,
+	.serdes_id	= ROCKCHIP_ID_RKX111,
+	.connector_type	= DRM_MODE_CONNECTOR_DSI,
+	.regmap_config	= &rkx111_regmap_config,
+	.pinctrl_info	= &rkx111_pinctrl_info,
+	.bridge_ops	= &rkx111_bridge_ops,
+	.pinctrl_ops	= &rkx111_pinctrl_ops,
+	.gpio_ops	= &rkx111_gpio_ops,
+	.pm_ops		= &rkx111_pm_ops,
+	.irq_ops	= &rkx111_irq_ops,
+};
+EXPORT_SYMBOL_GPL(serdes_rkx111_data);
+
+MODULE_LICENSE("GPL");
diff --git a/kernel/drivers/mfd/display-serdes/rockchip/rockchip-rkx121.c b/kernel/drivers/mfd/display-serdes/rockchip/rockchip-rkx121.c
new file mode 100644
index 0000000..338c3f9
--- /dev/null
+++ b/kernel/drivers/mfd/display-serdes/rockchip/rockchip-rkx121.c
@@ -0,0 +1,189 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * rockchip-rkx121.c  --  I2C register interface access for rkx121 serdes chip
+ *
+ * Copyright (c) 2023-2028 Rockchip Electronics Co. Ltd.
+ *
+ * Author: luowei <lw@rock-chips.com>
+ */
+
+#include "../core.h"
+
+static bool rkx121_volatile_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case 0x0076:
+	case 0x0086:
+	case 0x0100:
+	case 0x0200 ... 0x02ce:
+	case 0x7000:
+	case 0x7070:
+	case 0x7074:
+		return false;
+	default:
+		return true;
+	}
+}
+
+static struct regmap_config rkx121_regmap_config = {
+	.name = "rkx121",
+	.reg_bits = 16,
+	.val_bits = 8,
+	.max_register = 0x8000,
+	.volatile_reg = rkx121_volatile_reg,
+	.cache_type = REGCACHE_RBTREE,
+};
+
+static struct pinctrl_pin_desc rkx121_pins_desc[] = {
+};
+
+static struct group_desc rkx121_groups_desc[] = {
+	{ "null", NULL, 1, },
+};
+
+static struct function_desc rkx121_functions_desc[] = {
+	{ "null", NULL, 1, },
+};
+
+static struct serdes_chip_pinctrl_info rkx121_pinctrl_info = {
+	.pins = rkx121_pins_desc,
+	.num_pins = ARRAY_SIZE(rkx121_pins_desc),
+	.groups = rkx121_groups_desc,
+	.num_groups = ARRAY_SIZE(rkx121_groups_desc),
+	.functions = rkx121_functions_desc,
+	.num_functions = ARRAY_SIZE(rkx121_functions_desc),
+};
+
+static int rkx121_bridge_init(struct serdes *serdes)
+{
+	return 0;
+}
+
+static int rkx121_bridge_enable(struct serdes *serdes)
+{
+	return 0;
+}
+
+static int rkx121_bridge_disable(struct serdes *serdes)
+{
+	return 0;
+}
+
+static struct serdes_chip_panel_ops rkx121_panel_ops = {
+	.init = rkx121_bridge_init,
+	.enable = rkx121_bridge_enable,
+	.disable = rkx121_bridge_disable,
+};
+
+static int rkx121_pinctrl_config_get(struct serdes *serdes,
+				     unsigned int pin,
+				     unsigned long *config)
+{
+	return 0;
+}
+
+static int rkx121_pinctrl_config_set(struct serdes *serdes,
+				     unsigned int pin,
+				     unsigned long *configs,
+				     unsigned int num_configs)
+{
+	return 0;
+}
+
+static int rkx121_pinctrl_set_mux(struct serdes *serdes, unsigned int func_selector,
+				  unsigned int group_selector)
+{
+	return 0;
+}
+
+static struct serdes_chip_pinctrl_ops rkx121_pinctrl_ops = {
+	.pin_config_get = rkx121_pinctrl_config_get,
+	.pin_config_set = rkx121_pinctrl_config_set,
+	.set_mux = rkx121_pinctrl_set_mux,
+};
+
+static int rkx121_gpio_direction_input(struct serdes *serdes, int gpio)
+{
+	return 0;
+}
+
+static int rkx121_gpio_direction_output(struct serdes *serdes, int gpio, int value)
+{
+	return 0;
+}
+
+static int rkx121_gpio_get_level(struct serdes *serdes, int gpio)
+{
+	return 0;
+}
+
+static int rkx121_gpio_set_level(struct serdes *serdes, int gpio, int value)
+{
+	return 0;
+}
+
+static int rkx121_gpio_set_config(struct serdes *serdes, int gpio, unsigned long config)
+{
+	return 0;
+}
+
+static int rkx121_gpio_to_irq(struct serdes *serdes, int gpio)
+{
+	return 0;
+}
+
+static struct serdes_chip_gpio_ops rkx121_gpio_ops = {
+	.direction_input = rkx121_gpio_direction_input,
+	.direction_output = rkx121_gpio_direction_output,
+	.get_level = rkx121_gpio_get_level,
+	.set_level = rkx121_gpio_set_level,
+	.set_config = rkx121_gpio_set_config,
+	.to_irq = rkx121_gpio_to_irq,
+};
+
+static int rkx121_pm_suspend(struct serdes *serdes)
+{
+	return 0;
+}
+
+static int rkx121_pm_resume(struct serdes *serdes)
+{
+	return 0;
+}
+
+static struct serdes_chip_pm_ops rkx121_pm_ops = {
+	.suspend = rkx121_pm_suspend,
+	.resume = rkx121_pm_resume,
+};
+
+static int rkx121_irq_lock_handle(struct serdes *serdes)
+{
+	return IRQ_HANDLED;
+}
+
+static int rkx121_irq_err_handle(struct serdes *serdes)
+{
+	return IRQ_HANDLED;
+}
+
+static struct serdes_chip_irq_ops rkx121_irq_ops = {
+	.lock_handle = rkx121_irq_lock_handle,
+	.err_handle = rkx121_irq_err_handle,
+};
+
+struct serdes_chip_data serdes_rkx121_data = {
+	.name		= "rkx121",
+	.serdes_type	= TYPE_DES,
+	.serdes_id	= ROCKCHIP_ID_RKX121,
+	.connector_type	= DRM_MODE_CONNECTOR_LVDS,
+	.regmap_config	= &rkx121_regmap_config,
+	.pinctrl_info	= &rkx121_pinctrl_info,
+	.panel_ops	= &rkx121_panel_ops,
+	.pinctrl_ops	= &rkx121_pinctrl_ops,
+	.gpio_ops	= &rkx121_gpio_ops,
+	.pm_ops		= &rkx121_pm_ops,
+	.irq_ops	= &rkx121_irq_ops,
+};
+EXPORT_SYMBOL_GPL(serdes_rkx121_data);
+
+MODULE_LICENSE("GPL");
diff --git a/kernel/drivers/mfd/display-serdes/rohm/Kconfig b/kernel/drivers/mfd/display-serdes/rohm/Kconfig
new file mode 100644
index 0000000..a2e6d77
--- /dev/null
+++ b/kernel/drivers/mfd/display-serdes/rohm/Kconfig
@@ -0,0 +1,25 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# rohm serdes drivers configuration
+#
+
+menuconfig SERDES_DISPLAY_CHIP_ROHM
+	tristate "rohm serdes device support"
+	default y
+	help
+	  Enable this to be able to choose the drivers for controlling the
+	  rohm serdes.
+
+if SERDES_DISPLAY_CHIP_ROHM
+config SERDES_DISPLAY_CHIP_ROHM_BU18TL82
+	tristate "rohm bu18tl82 serdes"
+	default y
+	help
+	  To support rohm bu18tl82 display serdes.
+
+config SERDES_DISPLAY_CHIP_ROHM_BU18RL82
+	tristate "rohm bu18rl82 serdes"
+	default y
+	help
+	  To support rohm bu18rl82 display serdes.
+endif
diff --git a/kernel/drivers/mfd/display-serdes/rohm/Makefile b/kernel/drivers/mfd/display-serdes/rohm/Makefile
new file mode 100644
index 0000000..2939aac
--- /dev/null
+++ b/kernel/drivers/mfd/display-serdes/rohm/Makefile
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# rohm display serdes drivers configuration
+#
+obj-$(CONFIG_SERDES_DISPLAY_CHIP_ROHM_BU18TL82)		+= rohm-bu18tl82.o
+obj-$(CONFIG_SERDES_DISPLAY_CHIP_ROHM_BU18RL82)		+= rohm-bu18rl82.o
diff --git a/kernel/drivers/mfd/display-serdes/rohm/rohm-bu18rl82.c b/kernel/drivers/mfd/display-serdes/rohm/rohm-bu18rl82.c
new file mode 100644
index 0000000..d0e8f71
--- /dev/null
+++ b/kernel/drivers/mfd/display-serdes/rohm/rohm-bu18rl82.c
@@ -0,0 +1,460 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * maxim-bu18rl82.c  --  I2C register interface access for bu18rl82 serdes chip
+ *
+ * Copyright (c) 2023-2028 Rockchip Electronics Co. Ltd.
+ *
+ * Author: luowei <lw@rock-chips.com>
+ */
+
+#include "../core.h"
+#include "rohm-bu18rl82.h"
+
+#define PINCTRL_GROUP(a, b, c) { .name = a, .pins = b, .num_pins = c}
+
+static bool bu18rl82_volatile_reg(struct device *dev, unsigned int reg)
+{
+	return true;
+}
+
+static struct regmap_config bu18rl82_regmap_config = {
+	.name = "bu18rl82",
+	.reg_bits = 16,
+	.val_bits = 8,
+	.max_register = 0x0700,
+	.volatile_reg = bu18rl82_volatile_reg,
+	.cache_type = REGCACHE_RBTREE,
+};
+
+static int BU18RL82_GPIO0_pins[] = {0};
+static int BU18RL82_GPIO1_pins[] = {1};
+static int BU18RL82_GPIO2_pins[] = {2};
+static int BU18RL82_GPIO3_pins[] = {3};
+static int BU18RL82_GPIO4_pins[] = {4};
+static int BU18RL82_GPIO5_pins[] = {5};
+static int BU18RL82_GPIO6_pins[] = {6};
+static int BU18RL82_GPIO7_pins[] = {7};
+
+#define GROUP_DESC(nm) \
+{ \
+	.name = #nm, \
+	.pins = nm ## _pins, \
+	.num_pins = ARRAY_SIZE(nm ## _pins), \
+}
+
+struct serdes_function_data {
+	u8 gpio_rx_en:1;
+	u16 gpio_id;
+};
+
+static const char *serdes_gpio_groups[] = {
+	"BU18RL82_GPIO0", "BU18RL82_GPIO1", "BU18RL82_GPIO2", "BU18RL82_GPIO3",
+	"BU18RL82_GPIO4", "BU18RL82_GPIO5", "BU18RL82_GPIO6", "BU18RL82_GPIO7",
+};
+
+/*des -> ser -> soc*/
+#define FUNCTION_DESC_GPIO_INPUT(id) \
+{ \
+	.name = "DES_TO_SER_GPIO"#id, \
+	.group_names = serdes_gpio_groups, \
+	.num_group_names = ARRAY_SIZE(serdes_gpio_groups), \
+	.data = (void *)(const struct serdes_function_data []) { \
+		{ .gpio_rx_en = 1, .gpio_id = id + 2 } \
+	}, \
+} \
+
+/*soc -> ser -> des*/
+#define FUNCTION_DESC_GPIO_OUTPUT(id) \
+{ \
+	.name = "SER_GPIO"#id"_TO_DES", \
+	.group_names = serdes_gpio_groups, \
+	.num_group_names = ARRAY_SIZE(serdes_gpio_groups), \
+	.data = (void *)(const struct serdes_function_data []) { \
+		{ .gpio_rx_en = 0, .gpio_id = id + 2 } \
+	}, \
+} \
+
+/*des -> device*/
+#define FUNCTION_DESC_GPIO_OUTPUT_HIGH() \
+{ \
+	.name = "DES_GPIO_OUTPUT_HIGH", \
+	.group_names = serdes_gpio_groups, \
+	.num_group_names = ARRAY_SIZE(serdes_gpio_groups), \
+	.data = (void *)(const struct serdes_function_data []) { \
+		{ .gpio_rx_en = 0, .gpio_id = 1 } \
+	}, \
+} \
+
+#define FUNCTION_DESC_GPIO_OUTPUT_LOW() \
+{ \
+	.name = "DES_GPIO_OUTPUT_LOW", \
+	.group_names = serdes_gpio_groups, \
+	.num_group_names = ARRAY_SIZE(serdes_gpio_groups), \
+	.data = (void *)(const struct serdes_function_data []) { \
+		{ .gpio_rx_en = 0, .gpio_id = 0 } \
+	}, \
+} \
+
+static struct pinctrl_pin_desc bu18rl82_pins_desc[] = {
+	PINCTRL_PIN(ROHM_BU18RL82_GPIO0, "BU18RL82_GPIO0"),
+	PINCTRL_PIN(ROHM_BU18RL82_GPIO1, "BU18RL82_GPIO1"),
+	PINCTRL_PIN(ROHM_BU18RL82_GPIO2, "BU18RL82_GPIO2"),
+	PINCTRL_PIN(ROHM_BU18RL82_GPIO3, "BU18RL82_GPIO3"),
+	PINCTRL_PIN(ROHM_BU18RL82_GPIO4, "BU18RL82_GPIO4"),
+	PINCTRL_PIN(ROHM_BU18RL82_GPIO5, "BU18RL82_GPIO5"),
+	PINCTRL_PIN(ROHM_BU18RL82_GPIO6, "BU18RL82_GPIO6"),
+	PINCTRL_PIN(ROHM_BU18RL82_GPIO7, "BU18RL82_GPIO7"),
+};
+
+static struct group_desc bu18rl82_groups_desc[] = {
+	GROUP_DESC(BU18RL82_GPIO0),
+	GROUP_DESC(BU18RL82_GPIO1),
+	GROUP_DESC(BU18RL82_GPIO2),
+	GROUP_DESC(BU18RL82_GPIO3),
+	GROUP_DESC(BU18RL82_GPIO4),
+	GROUP_DESC(BU18RL82_GPIO5),
+	GROUP_DESC(BU18RL82_GPIO6),
+	GROUP_DESC(BU18RL82_GPIO7),
+};
+
+static struct function_desc bu18rl82_functions_desc[] = {
+	FUNCTION_DESC_GPIO_INPUT(0),
+	FUNCTION_DESC_GPIO_INPUT(1),
+	FUNCTION_DESC_GPIO_INPUT(2),
+	FUNCTION_DESC_GPIO_INPUT(3),
+	FUNCTION_DESC_GPIO_INPUT(4),
+	FUNCTION_DESC_GPIO_INPUT(5),
+	FUNCTION_DESC_GPIO_INPUT(6),
+	FUNCTION_DESC_GPIO_INPUT(7),
+	FUNCTION_DESC_GPIO_INPUT(8),
+	FUNCTION_DESC_GPIO_INPUT(9),
+	FUNCTION_DESC_GPIO_INPUT(10),
+	FUNCTION_DESC_GPIO_INPUT(11),
+	FUNCTION_DESC_GPIO_INPUT(12),
+	FUNCTION_DESC_GPIO_INPUT(13),
+	FUNCTION_DESC_GPIO_INPUT(14),
+	FUNCTION_DESC_GPIO_INPUT(15),
+
+	FUNCTION_DESC_GPIO_OUTPUT(0),
+	FUNCTION_DESC_GPIO_OUTPUT(1),
+	FUNCTION_DESC_GPIO_OUTPUT(2),
+	FUNCTION_DESC_GPIO_OUTPUT(3),
+	FUNCTION_DESC_GPIO_OUTPUT(4),
+	FUNCTION_DESC_GPIO_OUTPUT(5),
+	FUNCTION_DESC_GPIO_OUTPUT(6),
+	FUNCTION_DESC_GPIO_OUTPUT(7),
+	FUNCTION_DESC_GPIO_OUTPUT(8),
+	FUNCTION_DESC_GPIO_OUTPUT(9),
+	FUNCTION_DESC_GPIO_OUTPUT(10),
+	FUNCTION_DESC_GPIO_OUTPUT(11),
+	FUNCTION_DESC_GPIO_OUTPUT(12),
+	FUNCTION_DESC_GPIO_OUTPUT(13),
+	FUNCTION_DESC_GPIO_OUTPUT(14),
+	FUNCTION_DESC_GPIO_OUTPUT(15),
+
+	FUNCTION_DESC_GPIO_OUTPUT_HIGH(),
+	FUNCTION_DESC_GPIO_OUTPUT_LOW(),
+};
+
+static struct serdes_chip_pinctrl_info bu18rl82_pinctrl_info = {
+	.pins = bu18rl82_pins_desc,
+	.num_pins = ARRAY_SIZE(bu18rl82_pins_desc),
+	.groups = bu18rl82_groups_desc,
+	.num_groups = ARRAY_SIZE(bu18rl82_groups_desc),
+	.functions = bu18rl82_functions_desc,
+	.num_functions = ARRAY_SIZE(bu18rl82_functions_desc),
+};
+
+static void bu18rl82_bridge_swrst(struct serdes *serdes)
+{
+	struct device *dev = serdes->dev;
+	int ret;
+
+	ret = serdes_reg_write(serdes, BU18RL82_REG_RESET, BIT(0) | BIT(1) | BIT(7));
+	if (ret < 0)
+		dev_err(dev, "%s: failed to reset bu18rl82 0x11 ret=%d\n", __func__, ret);
+
+	mdelay(20);
+
+	SERDES_DBG_CHIP("%s: serdes %s ret=%d\n", __func__, serdes->chip_data->name, ret);
+}
+
+static void bu18rl82_enable_hwint(struct serdes *serdes, int enable)
+{
+	struct device *dev = serdes->dev;
+	int i, ret;
+
+	for (i = 0; i < ARRAY_SIZE(bu18rl82_reg_ien); i++) {
+		if (enable) {
+			ret = serdes_reg_write(serdes, bu18rl82_reg_ien[i].reg,
+					       bu18rl82_reg_ien[i].ien);
+			if (ret)
+				dev_err(dev, "reg 0x%04x write error\n", bu18rl82_reg_ien[i].reg);
+		} else {
+			ret = serdes_reg_write(serdes, bu18rl82_reg_ien[i].reg, 0);
+			if (ret)
+				dev_err(dev, "reg 0x%04x write error\n", bu18rl82_reg_ien[i].reg);
+		}
+	}
+
+	SERDES_DBG_CHIP("%s: serdes %s ret=%d\n", __func__,
+			serdes->chip_data->name, enable);
+}
+
+static int bu18rl82_bridge_init(struct serdes *serdes)
+{
+	return 0;
+
+	bu18rl82_bridge_swrst(serdes);
+
+	return 0;
+}
+
+static int bu18rl82_bridge_enable(struct serdes *serdes)
+{
+	int ret = 0;
+
+	/* 1:enable 0:disable */
+	bu18rl82_enable_hwint(serdes, 0);
+
+	SERDES_DBG_CHIP("%s: serdes %s ret=%d\n", __func__,
+			serdes->chip_data->name, ret);
+	return ret;
+}
+
+static int bu18rl82_bridge_disable(struct serdes *serdes)
+{
+	return 0;
+}
+
+static struct serdes_chip_bridge_ops bu18rl82_bridge_ops = {
+	.init = bu18rl82_bridge_init,
+	.enable = bu18rl82_bridge_enable,
+	.disable = bu18rl82_bridge_disable,
+};
+
+static int bu18rl82_pinctrl_config_get(struct serdes *serdes,
+				       unsigned int pin, unsigned long *config)
+{
+	enum pin_config_param param = pinconf_to_config_param(*config);
+	unsigned int bu18rl82_gpio_sw_reg, bu18rl82_gpio_pden_reg, bu18rl82_gpio_oen_reg;
+	u16 arg = 0;
+
+	serdes_reg_read(serdes, bu18rl82_gpio_sw[pin].reg, &bu18rl82_gpio_sw_reg);
+	serdes_reg_read(serdes, bu18rl82_gpio_pden[pin].reg, &bu18rl82_gpio_pden_reg);
+	serdes_reg_read(serdes, bu18rl82_gpio_oen[pin].reg, &bu18rl82_gpio_oen_reg);
+
+	SERDES_DBG_CHIP("%s: serdes chip %s pin=%d param=%d\n",
+			__func__, serdes->chip_data->name, pin, param);
+
+	switch (param) {
+	case PIN_CONFIG_DRIVE_STRENGTH:
+		arg = FIELD_GET(BIT(2) | BIT(1), bu18rl82_gpio_sw_reg);
+		break;
+	case PIN_CONFIG_BIAS_PULL_DOWN:
+		arg = FIELD_GET(BIT(4), bu18rl82_gpio_pden_reg);
+		break;
+	case PIN_CONFIG_OUTPUT:
+		arg = FIELD_GET(BIT(3), bu18rl82_gpio_oen_reg);
+		break;
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	*config = pinconf_to_config_packed(param, arg);
+
+	SERDES_DBG_CHIP("%s: serdes chip %s pin=%d arg=%d\n",
+			__func__, serdes->chip_data->name, pin, arg);
+
+	return 0;
+}
+
+static int bu18rl82_pinctrl_config_set(struct serdes *serdes,
+				       unsigned int pin, unsigned long *configs,
+				       unsigned int num_configs)
+{
+	enum pin_config_param param;
+	u32 arg;
+	int i;
+
+	for (i = 0; i < num_configs; i++) {
+		param = pinconf_to_config_param(configs[i]);
+		arg = pinconf_to_config_argument(configs[i]);
+
+		switch (param) {
+		case PIN_CONFIG_DRIVE_STRENGTH:
+			serdes_set_bits(serdes, bu18rl82_gpio_sw[pin].reg,
+					bu18rl82_gpio_sw[pin].mask,
+					FIELD_PREP(BIT(2) | BIT(1), arg));
+			SERDES_DBG_CHIP("%s: serdes chip %s pin=%d i=%d drive-strength arg=0x%x\n",
+					 __func__, serdes->chip_data->name, pin, i, arg);
+			break;
+		case PIN_CONFIG_BIAS_PULL_DOWN:
+			serdes_set_bits(serdes, bu18rl82_gpio_pden[pin].reg,
+					bu18rl82_gpio_pden[i].mask,
+					FIELD_PREP(BIT(4), arg));
+			SERDES_DBG_CHIP("%s: serdes chip %s pin=%d i=%d pull-down arg=0x%x\n",
+					__func__, serdes->chip_data->name, pin, i, arg);
+			break;
+
+			break;
+		case PIN_CONFIG_OUTPUT:
+			serdes_set_bits(serdes, bu18rl82_gpio_oen[pin].reg,
+					bu18rl82_gpio_oen[i].mask,
+					FIELD_PREP(BIT(3), arg));
+			SERDES_DBG_CHIP("%s: serdes chip %s pin=%d i=%d output arg=0x%x\n",
+					__func__, serdes->chip_data->name, pin, i, arg);
+			break;
+		default:
+			return -EOPNOTSUPP;
+		}
+	}
+
+	return 0;
+}
+
+static int bu18rl82_pinctrl_set_mux(struct serdes *serdes,
+				    unsigned int function, unsigned int group)
+{
+	struct serdes_pinctrl *pinctrl = serdes->pinctrl;
+	struct function_desc *func;
+	struct group_desc *grp;
+	int i, offset;
+
+	func = pinmux_generic_get_function(pinctrl->pctl, function);
+	if (!func)
+		return -EINVAL;
+
+	grp = pinctrl_generic_get_group(pinctrl->pctl, group);
+	if (!grp)
+		return -EINVAL;
+
+	SERDES_DBG_CHIP("%s: serdes chip %s func=%s data=%p group=%s data=%p, num_pin=%d\n",
+			__func__, serdes->chip_data->name, func->name,
+			func->data, grp->name, grp->data, grp->num_pins);
+
+	if (func->data) {
+		struct serdes_function_data *fdata = func->data;
+
+		for (i = 0; i < grp->num_pins; i++) {
+			offset = grp->pins[i] - pinctrl->pin_base;
+			if (offset > 7)
+				dev_err(serdes->dev, "%s gpio offset=%d too large > 7\n",
+					serdes->chip_data->name, offset);
+			else
+				SERDES_DBG_CHIP("%s: serdes chip %s gpio_id=0x%x, offset=%d\n",
+						__func__, serdes->chip_data->name, fdata->gpio_id, offset);
+			serdes_set_bits(serdes, bu18rl82_gpio_oen[offset].reg,
+					bu18rl82_gpio_oen[offset].mask,
+					FIELD_PREP(BIT(3), fdata->gpio_rx_en));
+
+			serdes_set_bits(serdes, bu18rl82_gpio_id_low[offset].reg,
+					bu18rl82_gpio_id_low[offset].mask,
+					FIELD_PREP(GENMASK(7, 0),
+					(fdata->gpio_id & 0xff)));
+
+			serdes_set_bits(serdes, bu18rl82_gpio_id_high[offset].reg,
+					bu18rl82_gpio_id_high[offset].mask,
+					FIELD_PREP(GENMASK(2, 0),
+					((fdata->gpio_id >> 8) & 0x7)));
+			serdes_set_bits(serdes, bu18rl82_gpio_pden[offset].reg,
+					bu18rl82_gpio_pden[offset].mask,
+					FIELD_PREP(BIT(4), 0));
+		}
+	}
+
+	return 0;
+}
+
+static struct serdes_chip_pinctrl_ops bu18rl82_pinctrl_ops = {
+	.pin_config_get = bu18rl82_pinctrl_config_get,
+	.pin_config_set = bu18rl82_pinctrl_config_set,
+	.set_mux = bu18rl82_pinctrl_set_mux,
+};
+
+static int bu18rl82_gpio_direction_input(struct serdes *serdes, int gpio)
+{
+	return 0;
+}
+
+static int bu18rl82_gpio_direction_output(struct serdes *serdes, int gpio, int value)
+{
+	return 0;
+}
+
+static int bu18rl82_gpio_get_level(struct serdes *serdes, int gpio)
+{
+	return 0;
+}
+
+static int bu18rl82_gpio_set_level(struct serdes *serdes, int gpio, int value)
+{
+	return 0;
+}
+
+static int bu18rl82_gpio_set_config(struct serdes *serdes, int gpio, unsigned long config)
+{
+	return 0;
+}
+
+static int bu18rl82_gpio_to_irq(struct serdes *serdes, int gpio)
+{
+	return 0;
+}
+
+static struct serdes_chip_gpio_ops bu18rl82_gpio_ops = {
+	.direction_input = bu18rl82_gpio_direction_input,
+	.direction_output = bu18rl82_gpio_direction_output,
+	.get_level = bu18rl82_gpio_get_level,
+	.set_level = bu18rl82_gpio_set_level,
+	.set_config = bu18rl82_gpio_set_config,
+	.to_irq = bu18rl82_gpio_to_irq,
+};
+
+static int bu18rl82_pm_suspend(struct serdes *serdes)
+{
+	return 0;
+}
+
+static int bu18rl82_pm_resume(struct serdes *serdes)
+{
+	return 0;
+}
+
+static struct serdes_chip_pm_ops bu18rl82_pm_ops = {
+	.suspend = bu18rl82_pm_suspend,
+	.resume = bu18rl82_pm_resume,
+};
+
+static int bu18rl82_irq_lock_handle(struct serdes *serdes)
+{
+	return IRQ_HANDLED;
+}
+
+static int bu18rl82_irq_err_handle(struct serdes *serdes)
+{
+	return IRQ_HANDLED;
+}
+
+static struct serdes_chip_irq_ops bu18rl82_irq_ops = {
+	.lock_handle = bu18rl82_irq_lock_handle,
+	.err_handle = bu18rl82_irq_err_handle,
+};
+
+struct serdes_chip_data serdes_bu18rl82_data = {
+	.name		= "bu18rl82",
+	.serdes_type	= TYPE_DES,
+	.serdes_id	= ROHM_ID_BU18RL82,
+	.bridge_type	= TYPE_BRIDGE_BRIDGE,
+	.connector_type	= DRM_MODE_CONNECTOR_LVDS,
+	.regmap_config	= &bu18rl82_regmap_config,
+	.pinctrl_info	= &bu18rl82_pinctrl_info,
+	.bridge_ops	= &bu18rl82_bridge_ops,
+	.pinctrl_ops	= &bu18rl82_pinctrl_ops,
+	.gpio_ops	= &bu18rl82_gpio_ops,
+	.pm_ops		= &bu18rl82_pm_ops,
+	.irq_ops	= &bu18rl82_irq_ops,
+};
+EXPORT_SYMBOL_GPL(serdes_bu18rl82_data);
+
+MODULE_LICENSE("GPL");
diff --git a/kernel/drivers/mfd/display-serdes/rohm/rohm-bu18rl82.h b/kernel/drivers/mfd/display-serdes/rohm/rohm-bu18rl82.h
new file mode 100644
index 0000000..9161afa
--- /dev/null
+++ b/kernel/drivers/mfd/display-serdes/rohm/rohm-bu18rl82.h
@@ -0,0 +1,376 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * include/linux/mfd/serdes/gpio.h -- GPIO for different serdes chip
+ *
+ * Copyright (c) 2023-2028 Rockchip Electronics Co. Ltd.
+ *
+ * Author: luowei <lw@rock-chips.com>
+ *
+ */
+
+#ifndef __MFD_SERDES_ROHM_BU18RL82_H__
+#define __MFD_SERDES_ROHM_BU18RL82_H__
+#define BU18RL82_REG_RESET 0X000E
+
+#define BU18RL82_BLOCK_EN_CLLRX0 0x0011	//h [0] 1’b1
+#define BU18RL82_BLOCK_EN_LVDSTX0 0x0011	//h [1] 1’b0
+#define BU18RL82_BLOCK_EN_VPLL0 0x0011	//h [3] 1’b0
+#define BU18RL82_BLOCK_EN_SSCG0 0x0011	//h [4] 1’b0
+#define BU18RL82_BLOCK_EN_CLLRX1 0x0012	//h [0] 1’b1
+#define BU18RL82_BLOCK_EN_LVDSTX1 0x0012	//h [1] 1’b0
+#define BU18RL82_BLOCK_EN_VPLL1 0x0012	//h [3] 1’b0
+#define BU18RL82_BLOCK_EN_SSCG1 0x0012	//h [4] 1’b0
+#define BU18RL82_BLOCK_EN_CLLTX 0x0013	//h [0] 1’b0
+#define BU18RL82_BLOCK_EN_FSAFETY 0x0013	//h [1] 1’b0
+
+#define BU18RL82_IO_SW_GPIO0 0x0057		//h [2:1] 2’b00
+#define BU18RL82_IO_OEN_GPIO0 0x0057	//h [3] 1’b1
+#define BU18RL82_IO_PDEN_GPIO0 0x0057	//h [4] 1’b1
+#define BU18RL82_IO_SW_GPIO1 0x005A		//h [2:1] 2’b00
+#define BU18RL82_IO_OEN_GPIO1 0x005A	//h [3] 1’b1
+#define BU18RL82_IO_PDEN_GPIO1 0x005A	//h [4] 1’b1
+#define BU18RL82_IO_SW_GPIO2 0x005D		//h [2:1] 2’b00
+#define BU18RL82_IO_OEN_GPIO2 0x005D	//h [3] 1’b1
+#define BU18RL82_IO_PDEN_GPIO2 0x005D	//h [4] 1’b1
+#define BU18RL82_IO_SW_GPIO3 0x0060		//h [2:1] 2’b00
+#define BU18RL82_IO_OEN_GPIO3 0x0060	//h [3] 1’b1
+#define BU18RL82_IO_PDEN_GPIO3 0x0060	//h [4] 1’b1
+#define BU18RL82_IO_SW_GPIO4 0x0063		//h [2:1] 2’b00
+#define BU18RL82_IO_OEN_GPIO4 0x0063	//h [3] 1’b1
+#define BU18RL82_IO_PDEN_GPIO4 0x0063	//h [4] 1’b1
+#define BU18RL82_IO_SW_GPIO5 0x0066		//h [2:1] 2’b00
+#define BU18RL82_IO_OEN_GPIO5 0x0066	//h [2:1] 2’b00
+#define BU18RL82_IO_PDEN_GPIO5 0x0066	//h [3] 1’b1
+#define BU18RL82_IO_SW_GPIO6 0x0069		//h [2:1] 2’b00
+#define BU18RL82_IO_OEN_GPIO6 0x0069	//h [3] 1’b1
+#define BU18RL82_IO_PDEN_GPIO6 0x0069	//h [4] 1’b1
+#define BU18RL82_IO_SW_GPIO7 0x006C		//h [2:1] 2’b00
+#define BU18RL82_IO_OEN_GPIO7 0x006C	//h [3] 1’b1
+#define BU18RL82_IO_PDEN_GPIO7 0x006C	//h [4] 1’b1
+
+/*
+ * gpio register for define connection with des gpiox.
+ * 11bits such as 0x002c:002b=[b2..b0 b7...b0]
+ *
+ * default value:
+ * ser gpio0-->des gpio0
+ * ser gpio1-->des gpio1
+ * ser gpio2-->des gpio2
+ * ser gpio3-->des gpio3
+ */
+#define BU18RL82_GPIO_SEL0_HIGH 0x0059	//h [2:0],
+#define BU18RL82_GPIO_SEL0_LOW 0x0058	//h [7:0]} 11’h002
+#define BU18RL82_GPIO_SEL1_HIGH 0x005C	//h [2:0],
+#define BU18RL82_GPIO_SEL1_LOW 0x005B	//h [7:0]} 11’h003
+#define BU18RL82_GPIO_SEL2_HIGH 0x005F	//h [2:0],
+#define BU18RL82_GPIO_SEL2_LOW 0x005E	//h [7:0]} 11’h012
+#define BU18RL82_GPIO_SEL3_HIGH 0x0062	//h [2:0],
+#define BU18RL82_GPIO_SEL3_LOW 0x0061	//h [7:0]} 11’h013
+#define BU18RL82_GPIO_SEL4_HIGH 0x0065	//h [2:0],
+#define BU18RL82_GPIO_SEL4_LOW 0x0064	//h [7:0]} 11’h006
+#define BU18RL82_GPIO_SEL5_HIGH 0x0068	//h [2:0],
+#define BU18RL82_GPIO_SEL5_LOW 0x0067	//h [7:0]} 11’h007
+#define BU18RL82_GPIO_SEL6_HIGH 0x006B	//h [2:0],
+#define BU18RL82_GPIO_SEL6_LOW 0x006A	//h [7:0]} 11’h008
+#define BU18RL82_GPIO_SEL7_HIGH 0x006E	//h [2:0],
+#define BU18RL82_GPIO_SEL7_LOW 0x006D	//h [7:0]} 11’h009
+
+/*gpio register for define bu18rl82 gpio pin, and gpio0 to gpio0 default*/
+#define BU18RL82_BCCTX0__SEL_GPI0 0x042B	//h [5:0] 6’h02
+#define BU18RL82_BCCTX0__SEL_GPI1 0x042C	//h [5:0] 6’h03
+#define BU18RL82_BCCTX0__SEL_GPI2 0x042D	//h [5:0] 6’h04
+#define BU18RL82_BCCTX0__SEL_GPI3 0x042E	//h [5:0] 6’h05
+#define BU18RL82_BCCTX0__SEL_GPI4 0x042F	//h [5:0] 6’h06
+#define BU18RL82_BCCTX0__SEL_GPI5 0x0430	//h [5:0] 6’h07
+#define BU18RL82_BCCTX0__SEL_GPI6 0x0431	//h [5:0] 6’h08
+#define BU18RL82_BCCTX0__SEL_GPI7 0x0432	//h [5:0] 6’h09
+#define BU18RL82_BCCTX1__SEL_GPI0 0x052B	//h [5:0] 6’h02
+#define BU18RL82_BCCTX1__SEL_GPI1 0x052C	//h [5:0] 6’h03
+#define BU18RL82_BCCTX1__SEL_GPI2 0x052D	//h [5:0] 6’h04
+#define BU18RL82_BCCTX1__SEL_GPI3 0x052E	//h [5:0] 6’h05
+#define BU18RL82_BCCTX1__SEL_GPI4 0x052F	//h [5:0] 6’h06
+#define BU18RL82_BCCTX1__SEL_GPI5 0x0530	//h [5:0] 6’h07
+#define BU18RL82_BCCTX1__SEL_GPI6 0x0531	//h [5:0] 6’h08
+#define BU18RL82_BCCTX1__SEL_GPI7 0x0532	//h [5:0] 6’h09
+
+#define BU18RL82_IEN_CLLRX0_LINK_UNLOCK 0x0109	//h [1] 1’b0
+#define BU18RL82_IEN_CLLRX0_BIT_ERR 0x0109		//h [3] 1’b0
+#define BU18RL82_IEN_CLLRX0_ERR_CNT_OVF 0x0109	//h [4] 1’b0
+#define BU18RL82_IEN_CLLRX1_LINK_UNLOCK 0x010B	//h [1] 1’b0
+#define BU18RL82_IEN_CLLRX1_BIT_ERR 0x010B		//h [3] 1’b0
+#define BU18RL82_IEN_CLLRX1_ERR_CNT_OVF 0x010B	//h [4] 1’b0
+#define BU18RL82_IEN_FCCRX0_CRCERR 0x010D		//h [0] 1’b0
+#define BU18RL82_IEN_FCCRX1_CRCERR 0x010E		//h [0] 1’b0
+#define BU18RL82_IEN_BCCDES0_ERR_CRC 0x010F		//h [3] 1’b0
+#define BU18RL82_IEN_CLLRX0_CRCERR_R  0x0110	//h [0] 1’b0
+#define BU18RL82_IEN_CLLRX0_CRCERR_G 0x0110		//h [1] 1’b0
+#define BU18RL82_IEN_CLLRX0_CRCERR_B 0x0110		//h [2] 1’b0
+#define BU18RL82_IEN_CLLRX1_CRCERR_R 0x0111		//h [0] 1’b0
+#define BU18RL82_IEN_CLLRX1_CRCERR_G 0x0111		//h [1] 1’b0
+#define BU18RL82_IEN_CLLRX1_CRCERR_B 0x0111		//h [2] 1’b0
+#define BU18RL82_IEN_FS_IMG_STATUS0 0x0112		//h [0] 1’b0
+#define BU18RL82_IEN_FS_IMG_STATUS1 0x0112		//h [1] 1’b0
+#define BU18RL82_IEN_FS_IMG_STATUS2 0x0112		//h [2] 1’b0
+#define BU18RL82_IEN_FS_IMG_STATUS3 0x0112		//h [3] 1’b0
+#define BU18RL82_IEN_FS_IMG_ERR_REGION0 0x0113	//h [0] 1’b0
+#define BU18RL82_IEN_FS_IMG_ERR_REGION1 0x0113	//h [1] 1’b0
+#define BU18RL82_IEN_FS_IMG_ERR_REGION2 0x0113	//h [2] 1’b0
+#define BU18RL82_IEN_FS_IMG_ERR_REGION3 0x0113	//h [3] 1’b0
+#define BU18RL82_IEN_FS_IMG_ERR_REGION4 0x0113	//h [4] 1’b0
+#define BU18RL82_IEN_FS_IMG_ERR_REGION5 0x0113	//h [5] 1’b0
+#define BU18RL82_IEN_FS_IMG_ERR_REGION6 0x0113	//h [6] 1’b0
+#define BU18RL82_IEN_FS_IMG_ERR_REGION7 0x0113	//h [7] 1’b0
+#define BU18RL82_IEN_IO_STUCK_GPIO0 0x0114		//h [0] 1’b0
+#define BU18RL82_IEN_IO_STUCK_GPIO1 0x0114		//h [1] 1’b0
+#define BU18RL82_IEN_IO_STUCK_GPIO2 0x0114		//h [2] 1’b0
+#define BU18RL82_IEN_IO_STUCK_GPIO3 0x0114		//h [3] 1’b0
+#define BU18RL82_IEN_IO_STUCK_GPIO4 0x0114		//h [4] 1’b0
+#define BU18RL82_IEN_IO_STUCK_GPIO5 0x0114		//h [5] 1’b0
+#define BU18RL82_IEN_IO_STUCK_GPIO6 0x0114		//h [6] 1’b0
+#define BU18RL82_IEN_IO_STUCK_GPIO7 0x0114		//h [7] 1’b0
+#define BU18RL82_IEN_IO_STUCK_IRQ 0x0115		//h [1] 1’b0
+#define BU18RL82_IEN_IDS_UNSTABLE 0x0115		//h [7] 1’b0
+#define BU18RL82_IEN_I2C_A_TIMEOUT 0x0116		//h [0] 1’b0
+#define BU18RL82_IEN_I2C_A_XMIT_ERR 0x0116		//h [1] 1’b0
+#define BU18RL82_IEN_REGCRC_ERR_PAGE0 0x0117	//h [0] 1’b0
+#define BU18RL82_IEN_REGCRC_ERR_PAGE1 0x0117	//h [1] 1’b0
+#define BU18RL82_IEN_REGCRC_ERR_PAGE2 0x0117	//h [2] 1’b0
+#define BU18RL82_IEN_REGCRC_ERR_PAGE3 0x0117	//h [3] 1’b0
+#define BU18RL82_IEN_REGCRC_ERR_PAGE4 0x0117	//h [4] 1’b0
+#define BU18RL82_IEN_REGCRC_ERR_PAGE5 0x0117	//h [5] 1’b0
+#define BU18RL82_IEN_REGCRC_ERR_PAGE6 0x0117	//h [6] 1’b0
+#define BU18RL82_IEN_REGCRC_ERR_PAGE7 0x0117	//h [7] 1’b0
+#define BU18RL82_IEN_CLKDETECT_CLKIN_STOP 0x0118			//h [0] 1’b0
+#define BU18RL82_IEN_CLKDETECT_CLKIN_UNLOCK 0x0118			//h [1] 1’b0
+#define BU18RL82_IEN_CLKDETECT_OSC_STOP 0x0118				//h [4] 1’b0
+#define BU18RL82_IEN_CLKDETECT_OSC_UNLOCK 0x0118			//h [5] 1’b0
+#define BU18RL82_IEN_CLKDETECT_CLLRX0_PCLK_STOP 0x0119		//h [0] 1’b0
+#define BU18RL82_IEN_CLKDETECT_CLLRX0_PCLK_UNLOCK 0x0119	//h [1] 1’b0
+#define BU18RL82_IEN_CLKDETECT_LVDSTX0_CLK_STOP 0x0119		//h [4] 1’b0
+#define BU18RL82_IEN_CLKDETECT_LVDSTX0_CLK_UNLOCK 0x0119	//h [5] 1’b0
+#define BU18RL82_IEN_CLKDETECT_CLLRX1_PCLK_STOP 0x011A		//h [0] 1’b0
+#define BU18RL82_IEN_CLKDETECT_CLLRX1_PCLK_UNLOCK 0x011A	//h [1] 1’b0
+#define BU18RL82_IEN_CLKDETECT_LVDSTX1_CLK_STOP 0x011A		//h [4] 1’b0
+#define BU18RL82_IEN_CLKDETECT_LVDSTX1_CLK_UNLOCK 0x011A	//h [5] 1’b0
+#define BU18RL82_IEN_CLKDETECT_CLLTX0_SCLK_STOP 0x011B		//h [0] 1’b0
+#define BU18RL82_IEN_CLKDETECT_CLLTX0_SCLK_UNLOCK 0x011B	//h [1] 1’b0
+#define BU18RL82_IEN_CLKDETECT_CLLTX0_PLLREF_STOP 0x011B	//h [4] 1’b0
+#define BU18RL82_IEN_CLKDETECT_CLLTX0_PLLREF_UNLOCK 0x011B	//h [5] 1’b0
+#define BU18RL82_IEN_CLKDETECT_LVDSTX0_PLLREF_STOP 0x011C	//h [0] 1’b0
+#define BU18RL82_IEN_CLKDETECT_LVDSTX0_PLLREF_UNLOCK0x011C	//h [1] 1’b0
+#define BU18RL82_IEN_CLKDETECT_LVDSTX1_PLLREF_STOP 0x011C	//h [4] 1’b0
+#define BU18RL82_IEN_CLKDETECT_LVDSTX1_PLLREF_UNLOCK 0x011C	//h [5] 1’b0
+
+#define BU18RL82_ISR_CLEAR_ALL 0x0105			//h [0] 1’b0
+#define BU18RL82_ISR_CLLRX0_LINK_UNLOCK 0x0129	//h [1] 1’b0
+#define BU18RL82_ISR_CLLRX0_BIT_ERR 0x0129		//h [3] 1’b0
+#define BU18RL82_ISR_CLLRX0_ERR_CNT_OVF 0x0129	//h [4] 1’b0
+#define BU18RL82_ISR_CLLRX1_ERR_CNT_OVF 0x012B	//h [4] 1’b0
+#define BU18RL82_ISR_CLLRX1_LINK_UNLOCK 0x012B	//h [1] 1’b0
+#define BU18RL82_ISR_CLLRX1_BIT_ERR 0x012B		//h [3] 1’b0
+#define BU18RL82_ISR_FCCRX0_CRCERR 0x012D		//h [0] 1’b0
+#define BU18RL82_ISR_FCCRX1_CRCERR 0x012E		//h [0] 1’b0
+#define BU18RL82_ISR_BCCDES0_ERR_CRC 0x012F		//h [3] 1’b0
+#define BU18RL82_ISR_CLLRX0_CRCERR_R  0x0130	//h [0] 1’b0
+#define BU18RL82_ISR_CLLRX0_CRCERR_G 0x0130		//h [1] 1’b0
+#define BU18RL82_ISR_CLLRX0_CRCERR_B 0x0130		//h [2] 1’b0
+#define BU18RL82_ISR_CLLRX1_CRCERR_R 0x0131		//h [0] 1’b0
+#define BU18RL82_ISR_CLLRX1_CRCERR_G 0x0131		//h [1] 1’b0
+#define BU18RL82_ISR_CLLRX1_CRCERR_B 0x0131		//h [2] 1’b0
+#define BU18RL82_ISR_FS_IMG_STATUS0 0x0132		//h [0] 1’b0
+#define BU18RL82_ISR_FS_IMG_STATUS1 0x0132		//h [1] 1’b0
+#define BU18RL82_ISR_FS_IMG_STATUS2 0x0132		//h [2] 1’b0
+#define BU18RL82_ISR_FS_IMG_STATUS3 0x0132		//h [3] 1’b0
+#define BU18RL82_ISR_FS_IMG_ERR_REGION0 0x0133	//h [0] 1’b0
+#define BU18RL82_ISR_FS_IMG_ERR_REGION1 0x0133	//h [1] 1’b0
+#define BU18RL82_ISR_FS_IMG_ERR_REGION2 0x0133	//h [2] 1’b0
+#define BU18RL82_ISR_FS_IMG_ERR_REGION3 0x0133	//h [3] 1’b0
+#define BU18RL82_ISR_FS_IMG_ERR_REGION4 0x0133	//h [4] 1’b0
+#define BU18RL82_ISR_FS_IMG_ERR_REGION5 0x0133	//h [5] 1’b0
+#define BU18RL82_ISR_FS_IMG_ERR_REGION6 0x0133	//h [6] 1’b0
+#define BU18RL82_ISR_FS_IMG_ERR_REGION7 0x0133	//h [7] 1’b0
+#define BU18RL82_ISR_IO_STUCK_GPIO0 0x0134	//h [0] 1’b0
+#define BU18RL82_ISR_IO_STUCK_GPIO1 0x0134	//h [1] 1’b0
+#define BU18RL82_ISR_IO_STUCK_GPIO2 0x0134	//h [2] 1’b0
+#define BU18RL82_ISR_IO_STUCK_GPIO3 0x0134	//h [3] 1’b0
+#define BU18RL82_ISR_IO_STUCK_GPIO4 0x0134	//h [4] 1’b0
+#define BU18RL82_ISR_IO_STUCK_GPIO5 0x0134	//h [5] 1’b0
+#define BU18RL82_ISR_IO_STUCK_GPIO6 0x0134	//h [6] 1’b0
+#define BU18RL82_ISR_IO_STUCK_GPIO7 0x0134	//h [7] 1’b0
+#define BU18RL82_ISR_IO_STUCK_IRQ 0x0135	//h [1] 1’b0
+#define BU18RL82_ISR_IDS_UNSTABLE 0x0135	//h [7] 1’b0
+#define BU18RL82_ISR_I2C_A_TIMEOUT 0x0136	//h [0] 1’b0
+#define BU18RL82_ISR_I2C_A_XMIT_ERR 0x0136	//h [1] 1’b0
+#define BU18RL82_ISR_REGCRC_ERR_PAGE0 0x0137	//h [0] 1’b0
+#define BU18RL82_ISR_REGCRC_ERR_PAGE1 0x0137	//h [1] 1’b0
+#define BU18RL82_ISR_REGCRC_ERR_PAGE2 0x0137	//h [2] 1’b0
+#define BU18RL82_ISR_REGCRC_ERR_PAGE3 0x0137	//h [3] 1’b0
+#define BU18RL82_ISR_REGCRC_ERR_PAGE4 0x0137	//h [4] 1’b0
+#define BU18RL82_ISR_REGCRC_ERR_PAGE5 0x0137	//h [5] 1’b0
+#define BU18RL82_ISR_REGCRC_ERR_PAGE6 0x0137	//h [6] 1’b0
+#define BU18RL82_ISR_REGCRC_ERR_PAGE7 0x0137	//h [7] 1’b0
+#define BU18RL82_ISR_CLKDETECT_CLKIN_STOP 0x0138			//h [0] 1’b0
+#define BU18RL82_ISR_CLKDETECT_CLKIN_UNLOCK 0x0138			//h [1] 1’b0
+#define BU18RL82_ISR_CLKDETECT_OSC_STOP 0x0138				//h [4] 1’b0
+#define BU18RL82_ISR_CLKDETECT_OSC_UNLOCK 0x0138			//h [5] 1’b0
+#define BU18RL82_ISR_CLKDETECT_CLLRX0_PCLK_STOP 0x0139		//h [0] 1’b0
+#define BU18RL82_ISR_CLKDETECT_CLLRX0_PCLK_UNLOCK 0x0139	//h [1] 1’b0
+#define BU18RL82_ISR_CLKDETECT_LVDSTX0_CLK_STOP 0x0139		//h [4] 1’b0
+#define BU18RL82_ISR_CLKDETECT_LVDSTX0_CLK_UNLOCK 0x0139	//h [5] 1’b0
+#define BU18RL82_ISR_CLKDETECT_CLLRX1_PCLK_STOP 0x013A		//h [0] 1’b0
+#define BU18RL82_ISR_CLKDETECT_CLLRX1_PCLK_UNLOCK 0x013A	//h [1] 1’b0
+#define BU18RL82_ISR_CLKDETECT_LVDSTX1_CLK_STOP 0x013A		//h [4] 1’b0
+#define BU18RL82_ISR_CLKDETECT_LVDSTX1_CLK_UNLOCK 0x013A	//h [5] 1’b0
+#define BU18RL82_ISR_CLKDETECT_CLLTX0_SCLK_STOP 0x013B		//h [0] 1’b0
+#define BU18RL82_ISR_CLKDETECT_CLLTX0_SCLK_UNLOCK 0x013B	//h [1] 1’b0
+#define BU18RL82_ISR_CLKDETECT_CLLTX0_PLLREF_STOP 0x013B	//h [4] 1’b0
+#define BU18RL82_ISR_CLKDETECT_CLLTX0_PLLREF_UNLOCK 0x013B	//h [5] 1’b0
+#define BU18RL82_ISR_CLKDETECT_LVDSTX0_PLLREF_STOP 0x013C	//h [0] 1’b0
+#define BU18RL82_ISR_CLKDETECT_LVDSTX0_PLLREF_UNLOCK0x013C	//h [1] 1’b0
+#define BU18RL82_ISR_CLKDETECT_LVDSTX1_PLLREF_STOP 0x013C	//h [4] 1’b0
+#define BU18RL82_ISR_CLKDETECT_LVDSTX1_PLLREF_UNLOCK 0x013C	//h [5] 1’b0
+
+struct bu18rl82_gpio_sw_reg {
+	unsigned int reg;
+	unsigned int mask;	//2/4/6/8ma
+};
+
+struct bu18rl82_gpio_oen_reg {
+	unsigned int reg;
+	unsigned int mask;	//0:output 1:input
+};
+
+struct bu18rl82_gpio_pden_reg {
+	unsigned int reg;
+	unsigned int mask;	//0:no pulldown 1:connect pulldown
+};
+
+struct bu18rl82_gpio_id_low_reg {
+	unsigned int reg;
+	unsigned int mask;	//b2b1b0
+};
+
+struct bu18rl82_gpio_id_high_reg {
+	unsigned int reg;
+	unsigned int mask;	//b11b10b9b8b7b6b5b4b3
+};
+
+static const struct bu18rl82_gpio_sw_reg bu18rl82_gpio_sw[8] = {
+	{BU18RL82_IO_SW_GPIO0, BIT(2) | BIT(1)},
+	{BU18RL82_IO_SW_GPIO1, BIT(2) | BIT(1)},
+	{BU18RL82_IO_SW_GPIO2, BIT(2) | BIT(1)},
+	{BU18RL82_IO_SW_GPIO3, BIT(2) | BIT(1)},
+	{BU18RL82_IO_SW_GPIO4, BIT(2) | BIT(1)},
+	{BU18RL82_IO_SW_GPIO5, BIT(2) | BIT(1)},
+	{BU18RL82_IO_SW_GPIO6, BIT(2) | BIT(1)},
+	{BU18RL82_IO_SW_GPIO7, BIT(2) | BIT(1)},
+};
+
+static const struct bu18rl82_gpio_oen_reg bu18rl82_gpio_oen[8] = {
+	{BU18RL82_IO_OEN_GPIO0, BIT(3)},
+	{BU18RL82_IO_OEN_GPIO1, BIT(3)},
+	{BU18RL82_IO_OEN_GPIO2, BIT(3)},
+	{BU18RL82_IO_OEN_GPIO3, BIT(3)},
+	{BU18RL82_IO_OEN_GPIO4, BIT(3)},
+	{BU18RL82_IO_OEN_GPIO5, BIT(3)},
+	{BU18RL82_IO_OEN_GPIO6, BIT(3)},
+	{BU18RL82_IO_OEN_GPIO7, BIT(3)},
+};
+
+static const struct bu18rl82_gpio_pden_reg bu18rl82_gpio_pden[8] = {
+	{BU18RL82_IO_PDEN_GPIO0, BIT(4)},
+	{BU18RL82_IO_PDEN_GPIO1, BIT(4)},
+	{BU18RL82_IO_PDEN_GPIO2, BIT(4)},
+	{BU18RL82_IO_PDEN_GPIO3, BIT(4)},
+	{BU18RL82_IO_PDEN_GPIO4, BIT(4)},
+	{BU18RL82_IO_PDEN_GPIO5, BIT(4)},
+	{BU18RL82_IO_PDEN_GPIO6, BIT(4)},
+	{BU18RL82_IO_PDEN_GPIO7, BIT(4)},
+};
+
+static const struct bu18rl82_gpio_id_low_reg bu18rl82_gpio_id_low[8] = {
+	{BU18RL82_GPIO_SEL0_LOW, GENMASK(7, 0)},
+	{BU18RL82_GPIO_SEL1_LOW, GENMASK(7, 0)},
+	{BU18RL82_GPIO_SEL2_LOW, GENMASK(7, 0)},
+	{BU18RL82_GPIO_SEL3_LOW, GENMASK(7, 0)},
+	{BU18RL82_GPIO_SEL4_LOW, GENMASK(7, 0)},
+	{BU18RL82_GPIO_SEL5_LOW, GENMASK(7, 0)},
+	{BU18RL82_GPIO_SEL6_LOW, GENMASK(7, 0)},
+	{BU18RL82_GPIO_SEL7_LOW, GENMASK(7, 0)},
+};
+
+static const struct bu18rl82_gpio_id_high_reg bu18rl82_gpio_id_high[8] = {
+	{BU18RL82_GPIO_SEL0_HIGH, GENMASK(2, 0)},
+	{BU18RL82_GPIO_SEL1_HIGH, GENMASK(2, 0)},
+	{BU18RL82_GPIO_SEL2_HIGH, GENMASK(2, 0)},
+	{BU18RL82_GPIO_SEL3_HIGH, GENMASK(2, 0)},
+	{BU18RL82_GPIO_SEL4_HIGH, GENMASK(2, 0)},
+	{BU18RL82_GPIO_SEL5_HIGH, GENMASK(2, 0)},
+	{BU18RL82_GPIO_SEL6_HIGH, GENMASK(2, 0)},
+	{BU18RL82_GPIO_SEL7_HIGH, GENMASK(2, 0)},
+};
+
+struct bu18rl82_ien_reg {
+	unsigned int reg;
+	unsigned int ien;
+};
+
+struct bu18rl82_isr_reg {
+	unsigned int reg;
+	unsigned int isr;
+};
+
+struct bu18rl82_gpio_reg {
+	unsigned int reg;
+	unsigned int val;
+};
+
+static const struct bu18rl82_ien_reg bu18rl82_reg_ien[18] = {
+	{BU18RL82_IEN_CLLRX0_LINK_UNLOCK, BIT(1) | BIT(3) | BIT(4)},
+	{BU18RL82_IEN_CLLRX1_LINK_UNLOCK, BIT(1) | BIT(3) | BIT(4)},
+	{BU18RL82_IEN_FCCRX0_CRCERR, BIT(0)},
+	{BU18RL82_IEN_FCCRX1_CRCERR, BIT(0)},
+
+	{BU18RL82_IEN_BCCDES0_ERR_CRC, BIT(3)},
+	{BU18RL82_IEN_CLLRX0_CRCERR_R, BIT(0) | BIT(1) | BIT(2)},
+	{BU18RL82_IEN_CLLRX1_CRCERR_R, BIT(0) | BIT(1) | BIT(2)},
+
+	{BU18RL82_IEN_FS_IMG_STATUS0, BIT(0) | BIT(1) | BIT(2) | BIT(3)},
+	{BU18RL82_IEN_FS_IMG_ERR_REGION0, 0xff},
+	{BU18RL82_IEN_IO_STUCK_GPIO0, 0xff},
+	{BU18RL82_IEN_IO_STUCK_IRQ, BIT(1) | BIT(7)},
+	{BU18RL82_IEN_I2C_A_TIMEOUT, BIT(0) | BIT(1)},
+
+	{BU18RL82_IEN_REGCRC_ERR_PAGE0, 0xff},
+	{BU18RL82_IEN_CLKDETECT_CLKIN_STOP, BIT(0) | BIT(1) | BIT(4) | BIT(5)},
+	{BU18RL82_IEN_CLKDETECT_CLLRX0_PCLK_STOP, BIT(0) | BIT(1) | BIT(4) | BIT(5)},
+	{BU18RL82_IEN_CLKDETECT_CLLRX1_PCLK_STOP, BIT(0) | BIT(1) | BIT(4) | BIT(5)},
+	{BU18RL82_IEN_CLKDETECT_CLLTX0_SCLK_STOP, BIT(0) | BIT(1) | BIT(4) | BIT(5)},
+	{BU18RL82_IEN_CLKDETECT_LVDSTX0_PLLREF_STOP, BIT(0) | BIT(1) | BIT(4) | BIT(5)},
+};
+
+static const struct bu18rl82_isr_reg bu18rl82_reg_isr[18] = {
+	{BU18RL82_ISR_CLLRX0_LINK_UNLOCK, BIT(1) | BIT(3) | BIT(4)},
+	{BU18RL82_ISR_CLLRX1_LINK_UNLOCK, BIT(1) | BIT(3) | BIT(4)},
+	{BU18RL82_ISR_FCCRX0_CRCERR, BIT(0)},
+	{BU18RL82_ISR_FCCRX1_CRCERR, BIT(0)},
+
+	{BU18RL82_ISR_BCCDES0_ERR_CRC, BIT(3)},
+	{BU18RL82_ISR_CLLRX0_CRCERR_R, BIT(0) | BIT(1) | BIT(2)},
+	{BU18RL82_ISR_CLLRX1_CRCERR_R, BIT(0) | BIT(1) | BIT(2)},
+
+	{BU18RL82_ISR_FS_IMG_STATUS0, BIT(0) | BIT(1) | BIT(2) | BIT(3)},
+	{BU18RL82_ISR_FS_IMG_ERR_REGION0, 0xff},
+	{BU18RL82_ISR_IO_STUCK_GPIO0, 0xff},
+	{BU18RL82_ISR_IO_STUCK_IRQ, BIT(1) | BIT(7)},
+	{BU18RL82_ISR_I2C_A_TIMEOUT, BIT(0) | BIT(1)},
+
+	{BU18RL82_ISR_REGCRC_ERR_PAGE0, 0xff},
+	{BU18RL82_ISR_CLKDETECT_CLKIN_STOP, BIT(0) | BIT(1) | BIT(4) | BIT(5)},
+	{BU18RL82_ISR_CLKDETECT_CLLRX0_PCLK_STOP, BIT(0) | BIT(1) | BIT(4) | BIT(5)},
+	{BU18RL82_ISR_CLKDETECT_CLLRX1_PCLK_STOP, BIT(0) | BIT(1) | BIT(4) | BIT(5)},
+	{BU18RL82_ISR_CLKDETECT_CLLTX0_SCLK_STOP, BIT(0) | BIT(1) | BIT(4) | BIT(5)},
+	{BU18RL82_ISR_CLKDETECT_LVDSTX0_PLLREF_STOP, BIT(0) | BIT(1) | BIT(4) | BIT(5)},
+};
+
+#endif
diff --git a/kernel/drivers/mfd/display-serdes/rohm/rohm-bu18tl82.c b/kernel/drivers/mfd/display-serdes/rohm/rohm-bu18tl82.c
new file mode 100644
index 0000000..824ea38
--- /dev/null
+++ b/kernel/drivers/mfd/display-serdes/rohm/rohm-bu18tl82.c
@@ -0,0 +1,461 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * maxim-bu18rl82.c  --  I2C register interface access for bu18rl82 serdes chip
+ *
+ * Copyright (c) 2023-2028 Rockchip Electronics Co. Ltd.
+ *
+ * Author: luowei <lw@rock-chips.com>
+ */
+
+#include "../core.h"
+#include "rohm-bu18tl82.h"
+
+#define PINCTRL_GROUP(a, b, c) { .name = a, .pins = b, .num_pins = c}
+
+static bool bu18tl82_volatile_reg(struct device *dev, unsigned int reg)
+{
+	return true;
+}
+
+static struct regmap_config bu18tl82_regmap_config = {
+	.name = "bu18tl82",
+	.reg_bits = 16,
+	.val_bits = 8,
+	.max_register = 0x0700,
+	.volatile_reg = bu18tl82_volatile_reg,
+	.cache_type = REGCACHE_RBTREE,
+};
+
+static int BU18TL82_GPIO0_pins[] = {0};
+static int BU18TL82_GPIO1_pins[] = {1};
+static int BU18TL82_GPIO2_pins[] = {2};
+static int BU18TL82_GPIO3_pins[] = {3};
+static int BU18TL82_GPIO4_pins[] = {4};
+static int BU18TL82_GPIO5_pins[] = {5};
+static int BU18TL82_GPIO6_pins[] = {6};
+static int BU18TL82_GPIO7_pins[] = {7};
+
+#define GROUP_DESC(nm) \
+{ \
+	.name = #nm, \
+	.pins = nm ## _pins, \
+	.num_pins = ARRAY_SIZE(nm ## _pins), \
+}
+
+struct serdes_function_data {
+	u8 gpio_rx_en:1;
+	u16 gpio_id;
+};
+
+static const char *serdes_gpio_groups[] = {
+	"BU18TL82_GPIO0", "BU18TL82_GPIO1", "BU18TL82_GPIO2", "BU18TL82_GPIO3",
+	"BU18TL82_GPIO4", "BU18TL82_GPIO5", "BU18TL82_GPIO6", "BU18TL82_GPIO7",
+};
+
+/*des -> ser -> soc*/
+#define FUNCTION_DESC_GPIO_INPUT(id) \
+{ \
+	.name = "DES_GPIO"#id"_TO_SER", \
+	.group_names = serdes_gpio_groups, \
+	.num_group_names = ARRAY_SIZE(serdes_gpio_groups), \
+	.data = (void *)(const struct serdes_function_data []) { \
+		{ .gpio_rx_en = 0, .gpio_id = (id < 8) ? (id + 2) : (id + 3) } \
+	}, \
+} \
+
+/*soc -> ser -> des*/
+#define FUNCTION_DESC_GPIO_OUTPUT(id) \
+{ \
+	.name = "SER_TO_DES_GPIO"#id, \
+	.group_names = serdes_gpio_groups, \
+	.num_group_names = ARRAY_SIZE(serdes_gpio_groups), \
+	.data = (void *)(const struct serdes_function_data []) { \
+		{ .gpio_rx_en = 1, .gpio_id = (id < 8) ? (id + 2) : (id + 3) } \
+	}, \
+} \
+
+static struct pinctrl_pin_desc bu18tl82_pins_desc[] = {
+	PINCTRL_PIN(ROHM_BU18TL82_GPIO0, "BU18TL82_GPIO0"),
+	PINCTRL_PIN(ROHM_BU18TL82_GPIO1, "BU18TL82_GPIO1"),
+	PINCTRL_PIN(ROHM_BU18TL82_GPIO2, "BU18TL82_GPIO2"),
+	PINCTRL_PIN(ROHM_BU18TL82_GPIO3, "BU18TL82_GPIO3"),
+	PINCTRL_PIN(ROHM_BU18TL82_GPIO4, "BU18TL82_GPIO4"),
+	PINCTRL_PIN(ROHM_BU18TL82_GPIO5, "BU18TL82_GPIO5"),
+	PINCTRL_PIN(ROHM_BU18TL82_GPIO6, "BU18TL82_GPIO6"),
+	PINCTRL_PIN(ROHM_BU18TL82_GPIO7, "BU18TL82_GPIO7"),
+};
+
+static struct group_desc bu18tl82_groups_desc[] = {
+	GROUP_DESC(BU18TL82_GPIO0),
+	GROUP_DESC(BU18TL82_GPIO1),
+	GROUP_DESC(BU18TL82_GPIO2),
+	GROUP_DESC(BU18TL82_GPIO3),
+	GROUP_DESC(BU18TL82_GPIO4),
+	GROUP_DESC(BU18TL82_GPIO5),
+	GROUP_DESC(BU18TL82_GPIO6),
+	GROUP_DESC(BU18TL82_GPIO7),
+};
+
+static struct function_desc bu18tl82_functions_desc[] = {
+	FUNCTION_DESC_GPIO_INPUT(0),
+	FUNCTION_DESC_GPIO_INPUT(1),
+	FUNCTION_DESC_GPIO_INPUT(2),
+	FUNCTION_DESC_GPIO_INPUT(3),
+	FUNCTION_DESC_GPIO_INPUT(4),
+	FUNCTION_DESC_GPIO_INPUT(5),
+	FUNCTION_DESC_GPIO_INPUT(6),
+	FUNCTION_DESC_GPIO_INPUT(7),
+	FUNCTION_DESC_GPIO_INPUT(8),
+	FUNCTION_DESC_GPIO_INPUT(9),
+	FUNCTION_DESC_GPIO_INPUT(10),
+	FUNCTION_DESC_GPIO_INPUT(11),
+	FUNCTION_DESC_GPIO_INPUT(12),
+	FUNCTION_DESC_GPIO_INPUT(13),
+	FUNCTION_DESC_GPIO_INPUT(14),
+	FUNCTION_DESC_GPIO_INPUT(15),
+
+	FUNCTION_DESC_GPIO_OUTPUT(0),
+	FUNCTION_DESC_GPIO_OUTPUT(1),
+	FUNCTION_DESC_GPIO_OUTPUT(2),
+	FUNCTION_DESC_GPIO_OUTPUT(3),
+	FUNCTION_DESC_GPIO_OUTPUT(4),
+	FUNCTION_DESC_GPIO_OUTPUT(5),
+	FUNCTION_DESC_GPIO_OUTPUT(6),
+	FUNCTION_DESC_GPIO_OUTPUT(7),
+	FUNCTION_DESC_GPIO_OUTPUT(8),
+	FUNCTION_DESC_GPIO_OUTPUT(9),
+	FUNCTION_DESC_GPIO_OUTPUT(10),
+	FUNCTION_DESC_GPIO_OUTPUT(11),
+	FUNCTION_DESC_GPIO_OUTPUT(12),
+	FUNCTION_DESC_GPIO_OUTPUT(13),
+	FUNCTION_DESC_GPIO_OUTPUT(14),
+	FUNCTION_DESC_GPIO_OUTPUT(15),
+};
+
+static struct serdes_chip_pinctrl_info bu18tl82_pinctrl_info = {
+	.pins = bu18tl82_pins_desc,
+	.num_pins = ARRAY_SIZE(bu18tl82_pins_desc),
+	.groups = bu18tl82_groups_desc,
+	.num_groups = ARRAY_SIZE(bu18tl82_groups_desc),
+	.functions = bu18tl82_functions_desc,
+	.num_functions = ARRAY_SIZE(bu18tl82_functions_desc),
+};
+
+static void bu18tl82_bridge_swrst(struct serdes *serdes)
+{
+	struct device *dev = serdes->dev;
+	int ret;
+
+	return;
+
+	ret = serdes_reg_write(serdes, BU18TL82_REG_SWRST_INTERNAL, 0x00ef);
+	if (ret < 0)
+		dev_err(dev, "%s: failed to reset serdes 0x11 ret=%d\n", __func__, ret);
+
+	ret = serdes_reg_write(serdes, BU18TL82_REG_SWRST_MIPIRX, 0x0003);
+	if (ret < 0)
+		dev_err(dev, "%s: failed to reset serdes 0x12 ret=%d\n", __func__, ret);
+
+	msleep(20);
+
+	SERDES_DBG_CHIP("%s: %s ret=%d\n", __func__, serdes->chip_data->name, ret);
+}
+
+static void bu18tl82_enable_hwint(struct serdes *serdes, int enable)
+{
+	struct device *dev = serdes->dev;
+	int i, ret;
+
+	for (i = 0; i < ARRAY_SIZE(bu18tl82_reg_ien); i++) {
+		if (enable) {
+			ret = serdes_reg_write(serdes, bu18tl82_reg_ien[i].reg,
+					       bu18tl82_reg_ien[i].ien);
+			if (ret)
+				dev_err(dev, "reg 0x%04x write error\n", bu18tl82_reg_ien[i].reg);
+		} else {
+			ret = serdes_reg_write(serdes, bu18tl82_reg_ien[i].reg, 0);
+			if (ret)
+				dev_err(dev, "reg 0x%04x write error\n", bu18tl82_reg_ien[i].reg);
+		}
+	}
+
+	SERDES_DBG_CHIP("%s: %s enable=%d\n", __func__, serdes->chip_data->name, enable);
+}
+
+static int bu18tl82_bridge_init(struct serdes *serdes)
+{
+	if (serdes->enable_gpio) {
+		gpiod_direction_output(serdes->enable_gpio, 1);
+		msleep(20);
+	}
+
+	if (serdes->reset_gpio) {
+		gpiod_direction_output(serdes->reset_gpio, 0);
+		msleep(30);
+	} else {
+		bu18tl82_bridge_swrst(serdes);
+	}
+
+	return 0;
+}
+
+static int bu18tl82_bridge_enable(struct serdes *serdes)
+{
+	return 0;
+}
+
+static int bu18tl82_bridge_disable(struct serdes *serdes)
+{
+	return 0;
+}
+
+static int bu18tl82_bridge_get_modes(struct serdes *serdes)
+{
+	return 0;
+}
+
+static int bu18tl82_bridge_pre_enable(struct serdes *serdes)
+{
+	int ret = 0;
+
+	/* 1:enable 0:disable */
+	bu18tl82_enable_hwint(serdes, 0);
+
+	msleep(160);
+
+	SERDES_DBG_CHIP("%s: %s ret=%d\n", __func__, serdes->chip_data->name, ret);
+
+	return ret;
+}
+
+static struct serdes_chip_bridge_ops bu18tl82_bridge_ops = {
+	.init = bu18tl82_bridge_init,
+	.get_modes = bu18tl82_bridge_get_modes,
+	.pre_enable = bu18tl82_bridge_pre_enable,
+	.enable = bu18tl82_bridge_enable,
+	.disable = bu18tl82_bridge_disable,
+};
+
+static int bu18tl82_pinctrl_config_get(struct serdes *serdes,
+				       unsigned int pin, unsigned long *config)
+{
+	enum pin_config_param param = pinconf_to_config_param(*config);
+	unsigned int bu18tl82_gpio_sw_reg, bu18tl82_gpio_pden_reg, bu18tl82_gpio_oen_reg;
+	u16 arg = 0;
+
+	serdes_reg_read(serdes, bu18tl82_gpio_sw[pin].reg, &bu18tl82_gpio_sw_reg);
+	serdes_reg_read(serdes, bu18tl82_gpio_pden[pin].reg, &bu18tl82_gpio_pden_reg);
+	serdes_reg_read(serdes, bu18tl82_gpio_oen[pin].reg, &bu18tl82_gpio_oen_reg);
+
+	SERDES_DBG_CHIP("%s: serdes chip %s pin=%d param=%d\n", __func__,
+			serdes->chip_data->name, pin, param);
+
+	switch (param) {
+	case PIN_CONFIG_DRIVE_STRENGTH:
+		arg = FIELD_GET(BIT(2) | BIT(1), bu18tl82_gpio_sw_reg);
+		break;
+	case PIN_CONFIG_BIAS_PULL_DOWN:
+		arg = FIELD_GET(BIT(4), bu18tl82_gpio_pden_reg);
+		break;
+	case PIN_CONFIG_OUTPUT:
+		arg = FIELD_GET(BIT(3), bu18tl82_gpio_oen_reg);
+		break;
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	*config = pinconf_to_config_packed(param, arg);
+
+	SERDES_DBG_CHIP("%s: serdes chip %s pin=%d arg=%d\n", __func__,
+			serdes->chip_data->name, pin, arg);
+
+	return 0;
+}
+
+static int bu18tl82_pinctrl_config_set(struct serdes *serdes,
+				       unsigned int pin, unsigned long *configs,
+				       unsigned int num_configs)
+{
+	enum pin_config_param param;
+	u32 arg;
+	int i;
+
+	for (i = 0; i < num_configs; i++) {
+		param = pinconf_to_config_param(configs[i]);
+		arg = pinconf_to_config_argument(configs[i]);
+
+		switch (param) {
+		case PIN_CONFIG_DRIVE_STRENGTH:
+			serdes_set_bits(serdes, bu18tl82_gpio_sw[pin].reg,
+					bu18tl82_gpio_sw[pin].mask,
+					FIELD_PREP(BIT(2) | BIT(1), arg));
+			SERDES_DBG_CHIP("%s: serdes chip %s pin=%d i=%d drive-strength arg=0x%x\n",
+					__func__, serdes->chip_data->name, pin, i, arg);
+			break;
+		case PIN_CONFIG_BIAS_PULL_DOWN:
+			serdes_set_bits(serdes, bu18tl82_gpio_pden[pin].reg,
+					bu18tl82_gpio_pden[i].mask,
+					FIELD_PREP(BIT(4), arg));
+			SERDES_DBG_CHIP("%s: serdes chip %s pin=%d i=%d pull-down arg=0x%x\n",
+					__func__, serdes->chip_data->name, pin, i, arg);
+			break;
+
+			break;
+		case PIN_CONFIG_OUTPUT:
+			serdes_set_bits(serdes, bu18tl82_gpio_oen[pin].reg,
+					bu18tl82_gpio_oen[i].mask,
+					FIELD_PREP(BIT(3), arg));
+			SERDES_DBG_CHIP("%s: serdes chip %s pin=%d i=%d output arg=0x%x\n",
+					__func__, serdes->chip_data->name, pin, i, arg);
+			break;
+		default:
+			return -EOPNOTSUPP;
+		}
+	}
+
+	return 0;
+}
+
+static int bu18tl82_pinctrl_set_mux(struct serdes *serdes,
+				    unsigned int function, unsigned int group)
+{
+	struct serdes_pinctrl *pinctrl = serdes->pinctrl;
+	struct function_desc *func;
+	struct group_desc *grp;
+	int i, offset;
+
+	func = pinmux_generic_get_function(pinctrl->pctl, function);
+	if (!func)
+		return -EINVAL;
+
+	grp = pinctrl_generic_get_group(pinctrl->pctl, group);
+	if (!grp)
+		return -EINVAL;
+
+	SERDES_DBG_CHIP("%s: serdes chip %s func=%s data=%p group=%s data=%p, num_pin=%d\n",
+			__func__, serdes->chip_data->name,
+			func->name, func->data, grp->name, grp->data, grp->num_pins);
+
+	if (func->data) {
+		struct serdes_function_data *fdata = func->data;
+
+		for (i = 0; i < grp->num_pins; i++) {
+			offset = grp->pins[i] - pinctrl->pin_base;
+			if (offset > 7)
+				dev_err(serdes->dev, "%s gpio offset=%d too large > 7\n",
+					serdes->chip_data->name, offset);
+			else
+				SERDES_DBG_CHIP("%s: serdes chip %s gpio_id=0x%x, offset=%d\n",
+						__func__, serdes->chip_data->name,
+						fdata->gpio_id, offset);
+			serdes_set_bits(serdes, bu18tl82_gpio_oen[offset].reg,
+					bu18tl82_gpio_oen[offset].mask,
+					FIELD_PREP(BIT(3), fdata->gpio_rx_en));
+			serdes_set_bits(serdes, bu18tl82_gpio_id_low[offset].reg,
+					bu18tl82_gpio_id_low[offset].mask,
+					FIELD_PREP(GENMASK(7, 0), (fdata->gpio_id & 0xff)));
+			serdes_set_bits(serdes, bu18tl82_gpio_id_high[offset].reg,
+					bu18tl82_gpio_id_high[offset].mask,
+					FIELD_PREP(GENMASK(2, 0), ((fdata->gpio_id >> 8) & 0x7)));
+			serdes_set_bits(serdes, bu18tl82_gpio_pden[offset].reg,
+					bu18tl82_gpio_pden[offset].mask,
+					FIELD_PREP(BIT(4), 0));
+		}
+	}
+
+	return 0;
+}
+
+static struct serdes_chip_pinctrl_ops bu18tl82_pinctrl_ops = {
+	.pin_config_get = bu18tl82_pinctrl_config_get,
+	.pin_config_set = bu18tl82_pinctrl_config_set,
+	.set_mux = bu18tl82_pinctrl_set_mux,
+};
+
+static int bu18tl82_gpio_direction_input(struct serdes *serdes, int gpio)
+{
+	return 0;
+}
+
+static int bu18tl82_gpio_direction_output(struct serdes *serdes, int gpio, int value)
+{
+	return 0;
+}
+
+static int bu18tl82_gpio_get_level(struct serdes *serdes, int gpio)
+{
+	return 0;
+}
+
+static int bu18tl82_gpio_set_level(struct serdes *serdes, int gpio, int value)
+{
+	return 0;
+}
+
+static int bu18tl82_gpio_set_config(struct serdes *serdes, int gpio, unsigned long config)
+{
+	return 0;
+}
+
+static int bu18tl82_gpio_to_irq(struct serdes *serdes, int gpio)
+{
+	return 0;
+}
+
+static struct serdes_chip_gpio_ops bu18tl82_gpio_ops = {
+	.direction_input = bu18tl82_gpio_direction_input,
+	.direction_output = bu18tl82_gpio_direction_output,
+	.get_level = bu18tl82_gpio_get_level,
+	.set_level = bu18tl82_gpio_set_level,
+	.set_config = bu18tl82_gpio_set_config,
+	.to_irq = bu18tl82_gpio_to_irq,
+};
+
+static int bu18tl82_pm_suspend(struct serdes *serdes)
+{
+	return 0;
+}
+
+static int bu18tl82_pm_resume(struct serdes *serdes)
+{
+	return 0;
+}
+
+static struct serdes_chip_pm_ops bu18tl82_pm_ops = {
+	.suspend = bu18tl82_pm_suspend,
+	.resume = bu18tl82_pm_resume,
+};
+
+static int bu18tl82_irq_lock_handle(struct serdes *serdes)
+{
+	return IRQ_HANDLED;
+}
+
+static int bu18tl82_irq_err_handle(struct serdes *serdes)
+{
+	return IRQ_HANDLED;
+}
+
+static struct serdes_chip_irq_ops bu18tl82_irq_ops = {
+	.lock_handle = bu18tl82_irq_lock_handle,
+	.err_handle = bu18tl82_irq_err_handle,
+};
+
+struct serdes_chip_data serdes_bu18tl82_data = {
+	.name		= "bu18tl82",
+	.serdes_type	= TYPE_SER,
+	.serdes_id	= ROHM_ID_BU18TL82,
+	.sequence_init = 1,
+	.bridge_type	= TYPE_BRIDGE_BRIDGE,
+	.connector_type	= DRM_MODE_CONNECTOR_eDP,
+	.regmap_config	= &bu18tl82_regmap_config,
+	.pinctrl_info	= &bu18tl82_pinctrl_info,
+	.bridge_ops	= &bu18tl82_bridge_ops,
+	.pinctrl_ops	= &bu18tl82_pinctrl_ops,
+	.gpio_ops	= &bu18tl82_gpio_ops,
+	.pm_ops		= &bu18tl82_pm_ops,
+	.irq_ops	= &bu18tl82_irq_ops,
+};
+EXPORT_SYMBOL_GPL(serdes_bu18tl82_data);
+
+MODULE_LICENSE("GPL");
diff --git a/kernel/drivers/mfd/display-serdes/rohm/rohm-bu18tl82.h b/kernel/drivers/mfd/display-serdes/rohm/rohm-bu18tl82.h
new file mode 100644
index 0000000..99d7f18
--- /dev/null
+++ b/kernel/drivers/mfd/display-serdes/rohm/rohm-bu18tl82.h
@@ -0,0 +1,438 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * include/linux/mfd/serdes/gpio.h -- GPIO for different serdes chip
+ *
+ * Copyright (c) 2023-2028 Rockchip Electronics Co. Ltd.
+ *
+ * Author: luowei <lw@rock-chips.com>
+ *
+ */
+
+#ifndef __MFD_SERDES_ROHM_BU18TL82_H__
+#define __MFD_SERDES_ROHM_BU18TL82_H__
+
+#define BU18TL82_REG_SWRST_INTERNAL 0x0011
+#define BU18TL82_REG_SWRST_MIPIRX 0x0012
+
+#define BU18TL82_REG_SWRST_INTERNAL 0x0011
+#define BU18TL82_REG_SWRST_MIPIRX 0x0012
+
+#define BU18TL82_BLOCK_EN_MIPIRX0 0x0013	//h [0] 1’b0
+#define BU18TL82_BLOCK_EN_LVDSRX0 0x0013	//h [1] 1’b0
+#define BU18TL82_BLOCK_EN_CLLTX0 0x0013	//h [3] 1’b0
+#define BU18TL82_BLOCK_EN_VPLL0 0x0013	//h [4] 1’b0
+#define BU18TL82_BLOCK_EN_SSCG0 0x0013	//h [5] 1’b0
+#define BU18TL82_BLOCK_EN_MIPIRX1 0x0014	//h [0] 1’b0
+#define BU18TL82_BLOCK_EN_LVDSRX1 0x0014	//h [1] 1’b0
+#define BU18TL82_BLOCK_EN_CLLTX1 0x0014	//h [3] 1’b0
+#define BU18TL82_BLOCK_EN_VPLL1 0x0014	//h [4] 1’b0
+#define BU18TL82_BLOCK_EN_SSCG1 0x0014	//h [5] 1’b0
+
+/*gpio register for driver/direction/pull-down*/
+#define BU18TL82_IO_SW_GPIO0 0x002A	//h [2:1] 2’b00
+#define BU18TL82_IO_OEN_GPIO0 0x002A	//h [3] 1’b1
+#define BU18TL82_IO_PDEN_GPIO0 0x002A	//h [4] 1’b1
+#define BU18TL82_IO_SW_GPIO1 0x002D	//h [2:1] 2’b00
+#define BU18TL82_IO_OEN_GPIO1 0x002D	//h [3] 1’b1
+#define BU18TL82_IO_PDEN_GPIO1 0x002D	//h [4] 1’b1
+#define BU18TL82_IO_SW_GPIO2 0x0030	//h [2:1] 2’b00
+#define BU18TL82_IO_OEN_GPIO2 0x0030	//h [3] 1’b1
+#define BU18TL82_IO_PDEN_GPIO2 0x0030	//h [4] 1’b1
+#define BU18TL82_IO_SW_GPIO3 0x0033	//h [2:1] 2’b00
+#define BU18TL82_IO_OEN_GPIO3 0x0033	//h [3] 1’b1
+#define BU18TL82_IO_PDEN_GPIO3 0x0033	//h [4] 1’b1
+#define BU18TL82_IO_SW_GPIO4 0x0036	//h [2:1] 2’b00
+#define BU18TL82_IO_OEN_GPIO4 0x0036	//h [3] 1’b1
+#define BU18TL82_IO_PDEN_GPIO4 0x0036	//h [4] 1’b1
+#define BU18TL82_IO_SW_GPIO5 0x0039	//h [2:1] 2’b00
+#define BU18TL82_IO_OEN_GPIO5 0x0039	//h [3] 1’b1
+#define BU18TL82_IO_PDEN_GPIO5 0x0039	//h [4] 1’b1
+#define BU18TL82_IO_SW_GPIO6 0x003C	//h [2:1] 2’b00
+#define BU18TL82_IO_OEN_GPIO6 0x003C	//h [3] 1’b1
+#define BU18TL82_IO_PDEN_GPIO6 0x003C	//h [4] 1’b1
+#define BU18TL82_IO_SW_GPIO7 0x003F	//h [2:1] 2’b00
+#define BU18TL82_IO_OEN_GPIO7 0x003F	//h [3] 1’b1
+#define BU18TL82_IO_PDEN_GPIO7 0x003F	//h [4] 1’b1
+
+/*
+ * gpio register for define connection with des gpiox.
+ * 11bits such as 0x002c:002b=[b2..b0 b7...b0]
+ *
+ * default value:
+ * ser gpio0-->des gpio0
+ * ser gpio1-->des gpio1
+ * ser gpio2-->des gpio2
+ * ser gpio3-->des gpio3
+ */
+#define BU18TL82_GPIO_SEL0_HIGH 0x002C	//h [2:0],
+#define BU18TL82_GPIO_SEL0_LOW 0x002B	//h [7:0] 11’h002
+#define BU18TL82_GPIO_SEL1_HIGH 0x002F	//h [2:0],
+#define BU18TL82_GPIO_SEL1_LOW 0x002E	//h [7:0] 11’h003
+#define BU18TL82_GPIO_SEL2_HIGH 0x0032	//h [2:0],
+#define BU18TL82_GPIO_SEL2_LOW 0x0031	//h [7:0] 11’h004
+#define BU18TL82_GPIO_SEL3_HIGH 0x0035	//h [2:0],
+#define BU18TL82_GPIO_SEL3_LOW 0x0034	//h [7:0] 11’h005
+#define BU18TL82_GPIO_SEL4_HIGH 0x0038	//h [2:0],
+#define BU18TL82_GPIO_SEL4_LOW 0x0037	//h [7:0] 11’h006
+#define BU18TL82_GPIO_SEL5_HIGH 0x003B	//h [2:0],
+#define BU18TL82_GPIO_SEL5_LOW 0x003A	//h [7:0] 11’h007
+/*datasheet about gpio6/7 need modify*/
+#define BU18TL82_GPIO_SEL6_LOW 0x003E	//h [2:0],
+#define BU18TL82_GPIO_SEL6_HIGH 0x003D	//h [7:0] 11’h008
+#define BU18TL82_GPIO_SEL7_LOW 0x0041	//h [2:0],
+#define BU18TL82_GPIO_SEL7_HIGH 0x0040	//h [7:0] 11’h009
+
+/*gpio register for define bu18tl82 gpio pin, and gpio0 to gpio0 default*/
+#define BU18TL82_FCCTX0_SEL_GPI0 0x02A7	//h [4:0] 5’h02
+#define BU18TL82_FCCTX0_SEL_GPI1 0x02A8	//h [4:0] 5’h03
+#define BU18TL82_FCCTX0_SEL_GPI2 0x02A9	//h [4:0] 5’h04
+#define BU18TL82_FCCTX0_SEL_GPI3 0x02AA	//h [4:0] 5’h05
+#define BU18TL82_FCCTX0_SEL_GPI4 0x02AB	//h [4:0] 5’h06
+#define BU18TL82_FCCTX0_SEL_GPI5 0x02AC	//h [4:0] 5’h07
+#define BU18TL82_FCCTX0_SEL_GPI6 0x02AD	//h [4:0] 5’h08
+#define BU18TL82_FCCTX0_SEL_GPI7 0x02AE	//h [4:0] 5’h09
+#define BU18TL82_CLLTX0_SEL_GPI0 0x02AF	//h [4:0] 5’h04
+#define BU18TL82_CLLTX0_SEL_GPI1 0x02B0	//h [4:0] 5’h05
+
+#define BU18TL82_FCCTX1_SEL_GPI0 0x03A7	//h [4:0] 5’h02
+#define BU18TL82_FCCTX1_SEL_GPI1 0x03A8	//h [4:0] 5’h03
+#define BU18TL82_FCCTX1_SEL_GPI2 0x03A9	//h [4:0] 5’h04
+#define BU18TL82_FCCTX1_SEL_GPI3 0x03AA	//h [4:0] 5’h05
+#define BU18TL82_FCCTX1_SEL_GPI4 0x03AB	//h [4:0] 5’h06
+#define BU18TL82_FCCTX1_SEL_GPI5 0x03AC	//h [4:0] 5’h07
+#define BU18TL82_FCCTX1_SEL_GPI6 0x03AD	//h [4:0] 5’h08
+#define BU18TL82_FCCTX1_SEL_GPI7 0x03AE	//h [4:0] 5’h09
+#define BU18TL82_CLLTX1_SEL_GPI0 0x03AF	//h [4:0] 5’h04
+#define BU18TL82_CLLTX1_SEL_GPI1 0x03B0	//h [4:0] 5’h05
+
+/*write 1'b0 to this register to clear all isr register value8*/
+#define BU18TL82_ISR_CLEAR_ALL 0x0105	//h[0]
+
+#define BU18TL82_ISR_BCCDES0_ERR_CRC 0x0131			//h [3] 1’b0
+#define BU18TL82_ISR_BCCRX0_STATUS_NEAR_LOST 0x0131	//h[7]
+#define BU18TL82_ISR_BCCDES1_ERR_CRC 0x0132			//h [3] 1’b0
+#define BU18TL82_ISR_BCCRX1_STATUS_NEAR_LOST 0x0132	//h[7]
+#define BU18TL82_ISR_MIPIRX0_SOT_ERR 0x0133			//h[0]
+#define BU18TL82_ISR_MIPIRX0_SOT_SYNC_ERR 0x0133	//h[1]
+#define BU18TL82_ISR_MIPIRX0_EOT_SYNC_ERR 0x0133	//h[2]
+#define BU18TL82_ISR_MIPIRX0_ECC1BIT_ERR 0x0134		//h[0]
+#define BU18TL82_ISR_MIPIRX0_ECCMULT_ERR 0x0134		//h[1]
+#define BU18TL82_ISR_MIPIRX0_CRC_ERR 0x0134			//h[2]
+#define BU18TL82_ISR_MIPIRX1_SOT_ERR 0x0135			//h[0]
+#define BU18TL82_ISR_MIPIRX1_SOT_SYNC_ERR 0x0135	//h[1]
+#define BU18TL82_ISR_MIPIRX1_EOT_SYNC_ERR 0x0135	//h[2]
+#define BU18TL82_ISR_MIPIRX1_ECC1BIT_ERR 0x0136		//h[0]
+#define BU18TL82_ISR_MIPIRX1_ECCMULT_ERR 0x0136		//h[1]
+#define BU18TL82_ISR_MIPIRX1_CRC_ERR 0x0136			//h[2]
+#define BU18TL82_ISR_LVDSRX0_V_TOTAL_MAX_ERR 0x0137		//h[0]
+#define BU18TL82_ISR_LVDSRX0_V_TOTAL_MIN_ERR 0x0137		//h[1]
+#define BU18TL82_ISR_LVDSRX0_V_ACTIVE_MAX_ERR 0x0137	//h[2]
+#define BU18TL82_ISR_LVDSRX0_V_ACTIVE_MIN_ERR 0x0137	//h[3]
+#define BU18TL82_ISR_LVDSRX0_H_TOTAL_MAX_ERR 0x0137		//h[4]
+#define BU18TL82_ISR_LVDSRX0_H_TOTAL_MIN_ERR 0x0137		//h[5]
+#define BU18TL82_ISR_LVDSRX0_H_ACTIVE_MAX_ERR 0x0137	//h[6]
+#define BU18TL82_ISR_LVDSRX0_H_ACTIVE_MIN_ERR 0x0137	//h[7]
+#define BU18TL82_ISR_LVDSRX1_V_TOTAL_MAX_ERR 0x0138		//h[0]
+#define BU18TL82_ISR_LVDSRX1_V_TOTAL_MIN_ERR 0x0138		//h[1]
+#define BU18TL82_ISR_LVDSRX1_V_ACTIVE_MAX_ERR 0x0138	//h[2]
+#define BU18TL82_ISR_LVDSRX1_V_ACTIVE_MIN_ERR 0x0138	//h[3]
+#define BU18TL82_ISR_LVDSRX1_H_TOTAL_MAX_ERR 0x0138		//h[4]
+#define BU18TL82_ISR_LVDSRX1_H_TOTAL_MIN_ERR 0x0138		//h[5]
+#define BU18TL82_ISR_LVDSRX1_H_ACTIVE_MAX_ERR 0x0138	//h[6]
+#define BU18TL82_ISR_LVDSRX1_H_ACTIVE_MIN_ERR 0x0138	//h[7]
+#define BU18TL82_ISR_IO_STUCK_GPIO0 0x0139	//h[0]
+#define BU18TL82_ISR_IO_STUCK_GPIO1 0x0139	//h[1]
+#define BU18TL82_ISR_IO_STUCK_GPIO2 0x0139	//h[2]
+#define BU18TL82_ISR_IO_STUCK_GPIO3 0x0139	//h[3]
+#define BU18TL82_ISR_IO_STUCK_GPIO4 0x0139	//h[4]
+#define BU18TL82_ISR_IO_STUCK_GPIO5 0x0139	//h[5]
+#define BU18TL82_ISR_IO_STUCK_GPIO6 0x0139	//h[6]
+#define BU18TL82_ISR_IO_STUCK_GPIO7 0x0139	//h[7]
+#define BU18TL82_ISR_IO_STUCK_IRQ 0x013a	//h[1]
+#define BU18TL82_ISR_IDS_UNSTABLE 0x013a	//h [7] 1’b0
+#define BU18TL82_ISR_I2C_A_TIMEOUT 0x013b	//h [0] 1’b0
+#define BU18TL82_ISR_I2C_A_XMIT_ERR 0x013b	//h [1] 1’b0
+#define BU18TL82_ISR_I2C_B_TIMEOUT 0x013c	//h [0] 1’b0
+#define BU18TL82_ISR_I2C_B_XMIT_ERR 0x013c	//h [1] 1’b0
+#define BU18TL82_ISR_REGCRC_ERR_PAGE0 0x013d	//h[0]
+#define BU18TL82_ISR_REGCRC_ERR_PAGE1 0x013d	//h[1]
+#define BU18TL82_ISR_REGCRC_ERR_PAGE2 0x013d	//h[2]
+#define BU18TL82_ISR_REGCRC_ERR_PAGE3 0x013d	//h[3]
+#define BU18TL82_ISR_REGCRC_ERR_PAGE4 0x013d	//h[4]
+#define BU18TL82_ISR_REGCRC_ERR_PAGE5 0x013d	//h[5]
+#define BU18TL82_ISR_CLKDETECT_CLKIN0_STOP 0x013e	//h [0] 1’b0
+#define BU18TL82_ISR_CLKDETECT_CLKIN0_UNLOCK 0x013e	//h [1] 1’b0
+#define BU18TL82_ISR_CLKDETECT_OSC_STOP 0x013e		//h [4] 1’b0
+#define BU18TL82_ISR_CLKDETECT_OSC_UNLOCK 0x013e	//h [5] 1’b0
+#define BU18TL82_ISR_CLKDETECT_CLKIN1_STOP 0x013f	//h [0] 1’b0
+#define BU18TL82_ISR_CLKDETECT_CLKIN1_UNLOCK 0x013f	//h [1] 1’b0
+#define BU18TL82_ISR_CLKDETECT_LVDSRX0_STOP 0x0140		//h [0] 1’b0
+#define BU18TL82_ISR_CLKDETECT_LVDSRX0_UNLOCK 0x0140	//h [1] 1’b0
+#define BU18TL82_ISR_CLKDETECT_MIPIRX0_STOP 0x0140		//h [4] 1’b0
+#define BU18TL82_ISR_CLKDETECT_MIPIRX0_UNLOCK 0x0140	//h [5] 1’b0
+#define BU18TL82_ISR_CLKDETECT_LVDSRX1_STOP 0x0141		//h [0] 1’b0
+#define BU18TL82_ISR_CLKDETECT_LVDSRX1_UNLOCK 0x0141	//h [1] 1’b0
+#define BU18TL82_ISR_CLKDETECT_MIPIRX1_STOP 0x0141		//h [4] 1’b0
+#define BU18TL82_ISR_CLKDETECT_MIPIRX1_UNLOCK 0x0141	//h [5] 1’b0
+#define BU18TL82_ISR_CLKDETECT_CLLTX0_SCLK_STOP 0x0142		//h [0] 1’b0
+#define BU18TL82_ISR_CLKDETECT_CLLTX0_SCLK_UNLOCK 0x0142	//h [1] 1’b0
+#define BU18TL82_ISR_CLKDETECT_CLLTX0_PLLREF_STOP 0x0142	//h [4] 1’b0
+#define BU18TL82_ISR_CLKDETECT_CLLTX0_PLLREF_UNLOCK 0x0142	//h [5] 1’b0
+#define BU18TL82_ISR_CLKDETECT_CLLTX1_SCLK_STOP 0x0143		//h [0] 1’b0
+#define BU18TL82_ISR_CLKDETECT_CLLTX1_SCLK_UNLOCK 0x0143	//h [1] 1’b0
+#define BU18TL82_ISR_CLKDETECT_CLLTX1_PLLREF_STOP 0x0143	//h [4] 1’b0
+#define BU18TL82_ISR_CLKDETECT_CLLTX1_PLLREF_UNLOCK 0x0143	//h [5] 1’b0
+#define BU18TL82_ISR_STATUS_RX0_ISR00 0x0149	//h [0] 1’b0
+#define BU18TL82_ISR_STATUS_RX0_ISR01 0x0149	//h [1] 1’b0
+#define BU18TL82_ISR_STATUS_RX0_ISR02 0x0149	//h [2] 1’b0
+#define BU18TL82_ISR_STATUS_RX0_ISR03 0x0149	//h [3] 1’b0
+#define BU18TL82_ISR_STATUS_RX0_ISR04 0x0149	//h [4] 1’b0
+#define BU18TL82_ISR_STATUS_RX0_ISR05 0x0149	//h [5] 1’b0
+#define BU18TL82_ISR_STATUS_RX0_ISR06 0x0149	//h [6] 1’b0
+#define BU18TL82_ISR_STATUS_RX0_ISR07 0x0149	//h [7] 1’b0
+#define BU18TL82_ISR_STATUS_RX0_ISR08 0x014a	//h [0] 1’b0
+#define BU18TL82_ISR_STATUS_RX0_ISR09 0x014a	//h [1] 1’b0
+#define BU18TL82_ISR_STATUS_RX0_ISR10 0x014a	//h [2] 1’b0
+
+#define BU18TL82_IEN_BCCDES0_ERR_CRC 0x0109			//h [3] 1’b0
+#define BU18TL82_IEN_BCCRX0_STATUS_NEAR_LOST 0x0109	//h[7]
+#define BU18TL82_IEN_BCCDES1_ERR_CRC 0x010A			//h [3] 1’b0
+#define BU18TL82_IEN_BCCRX1_STATUS_NEAR_LOST 0x010A	//h[7]
+#define BU18TL82_IEN_MIPIRX0_SOT_ERR 0x010B			//h[0]
+#define BU18TL82_IEN_MIPIRX0_SOT_SYNC_ERR 0x010B	//h[1]
+#define BU18TL82_IEN_MIPIRX0_EOT_SYNC_ERR 0x010B	//h[2]
+#define BU18TL82_IEN_MIPIRX0_ECC1BIT_ERR 0x010C		//h[0]
+#define BU18TL82_IEN_MIPIRX0_ECCMULT_ERR 0x010C		//h[1]
+#define BU18TL82_IEN_MIPIRX0_CRC_ERR 0x010C			//h[2]
+#define BU18TL82_IEN_MIPIRX1_SOT_ERR 0x010D			//h[0]
+#define BU18TL82_IEN_MIPIRX1_SOT_SYNC_ERR 0x010D	//h[1]
+#define BU18TL82_IEN_MIPIRX1_EOT_SYNC_ERR 0x010D	//h[2]
+#define BU18TL82_IEN_MIPIRX1_ECC1BIT_ERR 0x010E		//h[0]
+#define BU18TL82_IEN_MIPIRX1_ECCMULT_ERR 0x010E		//h[1]
+#define BU18TL82_IEN_MIPIRX1_CRC_ERR 0x010E			//h[2]
+#define BU18TL82_IEN_LVDSRX0_V_TOTAL_MAX_ERR 0x010F		//h[0]
+#define BU18TL82_IEN_LVDSRX0_V_TOTAL_MIN_ERR 0x010F		//h[1]
+#define BU18TL82_IEN_LVDSRX0_V_ACTIVE_MAX_ERR 0x010F	//h[2]
+#define BU18TL82_IEN_LVDSRX0_V_ACTIVE_MIN_ERR 0x010F	//h[3]
+#define BU18TL82_IEN_LVDSRX0_H_TOTAL_MAX_ERR 0x010F		//h[4]
+#define BU18TL82_IEN_LVDSRX0_H_TOTAL_MIN_ERR 0x010F		//h[5]
+#define BU18TL82_IEN_LVDSRX0_H_ACTIVE_MAX_ERR 0x010F	//h[6]
+#define BU18TL82_IEN_LVDSRX0_H_ACTIVE_MIN_ERR 0x010F	//h[7]
+#define BU18TL82_IEN_LVDSRX1_V_TOTAL_MAX_ERR 0x0110		//h[0]
+#define BU18TL82_IEN_LVDSRX1_V_TOTAL_MIN_ERR 0x0110		//h[1]
+#define BU18TL82_IEN_LVDSRX1_V_ACTIVE_MAX_ERR 0x0110	//h[2]
+#define BU18TL82_IEN_LVDSRX1_V_ACTIVE_MIN_ERR 0x0110	//h[3]
+#define BU18TL82_IEN_LVDSRX1_H_TOTAL_MAX_ERR 0x0110		//h[4]
+#define BU18TL82_IEN_LVDSRX1_H_TOTAL_MIN_ERR 0x0110		//h[5]
+#define BU18TL82_IEN_LVDSRX1_H_ACTIVE_MAX_ERR 0x0110	//h[6]
+#define BU18TL82_IEN_LVDSRX1_H_ACTIVE_MIN_ERR 0x0110	//h[7]
+#define BU18TL82_IEN_IO_STUCK_GPIO0 0x0111	//h[0]
+#define BU18TL82_IEN_IO_STUCK_GPIO1 0x0111	//h[1]
+#define BU18TL82_IEN_IO_STUCK_GPIO2 0x0111	//h[2]
+#define BU18TL82_IEN_IO_STUCK_GPIO3 0x0111	//h[3]
+#define BU18TL82_IEN_IO_STUCK_GPIO4 0x0111	//h[4]
+#define BU18TL82_IEN_IO_STUCK_GPIO5 0x0111	//h[5]
+#define BU18TL82_IEN_IO_STUCK_GPIO6 0x0111	//h[6]
+#define BU18TL82_IEN_IO_STUCK_GPIO7 0x0111	//h[7]
+#define BU18TL82_IEN_IO_STUCK_IRQ 0x0112	//h[1]
+#define BU18TL82_IEN_IDS_UNSTABLE 0x0112	//h [7] 1’b0
+#define BU18TL82_IEN_I2C_A_TIMEOUT 0x0113	//h [0] 1’b0
+#define BU18TL82_IEN_I2C_A_XMIT_ERR 0x0113	//h [1] 1’b0
+#define BU18TL82_IEN_I2C_B_TIMEOUT 0x0114	//h [0] 1’b0
+#define BU18TL82_IEN_I2C_B_XMIT_ERR 0x0114	//h [1] 1’b0
+#define BU18TL82_IEN_REGCRC_ERR_PAGE0 0x0115	//h[0]
+#define BU18TL82_IEN_REGCRC_ERR_PAGE1 0x0115	//h[1]
+#define BU18TL82_IEN_REGCRC_ERR_PAGE2 0x0115	//h[2]
+#define BU18TL82_IEN_REGCRC_ERR_PAGE3 0x0115	//h[3]
+#define BU18TL82_IEN_REGCRC_ERR_PAGE4 0x0115	//h[4]
+#define BU18TL82_IEN_REGCRC_ERR_PAGE5 0x0115	//h[5]
+#define BU18TL82_IEN_CLKDETECT_CLKIN0_STOP 0x0116	//h [0] 1’b0
+#define BU18TL82_IEN_CLKDETECT_CLKIN0_UNLOCK 0x0116	//h [1] 1’b0
+#define BU18TL82_IEN_CLKDETECT_OSC_STOP 0x0116		//h [4] 1’b0
+#define BU18TL82_IEN_CLKDETECT_OSC_UNLOCK 0x0116	//h [5] 1’b0
+#define BU18TL82_IEN_CLKDETECT_CLKIN1_STOP 0x0117	//h [0] 1’b0
+#define BU18TL82_IEN_CLKDETECT_CLKIN1_UNLOCK 0x0117	//h [1] 1’b0
+#define BU18TL82_IEN_CLKDETECT_LVDSRX0_STOP 0x0118		//h [0] 1’b0
+#define BU18TL82_IEN_CLKDETECT_LVDSRX0_UNLOCK 0x0118	//h [1] 1’b0
+#define BU18TL82_IEN_CLKDETECT_MIPIRX0_STOP 0x0118		//h [4] 1’b0
+#define BU18TL82_IEN_CLKDETECT_MIPIRX0_UNLOCK 0x0118	//h [5] 1’b0
+#define BU18TL82_IEN_CLKDETECT_LVDSRX1_STOP 0x0119		//h [0] 1’b0
+#define BU18TL82_IEN_CLKDETECT_LVDSRX1_UNLOCK 0x0119	//h [1] 1’b0
+#define BU18TL82_IEN_CLKDETECT_MIPIRX1_STOP 0x0119		//h [4] 1’b0
+#define BU18TL82_IEN_CLKDETECT_MIPIRX1_UNLOCK 0x0119	//h [5] 1’b0
+#define BU18TL82_IEN_CLKDETECT_CLLTX0_SCLK_STOP 0x011A		//h [0] 1’b0
+#define BU18TL82_IEN_CLKDETECT_CLLTX0_SCLK_UNLOCK 0x011A	//h [1] 1’b0
+#define BU18TL82_IEN_CLKDETECT_CLLTX0_PLLREF_STOP 0x011A	//h [4] 1’b0
+#define BU18TL82_IEN_CLKDETECT_CLLTX0_PLLREF_UNLOCK 0x011A	//h [5] 1’b0
+#define BU18TL82_IEN_CLKDETECT_CLLTX1_SCLK_STOP 0x011B		//h [0] 1’b0
+#define BU18TL82_IEN_CLKDETECT_CLLTX1_SCLK_UNLOCK 0x011B	//h [1] 1’b0
+#define BU18TL82_IEN_CLKDETECT_CLLTX1_PLLREF_STOP 0x011B	//h [4] 1’b0
+#define BU18TL82_IEN_CLKDETECT_CLLTX1_PLLREF_UNLOCK 0x011B	//h [5] 1’b0
+#define BU18TL82_IEN_STATUS_RX0_ISR00 0x0121	//h [0] 1’b0
+#define BU18TL82_IEN_STATUS_RX0_ISR01 0x0121	//h [1] 1’b0
+#define BU18TL82_IEN_STATUS_RX0_ISR02 0x0121	//h [2] 1’b0
+#define BU18TL82_IEN_STATUS_RX0_ISR03 0x0121	//h [3] 1’b0
+#define BU18TL82_IEN_STATUS_RX0_ISR04 0x0121	//h [4] 1’b0
+#define BU18TL82_IEN_STATUS_RX0_ISR05 0x0121	//h [5] 1’b0
+#define BU18TL82_IEN_STATUS_RX0_ISR06 0x0121	//h [6] 1’b0
+#define BU18TL82_IEN_STATUS_RX0_ISR07 0x0121	//h [7] 1’b0
+#define BU18TL82_IEN_STATUS_RX0_ISR08 0x0122	//h [0] 1’b0
+#define BU18TL82_IEN_STATUS_RX0_ISR09 0x0122	//h [1] 1’b0
+#define BU18TL82_IEN_STATUS_RX0_ISR10 0x0122	//h [2] 1’b0
+
+
+struct bu18tl82_gpio_sw_reg {
+	unsigned int reg;
+	unsigned int mask;	//2/4/6/8ma
+};
+
+struct bu18tl82_gpio_oen_reg {
+	unsigned int reg;
+	unsigned int mask;	//0:output 1:input
+};
+
+struct bu18tl82_gpio_pden_reg {
+	unsigned int reg;
+	unsigned int mask;	//0:no pulldown 1:connect pulldown
+};
+
+struct bu18tl82_gpio_id_low_reg {
+	unsigned int reg;
+	unsigned int mask;	//b2b1b0
+};
+
+struct bu18tl82_gpio_id_high_reg {
+	unsigned int reg;
+	unsigned int mask;	//b11b10b9b8b7b6b5b4b3
+};
+
+static const struct bu18tl82_gpio_sw_reg bu18tl82_gpio_sw[8] = {
+	{BU18TL82_IO_SW_GPIO0, BIT(2) | BIT(1)},
+	{BU18TL82_IO_SW_GPIO1, BIT(2) | BIT(1)},
+	{BU18TL82_IO_SW_GPIO2, BIT(2) | BIT(1)},
+	{BU18TL82_IO_SW_GPIO3, BIT(2) | BIT(1)},
+	{BU18TL82_IO_SW_GPIO4, BIT(2) | BIT(1)},
+	{BU18TL82_IO_SW_GPIO5, BIT(2) | BIT(1)},
+	{BU18TL82_IO_SW_GPIO6, BIT(2) | BIT(1)},
+	{BU18TL82_IO_SW_GPIO7, BIT(2) | BIT(1)},
+};
+
+static const struct bu18tl82_gpio_oen_reg bu18tl82_gpio_oen[8] = {
+	{BU18TL82_IO_OEN_GPIO0, BIT(3)},
+	{BU18TL82_IO_OEN_GPIO1, BIT(3)},
+	{BU18TL82_IO_OEN_GPIO2, BIT(3)},
+	{BU18TL82_IO_OEN_GPIO3, BIT(3)},
+	{BU18TL82_IO_OEN_GPIO4, BIT(3)},
+	{BU18TL82_IO_OEN_GPIO5, BIT(3)},
+	{BU18TL82_IO_OEN_GPIO6, BIT(3)},
+	{BU18TL82_IO_OEN_GPIO7, BIT(3)},
+};
+
+static const struct bu18tl82_gpio_pden_reg bu18tl82_gpio_pden[8] = {
+	{BU18TL82_IO_PDEN_GPIO0, BIT(4)},
+	{BU18TL82_IO_PDEN_GPIO1, BIT(4)},
+	{BU18TL82_IO_PDEN_GPIO2, BIT(4)},
+	{BU18TL82_IO_PDEN_GPIO3, BIT(4)},
+	{BU18TL82_IO_PDEN_GPIO4, BIT(4)},
+	{BU18TL82_IO_PDEN_GPIO5, BIT(4)},
+	{BU18TL82_IO_PDEN_GPIO6, BIT(4)},
+	{BU18TL82_IO_PDEN_GPIO7, BIT(4)},
+};
+
+static const struct bu18tl82_gpio_id_low_reg bu18tl82_gpio_id_low[8] = {
+	{BU18TL82_GPIO_SEL0_LOW, GENMASK(7, 0)},
+	{BU18TL82_GPIO_SEL1_LOW, GENMASK(7, 0)},
+	{BU18TL82_GPIO_SEL2_LOW, GENMASK(7, 0)},
+	{BU18TL82_GPIO_SEL3_LOW, GENMASK(7, 0)},
+	{BU18TL82_GPIO_SEL4_LOW, GENMASK(7, 0)},
+	{BU18TL82_GPIO_SEL5_LOW, GENMASK(7, 0)},
+	{BU18TL82_GPIO_SEL6_LOW, GENMASK(7, 0)},
+	{BU18TL82_GPIO_SEL7_LOW, GENMASK(7, 0)},
+};
+
+static const struct bu18tl82_gpio_id_high_reg bu18tl82_gpio_id_high[8] = {
+	{BU18TL82_GPIO_SEL0_HIGH, GENMASK(2, 0)},
+	{BU18TL82_GPIO_SEL1_HIGH, GENMASK(2, 0)},
+	{BU18TL82_GPIO_SEL2_HIGH, GENMASK(2, 0)},
+	{BU18TL82_GPIO_SEL3_HIGH, GENMASK(2, 0)},
+	{BU18TL82_GPIO_SEL4_HIGH, GENMASK(2, 0)},
+	{BU18TL82_GPIO_SEL5_HIGH, GENMASK(2, 0)},
+	{BU18TL82_GPIO_SEL6_HIGH, GENMASK(2, 0)},
+	{BU18TL82_GPIO_SEL7_HIGH, GENMASK(2, 0)},
+};
+
+struct bu18tl82_ien_reg {
+	unsigned int reg;
+	unsigned int ien;
+};
+
+struct bu18tl82_isr_reg {
+	unsigned int reg;
+	unsigned int isr;
+};
+
+static const struct bu18tl82_ien_reg bu18tl82_reg_ien[21] = {
+	{BU18TL82_IEN_BCCRX0_STATUS_NEAR_LOST, BIT(3) | BIT(7)},
+	{BU18TL82_IEN_BCCRX1_STATUS_NEAR_LOST, BIT(3) | BIT(7)},
+
+	{BU18TL82_IEN_MIPIRX0_SOT_ERR, BIT(0) | BIT(0) | BIT(2)},
+	{BU18TL82_IEN_MIPIRX0_ECC1BIT_ERR, BIT(0) | BIT(0) | BIT(2)},
+
+	{BU18TL82_IEN_MIPIRX1_SOT_ERR, BIT(0) | BIT(0) | BIT(2)},
+	{BU18TL82_IEN_MIPIRX1_ECC1BIT_ERR, BIT(0) | BIT(0) | BIT(2)},
+
+	{BU18TL82_IEN_LVDSRX0_V_TOTAL_MAX_ERR, 0XFF},
+	{BU18TL82_IEN_LVDSRX1_V_TOTAL_MAX_ERR, 0XFF},
+
+	{BU18TL82_IEN_IO_STUCK_GPIO0, 0XFF},
+	{BU18TL82_IEN_IO_STUCK_IRQ, BIT(1) | BIT(7)},
+	{BU18TL82_IEN_I2C_A_TIMEOUT, BIT(0) | BIT(1)},
+	{BU18TL82_IEN_I2C_B_TIMEOUT, BIT(0) | BIT(1)},
+
+	{BU18TL82_IEN_REGCRC_ERR_PAGE0, 0x3F},
+
+	{BU18TL82_IEN_CLKDETECT_CLKIN0_STOP, BIT(0) | BIT(1) | BIT(4) | BIT(5)},
+	{BU18TL82_IEN_CLKDETECT_CLKIN1_STOP, BIT(0) | BIT(1)},
+
+	{BU18TL82_IEN_CLKDETECT_LVDSRX0_STOP, BIT(0) | BIT(1) | BIT(4) | BIT(5)},
+	{BU18TL82_IEN_CLKDETECT_LVDSRX1_STOP, BIT(0) | BIT(1) | BIT(4) | BIT(5)},
+	{BU18TL82_IEN_CLKDETECT_CLLTX0_SCLK_STOP, BIT(0) | BIT(1) | BIT(4) | BIT(5)},
+	{BU18TL82_IEN_CLKDETECT_CLLTX1_SCLK_STOP, BIT(0) | BIT(1) | BIT(4) | BIT(5)},
+
+	{BU18TL82_IEN_STATUS_RX0_ISR00, 0xff},
+	{BU18TL82_IEN_STATUS_RX0_ISR08, BIT(0) | BIT(1) | BIT(2)},
+};
+
+static const struct bu18tl82_isr_reg bu18tl82_reg_isr[21] = {
+	{BU18TL82_ISR_BCCRX0_STATUS_NEAR_LOST, BIT(3) | BIT(7)},
+	{BU18TL82_ISR_BCCRX1_STATUS_NEAR_LOST, BIT(3) | BIT(7)},
+
+	{BU18TL82_ISR_MIPIRX0_SOT_ERR, BIT(0) | BIT(0) | BIT(2)},
+	{BU18TL82_ISR_MIPIRX0_ECC1BIT_ERR, BIT(0) | BIT(0) | BIT(2)},
+
+	{BU18TL82_ISR_MIPIRX1_SOT_ERR, BIT(0) | BIT(0) | BIT(2)},
+	{BU18TL82_ISR_MIPIRX1_ECC1BIT_ERR, BIT(0) | BIT(0) | BIT(2)},
+
+	{BU18TL82_ISR_LVDSRX0_V_TOTAL_MAX_ERR, 0XFF},
+	{BU18TL82_ISR_LVDSRX1_V_TOTAL_MAX_ERR, 0XFF},
+
+	{BU18TL82_ISR_IO_STUCK_GPIO0, 0XFF},
+	{BU18TL82_ISR_IO_STUCK_IRQ, BIT(1) | BIT(7)},
+	{BU18TL82_ISR_I2C_A_TIMEOUT, BIT(0) | BIT(1)},
+	{BU18TL82_ISR_I2C_B_TIMEOUT, BIT(0) | BIT(1)},
+
+	{BU18TL82_ISR_REGCRC_ERR_PAGE0, 0x3F},
+
+	{BU18TL82_ISR_CLKDETECT_CLKIN0_STOP, BIT(0) | BIT(1) | BIT(4) | BIT(5)},
+	{BU18TL82_ISR_CLKDETECT_CLKIN1_STOP, BIT(0) | BIT(1)},
+
+	{BU18TL82_ISR_CLKDETECT_LVDSRX0_STOP, BIT(0) | BIT(1) | BIT(4) | BIT(5)},
+	{BU18TL82_ISR_CLKDETECT_LVDSRX1_STOP, BIT(0) | BIT(1) | BIT(4) | BIT(5)},
+	{BU18TL82_ISR_CLKDETECT_CLLTX0_SCLK_STOP, BIT(0) | BIT(1) | BIT(4) | BIT(5)},
+	{BU18TL82_ISR_CLKDETECT_CLLTX1_SCLK_STOP, BIT(0) | BIT(1) | BIT(4) | BIT(5)},
+
+	{BU18TL82_ISR_STATUS_RX0_ISR00, 0xff},
+	{BU18TL82_ISR_STATUS_RX0_ISR08, BIT(0) | BIT(1) | BIT(2)},
+};
+
+#endif
diff --git a/kernel/drivers/mfd/display-serdes/serdes-bridge.c b/kernel/drivers/mfd/display-serdes/serdes-bridge.c
new file mode 100644
index 0000000..235d130
--- /dev/null
+++ b/kernel/drivers/mfd/display-serdes/serdes-bridge.c
@@ -0,0 +1,354 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * serdes-bridge.c  --  drm bridge access for different serdes chips
+ *
+ * Copyright (c) 2023-2028 Rockchip Electronics Co. Ltd.
+ *
+ * Author: luowei <lw@rock-chips.com>
+ */
+
+#include "core.h"
+
+static struct serdes_bridge *to_serdes_bridge(struct drm_bridge *bridge)
+{
+	return container_of(bridge, struct serdes_bridge, base_bridge);
+}
+
+static struct mipi_dsi_device *serdes_attach_dsi(struct serdes_bridge *serdes_bridge,
+						 struct device_node *dsi_node)
+{
+	struct mipi_dsi_device_info info = { "serdes", 0, NULL };
+	struct serdes *serdes = serdes_bridge->parent;
+	struct mipi_dsi_device *dsi;
+	struct mipi_dsi_host *host;
+	int ret;
+
+	if (serdes->chip_data->name)
+		memcpy(&info.type, serdes->chip_data->name, ARRAY_SIZE(info.type));
+
+	SERDES_DBG_MFD("%s: type=%s, name=%s\n", __func__,
+		       info.type, serdes->chip_data->name);
+
+	host = of_find_mipi_dsi_host_by_node(dsi_node);
+	if (!host) {
+		dev_err(serdes_bridge->dev, "failed to find serdes dsi host\n");
+		return ERR_PTR(-EPROBE_DEFER);
+	}
+
+	dsi = mipi_dsi_device_register_full(host, &info);
+	if (IS_ERR(dsi)) {
+		dev_err(serdes_bridge->dev, "failed to create serdes dsi device\n");
+		return dsi;
+	}
+
+	dsi->lanes = 4;
+	dsi->format = MIPI_DSI_FMT_RGB888;
+
+	if (serdes->chip_data->name) {
+		if ((!strcmp(serdes->chip_data->name, "bu18tl82")) ||
+		    (!strcmp(serdes->chip_data->name, "bu18rl82"))) {
+			dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
+					  MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_EOT_PACKET;
+			SERDES_DBG_MFD("%s: dsi mode MIPI_DSI_MODE_VIDEO_BURST 0x%lx\n",
+				       __func__, dsi->mode_flags);
+		}
+	} else {
+		dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE;
+		SERDES_DBG_MFD("%s: dsi mode MIPI_DSI_MODE_VIDEO_SYNC_PULSE 0x%lx\n",
+			       __func__, dsi->mode_flags);
+	}
+
+	ret = mipi_dsi_attach(dsi);
+	if (ret < 0) {
+		dev_err(serdes_bridge->dev, "failed to attach serdes dsi to host\n");
+		mipi_dsi_device_unregister(dsi);
+		return ERR_PTR(ret);
+	}
+
+	return dsi;
+}
+
+static int serdes_bridge_attach(struct drm_bridge *bridge,
+				enum drm_bridge_attach_flags flags)
+{
+	struct serdes_bridge *serdes_bridge = to_serdes_bridge(bridge);
+	struct serdes *serdes = serdes_bridge->parent;
+	int ret = 0;
+
+	ret = drm_of_find_panel_or_bridge(bridge->of_node, 1, -1,
+					  &serdes_bridge->panel, &serdes_bridge->next_bridge);
+	if (ret) {
+		dev_err(serdes_bridge->dev->parent, "failed to find serdes bridge, ret=%d\n", ret);
+		return ret;
+	}
+
+	if (serdes_bridge->sel_mipi) {
+		dev_info(serdes_bridge->dev->parent, "serdes sel_mipi %d\n",
+			 serdes_bridge->sel_mipi);
+		/* Attach primary DSI */
+		serdes_bridge->dsi = serdes_attach_dsi(serdes_bridge, serdes_bridge->dsi_node);
+		if (IS_ERR(serdes_bridge->dsi))
+			return PTR_ERR(serdes_bridge->dsi);
+	}
+
+	if (serdes_bridge->next_bridge) {
+		ret = drm_bridge_attach(bridge->encoder, serdes_bridge->next_bridge,
+					bridge, flags);
+		if (ret) {
+			if (serdes_bridge->sel_mipi)
+				mipi_dsi_device_unregister(serdes_bridge->dsi);
+
+			dev_err(serdes_bridge->dev->parent,
+				"failed to attach bridge, ret=%d\n", ret);
+			return ret;
+		}
+	}
+
+	if (serdes->chip_data->bridge_ops->attach)
+		ret = serdes->chip_data->bridge_ops->attach(serdes);
+
+	SERDES_DBG_MFD("%s: ret=%d\n", __func__, ret);
+
+	return ret;
+}
+
+static void serdes_bridge_detach(struct drm_bridge *bridge)
+{
+	struct serdes_bridge *serdes_bridge = to_serdes_bridge(bridge);
+
+	if (serdes_bridge->sel_mipi) {
+		mipi_dsi_detach(serdes_bridge->dsi);
+		mipi_dsi_device_unregister(serdes_bridge->dsi);
+	}
+
+	SERDES_DBG_MFD("%s\n", __func__);
+}
+
+static void serdes_bridge_disable(struct drm_bridge *bridge)
+{
+	struct serdes_bridge *serdes_bridge = to_serdes_bridge(bridge);
+	struct serdes *serdes = serdes_bridge->parent;
+	int ret = 0;
+
+	if (serdes_bridge->panel)
+		drm_panel_disable(serdes_bridge->panel);
+
+	if (serdes->chip_data->bridge_ops->disable)
+		ret = serdes->chip_data->bridge_ops->disable(serdes);
+
+	extcon_set_state_sync(serdes->extcon, EXTCON_JACK_VIDEO_OUT, false);
+
+	SERDES_DBG_MFD("%s: ret=%d\n", __func__, ret);
+}
+
+static void serdes_bridge_post_disable(struct drm_bridge *bridge)
+{
+	struct serdes_bridge *serdes_bridge = to_serdes_bridge(bridge);
+	struct serdes *serdes = serdes_bridge->parent;
+	int ret = 0;
+
+	serdes_set_pinctrl_sleep(serdes);
+
+	if (serdes_bridge->panel)
+		ret = drm_panel_unprepare(serdes_bridge->panel);
+
+	if (serdes->chip_data->bridge_ops->post_disable)
+		ret = serdes->chip_data->bridge_ops->post_disable(serdes);
+
+	SERDES_DBG_MFD("%s: ret=%d\n", __func__, ret);
+}
+
+static void serdes_bridge_pre_enable(struct drm_bridge *bridge)
+{
+	struct serdes_bridge *serdes_bridge = to_serdes_bridge(bridge);
+	struct serdes *serdes = serdes_bridge->parent;
+	int ret = 0;
+
+	if (serdes->chip_data->bridge_ops->init)
+		ret = serdes->chip_data->bridge_ops->init(serdes);
+
+	if (serdes->chip_data->serdes_type == TYPE_DES)
+		serdes_i2c_set_sequence(serdes);
+
+	if (serdes_bridge->panel)
+		ret = drm_panel_prepare(serdes_bridge->panel);
+
+	if (serdes->chip_data->bridge_ops->pre_enable)
+		ret = serdes->chip_data->bridge_ops->pre_enable(serdes);
+
+	serdes_set_pinctrl_default(serdes);
+
+	SERDES_DBG_MFD("%s: %s ret=%d\n", __func__, dev_name(serdes->dev), ret);
+}
+
+static void serdes_bridge_enable(struct drm_bridge *bridge)
+{
+	struct serdes_bridge *serdes_bridge = to_serdes_bridge(bridge);
+	struct serdes *serdes = serdes_bridge->parent;
+	int ret = 0;
+
+	if (serdes_bridge->panel)
+		ret = drm_panel_enable(serdes_bridge->panel);
+
+	if (serdes->chip_data->bridge_ops->enable)
+		ret = serdes->chip_data->bridge_ops->enable(serdes);
+
+	if (!ret) {
+		extcon_set_state_sync(serdes->extcon, EXTCON_JACK_VIDEO_OUT, true);
+		SERDES_DBG_MFD("%s: extcon is true\n", __func__);
+	}
+
+	SERDES_DBG_MFD("%s: %s ret=%d\n", __func__, dev_name(serdes->dev), ret);
+}
+
+static enum drm_connector_status
+serdes_bridge_detect(struct drm_bridge *bridge)
+{
+	struct serdes_bridge *serdes_bridge = to_serdes_bridge(bridge);
+	struct serdes *serdes = serdes_bridge->parent;
+	enum drm_connector_status status = connector_status_connected;
+
+	if (serdes->chip_data->bridge_ops->detect)
+		status = serdes->chip_data->bridge_ops->detect(serdes);
+
+	return status;
+}
+
+static int serdes_bridge_get_modes(struct drm_bridge *bridge,
+				   struct drm_connector *connector)
+{
+	struct serdes_bridge *serdes_bridge = to_serdes_bridge(bridge);
+	struct serdes *serdes = serdes_bridge->parent;
+	int ret = 0;
+
+	if (serdes->chip_data->bridge_ops->get_modes)
+		ret = serdes->chip_data->bridge_ops->get_modes(serdes);
+
+	if (serdes_bridge->next_bridge)
+		ret = drm_bridge_get_modes(serdes_bridge->next_bridge, connector);
+
+	if (serdes_bridge->panel)
+		ret = drm_panel_get_modes(serdes_bridge->panel, connector);
+
+	SERDES_DBG_MFD("%s:name=%s, type=%d\n", __func__,
+		       serdes->chip_data->name, serdes->type);
+
+	return ret;
+}
+
+static const struct drm_bridge_funcs serdes_bridge_funcs = {
+	.attach = serdes_bridge_attach,
+	.detach = serdes_bridge_detach,
+	.disable = serdes_bridge_disable,
+	.post_disable = serdes_bridge_post_disable,
+	.pre_enable = serdes_bridge_pre_enable,
+	.enable = serdes_bridge_enable,
+	.detect = serdes_bridge_detect,
+	.get_modes = serdes_bridge_get_modes,
+	.atomic_get_input_bus_fmts = drm_atomic_helper_bridge_propagate_bus_fmt,
+	.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
+	.atomic_reset = drm_atomic_helper_bridge_reset,
+};
+
+static int serdes_bridge_probe(struct platform_device *pdev)
+{
+	struct serdes *serdes = dev_get_drvdata(pdev->dev.parent);
+	struct device *dev = &pdev->dev;
+	struct serdes_bridge *serdes_bridge;
+
+	if (!serdes->dev)
+		return -1;
+
+	serdes_bridge = devm_kzalloc(dev, sizeof(*serdes_bridge), GFP_KERNEL);
+	if (!serdes_bridge)
+		return -ENOMEM;
+
+	serdes->serdes_bridge = serdes_bridge;
+	serdes_bridge->dev = dev;
+	serdes_bridge->parent = dev_get_drvdata(dev->parent);
+	platform_set_drvdata(pdev, serdes_bridge);
+	serdes_bridge->regmap = dev_get_regmap(dev->parent, NULL);
+	if (!serdes_bridge->regmap)
+		return dev_err_probe(dev, -ENODEV, "failed to get serdes regmap\n");
+
+	serdes_bridge->sel_mipi = of_property_read_bool(dev->parent->of_node, "sel-mipi");
+	if (serdes_bridge->sel_mipi) {
+		serdes_bridge->dsi_node = of_graph_get_remote_node(dev->parent->of_node, 0, -1);
+		if (!serdes_bridge->dsi_node)
+			return dev_err_probe(dev->parent, -ENODEV,
+					     "failed to get remote node for serdes dsi\n");
+
+		SERDES_DBG_MFD("%s: sel_mipi=%d\n", __func__, serdes_bridge->sel_mipi);
+	}
+
+	serdes_bridge->base_bridge.funcs = &serdes_bridge_funcs;
+	serdes_bridge->base_bridge.of_node = dev->parent->of_node;
+	serdes_bridge->base_bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_MODES;
+
+	if (serdes_bridge->sel_mipi) {
+		serdes_bridge->base_bridge.type = DRM_MODE_CONNECTOR_DSI;
+		SERDES_DBG_MFD("%s: type DRM_MODE_CONNECTOR_DSI\n", __func__);
+	} else if (serdes_bridge->parent->chip_data->connector_type) {
+		serdes_bridge->base_bridge.type = serdes_bridge->parent->chip_data->connector_type;
+		SERDES_DBG_MFD("%s: type 0x%x\n", __func__, serdes_bridge->base_bridge.type);
+	} else {
+		serdes_bridge->base_bridge.type = DRM_MODE_CONNECTOR_eDP;
+		SERDES_DBG_MFD("%s: type DRM_MODE_CONNECTOR_LVDS\n", __func__);
+	}
+
+	drm_bridge_add(&serdes_bridge->base_bridge);
+
+	dev_info(dev, "serdes %s, serdes_bridge_probe successful mipi=%d, of_node=%s\n",
+		 serdes->chip_data->name, serdes_bridge->sel_mipi,
+		 serdes_bridge->base_bridge.of_node->name);
+
+	return 0;
+}
+
+static int serdes_bridge_remove(struct platform_device *pdev)
+{
+	struct serdes_bridge *serdes_bridge = platform_get_drvdata(pdev);
+
+	drm_bridge_remove(&serdes_bridge->base_bridge);
+
+	return 0;
+}
+
+static const struct of_device_id serdes_bridge_of_match[] = {
+	{ .compatible = "rohm,bu18tl82-bridge", },
+	{ .compatible = "rohm,bu18rl82-bridge", },
+	{ .compatible = "maxim,max96745-bridge", },
+	{ .compatible = "maxim,max96752-bridge", },
+	{ .compatible = "maxim,max96755-bridge", },
+	{ .compatible = "maxim,max96772-bridge", },
+	{ .compatible = "rockchip,rkx111-bridge", },
+	{ .compatible = "rockchip,rkx121-bridge", },
+	{ }
+};
+
+static struct platform_driver serdes_bridge_driver = {
+	.driver = {
+		.name = "serdes-bridge",
+		.of_match_table = of_match_ptr(serdes_bridge_of_match),
+	},
+	.probe = serdes_bridge_probe,
+	.remove = serdes_bridge_remove,
+};
+
+static int __init serdes_bridge_init(void)
+{
+	return platform_driver_register(&serdes_bridge_driver);
+}
+device_initcall(serdes_bridge_init);
+
+static void __exit serdes_bridge_exit(void)
+{
+	platform_driver_unregister(&serdes_bridge_driver);
+}
+module_exit(serdes_bridge_exit);
+
+MODULE_AUTHOR("Luo Wei <lw@rock-chips.com>");
+MODULE_DESCRIPTION("display bridge interface for different serdes");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:serdes-bridge");
diff --git a/kernel/drivers/mfd/display-serdes/serdes-core.c b/kernel/drivers/mfd/display-serdes/serdes-core.c
new file mode 100644
index 0000000..5ef62f8
--- /dev/null
+++ b/kernel/drivers/mfd/display-serdes/serdes-core.c
@@ -0,0 +1,391 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * serdes-core.c  --  Device access for different serdes chips
+ *
+ * Copyright (c) 2023-2028 Rockchip Electronics Co. Ltd.
+ *
+ * Author: luowei <lw@rock-chips.com>
+ */
+
+#include "core.h"
+
+static const struct mfd_cell serdes_bu18tl82_devs[] = {
+	{
+		.name = "serdes-pinctrl",
+		.of_compatible = "rohm,bu18tl82-pinctrl",
+	},
+	{
+		.name = "serdes-bridge",
+		.of_compatible = "rohm,bu18tl82-bridge",
+	},
+};
+
+static const struct mfd_cell serdes_bu18rl82_devs[] = {
+	{
+		.name = "serdes-pinctrl",
+		.of_compatible = "rohm,bu18rl82-pinctrl",
+	},
+	{
+		.name = "serdes-bridge",
+		.of_compatible = "rohm,bu18rl82-bridge",
+	},
+};
+
+static const struct mfd_cell serdes_max96745_devs[] = {
+	{
+		.name = "serdes-pinctrl",
+		.of_compatible = "maxim,max96745-pinctrl",
+	},
+	{
+		.name = "serdes-bridge",
+		.of_compatible = "maxim,max96745-bridge",
+	},
+};
+
+static const struct mfd_cell serdes_max96755_devs[] = {
+	{
+		.name = "serdes-pinctrl",
+		.of_compatible = "maxim,max96755-pinctrl",
+	},
+	{
+		.name = "serdes-bridge",
+		.of_compatible = "maxim,max96755-bridge",
+	},
+};
+
+static const struct mfd_cell serdes_max96789_devs[] = {
+	{
+		.name = "serdes-pinctrl",
+		.of_compatible = "maxim,max96789-pinctrl",
+	},
+	{
+		.name = "serdes-bridge",
+		.of_compatible = "maxim,max96789-bridge",
+	},
+};
+
+static const struct mfd_cell serdes_max96752_devs[] = {
+	{
+		.name = "serdes-pinctrl",
+		.of_compatible = "maxim,max96752-pinctrl",
+	},
+	{
+		.name = "serdes-panel",
+		.of_compatible = "maxim,max96752-panel",
+	},
+};
+
+static const struct mfd_cell serdes_max96772_devs[] = {
+	{
+		.name = "serdes-pinctrl",
+		.of_compatible = "maxim,max96772-pinctrl",
+	},
+	{
+		.name = "serdes-panel",
+		.of_compatible = "maxim,max96772-panel",
+	},
+};
+
+static const struct mfd_cell serdes_rkx111_devs[] = {
+	{
+		.name = "serdes-pinctrl",
+		.of_compatible = "rockchip,rkx111-pinctrl",
+	},
+	{
+		.name = "serdes-bridge",
+		.of_compatible = "rockchip,rkx111-bridge",
+	},
+};
+
+static const struct mfd_cell serdes_rkx121_devs[] = {
+	{
+		.name = "serdes-pinctrl",
+		.of_compatible = "rockchip,rkx121-pinctrl",
+	},
+	{
+		.name = "serdes-bridge",
+		.of_compatible = "rockchip,rkx121-bridge",
+	},
+};
+
+static const struct mfd_cell serdes_nca9539_devs[] = {
+	{
+		.name = "serdes-pinctrl",
+		.of_compatible = "novo,nca9539-pinctrl",
+	},
+};
+
+/**
+ * serdes_reg_read: Read a single serdes register.
+ *
+ * @serdes: Device to read from.
+ * @reg: Register to read.
+ * @val: Data from register.
+ */
+int serdes_reg_read(struct serdes *serdes, unsigned int reg, unsigned int *val)
+{
+	int ret;
+
+	ret = regmap_read(serdes->regmap, reg, val);
+	SERDES_DBG_I2C("%s %s Read Reg%04x %04x\n", __func__,
+		       serdes->chip_data->name, reg, *val);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(serdes_reg_read);
+
+/**
+ * serdes_bulk_read: Read multiple serdes registers
+ *
+ * @serdes: Device to read from
+ * @reg: First register
+ * @count: Number of registers
+ * @buf: Buffer to fill.
+ */
+int serdes_bulk_read(struct serdes *serdes, unsigned int reg,
+		     int count, u16 *buf)
+{
+	int i = 0, ret = 0;
+
+	ret = regmap_bulk_read(serdes->regmap, reg, buf, count);
+	for (i = 0; i < count; i++) {
+		SERDES_DBG_I2C("%s %s %s Read Reg%04x %04x\n", __func__, dev_name(serdes->dev),
+			       serdes->chip_data->name, reg + i, buf[i]);
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(serdes_bulk_read);
+
+int serdes_bulk_write(struct serdes *serdes, unsigned int reg,
+		      int count, void *src)
+{
+	u16 *buf = src;
+	int i, ret;
+
+	WARN_ON(count <= 0);
+
+	mutex_lock(&serdes->io_lock);
+	for (i = 0; i < count; i++) {
+		SERDES_DBG_I2C("%s %s %s Write Reg%04x %04x\n", __func__, dev_name(serdes->dev),
+			       serdes->chip_data->name, reg, buf[i]);
+		ret = regmap_write(serdes->regmap, reg, buf[i]);
+		if (ret != 0) {
+			mutex_unlock(&serdes->io_lock);
+			return ret;
+		}
+	}
+	mutex_unlock(&serdes->io_lock);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(serdes_bulk_write);
+
+/**
+ * serdes_multi_reg_write: Write many serdes register.
+ *
+ * @serdes: Device to write to.
+ * @regs: Registers to write to.
+ * @num_regs: Number of registers to write.
+ */
+int serdes_multi_reg_write(struct serdes *serdes, const struct reg_sequence *regs,
+			   int num_regs)
+{
+	int i, ret;
+
+	SERDES_DBG_I2C("%s %s %s num=%d\n", __func__, dev_name(serdes->dev),
+		       serdes->chip_data->name, num_regs);
+	for (i = 0; i < num_regs; i++) {
+		SERDES_DBG_I2C("serdes %s Write Reg%04x %04x\n",
+			       serdes->chip_data->name, regs[i].reg, regs[i].def);
+	}
+
+	ret = regmap_multi_reg_write(serdes->regmap, regs, num_regs);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(serdes_multi_reg_write);
+
+/**
+ * serdes_reg_write: Write a single serdes register.
+ *
+ * @serdes: Device to write to.
+ * @reg: Register to write to.
+ * @val: Value to write.
+ */
+int serdes_reg_write(struct serdes *serdes, unsigned int reg,
+		     unsigned int val)
+{
+	int ret;
+
+	SERDES_DBG_I2C("%s %s %s Write Reg%04x %04x)\n", __func__, dev_name(serdes->dev),
+		       serdes->chip_data->name, reg, val);
+	ret = regmap_write(serdes->regmap, reg, val);
+	if (ret != 0)
+		return ret;
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(serdes_reg_write);
+
+/**
+ * serdes_set_bits: Set the value of a bitfield in a serdes register
+ *
+ * @serdes: Device to write to.
+ * @reg: Register to write to.
+ * @mask: Mask of bits to set.
+ * @val: Value to set (unshifted)
+ */
+int serdes_set_bits(struct serdes *serdes, unsigned int reg,
+		    unsigned int mask, unsigned int val)
+{
+	int ret;
+
+	SERDES_DBG_I2C("%s %s %s Write Reg%04x %04x) mask=%04x\n", __func__,
+		       dev_name(serdes->dev), serdes->chip_data->name, reg, val, mask);
+	ret = regmap_update_bits(serdes->regmap, reg, mask, val);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(serdes_set_bits);
+
+/*
+ * Instantiate the generic non-control parts of the device.
+ */
+int serdes_device_init(struct serdes *serdes)
+{
+	struct serdes_chip_data *chip_data = serdes->chip_data;
+	int ret = 0;
+	const struct mfd_cell *serdes_devs = NULL;
+	int mfd_num = 0;
+
+	switch (chip_data->serdes_id) {
+	case ROHM_ID_BU18TL82:
+		serdes_devs = serdes_bu18tl82_devs;
+		mfd_num = ARRAY_SIZE(serdes_bu18tl82_devs);
+		break;
+	case ROHM_ID_BU18RL82:
+		serdes_devs = serdes_bu18rl82_devs;
+		mfd_num = ARRAY_SIZE(serdes_bu18rl82_devs);
+		break;
+	case MAXIM_ID_MAX96745:
+		serdes_devs = serdes_max96745_devs;
+		mfd_num = ARRAY_SIZE(serdes_max96745_devs);
+		break;
+	case MAXIM_ID_MAX96752:
+		serdes_devs = serdes_max96752_devs;
+		mfd_num = ARRAY_SIZE(serdes_max96752_devs);
+		break;
+	case MAXIM_ID_MAX96755:
+		serdes_devs = serdes_max96755_devs;
+		mfd_num = ARRAY_SIZE(serdes_max96755_devs);
+		break;
+	case MAXIM_ID_MAX96772:
+		serdes_devs = serdes_max96772_devs;
+		mfd_num = ARRAY_SIZE(serdes_max96772_devs);
+		break;
+	case MAXIM_ID_MAX96789:
+		serdes_devs = serdes_max96789_devs;
+		mfd_num = ARRAY_SIZE(serdes_max96789_devs);
+		break;
+	case ROCKCHIP_ID_RKX111:
+		serdes_devs = serdes_rkx111_devs;
+		mfd_num = ARRAY_SIZE(serdes_rkx111_devs);
+		break;
+	case ROCKCHIP_ID_RKX121:
+		serdes_devs = serdes_rkx121_devs;
+		mfd_num = ARRAY_SIZE(serdes_rkx121_devs);
+		break;
+	case NOVO_ID_NCA9539:
+		serdes_devs = serdes_nca9539_devs;
+		mfd_num = ARRAY_SIZE(serdes_nca9539_devs);
+		break;
+	default:
+		dev_info(serdes->dev, "%s: unknown device\n", __func__);
+		break;
+	}
+
+	ret = devm_mfd_add_devices(serdes->dev, PLATFORM_DEVID_AUTO, serdes_devs,
+				   mfd_num, NULL, 0, NULL);
+	if (ret != 0) {
+		dev_err(serdes->dev, "Failed to add serdes children\n");
+		return ret;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(serdes_device_init);
+
+int serdes_set_pinctrl_default(struct serdes *serdes)
+{
+	int ret = 0;
+
+	if ((!IS_ERR(serdes->pinctrl_node)) && (!IS_ERR(serdes->pins_default))) {
+		ret = pinctrl_select_state(serdes->pinctrl_node, serdes->pins_default);
+		if (ret)
+			dev_err(serdes->dev, "could not set default pins\n");
+		SERDES_DBG_MFD("%s: name=%s\n", __func__, dev_name(serdes->dev));
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(serdes_set_pinctrl_default);
+
+int serdes_set_pinctrl_sleep(struct serdes *serdes)
+{
+	int ret = 0;
+
+	if ((!IS_ERR(serdes->pinctrl_node)) && (!IS_ERR(serdes->pins_sleep))) {
+		ret = pinctrl_select_state(serdes->pinctrl_node, serdes->pins_sleep);
+		if (ret)
+			dev_err(serdes->dev, "could not set sleep pins\n");
+		SERDES_DBG_MFD("%s: name=%s\n", __func__, dev_name(serdes->dev));
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(serdes_set_pinctrl_sleep);
+
+int serdes_device_suspend(struct serdes *serdes)
+{
+	int ret = 0;
+
+	if (!IS_ERR(serdes->vpower)) {
+		ret = regulator_disable(serdes->vpower);
+		if (ret) {
+			dev_err(serdes->dev, "fail to disable vpower regulator\n");
+			return ret;
+		}
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(serdes_device_suspend);
+
+int serdes_device_resume(struct serdes *serdes)
+{
+	int ret = 0;
+
+	if (!IS_ERR(serdes->vpower)) {
+		ret = regulator_enable(serdes->vpower);
+		if (ret) {
+			dev_err(serdes->dev, "fail to enable vpower regulator\n");
+			return ret;
+		}
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(serdes_device_resume);
+
+void serdes_device_shutdown(struct serdes *serdes)
+{
+	int ret = 0;
+
+	if ((!IS_ERR(serdes->pinctrl_node)) && (!IS_ERR(serdes->pins_sleep))) {
+		ret = pinctrl_select_state(serdes->pinctrl_node, serdes->pins_sleep);
+		if (ret)
+			dev_err(serdes->dev, "could not set sleep pins\n");
+	}
+}
+EXPORT_SYMBOL_GPL(serdes_device_shutdown);
+
+MODULE_LICENSE("GPL");
diff --git a/kernel/drivers/mfd/display-serdes/serdes-gpio.c b/kernel/drivers/mfd/display-serdes/serdes-gpio.c
new file mode 100644
index 0000000..8e86455
--- /dev/null
+++ b/kernel/drivers/mfd/display-serdes/serdes-gpio.c
@@ -0,0 +1,253 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * gpiolib support for different serdes chip
+ *
+ * Copyright (c) 2023-2028 Rockchip Electronics Co. Ltd.
+ *
+ * Author: luowei <lw@rock-chips.com>
+ *
+ */
+
+#include "core.h"
+
+static int serdes_gpio_direction_in(struct gpio_chip *chip, unsigned int offset)
+{
+	struct serdes_gpio *serdes_gpio = gpiochip_get_data(chip);
+	struct serdes *serdes = serdes_gpio->parent->parent;
+	int ret = 0;
+
+	if (serdes->chip_data->gpio_ops->direction_input)
+		ret = serdes->chip_data->gpio_ops->direction_input(serdes, offset);
+
+	SERDES_DBG_MFD("%s: %s %s gpio=%d\n", __func__, dev_name(serdes->dev),
+		       serdes->chip_data->name, offset);
+	return ret;
+}
+
+static int serdes_gpio_get(struct gpio_chip *chip, unsigned int offset)
+{
+	struct serdes_gpio *serdes_gpio = gpiochip_get_data(chip);
+	struct serdes *serdes = serdes_gpio->parent->parent;
+	int ret = 0;
+
+	if (serdes->chip_data->gpio_ops->get_level)
+		ret = serdes->chip_data->gpio_ops->get_level(serdes, offset);
+
+	SERDES_DBG_MFD("%s: %s %s gpio=%d\n", __func__, dev_name(serdes->dev),
+		       serdes->chip_data->name, offset);
+	return ret;
+}
+
+static void serdes_gpio_set(struct gpio_chip *chip, unsigned int offset, int value)
+{
+	struct serdes_gpio *serdes_gpio = gpiochip_get_data(chip);
+	struct serdes *serdes = serdes_gpio->parent->parent;
+	int ret = 0;
+
+	if (serdes->chip_data->gpio_ops->set_level)
+		ret = serdes->chip_data->gpio_ops->set_level(serdes, offset, value);
+
+	SERDES_DBG_MFD("%s: %s %s gpio=%d,val=%d\n", __func__, dev_name(serdes->dev),
+		       serdes->chip_data->name, offset, value);
+}
+
+static int serdes_gpio_direction_out(struct gpio_chip *chip,
+				     unsigned int offset, int value)
+{
+	struct serdes_gpio *serdes_gpio = gpiochip_get_data(chip);
+	struct serdes *serdes = serdes_gpio->parent->parent;
+	int ret = 0;
+
+	if (serdes->chip_data->gpio_ops->direction_output)
+		ret = serdes->chip_data->gpio_ops->direction_output(serdes, offset, value);
+
+	SERDES_DBG_MFD("%s: %s %s gpio=%d,val=%d\n", __func__, dev_name(serdes->dev),
+		       serdes->chip_data->name, offset, value);
+	return ret;
+}
+
+static int serdes_gpio_to_irq(struct gpio_chip *chip, unsigned int offset)
+{
+	struct serdes_gpio *serdes_gpio = gpiochip_get_data(chip);
+	struct serdes *serdes = serdes_gpio->parent->parent;
+	int ret = 0;
+
+	if (serdes->chip_data->gpio_ops->to_irq)
+		ret = serdes->chip_data->gpio_ops->to_irq(serdes, offset);
+
+	SERDES_DBG_MFD("%s: %s %s gpio=%d\n", __func__, dev_name(serdes->dev),
+		       serdes->chip_data->name, offset);
+	return ret;
+}
+
+static int serdes_set_config(struct gpio_chip *chip, unsigned int offset,
+			     unsigned long config)
+{
+	struct serdes_gpio *serdes_gpio = gpiochip_get_data(chip);
+	struct serdes *serdes = serdes_gpio->parent->parent;
+	//int param = pinconf_to_config_param(config);
+	int ret = 0;
+	//int gpio = offset;
+
+	if (serdes->chip_data->gpio_ops->set_config)
+		ret = serdes->chip_data->gpio_ops->set_config(serdes, offset, config);
+
+	SERDES_DBG_MFD("%s: %s %s gpio=%d,config=%d\n", __func__,
+		       dev_name(serdes->dev),
+		       serdes->chip_data->name, offset, pinconf_to_config_param(config));
+	return ret;
+}
+
+#ifdef CONFIG_DEBUG_FS
+static void serdes_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
+{
+	struct serdes_gpio *serdes_gpio = gpiochip_get_data(chip);
+	struct serdes *serdes = serdes_gpio->parent->parent;
+	int i = 0;
+	int ret = 0;
+
+	for (i = 0; i < chip->ngpio; i++) {
+		int gpio = i + chip->base;
+		const char *label, *level;
+
+		/* We report the GPIO even if it's not requested since
+		 * we're also reporting things like alternate
+		 * functions which apply even when the GPIO is not in
+		 * use as a GPIO.
+		 */
+		label = gpiochip_is_requested(chip, i);
+		if (!label)
+			label = "Unrequested";
+
+		seq_printf(s, " %s-gpio-%02d ", label, gpio);
+
+		if (serdes->chip_data->gpio_ops->get_level)
+			ret = serdes->chip_data->gpio_ops->get_level(serdes, i);
+		switch (ret) {
+		case SERDES_GPIO_LEVEL_HIGH:
+			level = "level-high";
+			break;
+		case SERDES_GPIO_LEVEL_LOW:
+			level = "level-low";
+			break;
+		default:
+			level = "invalid level";
+			break;
+		}
+
+		seq_printf(s, " %s\n", level);
+	}
+}
+#else
+#define serdes_gpio_dbg_show NULL
+#endif
+
+static const struct gpio_chip serdes_gpio_chip = {
+	.owner			= THIS_MODULE,
+	.request = gpiochip_generic_request,
+	.free = gpiochip_generic_free,
+	.direction_input	= serdes_gpio_direction_in,
+	.direction_output	= serdes_gpio_direction_out,
+	.get			= serdes_gpio_get,
+	.set			= serdes_gpio_set,
+	.set_config		= serdes_set_config,
+	.to_irq			= serdes_gpio_to_irq,
+	.dbg_show		= serdes_gpio_dbg_show,
+	.can_sleep		= true,
+};
+
+static int serdes_gpio_probe(struct platform_device *pdev)
+{
+	struct serdes_pinctrl *serdes_pinctrl = dev_get_drvdata(pdev->dev.parent);
+	struct serdes *serdes = serdes_pinctrl->parent;
+	struct serdes_chip_data *chip_data = serdes->chip_data;
+	struct device *dev = &pdev->dev;
+	struct serdes_gpio *serdes_gpio;
+	const char *list_name = "gpio-ranges";
+	struct of_phandle_args of_args;
+	int ret;
+
+	serdes_gpio = devm_kzalloc(&pdev->dev, sizeof(*serdes_gpio),
+				   GFP_KERNEL);
+	if (serdes_gpio == NULL)
+		return -ENOMEM;
+
+	ret = of_parse_phandle_with_fixed_args(dev->of_node, list_name, 3, 0, &of_args);
+	if (ret) {
+		dev_err(dev, "Unable to parse %s list property\n",
+			list_name);
+		return ret;
+	}
+
+	serdes_pinctrl->gpio = serdes_gpio;
+	serdes_gpio->dev = dev;
+	serdes_gpio->parent = serdes_pinctrl;
+	serdes_gpio->gpio_chip = serdes_gpio_chip;
+	serdes_gpio->gpio_chip.parent = pdev->dev.parent;
+	if (of_args.args[2]) {
+		serdes_gpio->gpio_chip.base = of_args.args[1];
+		serdes_gpio->gpio_chip.ngpio = of_args.args[2];
+	} else {
+		serdes_gpio->gpio_chip.base = -1;
+		serdes_gpio->gpio_chip.ngpio = 8;
+	}
+#ifdef CONFIG_OF_GPIO
+	serdes_gpio->gpio_chip.of_node = serdes_gpio->dev->of_node;
+#endif
+	serdes_gpio->gpio_chip.label = kasprintf(GFP_KERNEL, "%s-gpio", chip_data->name);
+
+	/* Add gpiochip */
+	ret = devm_gpiochip_add_data(&pdev->dev, &serdes_gpio->gpio_chip,
+				     serdes_gpio);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "Could not register serdes gpiochip, %d\n", ret);
+		return ret;
+	}
+
+	platform_set_drvdata(pdev, serdes_gpio);
+
+	dev_info(serdes_gpio->dev->parent->parent,
+		 "%s serdes_gpio_probe successful, base=%d, ngpio=%d\n",
+		 serdes->chip_data->name,
+		 serdes_gpio->gpio_chip.base, serdes_gpio->gpio_chip.ngpio);
+
+	return ret;
+}
+
+static const struct of_device_id serdes_gpio_of_match[] = {
+	{ .compatible = "rohm,bu18tl82-gpio", },
+	{ .compatible = "rohm,bu18rl82-gpio", },
+	{ .compatible = "maxim,max96745-gpio", },
+	{ .compatible = "maxim,max96752-gpio", },
+	{ .compatible = "maxim,max96755-gpio", },
+	{ .compatible = "maxim,max96772-gpio", },
+	{ .compatible = "rockchip,rkx111-gpio", },
+	{ .compatible = "rockchip,rkx121-gpio", },
+	{ .compatible = "novo,nca9539-gpio", },
+	{ }
+};
+
+static struct platform_driver serdes_gpio_driver = {
+	.driver = {
+		.name = "serdes-gpio",
+		.of_match_table = of_match_ptr(serdes_gpio_of_match),
+	},
+	.probe = serdes_gpio_probe,
+};
+
+static int __init serdes_gpio_init(void)
+{
+	return platform_driver_register(&serdes_gpio_driver);
+}
+device_initcall(serdes_gpio_init);
+
+static void __exit serdes_gpio_exit(void)
+{
+	platform_driver_unregister(&serdes_gpio_driver);
+}
+module_exit(serdes_gpio_exit);
+
+MODULE_AUTHOR("Luo Wei <lw@rock-chips.com>");
+MODULE_DESCRIPTION("display bridge interface for different serdes");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:serdes-bridge");
diff --git a/kernel/drivers/mfd/display-serdes/serdes-i2c.c b/kernel/drivers/mfd/display-serdes/serdes-i2c.c
new file mode 100644
index 0000000..317126e
--- /dev/null
+++ b/kernel/drivers/mfd/display-serdes/serdes-i2c.c
@@ -0,0 +1,337 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * serdes-i2c.c  --  I2C access for different serdes chips
+ *
+ * Copyright (c) 2023-2028 Rockchip Electronics Co. Ltd.
+ *
+ * Author: luowei <lw@rock-chips.com>
+ */
+
+#include "core.h"
+
+int serdes_i2c_set_sequence(struct serdes *serdes)
+{
+	struct device *dev = serdes->dev;
+	int i, ret = 0;
+	unsigned int def = 0;
+
+	for (i = 0; i < serdes->serdes_init_seq->reg_seq_cnt; i++) {
+		if (serdes->serdes_init_seq->reg_sequence[i].reg == 0xffff) {
+			SERDES_DBG_MFD("%s: delay 0x%04x us\n", __func__,
+				       serdes->serdes_init_seq->reg_sequence[i].def);
+			udelay(serdes->serdes_init_seq->reg_sequence[i].def);
+			continue;
+		}
+
+		ret = serdes_reg_write(serdes,
+				       serdes->serdes_init_seq->reg_sequence[i].reg,
+				       serdes->serdes_init_seq->reg_sequence[i].def);
+
+		if (ret < 0) {
+			dev_err(serdes->dev,
+				"failed to write register %04x, ret %d, write again now\n",
+				serdes->serdes_init_seq->reg_sequence[i].reg, ret);
+			ret = serdes_reg_write(serdes,
+					       serdes->serdes_init_seq->reg_sequence[i].reg,
+					       serdes->serdes_init_seq->reg_sequence[i].def);
+		}
+		serdes_reg_read(serdes, serdes->serdes_init_seq->reg_sequence[i].reg, &def);
+		if ((def != serdes->serdes_init_seq->reg_sequence[i].def) || (ret < 0)) {
+			/* if read value != write value then write again */
+			dev_err(dev, "read %04x %04x != %04x\n",
+				serdes->serdes_init_seq->reg_sequence[i].reg,
+				def, serdes->serdes_init_seq->reg_sequence[i].def);
+			serdes_reg_write(serdes,
+					 serdes->serdes_init_seq->reg_sequence[i].reg,
+					 serdes->serdes_init_seq->reg_sequence[i].def);
+		}
+	}
+
+	dev_info(dev, "serdes %s sequence_init\n", serdes->chip_data->name);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(serdes_i2c_set_sequence);
+
+static void serdes_mfd_work(struct work_struct *work)
+{
+	struct serdes *serdes = container_of(work, struct serdes, mfd_delay_work.work);
+
+	mutex_lock(&serdes->wq_lock);
+	serdes_device_init(serdes);
+	mutex_unlock(&serdes->wq_lock);
+}
+
+static const unsigned int serdes_cable[] = {
+	EXTCON_JACK_VIDEO_OUT,
+	EXTCON_NONE,
+};
+
+static int serdes_parse_init_seq(struct device *dev, const u16 *data,
+				 int length, struct serdes_init_seq *seq)
+{
+	struct reg_sequence *reg_sequence;
+	u16 *buf, *d;
+	unsigned int i, cnt;
+
+	if (!seq)
+		return -EINVAL;
+
+	buf = devm_kmemdup(dev, data, length, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	d = buf;
+	cnt = length / 4;
+	seq->reg_seq_cnt = cnt;
+
+	seq->reg_sequence = devm_kcalloc(dev, cnt, sizeof(struct reg_sequence), GFP_KERNEL);
+	if (!seq->reg_sequence)
+		return -ENOMEM;
+
+	for (i = 0; i < cnt; i++) {
+		reg_sequence = &seq->reg_sequence[i];
+		reg_sequence->reg = get_unaligned_be16(&d[0]);
+		reg_sequence->def = get_unaligned_be16(&d[1]);
+		d += 2;
+	}
+
+	return 0;
+}
+
+static int serdes_get_init_seq(struct serdes *serdes)
+{
+	struct device *dev = serdes->dev;
+	struct device_node *np = dev->of_node;
+	const void *data;
+	int err, len, ret = 0;
+
+	data = of_get_property(np, "serdes-init-sequence", &len);
+	if (!data) {
+		dev_err(dev, "failed to get serdes-init-sequence\n");
+		return -EINVAL;
+	}
+
+	serdes->serdes_init_seq = devm_kzalloc(dev, sizeof(*serdes->serdes_init_seq),
+					       GFP_KERNEL);
+	if (!serdes->serdes_init_seq)
+		return -ENOMEM;
+
+	err = serdes_parse_init_seq(dev, data, len, serdes->serdes_init_seq);
+	if (err) {
+		dev_err(dev, "failed to parse serdes-init-sequence\n");
+		return err;
+	}
+
+	/* init ser register(not des register) more early if uboot logo disabled */
+	serdes->route_enable = of_property_read_bool(dev->of_node, "route-enable");
+	if ((!serdes->route_enable) && (serdes->chip_data->serdes_type == TYPE_SER))
+		ret = serdes_i2c_set_sequence(serdes);
+
+	return ret;
+}
+
+static int serdes_i2c_probe(struct i2c_client *client,
+			    const struct i2c_device_id *id)
+{
+	struct device *dev = &client->dev;
+	struct serdes *serdes;
+	int ret;
+
+	serdes = devm_kzalloc(&client->dev, sizeof(struct serdes), GFP_KERNEL);
+	if (serdes == NULL)
+		return -ENOMEM;
+
+	serdes->dev = dev;
+	serdes->chip_data = (struct serdes_chip_data *)of_device_get_match_data(dev);
+	i2c_set_clientdata(client, serdes);
+
+	dev_info(dev, "serdes %s probe start\n", serdes->chip_data->name);
+
+	serdes->type = serdes->chip_data->serdes_type;
+	serdes->regmap = devm_regmap_init_i2c(client, serdes->chip_data->regmap_config);
+	if (IS_ERR(serdes->regmap)) {
+		ret = PTR_ERR(serdes->regmap);
+		dev_err(serdes->dev, "Failed to allocate serdes register map: %d\n",
+			ret);
+		return ret;
+	}
+
+	serdes->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_ASIS);
+	if (IS_ERR(serdes->reset_gpio))
+		return dev_err_probe(dev, PTR_ERR(serdes->reset_gpio),
+				     "failed to acquire serdes reset gpio\n");
+
+	serdes->enable_gpio = devm_gpiod_get_optional(dev, "enable", GPIOD_ASIS);
+	if (IS_ERR(serdes->enable_gpio))
+		return dev_err_probe(dev, PTR_ERR(serdes->enable_gpio),
+				     "failed to acquire serdes enable gpio\n");
+
+	serdes->vpower = devm_regulator_get_optional(dev, "vpower");
+	if (IS_ERR(serdes->vpower)) {
+		if (PTR_ERR(serdes->vpower) != -ENODEV)
+			return PTR_ERR(serdes->vpower);
+		dev_info(dev, "no vpower regulator found\n");
+	}
+
+	if (!IS_ERR(serdes->vpower)) {
+		ret = regulator_enable(serdes->vpower);
+		if (ret) {
+			dev_err(dev, "fail to enable vpower regulator\n");
+			return ret;
+		}
+	}
+
+	serdes->extcon = devm_extcon_dev_allocate(dev, serdes_cable);
+	if (IS_ERR(serdes->extcon))
+		return dev_err_probe(dev, PTR_ERR(serdes->extcon),
+				     "failed to allocate serdes extcon device\n");
+
+	ret = devm_extcon_dev_register(dev, serdes->extcon);
+	if (ret)
+		return dev_err_probe(dev, ret,
+				     "failed to register serdes extcon device\n");
+
+	ret = serdes_get_init_seq(serdes);
+	if (ret)
+		return dev_err_probe(dev, ret,
+				     "failed to write serdes register with i2c\n");
+
+	mutex_init(&serdes->io_lock);
+	dev_set_drvdata(serdes->dev, serdes);
+	ret = serdes_irq_init(serdes);
+	if (ret != 0) {
+		serdes_irq_exit(serdes);
+		return ret;
+	}
+
+	serdes->use_delay_work = of_property_read_bool(dev->of_node, "use-delay-work");
+	if (serdes->use_delay_work) {
+		serdes->mfd_wq = alloc_ordered_workqueue("%s",
+							 WQ_MEM_RECLAIM | WQ_FREEZABLE,
+							 "serdes-mfd-wq");
+		mutex_init(&serdes->wq_lock);
+		INIT_DELAYED_WORK(&serdes->mfd_delay_work, serdes_mfd_work);
+		queue_delayed_work(serdes->mfd_wq, &serdes->mfd_delay_work, msecs_to_jiffies(300));
+		SERDES_DBG_MFD("%s: use_delay_work=%d\n", __func__, serdes->use_delay_work);
+	} else {
+		ret = serdes_device_init(serdes);
+		SERDES_DBG_MFD("%s: use_delay_work=%d\n", __func__, serdes->use_delay_work);
+	}
+
+	dev_info(dev, "serdes %s serdes_i2c_probe successful version %s\n",
+		 serdes->chip_data->name, MFD_SERDES_DISPLAY_VERSION);
+
+	return ret;
+}
+
+static int serdes_i2c_prepare(struct device *dev)
+{
+	return 0;
+}
+
+static void serdes_i2c_complete(struct device *dev)
+{
+	struct serdes *serdes = dev_get_drvdata(dev);
+
+	if (serdes->chip_data->serdes_type == TYPE_SER)
+		serdes_i2c_set_sequence(serdes);
+
+	SERDES_DBG_MFD("%s: name=%s\n", __func__, dev_name(serdes->dev));
+}
+
+static int serdes_i2c_suspend(struct device *dev)
+{
+	struct serdes *serdes = dev_get_drvdata(dev);
+
+	serdes_device_suspend(serdes);
+
+	SERDES_DBG_MFD("%s: name=%s\n", __func__, dev_name(serdes->dev));
+	return 0;
+}
+
+static int serdes_i2c_resume(struct device *dev)
+{
+	struct serdes *serdes = dev_get_drvdata(dev);
+
+	if (serdes->chip_data->serdes_type == TYPE_OTHER)
+		serdes_i2c_set_sequence(serdes);
+
+	serdes_device_resume(serdes);
+	SERDES_DBG_MFD("%s: name=%s\n", __func__, dev_name(serdes->dev));
+	return 0;
+}
+
+static int serdes_i2c_poweroff(struct device *dev)
+{
+	struct serdes *serdes = dev_get_drvdata(dev);
+
+	serdes_device_shutdown(serdes);
+
+	return 0;
+}
+
+static const struct of_device_id serdes_of_match[] = {
+#if IS_ENABLED(CONFIG_SERDES_DISPLAY_CHIP_ROHM_BU18TL82)
+	{ .compatible = "rohm,bu18tl82", .data = &serdes_bu18tl82_data },
+#endif
+#if IS_ENABLED(CONFIG_SERDES_DISPLAY_CHIP_ROHM_BU18RL82)
+	{ .compatible = "rohm,bu18rl82", .data = &serdes_bu18rl82_data },
+#endif
+#if IS_ENABLED(CONFIG_SERDES_DISPLAY_CHIP_MAXIM_MAX96745)
+	{ .compatible = "maxim,max96745", .data = &serdes_max96745_data },
+#endif
+#if IS_ENABLED(CONFIG_SERDES_DISPLAY_CHIP_MAXIM_MAX96752)
+	{ .compatible = "maxim,max96752", .data = &serdes_max96752_data },
+#endif
+#if IS_ENABLED(CONFIG_SERDES_DISPLAY_CHIP_MAXIM_MAX96755)
+	{ .compatible = "maxim,max96755", .data = &serdes_max96755_data },
+#endif
+#if IS_ENABLED(CONFIG_SERDES_DISPLAY_CHIP_MAXIM_MAX96772)
+	{ .compatible = "maxim,max96772", .data = &serdes_max96772_data },
+#endif
+#if IS_ENABLED(CONFIG_SERDES_DISPLAY_CHIP_ROCKCHIP_RKX111)
+	{ .compatible = "rockchip,rkx111", .data = &serdes_rkx111_data },
+#endif
+#if IS_ENABLED(CONFIG_SERDES_DISPLAY_CHIP_ROCKCHIP_RKX121)
+	{ .compatible = "rockchip,rkx121", .data = &serdes_rkx121_data },
+#endif
+#if IS_ENABLED(CONFIG_SERDES_DISPLAY_CHIP_NOVO_NCA9539)
+	{ .compatible = "novo,nca9539", .data = &serdes_nca9539_data },
+#endif
+	{ }
+};
+
+static const struct dev_pm_ops serdes_pm_ops = {
+	.prepare = serdes_i2c_prepare,
+	.complete = serdes_i2c_complete,
+	.suspend = serdes_i2c_suspend,
+	.resume = serdes_i2c_resume,
+	.poweroff = serdes_i2c_poweroff,
+};
+
+static struct i2c_driver serdes_i2c_driver = {
+	.driver = {
+		.name = "serdes",
+		.pm = &serdes_pm_ops,
+		.of_match_table = of_match_ptr(serdes_of_match),
+	},
+	.probe = serdes_i2c_probe,
+};
+
+static int __init serdes_i2c_init(void)
+{
+	int ret;
+
+	ret = i2c_add_driver(&serdes_i2c_driver);
+	if (ret != 0)
+		pr_err("Failed to register serdes I2C driver: %d\n", ret);
+
+	return ret;
+}
+subsys_initcall(serdes_i2c_init);
+
+MODULE_AUTHOR("Luo Wei <lw@rock-chips.com>");
+MODULE_DESCRIPTION("display i2c interface for different serdes");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:serdes-i2c");
diff --git a/kernel/drivers/mfd/display-serdes/serdes-irq.c b/kernel/drivers/mfd/display-serdes/serdes-irq.c
new file mode 100644
index 0000000..f4d7740
--- /dev/null
+++ b/kernel/drivers/mfd/display-serdes/serdes-irq.c
@@ -0,0 +1,109 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * serdes-irq.c  --  Interrupt controller support for different serdes chips
+ *
+ * Copyright 2009 Wolfson Microelectronics PLC.
+ *
+ * Author: luowei <lw@rock-chips.com>
+ */
+
+#include "core.h"
+
+static irqreturn_t serdes_bridge_lock_irq_handler(int irq, void *arg)
+{
+	struct serdes *serdes = arg;
+	int ret = 0;
+
+	if (serdes->chip_data->irq_ops->lock_handle)
+		ret = serdes->chip_data->irq_ops->lock_handle(serdes);
+
+	if (extcon_get_state(serdes->extcon, EXTCON_JACK_VIDEO_OUT))
+		atomic_set(&serdes->serdes_bridge->triggered, 1);
+
+	SERDES_DBG_MFD("%s: ret=%d\n", __func__, ret);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t serdes_bridge_err_irq_handler(int irq, void *arg)
+{
+	struct serdes *serdes = arg;
+	int ret = 0;
+
+	if (serdes->chip_data->irq_ops->err_handle)
+		ret = serdes->chip_data->irq_ops->err_handle(serdes);
+
+	SERDES_DBG_MFD("%s: ret=%d\n", __func__, ret);
+
+	return IRQ_HANDLED;
+}
+
+int serdes_irq_init(struct serdes *serdes)
+{
+	int ret;
+
+	mutex_init(&serdes->irq_lock);
+
+	/* lock irq */
+	serdes->lock_gpio = devm_gpiod_get_optional(serdes->dev, "lock", GPIOD_IN);
+	if (IS_ERR(serdes->lock_gpio))
+		return dev_err_probe(serdes->dev, PTR_ERR(serdes->lock_gpio),
+				     "failed to get serdes lock GPIO\n");
+
+	if (serdes->lock_gpio) {
+		serdes->lock_irq = gpiod_to_irq(serdes->lock_gpio);
+		if (serdes->lock_irq < 0)
+			return serdes->lock_irq;
+
+		SERDES_DBG_MFD("%s %s lock_irq=%d\n", __func__,
+			       serdes->chip_data->name, serdes->lock_irq);
+
+		ret = devm_request_threaded_irq(serdes->dev, serdes->lock_irq, NULL,
+						serdes_bridge_lock_irq_handler,
+						IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+						dev_name(serdes->dev), serdes);
+		if (ret)
+			return dev_err_probe(serdes->dev, ret,
+					     "failed to request serdes lock IRQ\n");
+	}
+
+	/* error irq */
+	serdes->err_gpio = devm_gpiod_get_optional(serdes->dev, "err", GPIOD_IN);
+	if (IS_ERR(serdes->err_gpio))
+		return dev_err_probe(serdes->dev, PTR_ERR(serdes->err_gpio),
+				     "failed to get serdes err GPIO\n");
+
+	if (serdes->err_gpio) {
+		serdes->err_irq = gpiod_to_irq(serdes->err_gpio);
+		if (serdes->err_irq < 0)
+			return serdes->err_irq;
+
+		SERDES_DBG_MFD("%s %s err_irq=%d\n", __func__,
+			       serdes->chip_data->name, serdes->err_irq);
+
+		ret = devm_request_threaded_irq(serdes->dev, serdes->err_irq, NULL,
+						serdes_bridge_err_irq_handler,
+						IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+						dev_name(serdes->dev), serdes);
+		if (ret)
+			return dev_err_probe(serdes->dev, ret, "failed to request err IRQ\n");
+	}
+
+	SERDES_DBG_MFD("serdes %s serdes_irq_init successful, ret=%d\n",
+		       serdes->chip_data->name, ret);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(serdes_irq_init);
+
+void serdes_irq_exit(struct serdes *serdes)
+{
+	if (serdes->lock_irq)
+		devm_free_irq(serdes->dev, serdes->lock_irq, serdes);
+
+	if (serdes->err_irq)
+		devm_free_irq(serdes->dev, serdes->err_irq, serdes);
+}
+EXPORT_SYMBOL_GPL(serdes_irq_exit);
+
+MODULE_LICENSE("GPL");
diff --git a/kernel/drivers/mfd/display-serdes/serdes-panel.c b/kernel/drivers/mfd/display-serdes/serdes-panel.c
new file mode 100644
index 0000000..9e5cc95
--- /dev/null
+++ b/kernel/drivers/mfd/display-serdes/serdes-panel.c
@@ -0,0 +1,246 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * serdes-panel.c  --  drm panel access for different serdes chips
+ *
+ * Copyright (c) 2023-2028 Rockchip Electronics Co. Ltd.
+ *
+ * Author: luowei <lw@rock-chips.com>
+ */
+
+#include "core.h"
+
+static inline struct serdes_panel *to_serdes_panel(struct drm_panel *panel)
+{
+	return container_of(panel, struct serdes_panel, panel);
+}
+
+static int serdes_panel_prepare(struct drm_panel *panel)
+{
+	struct serdes_panel *serdes_panel = to_serdes_panel(panel);
+	struct serdes *serdes = serdes_panel->parent;
+	int ret = 0;
+
+	if (serdes->chip_data->panel_ops && serdes->chip_data->panel_ops->init)
+		ret = serdes->chip_data->panel_ops->init(serdes);
+
+	if (serdes->chip_data->serdes_type == TYPE_DES)
+		serdes_i2c_set_sequence(serdes);
+
+	if (serdes->chip_data->panel_ops && serdes->chip_data->panel_ops->prepare)
+		ret = serdes->chip_data->panel_ops->prepare(serdes);
+
+	serdes_set_pinctrl_default(serdes);
+
+	SERDES_DBG_MFD("%s: %s\n", __func__, serdes->chip_data->name);
+
+	return ret;
+}
+
+static int serdes_panel_unprepare(struct drm_panel *panel)
+{
+	struct serdes_panel *serdes_panel = to_serdes_panel(panel);
+	struct serdes *serdes = serdes_panel->parent;
+	int ret = 0;
+
+	if (serdes->chip_data->panel_ops && serdes->chip_data->panel_ops->unprepare)
+		ret = serdes->chip_data->panel_ops->unprepare(serdes);
+
+	serdes_set_pinctrl_sleep(serdes);
+
+	SERDES_DBG_MFD("%s: %s\n", __func__, serdes->chip_data->name);
+
+	return ret;
+}
+
+static int serdes_panel_enable(struct drm_panel *panel)
+{
+	struct serdes_panel *serdes_panel = to_serdes_panel(panel);
+	struct serdes *serdes = serdes_panel->parent;
+	int ret = 0;
+
+	if (serdes->chip_data->panel_ops && serdes->chip_data->panel_ops->enable)
+		ret = serdes->chip_data->panel_ops->enable(serdes);
+
+	backlight_enable(serdes_panel->backlight);
+
+	SERDES_DBG_MFD("%s: %s\n", __func__, serdes->chip_data->name);
+
+	return ret;
+}
+
+static int serdes_panel_disable(struct drm_panel *panel)
+{
+	struct serdes_panel *serdes_panel = to_serdes_panel(panel);
+	struct serdes *serdes = serdes_panel->parent;
+	int ret = 0;
+
+	if (serdes->chip_data->panel_ops && serdes->chip_data->panel_ops->disable)
+		ret = serdes->chip_data->panel_ops->disable(serdes);
+
+	backlight_disable(serdes_panel->backlight);
+
+	SERDES_DBG_MFD("%s: %s\n", __func__, serdes->chip_data->name);
+
+	return ret;
+}
+
+static int serdes_panel_get_modes(struct drm_panel *panel,
+				  struct drm_connector *connector)
+{
+	struct serdes_panel *serdes_panel = to_serdes_panel(panel);
+	struct serdes *serdes = serdes_panel->parent;
+	struct drm_display_mode *mode;
+	u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24;
+	int ret = 0;
+
+	connector->display_info.width_mm = serdes_panel->width_mm;	//323; //346;
+	connector->display_info.height_mm = serdes_panel->height_mm;	//182; //194;
+	drm_display_info_set_bus_formats(&connector->display_info, &bus_format, 1);
+
+	mode = drm_mode_duplicate(connector->dev, &serdes_panel->mode);
+	mode->width_mm = serdes_panel->width_mm;	//323; //346;
+	mode->height_mm = serdes_panel->height_mm;	//182; //194;
+	mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
+
+	drm_mode_set_name(mode);
+	drm_mode_probed_add(connector, mode);
+
+	if (serdes->chip_data->panel_ops && serdes->chip_data->panel_ops->get_modes)
+		ret = serdes->chip_data->panel_ops->get_modes(serdes);
+
+	SERDES_DBG_MFD("%s: %s node=%s\n", __func__,
+		       serdes->chip_data->name, panel->dev->of_node->name);
+
+	return ret;
+}
+
+static const struct drm_panel_funcs serdes_panel_funcs = {
+	.prepare = serdes_panel_prepare,
+	.unprepare = serdes_panel_unprepare,
+	.enable = serdes_panel_enable,
+	.disable = serdes_panel_disable,
+	.get_modes = serdes_panel_get_modes,
+};
+
+static int serdes_panel_parse_dt(struct serdes_panel *serdes_panel)
+{
+	struct device *dev = serdes_panel->dev;
+	struct display_timing dt;
+	struct videomode vm;
+	int ret, len;
+	unsigned int panel_size[2] = {320, 180};
+
+	serdes_panel->width_mm = panel_size[0];
+	serdes_panel->height_mm = panel_size[1];
+
+	if (of_find_property(dev->of_node, "panel-size", &len)) {
+		len /= sizeof(unsigned int);
+		ret = of_property_read_u32_array(dev->of_node, "panel-size",
+						 panel_size, len);
+		if (!ret) {
+			serdes_panel->width_mm = panel_size[0];
+			serdes_panel->height_mm = panel_size[1];
+		}
+	}
+
+	dev_info(dev, "panle size %dx%d\n",
+		 serdes_panel->width_mm, serdes_panel->height_mm);
+
+	ret = of_get_display_timing(dev->of_node, "panel-timing", &dt);
+	if (ret < 0) {
+		dev_err(dev, "%pOF:serdes no panel-timing node found\n", dev->of_node);
+		return ret;
+	}
+
+	videomode_from_timing(&dt, &vm);
+	drm_display_mode_from_videomode(&vm, &serdes_panel->mode);
+
+	return 0;
+}
+
+static int serdes_panel_probe(struct platform_device *pdev)
+{
+	struct serdes *serdes = dev_get_drvdata(pdev->dev.parent);
+	struct device *dev = &pdev->dev;
+	struct serdes_panel *serdes_panel;
+	int ret;
+
+	serdes_panel = devm_kzalloc(dev, sizeof(*serdes_panel), GFP_KERNEL);
+	if (!serdes_panel)
+		return -ENOMEM;
+
+	serdes->serdes_panel = serdes_panel;
+	serdes_panel->dev = dev;
+	serdes_panel->parent = dev_get_drvdata(dev->parent);
+	platform_set_drvdata(pdev, serdes_panel);
+
+	serdes_panel->regmap = dev_get_regmap(dev->parent, NULL);
+	if (!serdes_panel->regmap)
+		return dev_err_probe(dev, -ENODEV, "failed to get serdes regmap\n");
+
+	ret = serdes_panel_parse_dt(serdes_panel);
+	if (ret)
+		return dev_err_probe(dev, ret, "failed to parse serdes DT\n");
+
+	serdes_panel->backlight = devm_of_find_backlight(dev);
+	if (IS_ERR(serdes_panel->backlight))
+		return dev_err_probe(dev, PTR_ERR(serdes_panel->backlight),
+				     "failed to get serdes backlight\n");
+
+	if (serdes_panel->parent->chip_data->connector_type) {
+		drm_panel_init(&serdes_panel->panel, dev, &serdes_panel_funcs,
+			       serdes_panel->parent->chip_data->connector_type);
+	} else {
+		drm_panel_init(&serdes_panel->panel, dev, &serdes_panel_funcs,
+			       DRM_MODE_CONNECTOR_LVDS);
+	}
+	drm_panel_add(&serdes_panel->panel);
+
+	dev_info(dev, "serdes %s serdes_panel_probe successful\n", serdes->chip_data->name);
+
+	return 0;
+}
+
+static int serdes_panel_remove(struct platform_device *pdev)
+{
+	struct serdes_panel *serdes_panel = platform_get_drvdata(pdev);
+
+	drm_panel_remove(&serdes_panel->panel);
+
+	return 0;
+}
+
+static const struct of_device_id serdes_panel_of_match[] = {
+	{ .compatible = "rohm,bu18tl82-panel" },
+	{ .compatible = "rohm,bu18rl82-panel" },
+	{ .compatible = "maxim,max96752-panel" },
+	{ .compatible = "maxim,max96772-panel" },
+	{ .compatible = "rockchip,rkx121-panel" },
+	{ }
+};
+
+static struct platform_driver serdes_panel_driver = {
+	.driver = {
+		.name = "serdes-panel",
+		.of_match_table = of_match_ptr(serdes_panel_of_match),
+	},
+	.probe = serdes_panel_probe,
+	.remove = serdes_panel_remove,
+};
+
+static int __init serdes_panel_init(void)
+{
+	return platform_driver_register(&serdes_panel_driver);
+}
+device_initcall(serdes_panel_init);
+
+static void __exit serdes_panel_exit(void)
+{
+	platform_driver_unregister(&serdes_panel_driver);
+}
+module_exit(serdes_panel_exit);
+
+MODULE_AUTHOR("Luo Wei <lw@rock-chips.com>");
+MODULE_DESCRIPTION("display panel interface for different serdes");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:serdes-panel");
diff --git a/kernel/drivers/mfd/display-serdes/serdes-pinctrl.c b/kernel/drivers/mfd/display-serdes/serdes-pinctrl.c
new file mode 100644
index 0000000..9564e3d
--- /dev/null
+++ b/kernel/drivers/mfd/display-serdes/serdes-pinctrl.c
@@ -0,0 +1,393 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * serdes-pinctrl.c  -- serdes pin control driver.
+ *
+ * Copyright (c) 2023-2028 Rockchip Electronics Co. Ltd.
+ *
+ * Author: luowei <lw@rock-chips.com>
+ */
+
+#include "core.h"
+
+static const struct mfd_cell serdes_gpio_bu18tl82_devs[] = {
+	{
+		.name = "serdes-gpio",
+		.of_compatible = "rohm,bu18tl82-gpio",
+	},
+};
+
+static const struct mfd_cell serdes_gpio_bu18rl82_devs[] = {
+	{
+		.name = "serdes-gpio",
+		.of_compatible = "rohm,bu18rl82-gpio",
+	},
+};
+
+static const struct mfd_cell serdes_gpio_max96745_devs[] = {
+	{
+		.name = "serdes-gpio",
+		.of_compatible = "maxim,max96745-gpio",
+	},
+};
+
+static const struct mfd_cell serdes_gpio_max96755_devs[] = {
+	{
+		.name = "serdes-gpio",
+		.of_compatible = "maxim,max96755-gpio",
+	},
+};
+
+static const struct mfd_cell serdes_gpio_max96789_devs[] = {
+	{
+		.name = "serdes-gpio",
+		.of_compatible = "maxim,max96789-gpio",
+	},
+};
+
+static const struct mfd_cell serdes_gpio_max96752_devs[] = {
+	{
+		.name = "serdes-gpio",
+		.of_compatible = "maxim,max96752-gpio",
+	},
+};
+
+static const struct mfd_cell serdes_gpio_max96772_devs[] = {
+	{
+		.name = "serdes-gpio",
+		.of_compatible = "maxim,max96772-gpio",
+	},
+};
+
+static const struct mfd_cell serdes_gpio_rkx111_devs[] = {
+	{
+		.name = "serdes-gpio",
+		.of_compatible = "rockchip,rkx111-gpio",
+	},
+};
+
+static const struct mfd_cell serdes_gpio_rkx121_devs[] = {
+	{
+		.name = "serdes-gpio",
+		.of_compatible = "rockchip,rkx121-gpio",
+	},
+};
+
+static const struct mfd_cell serdes_gpio_nca9539_devs[] = {
+	{
+		.name = "serdes-gpio",
+		.of_compatible = "novo,nca9539-gpio",
+	},
+};
+
+static int serdes_pinmux_set_mux(struct pinctrl_dev *pctldev,
+				 unsigned int function, unsigned int group)
+{
+	struct serdes_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctldev);
+	struct serdes *serdes = pinctrl->parent;
+	int ret = 0;
+
+	if (serdes->chip_data->pinctrl_ops->set_mux)
+		ret = serdes->chip_data->pinctrl_ops->set_mux(serdes, function, group);
+
+	SERDES_DBG_MFD("%s: %s function=%d,group=%d\n", __func__,
+		 serdes->chip_data->name, function, group);
+	return ret;
+}
+
+static int serdes_pinconf_get(struct pinctrl_dev *pctldev,
+			      unsigned int pin, unsigned long *config)
+{
+	struct serdes_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctldev);
+	//enum pin_config_param param = pinconf_to_config_param(*config);
+	struct serdes *serdes = pinctrl->parent;
+	int ret = 0;
+
+	if (serdes->chip_data->pinctrl_ops->pin_config_get)
+		ret = serdes->chip_data->pinctrl_ops->pin_config_get(serdes,
+								     pin - pinctrl->pin_base,
+								     config);
+
+	SERDES_DBG_MFD("%s: %s pin=%d,config=%d\n", __func__,
+		       serdes->chip_data->name,
+		       pin - pinctrl->pin_base, pinconf_to_config_param(*config));
+	return ret;
+}
+
+static int serdes_pinconf_set(struct pinctrl_dev *pctldev,
+			      unsigned int pin, unsigned long *configs,
+			      unsigned int num_configs)
+{
+	struct serdes_pinctrl *pinctrl = pinctrl_dev_get_drvdata(pctldev);
+	//enum pin_config_param param = pinconf_to_config_param(*configs);
+	struct serdes *serdes = pinctrl->parent;
+	int ret = 0;
+
+	if (serdes->chip_data->pinctrl_ops->pin_config_set)
+		ret = serdes->chip_data->pinctrl_ops->pin_config_set(serdes,
+								     pin - pinctrl->pin_base,
+								     configs, num_configs);
+
+	SERDES_DBG_MFD("%s: %s pin=%d,config=%d\n", __func__, serdes->chip_data->name,
+		       pin - pinctrl->pin_base, pinconf_to_config_param(*configs));
+	return ret;
+}
+
+static const struct pinconf_ops serdes_pinconf_ops = {
+	.pin_config_get = serdes_pinconf_get,
+	.pin_config_set = serdes_pinconf_set,
+};
+
+static const struct pinmux_ops serdes_pinmux_ops = {
+	.get_functions_count = pinmux_generic_get_function_count,
+	.get_function_name = pinmux_generic_get_function_name,
+	.get_function_groups = pinmux_generic_get_function_groups,
+	.set_mux = serdes_pinmux_set_mux,
+};
+
+static const struct pinctrl_ops serdes_pinctrl_ops = {
+	.get_groups_count = pinctrl_generic_get_group_count,
+	.get_group_name = pinctrl_generic_get_group_name,
+	.get_group_pins = pinctrl_generic_get_group_pins,
+	.dt_node_to_map = pinconf_generic_dt_node_to_map_all,
+	.dt_free_map = pinconf_generic_dt_free_map,
+};
+
+static int serdes_pinctrl_gpio_init(struct serdes *serdes)
+{
+	struct serdes_chip_data *chip_data = serdes->chip_data;
+	struct serdes_pinctrl *pinctrl = serdes->pinctrl;
+	const struct mfd_cell *serdes_devs = NULL;
+	int ret = 0;
+	int mfd_num = 0;
+
+	switch (chip_data->serdes_id) {
+	case ROHM_ID_BU18TL82:
+		serdes_devs = serdes_gpio_bu18tl82_devs;
+		mfd_num = ARRAY_SIZE(serdes_gpio_bu18tl82_devs);
+		break;
+	case ROHM_ID_BU18RL82:
+		serdes_devs = serdes_gpio_bu18rl82_devs;
+		mfd_num = ARRAY_SIZE(serdes_gpio_bu18rl82_devs);
+		break;
+	case MAXIM_ID_MAX96745:
+		serdes_devs = serdes_gpio_max96745_devs;
+		mfd_num = ARRAY_SIZE(serdes_gpio_max96745_devs);
+		break;
+	case MAXIM_ID_MAX96752:
+		serdes_devs = serdes_gpio_max96752_devs;
+		mfd_num = ARRAY_SIZE(serdes_gpio_max96752_devs);
+		break;
+	case MAXIM_ID_MAX96755:
+		serdes_devs = serdes_gpio_max96755_devs;
+		mfd_num = ARRAY_SIZE(serdes_gpio_max96755_devs);
+		break;
+	case MAXIM_ID_MAX96772:
+		serdes_devs = serdes_gpio_max96772_devs;
+		mfd_num = ARRAY_SIZE(serdes_gpio_max96772_devs);
+		break;
+	case MAXIM_ID_MAX96789:
+		serdes_devs = serdes_gpio_max96789_devs;
+		mfd_num = ARRAY_SIZE(serdes_gpio_max96789_devs);
+		break;
+	case ROCKCHIP_ID_RKX111:
+		serdes_devs = serdes_gpio_rkx111_devs;
+		mfd_num = ARRAY_SIZE(serdes_gpio_rkx111_devs);
+		break;
+	case ROCKCHIP_ID_RKX121:
+		serdes_devs = serdes_gpio_rkx121_devs;
+		mfd_num = ARRAY_SIZE(serdes_gpio_rkx121_devs);
+		break;
+	case NOVO_ID_NCA9539:
+		serdes_devs = serdes_gpio_nca9539_devs;
+		mfd_num = ARRAY_SIZE(serdes_gpio_nca9539_devs);
+		break;
+	default:
+		dev_info(serdes->dev, "%s: unknown device\n", __func__);
+		break;
+
+	}
+
+	ret = devm_mfd_add_devices(pinctrl->dev, PLATFORM_DEVID_AUTO, serdes_devs,
+				   mfd_num, NULL, 0, NULL);
+	if (ret != 0)
+		dev_err(pinctrl->dev, "Failed to add serdes children\n");
+
+	return ret;
+}
+
+static int serdes_pinctrl_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct serdes *serdes = dev_get_drvdata(pdev->dev.parent);
+	const struct serdes_chip_data *chip_data = serdes->chip_data;
+	struct serdes_pinctrl *serdes_pinctrl;
+	struct pinctrl_desc *pinctrl_desc;
+	const struct serdes_chip_pinctrl_info *pinctrl_info;
+	struct device_node *child;
+	const char *list_name = "gpio-ranges";
+	struct of_phandle_args of_args;
+	int pin_base = 0;
+	int i, j, ret;
+
+	if (!serdes->dev)
+		return -1;
+
+	pinctrl_info = chip_data->pinctrl_info;
+	serdes_pinctrl = devm_kzalloc(dev, sizeof(*serdes_pinctrl), GFP_KERNEL);
+	if (!serdes_pinctrl)
+		return -ENOMEM;
+
+	serdes_pinctrl->dev = dev;
+	serdes_pinctrl->parent = serdes;
+	serdes->pinctrl = serdes_pinctrl;
+	platform_set_drvdata(pdev, serdes_pinctrl);
+
+	serdes_pinctrl->regmap = dev_get_regmap(dev->parent, NULL);
+	if (!serdes_pinctrl->regmap)
+		return dev_err_probe(dev, -ENODEV, "failed to get serdes regmap\n");
+
+	pinctrl_desc = devm_kzalloc(dev, sizeof(*pinctrl_desc), GFP_KERNEL);
+	if (!pinctrl_desc)
+		return -ENOMEM;
+
+	pinctrl_desc->name = dev_name(dev);
+	pinctrl_desc->owner = THIS_MODULE;
+	pinctrl_desc->pctlops = &serdes_pinctrl_ops;
+	pinctrl_desc->pmxops = &serdes_pinmux_ops;
+	pinctrl_desc->confops = &serdes_pinconf_ops;
+
+	pinctrl_desc->npins = pinctrl_info->num_pins;
+	serdes_pinctrl->pdesc = devm_kcalloc(dev,
+					     pinctrl_info->num_pins,
+					     sizeof(*serdes_pinctrl->pdesc),
+					     GFP_KERNEL);
+	pinctrl_desc->pins = serdes_pinctrl->pdesc;
+	if (!serdes_pinctrl->pdesc)
+		return -ENOMEM;
+
+	serdes_pinctrl->pinctrl_desc = pinctrl_desc;
+
+	for_each_available_child_of_node(dev->of_node, child) {
+		ret = of_parse_phandle_with_fixed_args(child, list_name, 3, 0, &of_args);
+		if (ret) {
+			dev_err(dev, "Unable to parse %s list property in %s node\n",
+				list_name, child->name);
+		} else {
+			pin_base = of_args.args[1];
+			SERDES_DBG_MFD("%s:gpio-range=<%d %d>\n", __func__, pin_base,
+				       pin_base + of_args.args[2]);
+		}
+	}
+
+	if (pin_base) {
+		for (i = 0; i < pinctrl_info->num_pins; i++) {
+			serdes_pinctrl->pdesc[i].number = pinctrl_info->pins[i].number + pin_base;
+			serdes_pinctrl->pdesc[i].name = kasprintf(GFP_KERNEL, "%s-gpio%d",
+								  pinctrl_info->pins[i].name,
+								  serdes_pinctrl->pdesc[i].number);
+			SERDES_DBG_MFD("%s:pdesc number=%d, name=%s\n", __func__,
+				       serdes_pinctrl->pdesc[i].number,
+				       serdes_pinctrl->pdesc[i].name);
+		}
+	} else {
+		dev_info(serdes->dev, "no pinctrl setting\n");
+		return 0;
+	}
+
+	serdes_pinctrl->pin_base = pin_base;
+
+	/* Add pinctrl */
+	ret = devm_pinctrl_register_and_init(dev, pinctrl_desc, serdes_pinctrl,
+					     &serdes_pinctrl->pctl);
+	if (ret)
+		return dev_err_probe(dev, ret, "failed to register serdes pinctrl\n");
+
+	for (i = 0; i < pinctrl_info->num_groups; i++) {
+		struct group_desc *group = &pinctrl_info->groups[i];
+		int *grp_pins = devm_kcalloc(dev,
+					     group->num_pins, sizeof(*group->pins), GFP_KERNEL);
+
+		for (j = 0; j < group->num_pins; j++) {
+			grp_pins[j] = pinctrl_info->groups[i].pins[j] + pin_base;
+			SERDES_DBG_MFD("%s group name %s pin %d base=%d\n", __func__,
+				       pinctrl_info->groups[i].name, grp_pins[j], pin_base);
+		}
+
+		ret = pinctrl_generic_add_group(serdes_pinctrl->pctl, group->name,
+						grp_pins, group->num_pins, group->data);
+		if (ret < 0) {
+			dev_err(dev, "Failed to register serdes group %s\n",
+				group->name);
+			return ret;
+		}
+	}
+
+	for (i = 0; i < pinctrl_info->num_functions; i++) {
+		const struct function_desc *func = &pinctrl_info->functions[i];
+
+		ret = pinmux_generic_add_function(serdes_pinctrl->pctl, func->name,
+						  func->group_names, func->num_group_names,
+						  func->data);
+		if (ret < 0) {
+			dev_err(dev, "Failed to register serdes function %s\n",
+				func->name);
+			return ret;
+		}
+	}
+
+	ret = pinctrl_enable(serdes_pinctrl->pctl);
+
+	ret = serdes_pinctrl_gpio_init(serdes);
+
+	/* pinctrl state*/
+	serdes->pinctrl_node = devm_pinctrl_get(dev);
+	if (!IS_ERR(serdes->pinctrl_node)) {
+		serdes->pins_default =
+			pinctrl_lookup_state(serdes->pinctrl_node, PINCTRL_STATE_DEFAULT);
+		serdes->pins_sleep =
+			pinctrl_lookup_state(serdes->pinctrl_node, PINCTRL_STATE_SLEEP);
+	}
+
+	dev_info(dev, "%s %s serdes_pinctrl_probe successful, pin_base=%d\n",
+		 dev_name(dev->parent), serdes->chip_data->name, pin_base);
+
+	return ret;
+}
+
+static const struct of_device_id serdes_pinctrl_of_match[] = {
+	{ .compatible = "rohm,bu18tl82-pinctrl" },
+	{ .compatible = "rohm,bu18rl82-pinctrl" },
+	{ .compatible = "maxim,max96745-pinctrl" },
+	{ .compatible = "maxim,max96752-pinctrl" },
+	{ .compatible = "maxim,max96755-pinctrl" },
+	{ .compatible = "maxim,max96772-pinctrl" },
+	{ .compatible = "rockchip,rkx111-pinctrl" },
+	{ .compatible = "rockchip,rkx121-pinctrl" },
+	{ .compatible = "novo,nca9539-pinctrl" },
+	{ }
+};
+
+static struct platform_driver serdes_pinctrl_driver = {
+	.driver = {
+		.name = "serdes-pinctrl",
+		.of_match_table = of_match_ptr(serdes_pinctrl_of_match),
+	},
+	.probe = serdes_pinctrl_probe,
+};
+
+static int __init serdes_pinctrl_init(void)
+{
+	return platform_driver_register(&serdes_pinctrl_driver);
+}
+subsys_initcall_sync(serdes_pinctrl_init);
+
+static void __exit serdes_pinctrl_exit(void)
+{
+	platform_driver_unregister(&serdes_pinctrl_driver);
+}
+module_exit(serdes_pinctrl_exit);
+
+MODULE_AUTHOR("Luo Wei <lw@rock-chips.com>");
+MODULE_DESCRIPTION("display pinctrl interface for different serdes");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:serdes-pinctrl");
diff --git a/kernel/drivers/mfd/max96755f.c b/kernel/drivers/mfd/max96755f.c
index db9d3a2..04618d4 100644
--- a/kernel/drivers/mfd/max96755f.c
+++ b/kernel/drivers/mfd/max96755f.c
@@ -48,8 +48,6 @@
 {
 	switch (reg) {
 	case 0x0002:
-	case 0x0010:
-	case 0x0013:
 	case 0x0053:
 	case 0x0057:
 	case 0x02be ... 0x02fc:
diff --git a/kernel/drivers/mfd/rkx110_x120/Kconfig b/kernel/drivers/mfd/rkx110_x120/Kconfig
new file mode 100644
index 0000000..e8af358
--- /dev/null
+++ b/kernel/drivers/mfd/rkx110_x120/Kconfig
@@ -0,0 +1,18 @@
+# SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+
+config MFD_RKX110_X120
+	tristate "Rockchip RKx110 RKx120 SerDes MFD Driver"
+	depends on I2C
+	depends on OF
+	select MFD_CORE
+	help
+	  if you say yes here you get support for the RKx110 RKx120 from Rockchip.
+
+config ROCKCHIP_SERDES_DRM_PANEL
+	tristate "Generic RK Serdes panel driver"
+	depends on OF
+	depends on BACKLIGHT_CLASS_DEVICE
+	select VIDEOMODE_HELPERS
+	help
+	  This driver supports rk serdes panels.
+
diff --git a/kernel/drivers/mfd/rkx110_x120/Makefile b/kernel/drivers/mfd/rkx110_x120/Makefile
new file mode 100644
index 0000000..c0b23cb
--- /dev/null
+++ b/kernel/drivers/mfd/rkx110_x120/Makefile
@@ -0,0 +1,23 @@
+# SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+
+obj-$(CONFIG_MFD_RKX110_X120) += rkx110_x120.o
+rkx110_x120-objs := \
+	hal/cru_core.o \
+	hal/cru_rkx110.o \
+	hal/cru_rkx120.o \
+	hal/cru_rkx111.o \
+	hal/cru_rkx121.o \
+	hal/pinctrl_rkx110_x120.o \
+	pattern_gen.o \
+	rkx110_combrxphy.o \
+	rkx110_dsi_rx.o \
+	rkx110_x120_core.o \
+	rkx110_linktx.o \
+	rkx110.o \
+	rkx120.o \
+	rkx120_combtxphy.o \
+	rkx120_dsi_tx.o \
+	rkx120_linkrx.o \
+	serdes_combphy.o
+
+obj-$(CONFIG_ROCKCHIP_SERDES_DRM_PANEL) += rkx110_x120_panel.o
diff --git a/kernel/drivers/mfd/rkx110_x120/hal/cru_api.h b/kernel/drivers/mfd/rkx110_x120/hal/cru_api.h
new file mode 100644
index 0000000..97de82c
--- /dev/null
+++ b/kernel/drivers/mfd/rkx110_x120/hal/cru_api.h
@@ -0,0 +1,210 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2022 Rockchip Electronics Co. Ltd.
+ *
+ * Author: Joseph Chen <chenjh@rock-chips.com>
+ */
+
+#ifndef _CRU_API_H_
+#define _CRU_API_H_
+
+#include "hal_def.h"
+#include "hal_os_def.h"
+#include "cru_core.h"
+#include "cru_rkx110.h"
+#include "cru_rkx120.h"
+#include "cru_rkx111.h"
+#include "cru_rkx121.h"
+
+static HAL_DEFINE_MUTEX(top_lock);
+
+static inline bool hwclk_is_enabled(struct hwclk *hw, uint32_t clk)
+{
+    bool ret;
+
+    HAL_MutexLock(&hw->lock);
+    ret = HAL_CRU_ClkIsEnabled(hw, clk);
+    HAL_MutexUnlock(&hw->lock);
+
+    return ret;
+}
+
+static inline int hwclk_enable(struct hwclk *hw, uint32_t clk)
+{
+    int ret;
+
+    HAL_MutexLock(&hw->lock);
+    ret = HAL_CRU_ClkEnable(hw, clk);
+    HAL_MutexUnlock(&hw->lock);
+
+    return ret;
+}
+
+static inline int hwclk_disable(struct hwclk *hw, uint32_t clk)
+{
+    int ret;
+
+    HAL_MutexLock(&hw->lock);
+    ret = HAL_CRU_ClkDisable(hw, clk);
+    HAL_MutexUnlock(&hw->lock);
+
+    return ret;
+}
+
+static inline bool hwclk_is_reset(struct hwclk *hw, uint32_t clk)
+{
+    bool ret;
+
+    HAL_MutexLock(&hw->lock);
+    ret = HAL_CRU_ClkIsReset(hw, clk);
+    HAL_MutexUnlock(&hw->lock);
+
+    return ret;
+}
+
+static inline int hwclk_reset(struct hwclk *hw, uint32_t clk)
+{
+    int ret;
+
+    HAL_MutexLock(&hw->lock);
+    ret = HAL_CRU_ClkResetAssert(hw, clk);
+    HAL_MutexUnlock(&hw->lock);
+
+    return ret;
+}
+
+static inline int hwclk_reset_deassert(struct hwclk *hw, uint32_t clk)
+{
+    int ret;
+
+    HAL_MutexLock(&hw->lock);
+    ret = HAL_CRU_ClkResetDeassert(hw, clk);
+    HAL_MutexUnlock(&hw->lock);
+
+    return ret;
+}
+
+static inline int hwclk_set_div(struct hwclk *hw, uint32_t clk, uint32_t div)
+{
+    int ret;
+
+    HAL_MutexLock(&hw->lock);
+    ret = HAL_CRU_ClkSetDiv(hw, clk, div);
+    HAL_MutexUnlock(&hw->lock);
+
+    return ret;
+}
+
+static inline uint32_t hwclk_get_div(struct hwclk *hw, uint32_t clk)
+{
+    uint32_t ret;
+
+    HAL_MutexLock(&hw->lock);
+    ret = HAL_CRU_ClkGetDiv(hw, clk);
+    HAL_MutexUnlock(&hw->lock);
+
+    return ret;
+}
+
+static inline int hwclk_set_mux(struct hwclk *hw, uint32_t clk, uint32_t mux)
+{
+    int ret;
+
+    HAL_MutexLock(&hw->lock);
+    ret = HAL_CRU_ClkSetMux(hw, clk, mux);
+    HAL_MutexUnlock(&hw->lock);
+
+    return ret;
+}
+
+static inline uint32_t hwclk_get_mux(struct hwclk *hw, uint32_t clk)
+{
+    uint32_t ret;
+
+    HAL_MutexLock(&hw->lock);
+    ret = HAL_CRU_ClkGetMux(hw, clk);
+    HAL_MutexUnlock(&hw->lock);
+
+    return ret;
+}
+
+static inline uint32_t hwclk_get_rate(struct hwclk *hw, uint32_t composite_clk)
+{
+    uint32_t ret;
+
+    HAL_MutexLock(&hw->lock);
+    ret = HAL_CRU_ClkGetFreq(hw, composite_clk);
+    HAL_MutexUnlock(&hw->lock);
+
+    return ret;
+}
+
+static inline int hwclk_set_rate(struct hwclk *hw, uint32_t composite_clk, uint32_t rate)
+{
+    int ret;
+
+    HAL_MutexLock(&hw->lock);
+    ret = HAL_CRU_ClkSetFreq(hw, composite_clk, rate);
+    HAL_MutexUnlock(&hw->lock);
+
+    return ret;
+}
+
+static inline int hwclk_set_testout(struct hwclk *hw, uint32_t composite_clk,
+                                    uint32_t mux, uint32_t div)
+{
+    int ret;
+
+    HAL_MutexLock(&hw->lock);
+    ret = HAL_CRU_ClkSetTestout(hw, composite_clk, mux, div);
+    HAL_MutexUnlock(&hw->lock);
+
+    return ret;
+}
+
+static inline void hwclk_dump_tree(HAL_ClockType type)
+{
+    HAL_MutexLock(&top_lock);
+    HAL_CRU_ClkDumpTree(type);
+    HAL_MutexUnlock(&top_lock);
+}
+
+static inline int hwclk_set_glbsrst(struct hwclk *hw, uint32_t type)
+{
+    int ret;
+
+    HAL_MutexLock(&hw->lock);
+    ret = HAL_CRU_SetGlbSrst(hw, type);
+    HAL_MutexUnlock(&hw->lock);
+
+    return ret;
+}
+
+static inline struct hwclk *hwclk_register(struct xferclk xfer)
+{
+    struct hwclk *ret;
+
+    HAL_MutexLock(&top_lock);
+    ret = HAL_CRU_Register(xfer);
+    HAL_MutexUnlock(&top_lock);
+
+    return ret;
+}
+
+static inline struct hwclk *hwclk_get(void *client)
+{
+    struct hwclk *ret;
+
+    HAL_MutexLock(&top_lock);
+    ret = HAL_CRU_ClkGet(client);
+    HAL_MutexUnlock(&top_lock);
+
+    return ret;
+}
+
+static inline int hwclk_init(void)
+{
+    return HAL_CRU_Init();
+}
+
+#endif
diff --git a/kernel/drivers/mfd/rkx110_x120/hal/cru_core.c b/kernel/drivers/mfd/rkx110_x120/hal/cru_core.c
new file mode 100644
index 0000000..985a30d
--- /dev/null
+++ b/kernel/drivers/mfd/rkx110_x120/hal/cru_core.c
@@ -0,0 +1,904 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2022 Rockchip Electronics Co. Ltd.
+ *
+ * Author: Joseph Chen <chenjh@rock-chips.com>
+ */
+
+#include "cru_core.h"
+
+/********************* Private MACRO Definition ******************************/
+#define REG_X4(i) ((i) * 4)
+
+#define PWRDOWN_SHIFT      13
+#define PWRDOWN_MASK       (1 << PWRDOWN_SHIFT)
+#define PLL_POSTDIV1_SHIFT 12
+#define PLL_POSTDIV1_MASK  (0x7 << PLL_POSTDIV1_SHIFT)
+#define PLL_FBDIV_SHIFT    0
+#define PLL_FBDIV_MASK     (0xfff << PLL_FBDIV_SHIFT)
+#define PLL_POSTDIV2_SHIFT 6
+#define PLL_POSTDIV2_MASK  (0x7 << PLL_POSTDIV2_SHIFT)
+#define PLL_REFDIV_SHIFT   0
+#define PLL_REFDIV_MASK    (0x3f << PLL_REFDIV_SHIFT)
+#define PLL_DSMPD_SHIFT    12
+#define PLL_DSMPD_MASK     (1 << PLL_DSMPD_SHIFT)
+#define PLL_FRAC_SHIFT     0
+#define PLL_FRAC_MASK      (0xffffff << PLL_FRAC_SHIFT)
+
+#define EXPONENT_OF_FRAC_PLL 24
+#define RK_PLL_MODE_SLOW     0
+#define RK_PLL_MODE_NORMAL   1
+#define RK_PLL_MODE_DEEP     2
+#define MHZ                  1000000
+
+#define PLL_GET_PLLMODE(val, shift, mask) \
+        (((uint32_t)(val) & mask) >> shift)
+#define PLL_GET_FBDIV(x) \
+        (((uint32_t)(x) & (PLL_FBDIV_MASK)) >> PLL_FBDIV_SHIFT)
+#define PLL_GET_REFDIV(x) \
+        (((uint32_t)(x) & (PLL_REFDIV_MASK)) >> PLL_REFDIV_SHIFT)
+#define PLL_GET_POSTDIV1(x) \
+         (((uint32_t)(x) & (PLL_POSTDIV1_MASK)) >> PLL_POSTDIV1_SHIFT)
+#define PLL_GET_POSTDIV2(x) \
+        (((uint32_t)(x) & (PLL_POSTDIV2_MASK)) >> PLL_POSTDIV2_SHIFT)
+#define PLL_GET_DSMPD(x) \
+        (((uint32_t)(x) & (PLL_DSMPD_MASK)) >> PLL_DSMPD_SHIFT)
+#define PLL_GET_FRAC(x) \
+        (((uint32_t)(x) & (PLL_FRAC_MASK)) >> PLL_FRAC_SHIFT)
+#define CRU_PLL_ROUND_UP_TO_KHZ(x) \
+        (HAL_DIV_ROUND_UP((x), 1000) * 1000)
+
+static struct hwclk g_hwclk_desc[HAL_HWCLK_MAX_NUM];
+static struct PLL_CONFIG g_AutoTable;
+
+/********************* Private Function Definition ***************************/
+uint32_t HAL_CRU_Read(struct hwclk *hw, uint32_t reg)
+{
+    uint32_t val;
+    int ret;
+
+    ret = hw->xfer.read(hw->xfer.client, reg, &val);
+    if (ret) {
+        CRU_ERR("%s: read reg=0x%08x failed, ret=%d\n", hw->name, reg, ret);
+    }
+
+    CRU_DBGR("%s: %s: reg=0x%08x, val=0x%08x\n", __func__, hw->name, reg, val);
+
+    return val;
+}
+
+uint32_t HAL_CRU_Write(struct hwclk *hw, uint32_t reg, uint32_t val)
+{
+    int ret;
+
+    CRU_DBGR("%s: %s: reg=0x%08x, val=0x%08x\n", __func__, hw->name, reg, val);
+
+    ret = hw->xfer.write(hw->xfer.client, reg, val);
+    if (ret) {
+        CRU_ERR("%s: write reg=0x%08x, val=0x%08x failed, ret=%d\n",
+                hw->name, reg, val, ret);
+    }
+
+    return ret;
+}
+
+uint32_t HAL_CRU_WriteMask(struct hwclk *hw, uint32_t reg,
+                           uint32_t msk, uint32_t val)
+{
+    return HAL_CRU_Write(hw, reg, VAL_MASK_WE(msk, val));
+}
+
+static int isBetterFreq(uint32_t now, uint32_t new, uint32_t best)
+{
+    return (new <= now && new > best);
+}
+
+int HAL_CRU_FreqGetMux4(struct hwclk *hw,
+                        uint32_t freq, uint32_t freq0,
+                        uint32_t freq1, uint32_t freq2, uint32_t freq3)
+{
+    uint32_t best = 0;
+
+    if (isBetterFreq(freq, freq0, best)) {
+        best = freq0;
+    }
+
+    if (isBetterFreq(freq, freq1, best)) {
+        best = freq1;
+    }
+
+    if (isBetterFreq(freq, freq2, best)) {
+        best = freq2;
+    }
+
+    if (isBetterFreq(freq, freq3, best)) {
+        best = freq3;
+    }
+
+    if (best == freq0) {
+        return 0;
+    } else if (best == freq1) {
+        return 1;
+    } else if (best == freq2) {
+        return 2;
+    } else if (best == freq3) {
+        return 3;
+    }
+
+    return HAL_INVAL;
+}
+
+int HAL_CRU_FreqGetMux3(struct hwclk *hw,
+                        uint32_t freq, uint32_t freq0,
+                        uint32_t freq1, uint32_t freq2)
+{
+    return HAL_CRU_FreqGetMux4(hw, freq, freq0, freq1, freq2, freq2);
+}
+
+int HAL_CRU_FreqGetMux2(struct hwclk *hw,
+                        uint32_t freq, uint32_t freq0, uint32_t freq1)
+{
+    return HAL_CRU_FreqGetMux4(hw, freq, freq0, freq1, freq1, freq1);
+}
+
+uint32_t HAL_CRU_MuxGetFreq4(struct hwclk *hw, uint32_t muxName,
+                             uint32_t freq0, uint32_t freq1,
+                             uint32_t freq2, uint32_t freq3)
+{
+    switch (HAL_CRU_ClkGetMux(hw, muxName)) {
+    case 0:
+
+        return freq0;
+    case 1:
+
+        return freq1;
+    case 2:
+
+        return freq2;
+    case 3:
+
+        return freq3;
+    }
+
+    return HAL_INVAL;
+}
+
+uint32_t HAL_CRU_MuxGetFreq3(struct hwclk *hw, uint32_t muxName,
+                             uint32_t freq0, uint32_t freq1, uint32_t freq2)
+{
+    return HAL_CRU_MuxGetFreq4(hw, muxName, freq0, freq1, freq2, freq2);
+}
+
+uint32_t HAL_CRU_MuxGetFreq2(struct hwclk *hw, uint32_t muxName,
+                             uint32_t freq0, uint32_t freq1)
+{
+    return HAL_CRU_MuxGetFreq4(hw, muxName, freq0, freq1, freq1, freq1);
+}
+
+int HAL_CRU_RoundFreqGetMux4(struct hwclk *hw, uint32_t freq,
+                             uint32_t pFreq0, uint32_t pFreq1,
+                             uint32_t pFreq2, uint32_t pFreq3, uint32_t *pFreqOut)
+{
+    uint32_t mux;
+
+    if (pFreq3 && (pFreq3 % freq == 0)) {
+        *pFreqOut = pFreq3;
+        mux = 3;
+    } else if (pFreq2 && (pFreq2 % freq == 0)) {
+        *pFreqOut = pFreq2;
+        mux = 2;
+    } else if (pFreq1 % freq == 0) {
+        *pFreqOut = pFreq1;
+        mux = 1;
+    } else {
+        *pFreqOut = pFreq0;
+        mux = 0;
+    }
+
+    return mux;
+}
+
+int HAL_CRU_RoundFreqGetMux3(struct hwclk *hw, uint32_t freq,
+                             uint32_t pFreq0, uint32_t pFreq1,
+                             uint32_t pFreq2, uint32_t *pFreqOut)
+{
+    return HAL_CRU_RoundFreqGetMux4(hw, freq, pFreq0, pFreq1, pFreq2, 0, pFreqOut);
+}
+
+int HAL_CRU_RoundFreqGetMux2(struct hwclk *hw, uint32_t freq,
+                             uint32_t pFreq0, uint32_t pFreq1, uint32_t *pFreqOut)
+{
+    return HAL_CRU_RoundFreqGetMux4(hw, freq, pFreq0, pFreq1, 0, 0, pFreqOut);
+}
+
+/******************************************************************************/
+uint32_t HAL_CRU_GetPllFreq(struct hwclk *hw, struct PLL_SETUP *pSetup)
+{
+    uint32_t refDiv, fbDiv, postdDv1, postDiv2, frac, dsmpd;
+    uint32_t mode = 0, rate = OSC_24M;
+
+    mode = PLL_GET_PLLMODE(HAL_CRU_Read(hw, pSetup->modeOffset),
+                           pSetup->modeShift,
+                           pSetup->modeMask);
+
+    switch (mode) {
+    case RK_PLL_MODE_SLOW:
+        rate = OSC_24M;
+        break;
+    case RK_PLL_MODE_NORMAL:
+        postdDv1 = PLL_GET_POSTDIV1(HAL_CRU_Read(hw, pSetup->conOffset0));
+        fbDiv = PLL_GET_FBDIV(HAL_CRU_Read(hw, pSetup->conOffset0));
+        postDiv2 = PLL_GET_POSTDIV2(HAL_CRU_Read(hw, pSetup->conOffset1));
+        refDiv = PLL_GET_REFDIV(HAL_CRU_Read(hw, pSetup->conOffset1));
+        dsmpd = PLL_GET_DSMPD(HAL_CRU_Read(hw, pSetup->conOffset1));
+        frac = PLL_GET_FRAC(HAL_CRU_Read(hw, pSetup->conOffset2));
+        rate = (rate / refDiv) * fbDiv;
+        if (dsmpd == 0) {
+            uint64_t fracRate = OSC_24M;
+
+            fracRate *= frac;
+            fracRate = fracRate >> EXPONENT_OF_FRAC_PLL;
+            fracRate = fracRate / refDiv;
+            rate += fracRate;
+        }
+        rate = rate / (postdDv1 * postDiv2);
+        rate = CRU_PLL_ROUND_UP_TO_KHZ(rate);
+        break;
+    case RK_PLL_MODE_DEEP:
+    default:
+        rate = 32768;
+        break;
+    }
+
+    return rate;
+}
+
+static uint32_t CRU_Gcd(uint32_t m, uint32_t n)
+{
+    int t;
+
+    while (m > 0) {
+        if (n > m) {
+            t = m;
+            m = n;
+            n = t;
+        }
+        m -= n;
+    }
+
+    return n;
+}
+
+static uint64_t HAL_DivU64Rem(uint64_t numerator, uint32_t denominator, uint32_t *pRemainder)
+{
+    uint64_t remainder = numerator;
+    uint64_t b = denominator;
+    uint64_t result;
+    uint64_t d = 1;
+    uint32_t high = numerator >> 32;
+
+    result = 0;
+    if (high >= denominator) {
+        high /= denominator;
+        result = (uint64_t)high << 32;
+        remainder -= (uint64_t)(high * denominator) << 32;
+    }
+
+    while ((int64_t)b > 0 && b < remainder) {
+        b = b + b;
+        d = d + d;
+    }
+
+    do {
+        if (remainder >= b) {
+            remainder -= b;
+            result += d;
+        }
+        b >>= 1;
+        d >>= 1;
+    } while (d);
+
+    if (pRemainder) {
+        *pRemainder = remainder;
+    }
+
+    return result;
+}
+
+static inline uint64_t HAL_DivU64(uint64_t numerator, uint32_t denominator)
+{
+    return HAL_DivU64Rem(numerator, denominator, HAL_NULL);
+}
+
+static const struct PLL_CONFIG *CRU_PllSetByAuto(struct hwclk *hw,
+                                                 struct PLL_SETUP *pSetup,
+                                                 uint32_t finHz,
+                                                 uint32_t foutHz)
+{
+    struct PLL_CONFIG *rateTable = &g_AutoTable;
+    uint32_t refDiv, fbDiv, dsmpd;
+    uint32_t postDiv1, postDiv2;
+    uint32_t clkGcd = 0;
+    uint64_t foutVco;
+    uint64_t intVco;
+    uint64_t frac64;
+
+    CRU_DBGF("%s: %s: pll=%d, refdiv: [%u, %u], foutVco: [%u, %u], fout: [%u, %u]\n",
+             __func__, hw->name, pSetup->id, pSetup->minRefdiv, pSetup->maxRefdiv,
+             pSetup->minVco, pSetup->maxVco, pSetup->minFout, pSetup->maxFout);
+
+    if (finHz == 0 || foutHz == 0 || foutHz == finHz) {
+        return HAL_NULL;
+    }
+
+    if ((foutHz < pSetup->minFout) || (foutHz > pSetup->maxFout)) {
+        return HAL_NULL;
+    }
+
+    for (postDiv1 = 1; postDiv1 <= 7; postDiv1++) {
+        for (postDiv2 = 1; postDiv2 <= 7; postDiv2++) {
+            if (postDiv1 < postDiv2) {
+                CRU_DBGF("%s: %s: pll=%d, Invalid postDiv1(%u) < postDiv2(%u)\n",
+                         __func__, hw->name, pSetup->id, postDiv1, postDiv2);
+                continue;
+            }
+
+            foutVco = (uint64_t)foutHz * postDiv1 * postDiv2;
+            if ((foutVco < pSetup->minVco) || (foutVco > pSetup->maxVco)) {
+                CRU_DBGF("%s: %s: pll=%d, Invalid foutVco(%llu), min_max[%u, %u]\n",
+                         __func__, hw->name, pSetup->id, foutVco, pSetup->minVco, pSetup->maxVco);
+                continue;
+            }
+
+            intVco = foutVco / MHZ * MHZ;
+            clkGcd = CRU_Gcd(finHz, intVco);
+            refDiv = finHz / clkGcd;
+            fbDiv = intVco / clkGcd;
+
+            if ((refDiv < pSetup->minRefdiv) || (refDiv > pSetup->maxRefdiv)) {
+                CRU_DBGF("%s: %s: pll=%d, Invalid refDiv(%u), min_max[%u, %u]\n",
+                         __func__, hw->name, pSetup->id, refDiv, pSetup->minRefdiv, pSetup->maxRefdiv);
+                continue;
+            }
+
+            if (foutHz / MHZ * MHZ == foutHz) {
+                dsmpd = 1;
+                frac64 = 0;
+            } else {
+                frac64 = (foutVco % MHZ) * refDiv;
+                frac64 = HAL_DivU64(frac64 << EXPONENT_OF_FRAC_PLL, OSC_24M);
+                if (frac64 > 0) {
+                    dsmpd = 0;
+                } else {
+                    dsmpd = 1;
+                }
+            }
+
+            if (dsmpd && !(fbDiv >= 16 && fbDiv <= 2500)) {
+                CRU_DBGF("%s: %s: pll=%d, Invalid fbDiv(%u) on int mode, min_max[16, 2500]\n",
+                         __func__, hw->name, pSetup->id, fbDiv);
+                continue;
+            }
+            if (!dsmpd && !(fbDiv >= 20 && fbDiv <= 500)) {
+                CRU_DBGF("%s: %s: pll=%d, Invalid fbDiv(%u) on frac mode, min_max[20, 500]\n",
+                         __func__, hw->name, pSetup->id, fbDiv);
+                continue;
+            }
+            if (frac64 > 0x00ffffff) {
+                CRU_DBGF("%s: %s: pll=%d, Invalid frac64(0x%llx) over max 0x00ffffff\n",
+                         __func__, hw->name, pSetup->id, frac64);
+                continue;
+            }
+
+            rateTable->refDiv = refDiv;
+            rateTable->fbDiv = fbDiv;
+            rateTable->postDiv1 = postDiv1;
+            rateTable->postDiv2 = postDiv2;
+            rateTable->dsmpd = dsmpd;
+            rateTable->frac = frac64;
+
+            return rateTable;
+        }
+    }
+
+    return HAL_NULL;
+}
+
+static const struct PLL_CONFIG *CRU_PllGetSettings(struct hwclk *hw,
+                                                   struct PLL_SETUP *pSetup,
+                                                   uint32_t rate)
+{
+    const struct PLL_CONFIG *rateTable = pSetup->rateTable;
+
+    if (!rateTable) {
+        CRU_ERR("%s: %s: Unavailable pll=%d rate table\n", __func__, hw->name, pSetup->id);
+
+        return HAL_NULL;
+    }
+
+    while (rateTable->rate) {
+        if (rateTable->rate == rate) {
+            return rateTable;
+        }
+        rateTable++;
+    }
+
+    rateTable = CRU_PllSetByAuto(hw, pSetup, OSC_24M, rate);
+    if (!rateTable) {
+        CRU_ERR("%s: %s: Unsupported pll=%d rate %d\n", __func__, hw->name, pSetup->id, rate);
+
+        return HAL_NULL;
+    } else {
+        CRU_MSG("%s: Auto PLL=%d: (%d, %d, %d, %d, %d, %d, %d)\n",
+                hw->name, pSetup->id, rate, rateTable->refDiv, rateTable->fbDiv,
+                rateTable->postDiv1, rateTable->postDiv2,
+                rateTable->dsmpd, rateTable->frac);
+    }
+
+    return rateTable;
+}
+
+/*
+ * Force PLL into slow mode
+ * Pll Power down
+ * Pll Config fbDiv, refDiv, postdDv1, postDiv2, dsmpd, frac
+ * Pll Power up
+ * Waiting for pll lock
+ * Force PLL into normal mode
+ */
+HAL_Status HAL_CRU_SetPllFreq(struct hwclk *hw, struct PLL_SETUP *pSetup, uint32_t rate)
+{
+    const struct PLL_CONFIG *pConfig;
+    int delay = 2400;
+
+    if (rate == HAL_CRU_GetPllFreq(hw, pSetup)) {
+        return HAL_OK;
+    }
+
+    pConfig = CRU_PllGetSettings(hw, pSetup, rate);
+    if (!pConfig) {
+        return HAL_ERROR;
+    }
+
+    /* Force PLL into slow mode to ensure output stable clock */
+    HAL_CRU_WriteMask(hw, pSetup->modeOffset, pSetup->modeMask, RK_PLL_MODE_SLOW << pSetup->modeShift);
+
+    /* Pll Power down */
+    HAL_CRU_WriteMask(hw, pSetup->conOffset1, PWRDOWN_MASK, 1 << PWRDOWN_SHIFT);
+
+    /* Pll Config */
+    HAL_CRU_WriteMask(hw, pSetup->conOffset1, PLL_POSTDIV2_MASK, pConfig->postDiv2 << PLL_POSTDIV2_SHIFT);
+    HAL_CRU_WriteMask(hw, pSetup->conOffset1, PLL_REFDIV_MASK, pConfig->refDiv << PLL_REFDIV_SHIFT);
+    HAL_CRU_WriteMask(hw, pSetup->conOffset0, PLL_POSTDIV1_MASK, pConfig->postDiv1 << PLL_POSTDIV1_SHIFT);
+    HAL_CRU_WriteMask(hw, pSetup->conOffset0, PLL_FBDIV_MASK, pConfig->fbDiv << PLL_FBDIV_SHIFT);
+    if (pSetup->sscgEn) {
+        HAL_CRU_WriteMask(hw, pSetup->conOffset1, PLL_DSMPD_MASK, 0 << PLL_DSMPD_SHIFT);
+    } else {
+        HAL_CRU_WriteMask(hw, pSetup->conOffset1, PLL_DSMPD_MASK, pConfig->dsmpd << PLL_DSMPD_SHIFT);
+    }
+
+    if (pConfig->frac) {
+        HAL_CRU_Write(hw, pSetup->conOffset2, (HAL_CRU_Read(hw, pSetup->conOffset2) & 0xff000000) | pConfig->frac);
+    }
+
+    /* Pll Power up */
+    HAL_CRU_WriteMask(hw, pSetup->conOffset1, PWRDOWN_MASK, 0 << PWRDOWN_SHIFT);
+
+    /* Waiting for pll lock */
+    while (delay > 0) {
+        if (pSetup->stat0) {
+            if (HAL_CRU_Read(hw, pSetup->stat0) & (1 << pSetup->lockShift)) {
+                break;
+            }
+        } else {
+            if (HAL_CRU_Read(hw, pSetup->conOffset1) & (1 << pSetup->lockShift)) {
+                break;
+            }
+        }
+        HAL_DelayUs(2);
+        delay--;
+    }
+    if (delay == 0) {
+        return HAL_TIMEOUT;
+    }
+
+    /* Force PLL into normal mode */
+    HAL_CRU_WriteMask(hw, pSetup->modeOffset, pSetup->modeMask, RK_PLL_MODE_NORMAL << pSetup->modeShift);
+
+    return HAL_OK;
+}
+
+HAL_Status HAL_CRU_SetPllPowerUp(struct hwclk *hw, struct PLL_SETUP *pSetup)
+{
+    int delay = 2400;
+
+    /* Pll Power up */
+    HAL_CRU_WriteMask(hw, pSetup->conOffset1, PWRDOWN_MASK, 0 << PWRDOWN_SHIFT);
+
+    /* Waiting for pll lock */
+    while (delay > 0) {
+        if (pSetup->stat0) {
+            if (HAL_CRU_Read(hw, pSetup->stat0) & (1 << pSetup->lockShift)) {
+                break;
+            }
+        } else {
+            if (HAL_CRU_Read(hw, pSetup->conOffset1) & (1 << pSetup->lockShift)) {
+                break;
+            }
+        }
+        HAL_DelayUs(1000);
+        delay--;
+    }
+    if (delay == 0) {
+        return HAL_TIMEOUT;
+    }
+
+    return HAL_OK;
+}
+
+HAL_Status HAL_CRU_SetPllPowerDown(struct hwclk *hw, struct PLL_SETUP *pSetup)
+{
+    HAL_CRU_Write(hw, pSetup->conOffset1, VAL_MASK_WE(PWRDOWN_MASK, 1 << PWRDOWN_SHIFT));
+
+    return HAL_OK;
+}
+
+HAL_Check HAL_CRU_ClkIsEnabled(struct hwclk *hw, uint32_t clk)
+{
+    uint32_t index = CLK_GATE_GET_REG_OFFSET(clk);
+    uint32_t shift = CLK_GATE_GET_BITS_SHIFT(clk);
+
+    return (HAL_Check)((HAL_CRU_Read(hw, hw->gate_con0 + REG_X4(index)) & (1 << shift)) >> shift);
+}
+
+HAL_Status HAL_CRU_ClkEnable(struct hwclk *hw, uint32_t clk)
+{
+    uint32_t index = CLK_GATE_GET_REG_OFFSET(clk);
+    uint32_t shift = CLK_GATE_GET_BITS_SHIFT(clk);
+    uint32_t gid = 16 * index + shift;
+
+    if (gid >= hw->num_gate) {
+        CRU_ERR("%s: %s: clock(#%08x) is unsupported, gid=%d\n", __func__, hw->name, clk, gid);
+
+        return HAL_INVAL;
+    }
+
+    if (hw->gate[gid].enable_count == 0) {
+        CRU_DBGF("%s: %s: clock(#%08x), gid=%d\n", __func__, hw->name, clk, gid);
+        HAL_CRU_Write(hw, hw->gate_con0 + REG_X4(index), VAL_MASK_WE(1U << shift, 0U << shift));
+    }
+
+    hw->gate[gid].enable_count++;
+
+    return HAL_OK;
+}
+
+HAL_Status HAL_CRU_ClkDisable(struct hwclk *hw, uint32_t clk)
+{
+    uint32_t index = CLK_GATE_GET_REG_OFFSET(clk);
+    uint32_t shift = CLK_GATE_GET_BITS_SHIFT(clk);
+    uint32_t gid = 16 * index + shift;
+
+    if (gid >= hw->num_gate) {
+        CRU_ERR("%s: %s: clock(#%08x) is unsupported\n", __func__, hw->name, clk);
+
+        return HAL_INVAL;
+    }
+
+    if (hw->gate[gid].enable_count == 0) {
+        CRU_ERR("%s: %s: clock(#%08x) is already disabled\n", __func__, hw->name, clk);
+
+        return HAL_OK;
+    }
+
+    hw->gate[gid].enable_count--;
+    if (hw->gate[gid].enable_count == 0) {
+        CRU_DBGF("%s: %s: clock(#%08x), gid=%d\n", __func__, hw->name, clk, gid);
+        HAL_CRU_Write(hw, hw->gate_con0 + REG_X4(index), VAL_MASK_WE(1U << shift, 1U << shift));
+    }
+
+    return HAL_OK;
+}
+
+HAL_Check HAL_CRU_ClkIsReset(struct hwclk *hw, uint32_t clk)
+{
+    uint32_t index = CLK_GATE_GET_REG_OFFSET(clk);
+    uint32_t shift = CLK_GATE_GET_BITS_SHIFT(clk);
+
+    return (HAL_Check)((HAL_CRU_Read(hw, hw->softrst_con0 + REG_X4(index)) & (1 << shift)) >> shift);
+}
+
+HAL_Status HAL_CRU_ClkResetAssert(struct hwclk *hw, uint32_t clk)
+{
+    uint32_t index = CLK_RESET_GET_REG_OFFSET(clk);
+    uint32_t shift = CLK_RESET_GET_BITS_SHIFT(clk);
+
+    HAL_CRU_Write(hw, hw->softrst_con0 + REG_X4(index), VAL_MASK_WE(1U << shift, 1U << shift));
+
+    return HAL_OK;
+}
+
+HAL_Status HAL_CRU_ClkResetDeassert(struct hwclk *hw, uint32_t clk)
+{
+    uint32_t index = CLK_RESET_GET_REG_OFFSET(clk);
+    uint32_t shift = CLK_RESET_GET_BITS_SHIFT(clk);
+
+    HAL_CRU_Write(hw, hw->softrst_con0 + REG_X4(index), VAL_MASK_WE(1U << shift, 0U << shift));
+
+    return HAL_OK;
+}
+
+HAL_Status HAL_CRU_ClkSetDiv(struct hwclk *hw, uint32_t divName, uint32_t divValue)
+{
+    uint32_t shift, mask, index;
+
+    index = CLK_DIV_GET_REG_OFFSET(divName);
+    shift = CLK_DIV_GET_BITS_SHIFT(divName);
+    mask = CLK_DIV_GET_MASK(divName);
+    if (divValue > mask) {
+        divValue = mask;
+    }
+
+    CRU_DBGF("%s: %s: clock(#%08x), selcon%d=0x%08x, shift=%d, mask=0x%x, div=%d\n",
+             __func__, hw->name, divName, index, hw->sel_con0 + REG_X4(index), shift, mask, divValue);
+
+    HAL_CRU_Write(hw, hw->sel_con0 + REG_X4(index), VAL_MASK_WE(mask, (divValue - 1U) << shift));
+
+    return HAL_OK;
+}
+
+uint32_t HAL_CRU_ClkGetDiv(struct hwclk *hw, uint32_t divName)
+{
+    uint32_t shift, mask, index, divValue;
+
+    index = CLK_DIV_GET_REG_OFFSET(divName);
+    shift = CLK_DIV_GET_BITS_SHIFT(divName);
+    mask = CLK_DIV_GET_MASK(divName);
+
+    divValue = ((HAL_CRU_Read(hw, hw->sel_con0 + REG_X4(index)) & mask) >> shift) + 1;
+
+    return divValue;
+}
+
+HAL_Status HAL_CRU_ClkSetMux(struct hwclk *hw, uint32_t muxName, uint32_t muxValue)
+{
+    uint32_t shift, mask, index;
+
+    index = CLK_MUX_GET_REG_OFFSET(muxName);
+    shift = CLK_MUX_GET_BITS_SHIFT(muxName);
+    mask = CLK_MUX_GET_MASK(muxName);
+
+    CRU_DBGF("%s: %s: clock(#%08x), selcon%d=0x%08x, shift=%d, mask=0x%x, mux=%d\n",
+             __func__, hw->name, muxName, index, hw->sel_con0 + REG_X4(index), shift, mask, muxValue);
+
+    HAL_CRU_Write(hw, hw->sel_con0 + REG_X4(index), VAL_MASK_WE(mask, muxValue << shift));
+
+    return HAL_OK;
+}
+
+uint32_t HAL_CRU_ClkGetMux(struct hwclk *hw, uint32_t muxName)
+{
+    uint32_t shift, mask, index, muxValue;
+
+    index = CLK_MUX_GET_REG_OFFSET(muxName);
+    shift = CLK_MUX_GET_BITS_SHIFT(muxName);
+    mask = CLK_MUX_GET_MASK(muxName);
+
+    muxValue = (HAL_CRU_Read(hw, hw->sel_con0 + REG_X4(index)) & mask) >> shift;
+
+    return muxValue;
+}
+
+HAL_Status HAL_CRU_ClkSetTestout(struct hwclk *hw, uint32_t clockName,
+                                 uint32_t muxValue, uint32_t divValue)
+{
+    if (hw->ops->clkInitTestout) {
+        hw->ops->clkInitTestout(hw, clockName, muxValue, divValue);
+    }
+
+    return HAL_OK;
+}
+
+void HAL_CRU_ClkDumpTree(HAL_ClockType type)
+{
+    struct hwclk *hw = &g_hwclk_desc[0];
+    struct clkTable *c;
+    const char *parent;
+    uint32_t clkMux, mux;
+    uint32_t i, freq;
+
+    for (i = 0; i < HAL_HWCLK_MAX_NUM; i++, hw++) {
+        if (hw->type == CLK_UNDEF) {
+            break;
+        }
+
+        if ((type != hw->type) && (type != CLK_ALL)) {
+            continue;
+        }
+
+        if (!hw->ops->clkTable) {
+            continue;
+        }
+
+        CRU_MSG("  ================== %s ==================\n", hw->name);
+        CRU_MSG("  Name                           Rate:hz      Parent\n");
+        CRU_MSG("  ====================================================\n");
+
+        for (c = hw->ops->clkTable; c->name; c++) {
+            if (c->type == DUMP_INT) {
+                if (c->numParents == 1) {
+                    mux = 0;
+                } else {
+                    clkMux = CLK_GET_MUX(c->clk);
+                    mux = HAL_CRU_ClkGetMux(hw, clkMux);
+                }
+                freq = HAL_CRU_ClkGetFreq(hw, c->clk);
+                parent = c->parents[mux];
+            } else if (c->type == DUMP_EXT) {
+                freq = c->getFreq(hw, c->clk);
+                parent = c->extParent ? c->extParent : "ext-in";
+            } else {
+                continue;
+            }
+
+            CRU_MSG("  %-30s %10d   %s\n", c->name, freq, parent);
+        }
+        CRU_MSG("\n");
+    }
+}
+
+HAL_Status HAL_CRU_SetGlbSrst(struct hwclk *hw, eCRU_GlbSrstType type)
+{
+    if (type == GLB_SRST_FST) {
+        HAL_CRU_Write(hw, hw->gbl_srst_fst, GLB_SRST_FST);
+    }
+
+    return HAL_INVAL;
+}
+
+uint32_t HAL_CRU_ClkGetFreq(struct hwclk *hw, uint32_t clockName)
+{
+    uint32_t rate;
+
+    rate = hw->ops->clkGetFreq(hw, clockName);
+
+    CRU_DBGF("%s: %s: clock(#%08x) get rate: %d\n", __func__, hw->name, clockName, rate);
+
+    return rate;
+}
+
+HAL_Status HAL_CRU_ClkSetFreq(struct hwclk *hw, uint32_t clockName, uint32_t rate)
+{
+    HAL_Status ret;
+    uint32_t now;
+
+    CRU_DBGF("%s: %s: clock(#%08x) set rate: %d\n", __func__, hw->name, clockName, rate);
+
+    ret = hw->ops->clkSetFreq(hw, clockName, rate);
+    if (hw->flags & CLK_FLG_SET_RATE_VERIFY) {
+        now = hw->ops->clkGetFreq(hw, clockName);
+        if (rate != now) {
+            CRU_MSG("%s: Set rate %d, but %d returned\n",
+                    hw->name, rate, now);
+            ret = HAL_ERROR;
+        }
+    }
+
+    return ret;
+}
+
+HAL_Status HAL_CRU_Init(void)
+{
+    return HAL_OK;
+}
+
+struct hwclk *HAL_CRU_ClkGet(void *client)
+{
+    struct hwclk *hw = &g_hwclk_desc[0];
+    int i;
+
+    if (!client) {
+        return HAL_NULL;
+    }
+
+    for (i = 0; i < HAL_HWCLK_MAX_NUM; i++, hw++) {
+        if (hw->xfer.client == client) {
+            return hw;
+        }
+    }
+
+    return HAL_NULL;
+}
+
+struct hwclk *HAL_CRU_Register(struct xferclk xfer)
+{
+    struct hwclk hw;
+    int i, ret;
+
+    if (!xfer.read || !xfer.write || !xfer.client || !xfer.name[0]) {
+        return HAL_NULL;
+    }
+
+    if (xfer.type == CLK_UNDEF || xfer.type >= CLK_MAX) {
+        return HAL_NULL;
+    }
+
+    /* registered ? */
+    for (i = 0; i < HAL_HWCLK_MAX_NUM; i++) {
+        if (g_hwclk_desc[i].type == CLK_UNDEF) {
+            break;
+        }
+
+        if (g_hwclk_desc[i].xfer.client == xfer.client) {
+            return &g_hwclk_desc[i];
+        }
+    }
+
+    if (i >= HAL_HWCLK_MAX_NUM) {
+        CRU_ERR("CLK: No more space for register\n");
+
+        return HAL_NULL;
+    }
+
+    memset(&hw, 0, sizeof(hw));
+    hw.type = xfer.type;
+    hw.xfer = xfer;
+
+    switch (xfer.type) {
+    case CLK_RKX110:
+        if (xfer.version == 0) {
+            hw.ops = &rkx110_clk_ops;
+        } else if (xfer.version == 1) {
+            hw.ops = &rkx111_clk_ops;
+        }
+        break;
+
+    case CLK_RKX120:
+        if (xfer.version == 0) {
+            hw.ops = &rkx120_clk_ops;
+        } else if (xfer.version == 1) {
+            hw.ops = &rkx121_clk_ops;
+        }
+        break;
+
+    default:
+        CRU_ERR("%s: Invalid type: %d\n", __func__, xfer.type);
+
+        return HAL_NULL;
+    }
+
+    if (!hw.ops || !hw.ops->clkInit || !hw.ops->clkGetFreq || !hw.ops->clkSetFreq) {
+        CRU_ERR("No available clkOps or .clkInit() or .clkGetFreq() or .clkSetFreq()\n");
+
+        return HAL_NULL;
+    }
+
+    HAL_MutexInit(&hw.lock);
+    ret = hw.ops->clkInit(&hw, &xfer);
+    if (ret) {
+        CRU_ERR("%s: Init clock failed, ret=%d\n", hw.name, ret);
+
+        return HAL_NULL;
+    }
+
+    if (!hw.num_gate) {
+        CRU_ERR("No available .gate\n");
+
+        return HAL_NULL;
+    }
+
+#if HAL_ENABLE_SET_RATE_VERIFY
+    hw->flags |= CLK_FLG_SET_RATE_VERIFY;
+#endif
+
+    g_hwclk_desc[i] = hw;
+
+    CRU_MSG("CLK: register '%s' with client 0x%08lx successfully.\n",
+            hw.name, (unsigned long)hw.xfer.client);
+
+#if DEBUG_CRU_INIT
+    HAL_CRU_ClkDumpTree(xfer.type);
+#endif
+
+    return &g_hwclk_desc[i];
+}
diff --git a/kernel/drivers/mfd/rkx110_x120/hal/cru_core.h b/kernel/drivers/mfd/rkx110_x120/hal/cru_core.h
new file mode 100644
index 0000000..33bf826
--- /dev/null
+++ b/kernel/drivers/mfd/rkx110_x120/hal/cru_core.h
@@ -0,0 +1,450 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2022 Rockchip Electronics Co. Ltd.
+ *
+ * Author: Joseph Chen <chenjh@rock-chips.com>
+ */
+
+#ifndef _CRU_CORE_H_
+#define _CRU_CORE_H_
+
+#include "hal_def.h"
+#include "hal_os_def.h"
+
+#define DEBUG_CRU_INIT 0
+#define CRU_LOGLEVEL   3
+
+#define __cru_print(level, fmt, ...)                                        \
+({                                                                          \
+    level < CRU_LOGLEVEL ? HAL_SYSLOG("[HAL CRU] " fmt, ##__VA_ARGS__) : 0; \
+})
+#define CRU_ERR(fmt, ...)  __cru_print(0, "ERROR: " fmt, ##__VA_ARGS__)
+#define CRU_WARN(fmt, ...) __cru_print(1, "WARN: " fmt, ##__VA_ARGS__)
+#define CRU_MSG(fmt, ...)  __cru_print(2, fmt, ##__VA_ARGS__)
+#define CRU_DBG(fmt, ...)  __cru_print(3, fmt, ##__VA_ARGS__) /* driver */
+#define CRU_DBGF(fmt, ...) __cru_print(4, fmt, ##__VA_ARGS__) /* core function */
+#define CRU_DBGR(fmt, ...) __cru_print(5, fmt, ##__VA_ARGS__) /* core register */
+
+#define HAL_ENABLE_SET_RATE_VERIFY 0
+
+#define _MHZ(n)           ((n) * 1000000U)
+#define OSC_24M           _MHZ(24)
+#define HAL_HWCLK_MAX_NUM 16
+#define PLL_MAX_NUM       2
+
+typedef enum {
+    RKX110_CPLL = 0,
+    RKX110_RXPLL,
+    RKX120_CPLL = 0,
+    RKX120_TXPLL,
+} HAL_PLLType;
+
+typedef enum {
+    CLK_UNDEF,
+    CLK_RKX110,
+    CLK_RKX120,
+    CLK_ALL,
+    CLK_MAX,
+} HAL_ClockType;
+
+/*
+ * hwclk flags
+ */
+#define CLK_FLG_SET_RATE_VERIFY (1 << 0)  /* set and readback verify */
+
+struct hwclk;
+
+struct xferclk {
+    HAL_ClockType type;
+    u32 version;
+    char *name; /* slave addr is expected */
+    void *client;
+    HAL_RegRead_t *read;
+    HAL_RegWrite_t *write;
+};
+
+struct clkTable {
+    uint8_t type;
+    const char *name;
+    uint32_t clk;
+    const char * *parents;
+    uint32_t numParents;
+    const char *extParent;
+    uint32_t (*getFreq)(struct hwclk *hw, uint32_t clockName);
+};
+
+struct clkOps {
+    struct clkTable *clkTable;
+    HAL_Status (*clkInit)(struct hwclk *hw, struct xferclk *xfer);
+    HAL_Status (*clkInitTestout)(struct hwclk *hw, uint32_t clockName,
+                                 uint32_t muxValue, uint32_t divValue);
+    HAL_Status (*clkSetFreq)(struct hwclk *hw, uint32_t clockName, uint32_t rate);
+    uint32_t (*clkGetFreq)(struct hwclk *hw, uint32_t clockName);
+};
+
+struct clkGate {
+    uint8_t enable_count;
+};
+
+struct hwclk {
+    char name[32];
+    uint32_t flags;
+    HAL_ClockType type;
+    uint32_t pllRate[PLL_MAX_NUM];
+
+    uint32_t cru_base;
+    uint32_t gate_con0;
+    uint32_t sel_con0;
+    uint32_t softrst_con0;
+    uint32_t gbl_srst_fst;
+
+    struct xferclk xfer;
+    struct clkOps *ops;
+    struct clkGate *gate;
+    uint32_t num_gate;
+
+    HAL_Mutex lock;
+};
+
+#define MASK_TO_WE(msk)       ((msk) << 16)
+#define VAL_MASK_WE(msk, val) ((MASK_TO_WE(msk)) | (val))
+#define WRITE_REG_MASK_WE(reg, msk, val) \
+                 WRITE_REG(reg, (VAL_MASK_WE(msk, val)))
+
+#define _GENMASK(h, l)           (((~0UL) << (l)) & (~0UL >> (HAL_BITS_PER_LONG - 1 - (h))))
+#define _GENVAL(x, h, l)         ((uint32_t)(((x) & _GENMASK(h, l)) >> (l)))
+#define _GENVAL_D16(x, h, l)     ((uint32_t)(((x) & _GENMASK(h, l)) / 16))
+#define _GENVAL_D16_REM(x, h, l) ((uint32_t)(((x) & _GENMASK(h, l)) % 16))
+#define _WIDTH_TO_MASK(w)        ((1 << (w)) - 1)
+
+/*
+ * RESET/GATE fields:
+ *   [31:16]: reserved
+ *   [15:12]: bank
+ *   [11:0]:  id
+ */
+#define CLK_RESET_GET_REG_OFFSET(x) _GENVAL_D16(x, 11, 0)
+#define CLK_RESET_GET_BITS_SHIFT(x) _GENVAL_D16_REM(x, 11, 0)
+#define CLK_RESET_GET_REG_BANK(x)   _GENVAL(x, 15, 12)
+
+#define CLK_GATE_GET_REG_OFFSET(x) CLK_RESET_GET_REG_OFFSET(x)
+#define CLK_GATE_GET_BITS_SHIFT(x) CLK_RESET_GET_BITS_SHIFT(x)
+#define CLK_GATE_GET_REG_BANK(x)   CLK_RESET_GET_REG_BANK(x)
+
+/*
+ * MUX/DIV fields:
+ *   [31:24]: width
+ *   [23:16]: shift
+ *   [15:12]: reserved
+ *   [11:8]:  bank
+ *   [7:0]:   reg
+ */
+#define CLK_MUX_GET_REG_OFFSET(x) _GENVAL(x, 7,  0)
+#define CLK_MUX_GET_BANK(x)       _GENVAL(x, 11, 8)
+#define CLK_MUX_GET_BITS_SHIFT(x) _GENVAL(x, 23, 16)
+#define CLK_MUX_GET_WIDTH(x)      _GENVAL(x, 31, 24)
+#define CLK_MUX_GET_MASK(x)       (_WIDTH_TO_MASK(CLK_MUX_GET_WIDTH(x)) << CLK_MUX_GET_BITS_SHIFT(x))
+
+#define CLK_DIV_GET_REG_OFFSET(x) CLK_MUX_GET_REG_OFFSET(x)
+#define CLK_DIV_GET_BANK(x)       CLK_MUX_GET_BANK(x)
+#define CLK_DIV_GET_BITS_SHIFT(x) CLK_MUX_GET_BITS_SHIFT(x)
+#define CLK_DIV_GET_WIDTH(x)      CLK_MUX_GET_WIDTH(x)
+#define CLK_DIV_GET_MASK(x)       CLK_MUX_GET_MASK(x)
+#define CLK_DIV_GET_MAXDIV(x)     ((1 << CLK_DIV_GET_WIDTH(x)) - 1)
+
+#define CLK_GET_MUX(v32) \
+        ((uint32_t)((v32) & 0x0F0F00FFU))
+#define CLK_GET_DIV(v32)                           \
+        ((uint32_t)((((v32) & 0x0000FF00U) >> 8) | \
+        (((v32) & 0xF0F00000U) >> 4)))
+#define COMPOSITE_CLK(mux, div)                           \
+        (((mux) & 0x0F0F00FFU) | (((div) & 0xFFU) << 8) | \
+        (((div) & 0x0F0F0000U) << 4))
+
+#define PNAME(x) static const char *x[]
+
+typedef enum {
+    DUMP_UNDEF,
+    DUMP_INT,
+    DUMP_EXT,
+} HAL_DumpType;
+
+#define CLK_DECLARE(_type, _name, _clk, _parents, _numParents, _extParent, _getFreq) \
+{                                                                                    \
+    .type = _type,                                                                   \
+    .name = _name,                                                                   \
+    .clk = _clk,                                                                     \
+    .parents = _parents,                                                             \
+    .numParents = _numParents,                                                       \
+    .extParent = _extParent,                                                         \
+    .getFreq = _getFreq,                                                             \
+}
+
+#define CLK_DECLARE_INT(_name, _clk, _parents) \
+    CLK_DECLARE(DUMP_INT, _name, _clk, _parents, HAL_ARRAY_SIZE(_parents), HAL_NULL, HAL_NULL)
+
+#define CLK_DECLARE_EXT_PARENT(_name, _clk, _extParent, _getFreq) \
+    CLK_DECLARE(DUMP_EXT, _name, _clk, HAL_NULL, 0, _extParent, _getFreq)
+
+#define CLK_DECLARE_EXT(_name, _clk, _getFreq) \
+    CLK_DECLARE_EXT_PARENT(_name, _clk, HAL_NULL, _getFreq)
+
+#define RK_PLL_RATE(_rate, _refdiv, _fbdiv, _postdiv1, _postdiv2, _dsmpd, _frac) \
+    {                                                                            \
+        .rate = _rate##U, .fbDiv = _fbdiv, .postDiv1 = _postdiv1,                \
+        .refDiv = _refdiv, .postDiv2 = _postdiv2, .dsmpd = _dsmpd,               \
+        .frac = _frac,                                                           \
+    }
+
+#define DIV_NO_REM(pFreq, freq, maxDiv) \
+        ((!((pFreq) % (freq))) && ((pFreq) / (freq) <= (maxDiv)))
+
+#define HAL_DIV_ROUND_UP(x, y) (((x) + (y) - 1) / (y))
+
+/***************************** Structure Definition **************************/
+
+struct PLL_CONFIG {
+    uint32_t rate;
+
+    union {
+        struct {
+            uint32_t fbDiv;
+            uint32_t postDiv1;
+            uint32_t refDiv;
+            uint32_t postDiv2;
+            uint32_t dsmpd;
+            uint32_t frac;
+        };
+    };
+};
+
+typedef enum {
+    PLL_CPLL,
+    PLL_RXPLL,
+    PLL_TXPLL,
+} eCRU_PLL;
+
+struct PLL_SETUP {
+    eCRU_PLL id;
+    uint32_t conOffset0;
+    uint32_t conOffset1;
+    uint32_t conOffset2;
+    uint32_t conOffset3;
+    uint32_t conOffset6;
+    uint32_t modeOffset;
+    uint32_t stat0;
+    uint32_t modeShift;
+    uint32_t lockShift;
+    uint32_t modeMask;
+    const struct PLL_CONFIG *rateTable;
+    uint8_t minRefdiv;
+    uint8_t maxRefdiv;
+    uint32_t minVco;
+    uint32_t maxVco;
+    uint32_t minFout;
+    uint32_t maxFout;
+    uint8_t sscgEn;
+};
+
+typedef enum {
+    GLB_SRST_FST = 0xfdb9,
+    GLB_SRST_SND = 0xeca8,
+} eCRU_GlbSrstType;
+
+/***************************** Function Declare ******************************/
+uint32_t HAL_CRU_Read(struct hwclk *hw, uint32_t reg);
+uint32_t HAL_CRU_Write(struct hwclk *hw, uint32_t reg, uint32_t val);
+uint32_t HAL_CRU_WriteMask(struct hwclk *hw, uint32_t reg,
+                           uint32_t msk, uint32_t val);
+
+int HAL_CRU_FreqGetMux4(struct hwclk *hw,
+                        uint32_t freq, uint32_t freq0,
+                        uint32_t freq1, uint32_t freq2, uint32_t freq3);
+int HAL_CRU_FreqGetMux3(struct hwclk *hw,
+                        uint32_t freq, uint32_t freq0,
+                        uint32_t freq1, uint32_t freq2);
+int HAL_CRU_FreqGetMux2(struct hwclk *hw,
+                        uint32_t freq, uint32_t freq0, uint32_t freq1);
+
+uint32_t HAL_CRU_MuxGetFreq4(struct hwclk *hw, uint32_t muxName,
+                             uint32_t freq0, uint32_t freq1,
+                             uint32_t freq2, uint32_t freq3);
+uint32_t HAL_CRU_MuxGetFreq3(struct hwclk *hw, uint32_t muxName,
+                             uint32_t freq0, uint32_t freq1, uint32_t freq2);
+uint32_t HAL_CRU_MuxGetFreq2(struct hwclk *hw, uint32_t muxName,
+                             uint32_t freq0, uint32_t freq1);
+
+int HAL_CRU_RoundFreqGetMux4(struct hwclk *hw, uint32_t freq,
+                             uint32_t pFreq0, uint32_t pFreq1,
+                             uint32_t pFreq2, uint32_t pFreq3, uint32_t *pFreqOut);
+int HAL_CRU_RoundFreqGetMux3(struct hwclk *hw, uint32_t freq,
+                             uint32_t pFreq0, uint32_t pFreq1,
+                             uint32_t pFreq2, uint32_t *pFreqOut);
+int HAL_CRU_RoundFreqGetMux2(struct hwclk *hw, uint32_t freq,
+                             uint32_t pFreq0, uint32_t pFreq1, uint32_t *pFreqOut);
+
+/**
+ * @brief Get pll freq.
+ * @param  pSetup: Contains PLL register parameters
+ * @return pll rate.
+ */
+uint32_t HAL_CRU_GetPllFreq(struct hwclk *hw, struct PLL_SETUP *pSetup);
+
+/**
+ * @brief Set pll freq.
+ * @param  pSetup: Contains PLL register parameters
+ * @param  rate: pll set
+ * @return HAL_Status.
+ */
+HAL_Status HAL_CRU_SetPllFreq(struct hwclk *hw, struct PLL_SETUP *pSetup, uint32_t rate);
+
+/**
+ * @brief Set pll power up.
+ * @param  pSetup: Contains PLL register parameters
+ * @return HAL_Status.
+ */
+HAL_Status HAL_CRU_SetPllPowerUp(struct hwclk *hw, struct PLL_SETUP *pSetup);
+
+/**
+ * @brief Set pll power down.
+ * @param  pSetup: Contains PLL register parameters
+ * @return HAL_Status.
+ */
+HAL_Status HAL_CRU_SetPllPowerDown(struct hwclk *hw, struct PLL_SETUP *pSetup);
+
+/**
+ * @brief Check if clk is enabled
+ * @param  clk: clock to check
+ * @return HAL_Check.
+ */
+HAL_Check HAL_CRU_ClkIsEnabled(struct hwclk *hw, uint32_t clk);
+
+/**
+ * @brief Enable clk
+ * @param  clk: clock to set
+ * @return HAL_Status.
+ */
+HAL_Status HAL_CRU_ClkEnable(struct hwclk *hw, uint32_t clk);
+
+/**
+ * @brief Disable clk
+ * @param  clk: clock to set
+ * @return HAL_Status.
+ */
+HAL_Status HAL_CRU_ClkDisable(struct hwclk *hw, uint32_t clk);
+
+/**
+ * @brief Check if clk is reset
+ * @param  clk: clock to check
+ * @return HAL_Check.
+ */
+HAL_Check HAL_CRU_ClkIsReset(struct hwclk *hw, uint32_t clk);
+
+/**
+ * @brief Assert the reset to the clk
+ * @param  clk: clock to assert
+ * @return HAL_Status.
+ */
+HAL_Status HAL_CRU_ClkResetAssert(struct hwclk *hw, uint32_t clk);
+
+/**
+ * @brief Deassert the reset to the clk
+ * @param  clk: clock to deassert
+ * @return HAL_Status.
+ */
+HAL_Status HAL_CRU_ClkResetDeassert(struct hwclk *hw, uint32_t clk);
+
+/**
+ * @brief  Set integer div
+ * @param  divName: div id(struct hwclk *hw, Contains div offset, shift, mask information)
+ * @param  divValue: div value
+ * @return NONE
+ */
+HAL_Status HAL_CRU_ClkSetDiv(struct hwclk *hw, uint32_t divName, uint32_t divValue);
+
+/**
+ * @brief  Get integer div
+ * @param  divName: div id (struct hwclk *hw,, Contains div offset, shift, mask information)
+ * @return div value
+ */
+uint32_t HAL_CRU_ClkGetDiv(struct hwclk *hw, uint32_t divName);
+
+/**
+ * @brief  Set mux
+ * @param  muxName: mux id (struct hwclk *hw,, Contains mux offset, shift, mask information)
+ * @param  muxValue: mux value
+ * @return NONE
+ */
+HAL_Status HAL_CRU_ClkSetMux(struct hwclk *hw, uint32_t muxName, uint32_t muxValue);
+
+/**
+ * @brief  Get mux
+ * @param  muxName: mux id (struct hwclk *hw, Contains mux offset, shift, mask information)
+ * @return mux value
+ */
+uint32_t HAL_CRU_ClkGetMux(struct hwclk *hw, uint32_t muxName);
+
+/**
+ * @brief Get clk freq.
+ * @param  clockName: CLOCK_Name id.
+ * @return rate.
+ * @attention these APIs allow direct use in the HAL layer.
+ */
+uint32_t HAL_CRU_ClkGetFreq(struct hwclk *hw, uint32_t clockName);
+
+/**
+ * @brief Set clk freq.
+ * @param  clockName: CLOCK_Name id.
+ * @param  rate: clk rate.
+ * @return HAL_Status.
+ * @attention these APIs allow direct use in the HAL layer.
+ */
+HAL_Status HAL_CRU_ClkSetFreq(struct hwclk *hw, uint32_t clockName, uint32_t rate);
+
+/**
+ * @brief  assert CRU global software reset.
+ * @param  type: global software reset type.
+ * @return HAL_INVAL if the SoC does not support.
+ */
+HAL_Status HAL_CRU_SetGlbSrst(struct hwclk *hw, eCRU_GlbSrstType type);
+
+/**
+ * @brief  Set clk testout
+ * @param  clockName: CLOCK_Name id.
+ * @param  muxValue: mux value.
+ * @param  divValue: div value.
+ * @return HAL_INVAL if the SoC does not support.
+ */
+HAL_Status HAL_CRU_ClkSetTestout(struct hwclk *hw, uint32_t clockName,
+                                 uint32_t muxValue, uint32_t divValue);
+/**
+ * @brief  Dump all clock tree
+ */
+void HAL_CRU_ClkDumpTree(HAL_ClockType type);
+
+/**
+ * @brief  CRU init for chip
+ * @return HAL_INVAL if the SoC does not support.
+ */
+HAL_Status HAL_CRU_Init(void);
+
+/**
+ * @brief  Register CRU
+ * @param  xfer: the data to register
+ * @return hwclk descriptor.
+ */
+struct hwclk *HAL_CRU_Register(struct xferclk xfer);
+
+/**
+ * @brief  Get hwclk
+ * @param  client: the unit data to find hwclk
+ * @return hwclk descriptor.
+ */
+struct hwclk *HAL_CRU_ClkGet(void *client);
+
+/************************** chip ops *************************************/
+extern struct clkOps rkx110_clk_ops;
+extern struct clkOps rkx120_clk_ops;
+extern struct clkOps rkx111_clk_ops;
+extern struct clkOps rkx121_clk_ops;
+#endif
diff --git a/kernel/drivers/mfd/rkx110_x120/hal/cru_rkx110.c b/kernel/drivers/mfd/rkx110_x120/hal/cru_rkx110.c
new file mode 100644
index 0000000..5d53ec3
--- /dev/null
+++ b/kernel/drivers/mfd/rkx110_x120/hal/cru_rkx110.c
@@ -0,0 +1,612 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2022 Rockchip Electronics Co. Ltd.
+ *
+ * Author: Joseph Chen <chenjh@rock-chips.com>
+ */
+
+#include "cru_core.h"
+#include "cru_rkx110.h"
+
+/*
+ *			[RKX110 CHIP]: RX
+ *
+ * ================= SECTION: Input clock from devices =========================
+ *
+ * ### 300M ###
+ * clk_8x_pma2pcs0
+ * clk_8x_pma2pcs1
+ *
+ *
+ * ### 200M ###
+ * sclk_i2s_link2pcs --|-- sclk_i2s_link2pcs_inter1
+ *                     |-- sclk_i2s_link2pcs_inter2
+ *
+ *
+ * ### 200M ###
+ * clk_link_pcs0 --|-- clk_pma2pcs0                |-- clk_pma2link2pcs_link
+ *                 |                               |
+ *                 |-- clk_pma2link2pcs_cm (MUX) --|-- clk_pma2link2pcs_psc0
+ * clk_link_pcs1 --|                               |
+ *                 |-- clk_pma2pcs1                |-- clk_pma2link2pcs_psc1
+ *
+ * clk_rxbytehs_csihost0
+ * clk_rxbytehs_csihost1
+ * iclk_dsi0
+ * iclk_dsi1
+ * iclk_vicap
+ *
+ *
+ * ### 150M ###
+ * dclk_lvds0 -- clk_d_lvds0_rklink_tx_cm --|-- clk_d_lvds0_pattern_gen_en
+ *                                          |-- clk_d_lvds0_rklink_tx
+ *
+ * dclk_lvds1 -- clk_d_lvds1_rklink_tx_cm --|-- clk_d_lvds1_pattern_gen_en
+ *                                          |-- clk_d_lvds1_rklink_tx
+ *
+ * clk_d_rgb_rklink_tx
+ * pclkin_vicap_dvp_rx
+ * rxpclk_lvds_align
+ * rxpclk_vicap_lvds
+ */
+
+#define RKX110_SSCG_RXPLL_EN 0
+#define RKX110_SSCG_CPLL_EN  0
+#define RKX110_TESTOUT_MUX   -1 /* valid options: RKX110_TEST_CLKOUT_IOUT_SEL_? */
+
+#define RKX110_GRF_GPIO0B_IOMUX_H 0x0001000c
+#define GPIO0B7_TEST_CLKOUT       0x01c00080
+
+#define I2S_122888_BEST_PRATE 393216000
+#define I2S_112896_BEST_PRATE 756403200
+
+static struct PLL_CONFIG PLL_TABLE[] = {
+    /* _mhz, _refDiv, _fbDiv, _postdDv1, _postDiv2, _dsmpd, _frac */
+    RK_PLL_RATE(1200000000, 1, 50, 1, 1, 1, 0),
+    RK_PLL_RATE(600000000, 1, 25, 1, 1, 1, 0),
+
+    /* display */
+    RK_PLL_RATE(1188000000, 2, 99, 1, 1, 1, 0),
+
+    /* audio: 12.288M */
+    RK_PLL_RATE(688128000, 1, 86, 3, 1, 0, 268435),   /* div=56, vco=2064.384M */
+    RK_PLL_RATE(393216000, 2, 131, 4, 1, 0, 1207959), /* div=32, vco=1572.864M */
+    RK_PLL_RATE(344064000, 1, 43, 3, 1, 0, 134217),   /* div=28, vco=1032.192M */
+    /* audio: 11.2896M */
+    RK_PLL_RATE(474163200, 1, 79, 4, 1, 0, 456340),   /* div=42, vco=1896.6528M */
+    RK_PLL_RATE(756403200, 1, 63, 2, 1, 0, 563714),   /* div=67, vco=1512.8064M */
+    RK_PLL_RATE(564480000, 1, 47, 2, 1, 0, 671088),   /* div=50, vco=1128.96M */
+
+    { /* sentinel */ },
+};
+
+static struct PLL_SETUP RXPLL_SETUP = {
+    .id = PLL_RXPLL,
+    .conOffset0 = 0x00000020,
+    .conOffset1 = 0x00000024,
+    .conOffset2 = 0x00000028,
+    .conOffset3 = 0x0000002c,
+    .modeOffset = 0x00000600,
+    .modeShift = 0,         /* 0: slow-mode, 1: normal-mode */
+    .lockShift = 10,
+    .modeMask = 0x1,
+    .rateTable = PLL_TABLE,
+
+    .minRefdiv = 1,
+    .maxRefdiv = 2,
+    .minVco = _MHZ(375),
+    .maxVco = _MHZ(2400),
+    .minFout = _MHZ(24),
+    .maxFout = _MHZ(1200),
+    .sscgEn = RKX110_SSCG_RXPLL_EN,
+};
+
+static struct PLL_SETUP CPLL_SETUP = {
+    .id = PLL_CPLL,
+    .conOffset0 = 0x00000000,
+    .conOffset1 = 0x00000004,
+    .conOffset2 = 0x00000008,
+    .conOffset3 = 0x0000000c,
+    .modeOffset = 0x00000600,
+    .modeShift = 2,
+    .lockShift = 10,
+    .modeMask = 0x1 << 2,
+    .rateTable = PLL_TABLE,
+
+    .minRefdiv = 1,
+    .maxRefdiv = 2,
+    .minVco = _MHZ(375),
+    .maxVco = _MHZ(2400),
+    .minFout = _MHZ(24),
+    .maxFout = _MHZ(1200),
+    .sscgEn = RKX110_SSCG_CPLL_EN,
+};
+
+static uint32_t RKX11x_HAL_CRU_ClkGetFreq(struct hwclk *hw, uint32_t clockName)
+{
+    uint32_t pRate = 0, freq = 0;
+    uint32_t clkMux, clkDiv;
+
+    if (clockName == RKX110_CLK_D_DSI_0_PATTERN_GEN ||
+        clockName == RKX110_CLK_D_DSI_1_PATTERN_GEN) {
+        clockName = RKX110_CPS_DCLK_RX_PRE;
+    }
+
+    clkMux = CLK_GET_MUX(clockName);
+    clkDiv = CLK_GET_DIV(clockName);
+
+    switch (clockName) {
+    case RKX110_CPS_PLL_RXPLL:
+        hw->pllRate[RKX110_RXPLL] = HAL_CRU_GetPllFreq(hw, &RXPLL_SETUP);
+
+        return hw->pllRate[RKX110_RXPLL];
+
+    case RKX110_CPS_PLL_CPLL:
+        hw->pllRate[RKX110_CPLL] = HAL_CRU_GetPllFreq(hw, &CPLL_SETUP);
+
+        return hw->pllRate[RKX110_CPLL];
+
+    /*
+     * 400MHZ => down to 200M
+     * === RX_DCLK_RX_PRE gate children ===
+     *
+     * dclk_dsi0
+     * dclk_dsi1
+     * dclk_vicap_csi
+     *
+     * clk_c_dvp_rklink_tx
+     * clk_c_csi_rklink_tx
+     *
+     * clk_d_dsi_0_rklink_tx
+     * clk_d_dsi_1_rklink_tx
+     * clk_d_dsi_0_pattern_gen
+     * clk_d_dsi_1_pattern_gen
+     * clk_c_lvds_rklink_tx
+     *
+     * NOTE: `clk_d_rgb_rklink_tx` is an input clock.
+     *
+     */
+    case RKX110_CPS_DCLK_RX_PRE:
+    /* camera: 150M */
+    case RKX110_CPS_CLK_CAM0_OUT2IO:
+    case RKX110_CPS_CLK_CAM1_OUT2IO:
+    case RKX110_CPS_CLK_CIF_OUT2IO:
+    /* dsi: 200M */
+    case RKX110_CPS_DCLK_D_DSI_0_REC_RKLINK_TX:
+    case RKX110_CPS_DCLK_D_DSI_1_REC_RKLINK_TX:
+    /* lvds: 300M */
+    case RKX110_CPS_CLK_2X_LVDS_RKLINK_TX:
+    /* i2s: 600M => down to 300M */
+    case RKX110_CPS_CLK_I2S_SRC_RKLINK_TX:
+        freq = HAL_CRU_MuxGetFreq3(hw, clkMux, hw->pllRate[RKX110_RXPLL],
+                                   hw->pllRate[RKX110_CPLL], OSC_24M);
+        break;
+
+    /* mipi: ref_1000M, cfg_100M */
+    case RKX110_CPS_CKREF_MIPIRXPHY0:
+    case RKX110_CPS_CKREF_MIPIRXPHY1:
+    case RKX110_CPS_CFGCLK_MIPIRXPHY0:
+    case RKX110_CPS_CFGCLK_MIPIRXPHY1:
+    /* pre-bus: 100M */
+    case RKX110_CPS_BUSCLK_RX_PRE0:
+        freq = HAL_CRU_MuxGetFreq2(hw, clkMux, hw->pllRate[RKX110_RXPLL],
+                                   hw->pllRate[RKX110_CPLL]);
+        break;
+
+    /*
+     * bus: 100MHZ => down to 24M
+     *
+     * === RX_BUSCLK_RX_PRE gate children ===
+     *
+     * pclk_rx_cru
+     * pclk_rx_grf
+     * pclk_rx_gpio0/1
+     * pclk_rx_efuse
+     * pclk_mipi_grf_rx0/1
+     * pclk_rx_i2c2apb
+     * pclk_rx_i2c2apb_debug
+     * pclk_csihost0/1
+     * pclk_rklink_tx
+     * pclk_dsi_{0,1}_pattern_gen
+     * pclk_lvds_{0,1}_pattern_gen
+     * pclk_pcs0/1
+     * pclk_pcs{0,1}_ada
+     * pclk_mipirxphy0/1
+     * pclk_dft2apb
+     *
+     * hclk_vicap
+     * hclk_dsi0/1
+     */
+    case RKX110_CPS_BUSCLK_RX_PRE:
+        pRate = RKX11x_HAL_CRU_ClkGetFreq(hw, RKX110_CPS_BUSCLK_RX_PRE0);
+        freq = HAL_CRU_MuxGetFreq2(hw, clkMux, OSC_24M, pRate);
+        break;
+
+    case RKX110_CPS_CLK_PMA2LINK2PCS_CM:
+
+        return _MHZ(200);
+
+    /* gpio: 24M */
+    case RKX110_CPS_DCLK_RX_GPIO0:
+    case RKX110_CPS_DCLK_RX_GPIO1:
+    /* efuse: 24M */
+    case RKX110_CPS_RX_EFUSE:
+    /* pcs_ada: 24M */
+    case RKX110_CPS_PCS0_ADA:
+    case RKX110_CPS_PCS1_ADA:
+
+        return OSC_24M;
+
+    default:
+        CRU_ERR("%s: %s: Unknown clk 0x%08x\n", __func__, hw->name, clockName);
+
+        return HAL_INVAL;
+    }
+
+    if (!clkMux && !clkDiv) {
+        return 0;
+    }
+
+    if (clkDiv) {
+        freq /= (HAL_CRU_ClkGetDiv(hw, clkDiv));
+    }
+
+    return freq;
+}
+
+static HAL_Status RKX11x_HAL_CRU_ClkSetFreq(struct hwclk *hw, uint32_t clockName, uint32_t rate)
+{
+    uint32_t clkMux, clkDiv;
+    uint32_t mux = 0, div = 1;
+    uint32_t pRate = 0;
+    uint32_t maxDiv;
+    uint32_t pll;
+    uint8_t overMax = 0;
+    HAL_Status ret = HAL_OK;
+
+    if (clockName == RKX110_CLK_D_DSI_0_PATTERN_GEN ||
+        clockName == RKX110_CLK_D_DSI_1_PATTERN_GEN) {
+        clockName = RKX110_CPS_DCLK_RX_PRE;
+    }
+
+    clkMux = CLK_GET_MUX(clockName);
+    clkDiv = CLK_GET_DIV(clockName);
+
+    switch (clockName) {
+    case RKX110_CPS_PLL_RXPLL:
+        ret = HAL_CRU_SetPllFreq(hw, &RXPLL_SETUP, rate);
+        if (ret != HAL_OK) {
+            CRU_ERR("%s: RXPLL set rate: %d failed\n", hw->name, rate);
+        } else {
+            hw->pllRate[RKX110_RXPLL] = rate;
+            CRU_MSG("%s: RXPLL set rate: %d\n", hw->name, rate);
+        }
+
+        return ret;
+
+    case RKX110_CPS_PLL_CPLL:
+        ret = HAL_CRU_SetPllFreq(hw, &CPLL_SETUP, rate);
+        if (ret != HAL_OK) {
+            CRU_ERR("%s: CPLL set rate: %d failed\n", hw->name, rate);
+        } else {
+            hw->pllRate[RKX110_CPLL] = rate;
+            CRU_MSG("%s: CPLL set rate: %d\n", hw->name, rate);
+        }
+
+        return ret;
+
+    /* link(dclk): Allowed to change PLL rate if need ! */
+    case RKX110_CPS_DCLK_D_DSI_0_REC_RKLINK_TX:
+    case RKX110_CPS_DCLK_D_DSI_1_REC_RKLINK_TX:
+    case RKX110_CPS_CLK_2X_LVDS_RKLINK_TX:
+    /* i2s */
+    case RKX110_CPS_CLK_I2S_SRC_RKLINK_TX:
+        maxDiv = CLK_DIV_GET_MAXDIV(clkDiv);
+
+        if (DIV_NO_REM(hw->pllRate[RKX110_RXPLL], rate, maxDiv)) {
+            mux = 0;
+            pRate = hw->pllRate[RKX110_RXPLL];
+        } else if (DIV_NO_REM(hw->pllRate[RKX110_CPLL], rate, maxDiv)) {
+            mux = 1;
+            pRate = hw->pllRate[RKX110_CPLL];
+        } else if (DIV_NO_REM(OSC_24M, rate, maxDiv)) {
+            mux = 2;
+            pRate = OSC_24M;
+        } else {
+            if (clockName == RKX110_CPS_CLK_I2S_SRC_RKLINK_TX) {
+                pll = RKX110_CPS_PLL_CPLL;
+                if (DIV_NO_REM(I2S_122888_BEST_PRATE, rate, maxDiv)) {
+                    pRate = I2S_122888_BEST_PRATE;
+                } else if (DIV_NO_REM(I2S_112896_BEST_PRATE, rate, maxDiv)) {
+                    pRate = I2S_112896_BEST_PRATE;
+                }
+            } else {
+                pll = RKX110_CPS_PLL_RXPLL;
+            }
+
+            /* PLL change closest new rate <= 1200M if need */
+            if (!pRate) {
+                pRate = (_MHZ(1200) / rate) * rate;
+            }
+
+            ret = RKX11x_HAL_CRU_ClkSetFreq(hw, pll, pRate);
+            if (ret != HAL_OK) {
+                return ret;
+            }
+
+            /* if success, continue to set divider */
+        }
+        break;
+
+    /* bus */
+    case RKX110_CPS_DCLK_RX_PRE:
+    /* camera */
+    case RKX110_CPS_CLK_CAM0_OUT2IO:
+    case RKX110_CPS_CLK_CAM1_OUT2IO:
+    case RKX110_CPS_CLK_CIF_OUT2IO:
+        mux = HAL_CRU_RoundFreqGetMux3(hw, rate, hw->pllRate[RKX110_RXPLL],
+                                       hw->pllRate[RKX110_CPLL], OSC_24M, &pRate);
+        break;
+
+    /* mipi */
+    case RKX110_CPS_CKREF_MIPIRXPHY0:
+    case RKX110_CPS_CKREF_MIPIRXPHY1:
+    case RKX110_CPS_CFGCLK_MIPIRXPHY0:
+    case RKX110_CPS_CFGCLK_MIPIRXPHY1:
+    /* pre-bus */
+    case RKX110_CPS_BUSCLK_RX_PRE0:
+        mux = HAL_CRU_RoundFreqGetMux2(hw, rate, hw->pllRate[RKX110_RXPLL],
+                                       hw->pllRate[RKX110_CPLL], &pRate);
+        break;
+
+    /* bus */
+    case RKX110_CPS_BUSCLK_RX_PRE:
+        if (rate == OSC_24M) {
+            return HAL_CRU_ClkSetMux(hw, clkMux, 0);
+        } else {
+            RKX11x_HAL_CRU_ClkSetFreq(hw, RKX110_CPS_BUSCLK_RX_PRE0, rate);
+
+            return HAL_CRU_ClkSetMux(hw, clkMux, 1);
+        }
+        break;
+
+    /* gpio */
+    case RKX110_CPS_DCLK_RX_GPIO0:
+    case RKX110_CPS_DCLK_RX_GPIO1:
+    /* efuse */
+    case RKX110_CPS_RX_EFUSE:
+    /* pcs_ada */
+    case RKX110_CPS_PCS0_ADA:
+    case RKX110_CPS_PCS1_ADA:
+
+        return rate == OSC_24M ? 0 : HAL_INVAL;
+
+    case RKX110_CPS_CLK_PMA2LINK2PCS_CM:
+        if (rate != _MHZ(200)) {
+            return HAL_INVAL;
+        }
+
+        HAL_CRU_ClkSetMux(hw, clkMux, 0);
+
+        return HAL_OK;
+
+    default:
+        CRU_ERR("%s: %s: Unknown clk 0x%08x\n", __func__, hw->name, clockName);
+
+        return HAL_INVAL;
+    }
+
+    if (!clkMux && !clkDiv) {
+        return HAL_INVAL;
+    }
+
+    if (pRate) {
+        div = HAL_DIV_ROUND_UP(pRate, rate);
+    }
+
+    if (clkDiv) {
+        overMax = div > CLK_DIV_GET_MAXDIV(clkDiv);
+        if (overMax) {
+            CRU_MSG("%s: %s: Clk '0x%08x' req div(%d) over max(%d)!\n",
+                    __func__, hw->name, clockName, div, CLK_DIV_GET_MAXDIV(clkDiv));
+            div = CLK_DIV_GET_MAXDIV(clkDiv);
+        }
+        HAL_CRU_ClkSetDiv(hw, clkDiv, div);
+    }
+
+    if (clkMux) {
+        HAL_CRU_ClkSetMux(hw, clkMux, mux);
+    }
+
+    return HAL_OK;
+}
+
+#if RKX110_SSCG_CPLL_EN || RKX110_SSCG_RXPLL_EN
+static void RKX11x_HAL_CRU_Init_SSCG(struct hwclk *hw)
+{
+    uint8_t down_spread = 1; /* 0: center spread */
+    uint8_t amplitude = 8;   /* range: 0x00 - 0x1f */
+
+#if RKX110_SSCG_CPLL_EN
+    /* down-spread, 0.8%, 37.5khz */
+    HAL_CRU_Write(hw, hw->cru_base + 0x0c, 0x1f000000 | ((amplitude & 0x1f) << 8));
+    HAL_CRU_Write(hw, hw->cru_base + 0x0c, 0x00f00050);
+    HAL_CRU_Write(hw, hw->cru_base + 0x0c, 0x00080000 | ((down_spread & 0x1) << 3));
+    HAL_CRU_Write(hw, hw->cru_base + 0x04, 0x10000000);
+    HAL_CRU_Write(hw, hw->cru_base + 0x0c, 0x00070000);
+    CRU_MSG("%s: CPLL enable SSCG\n", hw->name);
+#endif
+#if RKX110_SSCG_RXPLL_EN
+    /* down-spread, 0.8%, 37.5khz */
+    HAL_CRU_Write(hw, hw->cru_base + 0x2c, 0x1f000000 | ((amplitude & 0x1f) << 8));
+    HAL_CRU_Write(hw, hw->cru_base + 0x2c, 0x00f00050);
+    HAL_CRU_Write(hw, hw->cru_base + 0x2c, 0x00080000 | ((down_spread & 0x1) << 3));
+    HAL_CRU_Write(hw, hw->cru_base + 0x24, 0x10000000);
+    HAL_CRU_Write(hw, hw->cru_base + 0x2c, 0x00070000);
+    CRU_MSG("%s: RXPLL enable SSCG\n", hw->name);
+#endif
+}
+#endif
+
+static HAL_Status RKX11x_HAL_CRU_InitTestout(struct hwclk *hw, uint32_t clockName,
+                                             uint32_t muxValue, uint32_t divValue)
+{
+    uint32_t clkMux = CLK_GET_MUX(clockName);
+    uint32_t clkDiv = CLK_GET_DIV(clockName);
+
+    /* gpio0_b7: iomux to clk_testout */
+    HAL_CRU_Write(hw, RKX110_GRF_GPIO0B_IOMUX_H, GPIO0B7_TEST_CLKOUT);
+
+    /* Enable clock */
+    HAL_CRU_ClkEnable(hw, RKX110_CLK_TESTOUT_TOP_GATE);
+
+    /* Mux, div */
+    HAL_CRU_ClkSetDiv(hw, clkDiv, divValue);
+    HAL_CRU_ClkSetMux(hw, clkMux, muxValue);
+
+    CRU_MSG("%s: Testout div=%d, mux=%d\n", hw->name, divValue, muxValue);
+
+    return HAL_OK;
+}
+
+static HAL_Status RKX11x_HAL_CRU_Init(struct hwclk *hw, struct xferclk *xfer)
+{
+    hw->cru_base = 0x0;
+    hw->sel_con0 = hw->cru_base + 0x100;
+    hw->gate_con0 = hw->cru_base + 0x300;
+    hw->softrst_con0 = hw->cru_base + 0x400;
+    hw->gbl_srst_fst = 0x0614;
+    hw->flags = 0;
+    hw->num_gate = 16 * 12;
+    hw->gate = HAL_KCALLOC(hw->num_gate, sizeof(struct clkGate));
+    if (!hw->gate) {
+        return HAL_NOMEM;
+    }
+    strcat(hw->name, "<CRU.110@");
+    strcat(hw->name, xfer->name);
+    strcat(hw->name, ">");
+
+    /* Don't change order */
+#if RKX110_SSCG_CPLL_EN || RKX110_SSCG_RXPLL_EN
+    RKX11x_HAL_CRU_Init_SSCG(hw);
+#endif
+    RKX11x_HAL_CRU_ClkSetFreq(hw, RKX110_CPS_PLL_CPLL, _MHZ(1200));
+    RKX11x_HAL_CRU_ClkSetFreq(hw, RKX110_CPS_PLL_RXPLL, _MHZ(1188));
+    RKX11x_HAL_CRU_ClkSetFreq(hw, RKX110_CPS_BUSCLK_RX_PRE, _MHZ(24));
+    RKX11x_HAL_CRU_ClkSetFreq(hw, RKX110_CPS_CLK_PMA2LINK2PCS_CM, _MHZ(200));
+    RKX11x_HAL_CRU_ClkSetFreq(hw, RKX110_CPS_DCLK_RX_PRE, _MHZ(200));
+    RKX11x_HAL_CRU_ClkSetFreq(hw, RKX110_CPS_CLK_I2S_SRC_RKLINK_TX, _MHZ(300));
+    RKX11x_HAL_CRU_ClkSetFreq(hw, RKX110_CPS_CFGCLK_MIPIRXPHY0, _MHZ(100));
+
+    HAL_CRU_ClkEnable(hw, RKX110_CLK_TESTOUT_TOP_GATE);
+
+#if RKX110_TESTOUT_MUX >= 0
+    /* clk_testout support max 150M output, so set div=10 by default if not 24M */
+    RKX11x_HAL_CRU_InitTestout(hw, RKX110_CPS_TEST_CLKOUT, RKX110_TESTOUT_MUX,
+                               RKX110_TESTOUT_MUX == 0 ? 1 : 10);
+#endif
+
+    return HAL_OK;
+}
+
+PNAME(mux_24m_p) = { "xin24m" };
+PNAME(mux_rxpll_cpll_p) = { "rxpll", "cpll" };
+PNAME(mux_rxpll_cpll_24m_p) = { "rxpll", "cpll", "xin24m" };
+PNAME(mux_rxpre0_24m_p) = { "xin24m", "busclk_rx_pre0" };
+
+#define CAL_FREQ_REG 0xf00
+
+static uint32_t RKX11x_HAL_CRU_ClkGetExtFreq(struct hwclk *hw, uint32_t clk)
+{
+    uint32_t clkMux = CLK_GET_MUX(RKX110_CPS_TEST_CLKOUT);
+    uint32_t clkDiv = CLK_GET_DIV(RKX110_CPS_TEST_CLKOUT);
+    uint32_t freq, mhz;
+    uint8_t div = 10;
+
+    HAL_CRU_ClkSetDiv(hw, clkDiv, div);
+    HAL_CRU_ClkSetMux(hw, clkMux, 0);
+    HAL_SleepMs(2);
+    HAL_CRU_ClkSetMux(hw, clkMux, clk);
+    HAL_SleepMs(2);
+    freq = HAL_CRU_Read(hw, CAL_FREQ_REG);
+
+    /* Fix accuracy */
+    if ((freq % 10) == 0x9) {
+        freq++;
+    }
+
+    freq *= (1000 * div);
+
+    /* If no external input, freq is close to 24M */
+    mhz = freq / 1000000;
+    if ((clk != 0) && (mhz == 23 || mhz == 24)) {
+        freq = 0;
+    }
+
+    return freq;
+}
+
+static struct clkTable rkx11x_clkTable[] = {
+    /* internal */
+    CLK_DECLARE_INT("rxpll", RKX110_CPS_PLL_RXPLL, mux_24m_p),
+    CLK_DECLARE_INT("cpll", RKX110_CPS_PLL_CPLL, mux_24m_p),
+    CLK_DECLARE_INT("dclk_rx_pre", RKX110_CPS_DCLK_RX_PRE, mux_rxpll_cpll_24m_p),
+    CLK_DECLARE_INT("clk_d_dsi_0_pattern", RKX110_CPS_DCLK_RX_PRE, mux_rxpll_cpll_24m_p),
+    CLK_DECLARE_INT("clk_d_dsi_1_pattern", RKX110_CPS_DCLK_RX_PRE, mux_rxpll_cpll_24m_p),
+    CLK_DECLARE_INT("clk_cam0_out2io", RKX110_CPS_CLK_CAM0_OUT2IO, mux_rxpll_cpll_24m_p),
+    CLK_DECLARE_INT("clk_cam1_out2io", RKX110_CPS_CLK_CAM1_OUT2IO, mux_rxpll_cpll_24m_p),
+    CLK_DECLARE_INT("clk_cif_out2io", RKX110_CPS_CLK_CIF_OUT2IO, mux_rxpll_cpll_24m_p),
+    CLK_DECLARE_INT("dclk_d_dsi_0_rec_rklink_tx", RKX110_CPS_DCLK_D_DSI_0_REC_RKLINK_TX, mux_rxpll_cpll_24m_p),
+    CLK_DECLARE_INT("dclk_d_dsi_1_rec_rklink_tx", RKX110_CPS_DCLK_D_DSI_1_REC_RKLINK_TX, mux_rxpll_cpll_24m_p),
+    CLK_DECLARE_INT("clk_2x_lvds_rklink_tx", RKX110_CPS_CLK_2X_LVDS_RKLINK_TX, mux_rxpll_cpll_24m_p),
+    CLK_DECLARE_INT("clk_i2s_src_rklink_tx", RKX110_CPS_CLK_I2S_SRC_RKLINK_TX, mux_rxpll_cpll_24m_p),
+    CLK_DECLARE_INT("ckref_mipirxphy0", RKX110_CPS_CKREF_MIPIRXPHY0, mux_rxpll_cpll_p),
+    CLK_DECLARE_INT("ckref_mipirxphy1", RKX110_CPS_CKREF_MIPIRXPHY1, mux_rxpll_cpll_p),
+    CLK_DECLARE_INT("cfgclk_mipirxphy0", RKX110_CPS_CFGCLK_MIPIRXPHY0, mux_rxpll_cpll_p),
+    CLK_DECLARE_INT("cfgclk_mipirxphy1", RKX110_CPS_CFGCLK_MIPIRXPHY1, mux_rxpll_cpll_p),
+    CLK_DECLARE_INT("busclk_rx_pre0", RKX110_CPS_BUSCLK_RX_PRE0, mux_rxpll_cpll_p),
+    CLK_DECLARE_INT("busclk_rx_pre", RKX110_CPS_BUSCLK_RX_PRE, mux_rxpre0_24m_p),
+    CLK_DECLARE_INT("dclk_rx_gpio0", RKX110_CPS_DCLK_RX_GPIO0, mux_24m_p),
+    CLK_DECLARE_INT("dclk_rx_gpio1", RKX110_CPS_DCLK_RX_GPIO1, mux_24m_p),
+    CLK_DECLARE_INT("rx_efuse", RKX110_CPS_RX_EFUSE, mux_24m_p),
+    CLK_DECLARE_INT("pcs0_ada", RKX110_CPS_PCS0_ADA, mux_24m_p),
+    CLK_DECLARE_INT("pcs1_ada", RKX110_CPS_PCS1_ADA, mux_24m_p),
+
+    /* external */
+    CLK_DECLARE_EXT("xin24m", 0, RKX11x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT("rxpclk_vicap_lvds", 3, RKX11x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT("clk_rxbytehs_csihost0", 4, RKX11x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT("clk_rxbytehs_csihost1", 5, RKX11x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT("clk_d_rgb_rklink_tx", 6, RKX11x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT("dclk_lvds0", 7, RKX11x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT("dclk_lvds1", 8, RKX11x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT("clk_link_pcs0", 9, RKX11x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT("clk_link_pcs1", 10, RKX11x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT("pclkin_vicap_dvp_rx", 21, RKX11x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT("iclk_dsi0", 22, RKX11x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT("iclk_dsi1", 23, RKX11x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT("rxpclk_lvds_align", 24, RKX11x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT("clk_8x_pma2pcs0", 25, RKX11x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT("clk_8x_pma2pcs1", 26, RKX11x_HAL_CRU_ClkGetExtFreq),
+
+    CLK_DECLARE_EXT_PARENT("clk_d_lvds0_rklink_tx_cm", 7, "dclk_lvds0", RKX11x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT_PARENT("clk_d_lvds0_rklink_tx", 7, "clk_d_lvds0_rklink_tx_cm", RKX11x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT_PARENT("clk_d_lvds0_pattern_gen_en", 7, "clk_d_lvds0_rklink_tx_cm", RKX11x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT_PARENT("clk_d_lvds1_rklink_tx_cm", 8, "dclk_lvds1", RKX11x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT_PARENT("clk_d_lvds1_rklink_tx", 8, "clk_d_lvds1_rklink_tx_cm", RKX11x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT_PARENT("clk_d_lvds1_pattern_gen_en", 8, "clk_d_lvds1_rklink_tx_cm", RKX11x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT_PARENT("clk_pma2pcs0", 9, "clk_link_pcs0", RKX11x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT_PARENT("clk_pma2link2pcs_cm", 9, "clk_link_pcs0", RKX11x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT_PARENT("clk_pma2pcs1", 9, "clk_link_pcs1", RKX11x_HAL_CRU_ClkGetExtFreq),
+
+    { /* sentinel */ },
+};
+
+struct clkOps rkx110_clk_ops =
+{
+    .clkTable = rkx11x_clkTable,
+    .clkInit = RKX11x_HAL_CRU_Init,
+    .clkGetFreq = RKX11x_HAL_CRU_ClkGetFreq,
+    .clkSetFreq = RKX11x_HAL_CRU_ClkSetFreq,
+    .clkInitTestout = RKX11x_HAL_CRU_InitTestout,
+};
diff --git a/kernel/drivers/mfd/rkx110_x120/hal/cru_rkx110.h b/kernel/drivers/mfd/rkx110_x120/hal/cru_rkx110.h
new file mode 100644
index 0000000..7541b7f
--- /dev/null
+++ b/kernel/drivers/mfd/rkx110_x120/hal/cru_rkx110.h
@@ -0,0 +1,358 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2022 Rockchip Electronics Co. Ltd.
+ *
+ * Author: Joseph Chen <chenjh@rock-chips.com>
+ */
+
+#ifndef _CRU_RKX110_H
+
+#include "cru_core.h"
+
+// ======================== RXCRU module definition START ======================
+// RXCRU_SOFTRST_CON01(Offset:0x404)
+#define RKX110_SRST_PRESETN_RX_CRU                           0x00000010
+#define RKX110_SRST_PRESETN_RX_GRF                           0x00000011
+#define RKX110_SRST_PRESETN_RX_GPIO0                         0x00000012
+#define RKX110_SRST_DRESETN_RX_GPIO0                         0x00000013
+#define RKX110_SRST_PRESETN_RX_GPIO1                         0x00000014
+#define RKX110_SRST_DRESETN_RX_GPIO1                         0x00000015
+#define RKX110_SRST_PRESETN_RX_EFUSE                         0x00000016
+#define RKX110_SRST_RESETN_RX_EFUSE                          0x00000017
+#define RKX110_SRST_PRESETN_MIPI_GRF_RX0                     0x0000001A
+#define RKX110_SRST_PRESETN_MIPI_GRF_RX1                     0x0000001B
+#define RKX110_SRST_PRESETN_RX_I2C2APB                       0x0000001E
+#define RKX110_SRST_PRESETN_RX_I2C2APB_DEBUG                 0x0000001F
+
+// RXCRU_SOFTRST_CON02(Offset:0x408)
+#define RKX110_SRST_DRESETN_VICAP_CSI                        0x00000021
+#define RKX110_SRST_HRESETN_VICAP                            0x00000022
+#define RKX110_SRST_IRESETN_VICAP                            0x00000023
+#define RKX110_SRST_RXPRESETN_VICAP_LVDS                     0x00000024
+#define RKX110_SRST_PRESETN_VICAP_DVP_RX                     0x00000025
+#define RKX110_SRST_DRESETN_DSI0                             0x00000026
+#define RKX110_SRST_HRESETN_DSI0                             0x00000027
+#define RKX110_SRST_IRESETN_DSI0                             0x00000028
+#define RKX110_SRST_RXPRESETN_LVDS_ALIGN                     0x00000029
+#define RKX110_SRST_DRESETN_DSI1                             0x0000002A
+#define RKX110_SRST_HRESETN_DSI1                             0x0000002B
+#define RKX110_SRST_IRESETN_DSI1                             0x0000002C
+
+// RXCRU_SOFTRST_CON03(Offset:0x40C)
+#define RKX110_SRST_PRESETN_CSIHOST0                         0x00000030
+#define RKX110_SRST_PRESETN_CSIHOST1                         0x00000032
+
+// RXCRU_SOFTRST_CON04(Offset:0x410)
+#define RKX110_SRST_PRESETN_RKLINK_TX                        0x00000040
+#define RKX110_SRST_RESETN_C_DVP_RKLINK_TX                   0x00000041
+#define RKX110_SRST_RESETN_C_CSI_RKLINK_TX                   0x00000042
+#define RKX110_SRST_RESETN_C_LVDS_RKLINK_TX                  0x00000043
+#define RKX110_SRST_RESETN_D_DSI_0_RKLINK_TX                 0x00000044
+#define RKX110_SRST_RESETN_D_DSI_1_RKLINK_TX                 0x00000045
+#define RKX110_SRST_RESETN_D_RGB_RKLINK_TX                   0x00000046
+#define RKX110_SRST_RESETN_D_LVDS0_RKLINK_TX                 0x00000048
+#define RKX110_SRST_RESETN_D_LVDS1_RKLINK_TX                 0x0000004A
+#define RKX110_SRST_RESETN_2X_LVDS_RKLINK_TX                 0x0000004B
+#define RKX110_SRST_RESETN_I2S_SRC_RKLINK_TX                 0x0000004C
+#define RKX110_SRST_RESETN_D_DSI_0_REC_RKLINK_TX             0x0000004D
+#define RKX110_SRST_RESETN_D_DSI_1_REC_RKLINK_TX             0x0000004E
+
+// RXCRU_SOFTRST_CON05(Offset:0x414)
+#define RKX110_SRST_RESETN_PMA2LINK2PCS_LINK                 0x00000051
+#define RKX110_SRST_RESETN_PMA2LINK2PCS_PCS0                 0x00000052
+#define RKX110_SRST_RESETN_PMA2LINK2PCS_PCS1                 0x00000053
+#define RKX110_SRST_SRESETN_I2S_PCS0                         0x00000054
+#define RKX110_SRST_SRESETN_I2S_PCS1                         0x00000055
+
+// RXCRU_SOFTRST_CON06(Offset:0x418)
+#define RKX110_SRST_RESETN_D_DSI_0_PATTERN_GEN               0x00000060
+#define RKX110_SRST_RESETN_D_DSI_1_PATTERN_GEN               0x00000061
+#define RKX110_SRST_RESETN_D_LVDS0_PATTERN_GEN               0x00000062
+#define RKX110_SRST_RESETN_D_LVDS1_PATTERN_GEN               0x00000063
+#define RKX110_SRST_PRESETN_DSI_0_PATTERN_GEN                0x00000068
+#define RKX110_SRST_PRESETN_DSI_1_PATTERN_GEN                0x00000069
+#define RKX110_SRST_PRESETN_LVDS_0_PATTERN_GEN               0x0000006A
+#define RKX110_SRST_PRESETN_LVDS_1_PATTERN_GEN               0x0000006B
+
+// RXCRU_SOFTRST_CON07(Offset:0x41C)
+#define RKX110_SRST_PRESETN_PCS0                             0x00000070
+#define RKX110_SRST_RESETN_8X_PMA2PCS0                       0x00000071
+#define RKX110_SRST_RESETN_PMA2PCS0                          0x00000073
+#define RKX110_SRST_PRESETN_PCS0_ADA                         0x00000074
+#define RKX110_SRST_RESETN_PCS0_ADA                          0x00000075
+
+// RXCRU_SOFTRST_CON08(Offset:0x420)
+#define RKX110_SRST_PRESETN_PCS1                             0x00000080
+#define RKX110_SRST_RESETN_8X_PMA2PCS1                       0x00000081
+#define RKX110_SRST_RESETN_PMA2PCS1                          0x00000083
+#define RKX110_SRST_PRESETN_PCS1_ADA                         0x00000084
+#define RKX110_SRST_RESETN_PCS1_ADA                          0x00000085
+
+// RXCRU_SOFTRST_CON10(Offset:0x428)
+#define RKX110_SRST_PRESETN_MIPIRXPHY0                       0x000000A0
+#define RKX110_SRST_CFGRESETN_MIPIRXPHY0                     0x000000A1
+#define RKX110_SRST_PRESETN_MIPIRXPHY1                       0x000000A8
+#define RKX110_SRST_CFGRESETN_MIPIRXPHY1                     0x000000A9
+
+// RXCRU_SOFTRST_CON11(Offset:0x42C)
+#define RKX110_SRST_PRESETN_DFT2APB                          0x000000B0
+
+// RXCRU_GATE_CON00(Offset:0x300)
+#define RKX110_CLK_TESTOUT_TOP_GATE                     0x00000000
+#define RKX110_BUSCLK_RX_PRE0_GATE                      0x00000001
+#define RKX110_BUSCLK_RX_PRE_GATE                       0x00000002
+
+// RXCRU_GATE_CON01(Offset:0x304)
+#define RKX110_PCLK_RX_CRU_GATE                         0x00000010
+#define RKX110_PCLK_RX_GRF_GATE                         0x00000011
+#define RKX110_PCLK_RX_GPIO0_GATE                       0x00000012
+#define RKX110_DCLK_RX_GPIO0_GATE                       0x00000013
+#define RKX110_PCLK_RX_GPIO1_GATE                       0x00000014
+#define RKX110_DCLK_RX_GPIO1_GATE                       0x00000015
+#define RKX110_PCLK_RX_EFUSE_GATE                       0x00000016
+#define RKX110_CLK_RX_EFUSE_GATE                        0x00000017
+#define RKX110_PCLK_MIPI_GRF_RX0_GATE                   0x0000001A
+#define RKX110_PCLK_MIPI_GRF_RX1_GATE                   0x0000001B
+#define RKX110_PCLK_RX_I2C2APB_GATE                     0x0000001E
+#define RKX110_PCLK_RX_I2C2APB_DEBUG_GATE               0x0000001F
+
+// RXCRU_GATE_CON02(Offset:0x308)
+#define RKX110_DCLK_RX_PRE_GATE                         0x00000020
+#define RKX110_DCLK_VICAP_CSI_GATE                      0x00000021
+#define RKX110_HCLK_VICAP_GATE                          0x00000022
+#define RKX110_ICLK_VICAP_INTER_GATE                    0x00000023
+#define RKX110_RXPCLK_VICAP_LVDS_DFT_GATE               0x00000024
+#define RKX110_PCLKIN_VICAP_DVP_RX_DFT_GATE             0x00000025
+#define RKX110_DCLK_DSI0_GATE                           0x00000026
+#define RKX110_HCLK_DSI0_GATE                           0x00000027
+#define RKX110_ICLK_DSI0_INTER_GATE                     0x00000028
+#define RKX110_RXPCLK_LVDS_ALIGN_DFT_GATE               0x00000029
+#define RKX110_DCLK_DSI1_GATE                           0x0000002A
+#define RKX110_HCLK_DSI1_GATE                           0x0000002B
+#define RKX110_ICLK_DSI1_INTER_GATE                     0x0000002C
+
+// RXCRU_GATE_CON03(Offset:0x30C)
+#define RKX110_PCLK_CSIHOST0_GATE                       0x00000030
+#define RKX110_CLK_RXBYTEHS_CSIHOST0_DFT_GATE           0x00000031
+#define RKX110_PCLK_CSIHOST1_GATE                       0x00000032
+#define RKX110_CLK_RXBYTEHS_CSIHOST1_DFT_GATE           0x00000033
+
+// RXCRU_GATE_CON04(Offset:0x310)
+#define RKX110_PCLK_RKLINK_TX_GATE                      0x00000040
+#define RKX110_CLK_C_DVP_RKLINK_TX_GATE                 0x00000041
+#define RKX110_CLK_C_CSI_RKLINK_TX_GATE                 0x00000042
+#define RKX110_CLK_C_LVDS_RKLINK_TX_GATE                0x00000043
+#define RKX110_CLK_D_DSI_0_RKLINK_TX_GATE               0x00000044
+#define RKX110_CLK_D_DSI_1_RKLINK_TX_GATE               0x00000045
+#define RKX110_CLK_D_RGB_RKLINK_TX_DFT_GATE             0x00000046
+#define RKX110_CLK_D_LVDS0_RKLINK_TX_CM_GATE            0x00000047
+#define RKX110_CLK_D_LVDS0_RKLINK_TX_GATE               0x00000048
+#define RKX110_CLK_D_LVDS1_RKLINK_TX_CM_GATE            0x00000049
+#define RKX110_CLK_D_LVDS1_RKLINK_TX_GATE               0x0000004A
+#define RKX110_CLK_2X_LVDS_RKLINK_TX_GATE               0x0000004B
+#define RKX110_CLK_I2S_SRC_RKLINK_TX_GATE               0x0000004C
+#define RKX110_DCLK_D_DSI_0_REC_RKLINK_TX_GATE          0x0000004D
+#define RKX110_DCLK_D_DSI_1_REC_RKLINK_TX_GATE          0x0000004E
+
+// RXCRU_GATE_CON05(Offset:0x314)
+#define RKX110_CLK_PMA2LINK2PCS_CM_GATE                 0x00000050
+#define RKX110_CLK_PMA2LINK2PCS_LINK_GATE               0x00000051
+#define RKX110_CLK_PMA2LINK2PCS_PSC0_GATE               0x00000052
+#define RKX110_CLK_PMA2LINK2PCS_PSC1_GATE               0x00000053
+#define RKX110_SCLK_I2S_LINK2PCS_INTER1_GATE            0x00000054
+#define RKX110_SCLK_I2S_LINK2PCS_INTER2_GATE            0x00000055
+
+// RXCRU_GATE_CON06(Offset:0x318)
+#define RKX110_CLK_D_DSI_0_PATTERN_GEN_GATE             0x00000060
+#define RKX110_CLK_D_DSI_1_PATTERN_GEN_GATE             0x00000061
+#define RKX110_CLK_D_LVDS0_PATTERN_GEN_GATE             0x00000062
+#define RKX110_CLK_D_LVDS1_PATTERN_GEN_GATE             0x00000063
+#define RKX110_PCLK_DSI_0_PATTERN_GEN_GATE              0x00000068
+#define RKX110_PCLK_DSI_1_PATTERN_GEN_GATE              0x00000069
+#define RKX110_PCLK_LVDS_0_PATTERN_GEN_GATE             0x0000006A
+#define RKX110_PCLK_LVDS_1_PATTERN_GEN_GATE             0x0000006B
+
+// RXCRU_GATE_CON07(Offset:0x31C)
+#define RKX110_PCLK_PCS0_GATE                           0x00000070
+#define RKX110_CLK_8X_PMA2PCS0_DFT_GATE                 0x00000071
+#define RKX110_CLK_LINK_PCS0_DFT_GATE                   0x00000072
+#define RKX110_CLK_PMA2PCS0_GATE                        0x00000073
+#define RKX110_PCLK_PCS0_ADA_GATE                       0x00000074
+#define RKX110_CLK_PCS0_ADA_GATE                        0x00000075
+
+// RXCRU_GATE_CON08(Offset:0x320)
+#define RKX110_PCLK_PCS1_GATE                           0x00000080
+#define RKX110_CLK_8X_PMA2PCS1_DFT_GATE                 0x00000081
+#define RKX110_CLK_LINK_PCS1_DFT_GATE                   0x00000082
+#define RKX110_CLK_PMA2PCS1_GATE                        0x00000083
+#define RKX110_PCLK_PCS1_ADA_GATE                       0x00000084
+#define RKX110_CLK_PCS1_ADA_GATE                        0x00000085
+
+// RXCRU_GATE_CON09(Offset:0x324)
+#define RKX110_CLK_CAM_OUT2IO_GATE                      0x00000090
+
+// RXCRU_GATE_CON10(Offset:0x328)
+#define RKX110_PCLK_MIPIRXPHY0_GATE                     0x000000A0
+#define RKX110_CFGCLK_MIPIRXPHY0_GATE                   0x000000A1
+#define RKX110_CKREF_MIPIRXPHY0_GATE                    0x000000A2
+#define RKX110_PCLK_MIPIRXPHY1_GATE                     0x000000A8
+#define RKX110_CFGCLK_MIPIRXPHY1_GATE                   0x000000A9
+#define RKX110_CKREF_MIPIRXPHY1_GATE                    0x000000AA
+#define RKX110_CLK_CAM0_OUT2IO_GATE                     0x000000AB
+#define RKX110_CLK_CAM1_OUT2IO_GATE                     0x000000AC
+
+// RXCRU_GATE_CON11(Offset:0x32C)
+#define RKX110_PCLK_DFT2APB_GATE                        0x000000B0
+
+// RXCRU_CLKSEL_CON00(Offset:0x100)
+#define RKX110_TEST_CLKOUT_IOUT_DIV                     	0x08000000
+#define RKX110_TEST_CLKOUT_IOUT_SEL                     	0x05080000
+#define RKX110_TEST_CLKOUT_IOUT_SEL_XIN_OSC0_FUNC               0U
+#define RKX110_TEST_CLKOUT_IOUT_SEL_CLK_RXPLL_MUX               1U
+#define RKX110_TEST_CLKOUT_IOUT_SEL_CLK_CPLL_MUX                2U
+#define RKX110_TEST_CLKOUT_IOUT_SEL_RXPCLK_VICAP_LVDS           3U
+#define RKX110_TEST_CLKOUT_IOUT_SEL_CLK_RXBYTEHS_CSIHOST0       4U
+#define RKX110_TEST_CLKOUT_IOUT_SEL_CLK_RXBYTEHS_CSIHOST1       5U
+#define RKX110_TEST_CLKOUT_IOUT_SEL_CLK_D_RGB_RKLINK_TX         6U
+#define RKX110_TEST_CLKOUT_IOUT_SEL_DCLK_LVDS0                  7U
+#define RKX110_TEST_CLKOUT_IOUT_SEL_DCLK_LVDS1                  8U
+#define RKX110_TEST_CLKOUT_IOUT_SEL_CLK_LINK_PCS0               9U
+#define RKX110_TEST_CLKOUT_IOUT_SEL_CLK_LINK_PCS1               10U
+#define RKX110_TEST_CLKOUT_IOUT_SEL_BUSCLK_RX_PRE               11U
+#define RKX110_TEST_CLKOUT_IOUT_SEL_DCLK_RX_PRE                 12U
+#define RKX110_TEST_CLKOUT_IOUT_SEL_CLK_2X_LVDS_RKLINK_TX       13U
+#define RKX110_TEST_CLKOUT_IOUT_SEL_CLK_I2S_SRC_RKLINK_TX       14U
+#define RKX110_TEST_CLKOUT_IOUT_SEL_DCLK_D_DSI_0_REC_RKLINK_TX  15U
+#define RKX110_TEST_CLKOUT_IOUT_SEL_DCLK_D_DSI_1_REC_RKLINK_TX  16U
+#define RKX110_TEST_CLKOUT_IOUT_SEL_CFGCLK_MIPIRXPHY0           17U
+#define RKX110_TEST_CLKOUT_IOUT_SEL_CKREF_MIPIRXPHY0            18U
+#define RKX110_TEST_CLKOUT_IOUT_SEL_CFGCLK_MIPIRXPHY1           19U
+#define RKX110_TEST_CLKOUT_IOUT_SEL_CKREF_MIPIRXPHY1            20U
+#define RKX110_TEST_CLKOUT_IOUT_SEL_PCLKIN_VICAP_DVP_RX         21U
+#define RKX110_TEST_CLKOUT_IOUT_SEL_ICLK_DSI0                   22U
+#define RKX110_TEST_CLKOUT_IOUT_SEL_ICLK_DSI1                   23U
+#define RKX110_TEST_CLKOUT_IOUT_SEL_RXPCLK_LVDS_ALIGN           24U
+#define RKX110_TEST_CLKOUT_IOUT_SEL_CLK_8X_PMA2PCS0             25U
+#define RKX110_TEST_CLKOUT_IOUT_SEL_CLK_8X_PMA2PCS1             26U
+
+// RXCRU_CLKSEL_CON01(Offset:0x104)
+#define RKX110_BUSCLK_RX_PRE0_DIV                       0x06000001
+#define RKX110_BUSCLK_RX_PRE0_SEL                       0x01070001
+#define RKX110_BUSCLK_RX_PRE0_SEL_CLK_RXPLL_MUX         0U
+#define RKX110_BUSCLK_RX_PRE0_SEL_CLK_CPLL_MUX          1U
+#define RKX110_BUSCLK_RX_PRE_SEL                        0x01080001
+#define RKX110_BUSCLK_RX_PRE_SEL_XIN_OSC0_FUNC          0U
+#define RKX110_BUSCLK_RX_PRE_SEL_BUSCLK_RX_PRE0         1U
+
+// RXCRU_CLKSEL_CON02(Offset:0x108)
+#define RKX110_DCLK_RX_PRE_DIV                          0x06000002
+#define RKX110_DCLK_RX_PRE_SEL                          0x02060002
+#define RKX110_DCLK_RX_PRE_SEL_CLK_RXPLL_MUX            0U
+#define RKX110_DCLK_RX_PRE_SEL_CLK_CPLL_MUX             1U
+#define RKX110_DCLK_RX_PRE_SEL_XIN_OSC0_FUNC            2U
+
+// RXCRU_CLKSEL_CON03(Offset:0x10C)
+#define RKX110_CLK_2X_LVDS_RKLINK_TX_DIV                0x08000003
+#define RKX110_CLK_2X_LVDS_RKLINK_TX_SEL                0x020E0003
+#define RKX110_CLK_2X_LVDS_RKLINK_TX_SEL_CLK_RXPLL_MUX  0U
+#define RKX110_CLK_2X_LVDS_RKLINK_TX_SEL_CLK_CPLL_MUX   1U
+#define RKX110_CLK_2X_LVDS_RKLINK_TX_SEL_XIN_OSC0_FUNC  2U
+
+// RXCRU_CLKSEL_CON04(Offset:0x110)
+#define RKX110_CLK_I2S_SRC_RKLINK_TX_DIV                0x08000004
+#define RKX110_CLK_I2S_SRC_RKLINK_TX_SEL                0x020E0004
+#define RKX110_CLK_I2S_SRC_RKLINK_TX_SEL_CLK_RXPLL_MUX  0U
+#define RKX110_CLK_I2S_SRC_RKLINK_TX_SEL_CLK_CPLL_MUX   1U
+#define RKX110_CLK_I2S_SRC_RKLINK_TX_SEL_XIN_OSC0_FUNC  2U
+
+// RXCRU_CLKSEL_CON05(Offset:0x114)
+#define RKX110_DCLK_D_DSI_0_REC_RKLINK_TX_DIV           0x08000005
+#define RKX110_DCLK_D_DSI_0_REC_RKLINK_TX_SEL           0x020E0005
+#define RKX110_DCLK_D_DSI_0_REC_RKLINK_TX_SEL_CLK_RXPLL_MUX 0U
+#define RKX110_DCLK_D_DSI_0_REC_RKLINK_TX_SEL_CLK_CPLL_MUX 1U
+#define RKX110_DCLK_D_DSI_0_REC_RKLINK_TX_SEL_XIN_OSC0_FUNC 2U
+
+// RXCRU_CLKSEL_CON06(Offset:0x118)
+#define RKX110_DCLK_D_DSI_1_REC_RKLINK_TX_DIV           0x08000006
+#define RKX110_DCLK_D_DSI_1_REC_RKLINK_TX_SEL           0x020E0006
+#define RKX110_DCLK_D_DSI_1_REC_RKLINK_TX_SEL_CLK_RXPLL_MUX 0U
+#define RKX110_DCLK_D_DSI_1_REC_RKLINK_TX_SEL_CLK_CPLL_MUX 1U
+#define RKX110_DCLK_D_DSI_1_REC_RKLINK_TX_SEL_XIN_OSC0_FUNC 2U
+
+// RXCRU_CLKSEL_CON07(Offset:0x11C)
+#define RKX110_CLK_PMA2LINK2PCS_CM_SEL                  0x010F0007
+#define RKX110_CLK_PMA2LINK2PCS_CM_SEL_CLK_LINK_PCS0_DFT 0U
+#define RKX110_CLK_PMA2LINK2PCS_CM_SEL_CLK_LINK_PCS1_DFT 1U
+
+// RXCRU_CLKSEL_CON08(Offset:0x120)
+#define RKX110_CLK_CIF_OUT2IO_DIV                       0x08000008
+#define RKX110_CLK_CIF_OUT2IO_SEL                       0x020E0008
+#define RKX110_CLK_CIF_OUT2IO_SEL_CLK_RXPLL_MUX         0U
+#define RKX110_CLK_CIF_OUT2IO_SEL_CLK_CPLL_MUX          1U
+#define RKX110_CLK_CIF_OUT2IO_SEL_XIN_OSC0_FUNC         2U
+
+// RXCRU_CLKSEL_CON09(Offset:0x124)
+#define RKX110_CFGCLK_MIPIRXPHY0_DIV                    0x08000009
+#define RKX110_CFGCLK_MIPIRXPHY0_SEL                    0x010F0009
+#define RKX110_CFGCLK_MIPIRXPHY0_SEL_CLK_RXPLL_MUX      0U
+#define RKX110_CFGCLK_MIPIRXPHY0_SEL_CLK_CPLL_MUX       1U
+
+// RXCRU_CLKSEL_CON10(Offset:0x128)
+#define RKX110_CKREF_MIPIRXPHY0_DIV                     0x0400000A
+#define RKX110_CKREF_MIPIRXPHY0_SEL                     0x0107000A
+#define RKX110_CKREF_MIPIRXPHY0_SEL_CLK_RXPLL_MUX       0U
+#define RKX110_CKREF_MIPIRXPHY0_SEL_CLK_CPLL_MUX        1U
+
+// RXCRU_CLKSEL_CON11(Offset:0x12C)
+#define RKX110_CFGCLK_MIPIRXPHY1_DIV                    0x0800000B
+#define RKX110_CFGCLK_MIPIRXPHY1_SEL                    0x010F000B
+#define RKX110_CFGCLK_MIPIRXPHY1_SEL_CLK_RXPLL_MUX      0U
+#define RKX110_CFGCLK_MIPIRXPHY1_SEL_CLK_CPLL_MUX       1U
+
+// RXCRU_CLKSEL_CON12(Offset:0x130)
+#define RKX110_CKREF_MIPIRXPHY1_DIV                     0x0408000C
+#define RKX110_CKREF_MIPIRXPHY1_SEL                     0x010F000C
+#define RKX110_CKREF_MIPIRXPHY1_SEL_CLK_RXPLL_MUX       0U
+#define RKX110_CKREF_MIPIRXPHY1_SEL_CLK_CPLL_MUX        1U
+
+// RXCRU_CLKSEL_CON15(Offset:0x13C)
+#define RKX110_CLK_CAM0_OUT2IO_DIV                      0x0600000F
+#define RKX110_CLK_CAM1_OUT2IO_DIV                      0x0608000F
+#define RKX110_CLK_CAM0_OUT2IO_SEL                      0x0206000F
+#define RKX110_CLK_CAM0_OUT2IO_SEL_CLK_RXPLL_MUX        0U
+#define RKX110_CLK_CAM0_OUT2IO_SEL_CLK_CPLL_MUX         1U
+#define RKX110_CLK_CAM0_OUT2IO_SEL_XIN_OSC0_FUNC        2U
+#define RKX110_CLK_CAM1_OUT2IO_SEL                      0x020E000F
+#define RKX110_CLK_CAM1_OUT2IO_SEL_CLK_RXPLL_MUX        0U
+#define RKX110_CLK_CAM1_OUT2IO_SEL_CLK_CPLL_MUX         1U
+#define RKX110_CLK_CAM1_OUT2IO_SEL_XIN_OSC0_FUNC        2U
+
+// ======================== RXCRU module definition END ========================
+#define RKX110_CPS_INVAL                          0
+#define RKX110_CPS_PLL_CPLL                       1
+#define RKX110_CPS_PLL_RXPLL                      2
+#define RKX110_CPS_DCLK_RX_GPIO0                  3
+#define RKX110_CPS_DCLK_RX_GPIO1                  4
+#define RKX110_CPS_RX_EFUSE                       5
+#define RKX110_CPS_PCS0_ADA                       6
+#define RKX110_CPS_PCS1_ADA                       7
+#define RKX110_CLK_D_DSI_0_PATTERN_GEN            8
+#define RKX110_CLK_D_DSI_1_PATTERN_GEN            9
+#define RKX110_CPS_DCLK_RX_PRE                    COMPOSITE_CLK(RKX110_DCLK_RX_PRE_SEL, RKX110_DCLK_RX_PRE_DIV)
+#define RKX110_CPS_CLK_2X_LVDS_RKLINK_TX          COMPOSITE_CLK(RKX110_CLK_2X_LVDS_RKLINK_TX_SEL, RKX110_CLK_2X_LVDS_RKLINK_TX_DIV)
+#define RKX110_CPS_CLK_I2S_SRC_RKLINK_TX          COMPOSITE_CLK(RKX110_CLK_I2S_SRC_RKLINK_TX_SEL, RKX110_CLK_I2S_SRC_RKLINK_TX_DIV)
+#define RKX110_CPS_DCLK_D_DSI_0_REC_RKLINK_TX     COMPOSITE_CLK(RKX110_DCLK_D_DSI_0_REC_RKLINK_TX_SEL, RKX110_DCLK_D_DSI_0_REC_RKLINK_TX_DIV)
+#define RKX110_CPS_DCLK_D_DSI_1_REC_RKLINK_TX     COMPOSITE_CLK(RKX110_DCLK_D_DSI_1_REC_RKLINK_TX_SEL, RKX110_DCLK_D_DSI_1_REC_RKLINK_TX_DIV)
+#define RKX110_CPS_CLK_PMA2LINK2PCS_CM            COMPOSITE_CLK(RKX110_CLK_PMA2LINK2PCS_CM_SEL, 0)
+#define RKX110_CPS_CLK_CIF_OUT2IO                 COMPOSITE_CLK(RKX110_CLK_CIF_OUT2IO_SEL, RKX110_CLK_CIF_OUT2IO_DIV)
+#define RKX110_CPS_CKREF_MIPIRXPHY0               COMPOSITE_CLK(RKX110_CKREF_MIPIRXPHY0_SEL, RKX110_CKREF_MIPIRXPHY0_DIV)
+#define RKX110_CPS_CKREF_MIPIRXPHY1               COMPOSITE_CLK(RKX110_CKREF_MIPIRXPHY1_SEL, RKX110_CKREF_MIPIRXPHY1_DIV)
+#define RKX110_CPS_CLK_CAM0_OUT2IO                COMPOSITE_CLK(RKX110_CLK_CAM0_OUT2IO_SEL, RKX110_CLK_CAM0_OUT2IO_DIV)
+#define RKX110_CPS_CLK_CAM1_OUT2IO                COMPOSITE_CLK(RKX110_CLK_CAM1_OUT2IO_SEL, RKX110_CLK_CAM1_OUT2IO_DIV)
+#define RKX110_CPS_BUSCLK_RX_PRE0                 COMPOSITE_CLK(RKX110_BUSCLK_RX_PRE0_SEL, RKX110_BUSCLK_RX_PRE0_DIV)
+#define RKX110_CPS_BUSCLK_RX_PRE                  COMPOSITE_CLK(RKX110_BUSCLK_RX_PRE_SEL, 0)
+#define RKX110_CPS_CFGCLK_MIPIRXPHY0              COMPOSITE_CLK(RKX110_CFGCLK_MIPIRXPHY0_SEL, RKX110_CFGCLK_MIPIRXPHY0_DIV)
+#define RKX110_CPS_CFGCLK_MIPIRXPHY1              COMPOSITE_CLK(RKX110_CFGCLK_MIPIRXPHY1_SEL, RKX110_CFGCLK_MIPIRXPHY1_DIV)
+#define RKX110_CPS_TEST_CLKOUT                    COMPOSITE_CLK(RKX110_TEST_CLKOUT_IOUT_SEL, RKX110_TEST_CLKOUT_IOUT_DIV)
+#endif
+
diff --git a/kernel/drivers/mfd/rkx110_x120/hal/cru_rkx111.c b/kernel/drivers/mfd/rkx110_x120/hal/cru_rkx111.c
new file mode 100644
index 0000000..5f24c62
--- /dev/null
+++ b/kernel/drivers/mfd/rkx110_x120/hal/cru_rkx111.c
@@ -0,0 +1,706 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2023 Rockchip Electronics Co. Ltd.
+ *
+ * Author: Joseph Chen <chenjh@rock-chips.com>
+ */
+
+#include "cru_core.h"
+#include "cru_rkx111.h"
+
+/*
+ *			[RKX111 CHIP]: RX
+ *
+ * ================= SECTION: Input clock from devices =========================
+ *
+ * ### 400M ###
+ * clk_8x_pma2pcs0
+ * clk_8x_pma2pcs1
+ *
+ *
+ * ### 200M ###
+ * sclk_i2s_link2pcs --|-- sclk_i2s_link2pcs_inter1
+ *                     |-- sclk_i2s_link2pcs_inter2
+ *
+ *
+ * ### 150M ###
+ * clk_link_pcs0 --|-- clk_pma2pcs0                |-- clk_pma2link2pcs_link
+ *                 |                               |
+ *                 |-- clk_pma2link2pcs_cm (MUX) --|-- clk_pma2link2pcs_psc0
+ * clk_link_pcs1 --|                               |
+ *                 |-- clk_pma2pcs1                |-- clk_pma2link2pcs_psc1
+ *
+ *
+ * clk_rxbytehs_csihost0
+ * clk_rxbytehs_csihost1
+ * iclk_dsi0
+ * iclk_dsi1
+ * iclk_vicap
+ *
+ *
+ * ### 200M ###
+ * dclk_lvds0 -- clk_d_lvds0_rklink_tx_cm --|-- clk_d_lvds0_pattern_gen_en
+ *                                          |-- clk_d_lvds0_rklink_tx
+ *
+ * dclk_lvds1 -- clk_d_lvds1_rklink_tx_cm --|-- clk_d_lvds1_pattern_gen_en
+ *                                          |-- clk_d_lvds1_rklink_tx
+ *
+ * clk_d_rgb_rklink_tx
+ * pclkin_vicap_dvp_rx
+ * rxpclk_lvds_align
+ * rxpclk_vicap_lvds
+ */
+
+#define RKX110_SSCG_RXPLL_EN 0
+#define RKX110_SSCG_CPLL_EN  0
+#define RKX110_TESTOUT_MUX   -1 /* valid options: RKX110_TEST_CLKOUT_IOUT_SEL_? */
+
+#define RKX110_GRF_GPIO0B_IOMUX_H 0x0001000c
+#define GPIO0B7_TEST_CLKOUT       0x01c00080
+
+#define I2S_122888_BEST_PRATE 393216000
+#define I2S_112896_BEST_PRATE 756403200
+
+static struct PLL_CONFIG PLL_TABLE[] = {
+    /* _mhz, _refDiv, _fbDiv, _postdDv1, _postDiv2, _dsmpd, _frac */
+    RK_PLL_RATE(1200000000, 1, 50, 1, 1, 1, 0),
+    RK_PLL_RATE(600000000, 1, 25, 1, 1, 1, 0),
+
+    /* display */
+    RK_PLL_RATE(1188000000, 2, 99, 1, 1, 1, 0),
+
+    /* audio: 12.288M */
+    RK_PLL_RATE(688128000, 1, 86, 3, 1, 0, 268435),   /* div=56, vco=2064.384M */
+    RK_PLL_RATE(393216000, 2, 131, 4, 1, 0, 1207959), /* div=32, vco=1572.864M */
+    RK_PLL_RATE(344064000, 1, 43, 3, 1, 0, 134217),   /* div=28, vco=1032.192M */
+    /* audio: 11.2896M */
+    RK_PLL_RATE(474163200, 1, 79, 4, 1, 0, 456340),   /* div=42, vco=1896.6528M */
+    RK_PLL_RATE(756403200, 1, 63, 2, 1, 0, 563714),   /* div=67, vco=1512.8064M */
+    RK_PLL_RATE(564480000, 1, 47, 2, 1, 0, 671088),   /* div=50, vco=1128.96M */
+
+    { /* sentinel */ },
+};
+
+static struct PLL_SETUP RXPLL_SETUP = {
+    .id = PLL_RXPLL,
+    .conOffset0 = 0x00000020,
+    .conOffset1 = 0x00000024,
+    .conOffset2 = 0x00000028,
+    .conOffset3 = 0x0000002c,
+    .modeOffset = 0x00000600,
+    .modeShift = 0,         /* 0: slow-mode, 1: normal-mode */
+    .lockShift = 10,
+    .modeMask = 0x1,
+    .rateTable = PLL_TABLE,
+
+    .minRefdiv = 1,
+    .maxRefdiv = 2,
+    .minVco = _MHZ(375),
+    .maxVco = _MHZ(2400),
+    .minFout = _MHZ(24),
+    .maxFout = _MHZ(1200),
+    .sscgEn = RKX110_SSCG_RXPLL_EN,
+};
+
+static struct PLL_SETUP CPLL_SETUP = {
+    .id = PLL_CPLL,
+    .conOffset0 = 0x00000000,
+    .conOffset1 = 0x00000004,
+    .conOffset2 = 0x00000008,
+    .conOffset3 = 0x0000000c,
+    .modeOffset = 0x00000600,
+    .modeShift = 2,
+    .lockShift = 10,
+    .modeMask = 0x1 << 2,
+    .rateTable = PLL_TABLE,
+
+    .minRefdiv = 1,
+    .maxRefdiv = 2,
+    .minVco = _MHZ(375),
+    .maxVco = _MHZ(2400),
+    .minFout = _MHZ(24),
+    .maxFout = _MHZ(1200),
+    .sscgEn = RKX110_SSCG_CPLL_EN,
+};
+
+static uint32_t RKX11x_HAL_CRU_ClkGetFreq(struct hwclk *hw, uint32_t clockName)
+{
+    uint32_t pRate = 0, freq = 0;
+    uint32_t pRate0 = 0, pRate1 = 0;
+    uint32_t clkMux, clkDiv;
+
+    if (clockName == RKX110_CLK_D_DSI_0_PATTERN_GEN) {
+        clockName = RKX111_CPS_DCLK_D_DSI_0_REC;
+    }
+    if (clockName == RKX110_CLK_D_DSI_1_PATTERN_GEN) {
+        clockName = RKX111_CPS_DCLK_D_DSI_1_REC;
+    }
+
+    clkMux = CLK_GET_MUX(clockName);
+    clkDiv = CLK_GET_DIV(clockName);
+
+    switch (clockName) {
+    case RKX110_CPS_PLL_RXPLL:
+        hw->pllRate[RKX110_RXPLL] = HAL_CRU_GetPllFreq(hw, &RXPLL_SETUP);
+
+        return hw->pllRate[RKX110_RXPLL];
+
+    case RKX110_CPS_PLL_CPLL:
+        hw->pllRate[RKX110_CPLL] = HAL_CRU_GetPllFreq(hw, &CPLL_SETUP);
+
+        return hw->pllRate[RKX110_CPLL];
+
+    /*
+     * 400MHZ => down to 200M
+     * === RX_DCLK_RX_PRE gate children ===
+     *
+     * dclk_dsi0
+     * dclk_vicap_csi
+     * clk_c_csi_rklink_tx
+     * clk_d_dsi_0_rklink_tx
+     *
+     * NOTE: `clk_d_rgb_rklink_tx` is an input clock.
+     *
+     */
+    case RKX110_CPS_DCLK_RX_PRE:
+
+    /*
+     * === DCLK_RX_PRE_200M gate children ===
+     *
+     * clk_c_dvp_rklink_tx
+     * clk_c_lvds_rklink_tx
+     * clk_d_dsi_1_rklink_tx
+     * dclk_dsi1
+     * dclk_vicap_csi_ls
+     */
+    case RKX111_CPS_DCLK_RX_PRE_200M:
+
+    /* camera: 150M */
+    case RKX110_CPS_CLK_CAM0_OUT2IO:
+    case RKX110_CPS_CLK_CAM1_OUT2IO:
+    case RKX110_CPS_CLK_CIF_OUT2IO:
+
+    /* dsi0: 200M, child: clk_d_dsi_0_pattern_gen */
+    case RKX111_CPS_DCLK_D_DSI_0_REC:
+    /* dsi1: 200M, child: clk_d_dsi_1_pattern_gen */
+    case RKX111_CPS_DCLK_D_DSI_1_REC:
+
+    /* lvds pattern gen: 150M */
+    case RKX111_CPS_CLK_D_LVDS0_PATTERN_GEN:
+    case RKX111_CPS_CLK_D_LVDS1_PATTERN_GEN:
+
+    /* lvds: 200M */
+    case RKX110_CPS_CLK_2X_LVDS_RKLINK_TX:
+
+    /* i2s: 600M => down to 300M */
+    case RKX110_CPS_CLK_I2S_SRC_RKLINK_TX:
+        freq = HAL_CRU_MuxGetFreq3(hw, clkMux, hw->pllRate[RKX110_RXPLL],
+                                   hw->pllRate[RKX110_CPLL], OSC_24M);
+        break;
+
+    /* mipi: ref_1000M, cfg_100M */
+    case RKX110_CPS_CKREF_MIPIRXPHY0:
+    case RKX110_CPS_CKREF_MIPIRXPHY1:
+    case RKX110_CPS_CFGCLK_MIPIRXPHY0:
+    case RKX110_CPS_CFGCLK_MIPIRXPHY1:
+    /* pre-bus: 100M */
+    case RKX110_CPS_BUSCLK_RX_PRE0:
+        freq = HAL_CRU_MuxGetFreq2(hw, clkMux, hw->pllRate[RKX110_RXPLL],
+                                   hw->pllRate[RKX110_CPLL]);
+        break;
+
+    /*
+     * bus: 100MHZ => down to 24M
+     *
+     * === RX_BUSCLK_RX_PRE gate children ===
+     *
+     * pclk_rx_cru
+     * pclk_rx_grf
+     * pclk_rx_gpio0/1
+     * pclk_rx_efuse
+     * pclk_mipi_grf_rx0/1
+     * pclk_rx_i2c2apb
+     * pclk_rx_i2c2apb_debug
+     * pclk_csihost0/1
+     * pclk_rklink_tx
+     * pclk_dsi_{0,1}_pattern_gen
+     * pclk_lvds_{0,1}_pattern_gen
+     * pclk_pcs0/1
+     * pclk_pcs{0,1}_ada
+     * pclk_mipirxphy0/1
+     * pclk_dft2apb
+     * pclk_lbist_ada_rx (new)
+     *
+     * hclk_vicap
+     * hclk_dsi0/1
+     */
+    case RKX110_CPS_BUSCLK_RX_PRE:
+        pRate = RKX11x_HAL_CRU_ClkGetFreq(hw, RKX110_CPS_BUSCLK_RX_PRE0);
+        freq = HAL_CRU_MuxGetFreq2(hw, clkMux, OSC_24M, pRate);
+        break;
+
+    case RKX111_CPS_CLK_D_LVDS0_RKLINK_TX:
+        pRate0 = _MHZ(150); /* input clock: dclk_lvds0 */
+        pRate1 = RKX11x_HAL_CRU_ClkGetFreq(hw, RKX111_CPS_CLK_D_LVDS0_PATTERN_GEN);
+        freq = HAL_CRU_MuxGetFreq2(hw, clkMux, pRate0, pRate1);
+        break;
+
+    case RKX111_CPS_CLK_D_LVDS1_RKLINK_TX:
+        pRate0 = _MHZ(150); /* input clock: dclk_lvds1 */
+        pRate1 = RKX11x_HAL_CRU_ClkGetFreq(hw, RKX111_CPS_CLK_D_LVDS1_PATTERN_GEN);
+        freq = HAL_CRU_MuxGetFreq2(hw, clkMux, pRate0, pRate1);
+        break;
+
+    case RKX111_CPS_CLK_D_DSI_0_RKLINK_TX:
+        pRate0 = RKX11x_HAL_CRU_ClkGetFreq(hw, RKX110_CPS_DCLK_RX_PRE); /* 400M */
+        pRate1 = RKX11x_HAL_CRU_ClkGetFreq(hw, RKX110_CLK_D_DSI_0_PATTERN_GEN);
+        freq = HAL_CRU_MuxGetFreq2(hw, clkMux, pRate0, pRate1);
+        break;
+
+    case RKX111_CPS_CLK_D_DSI_1_RKLINK_TX:
+        pRate0 = RKX11x_HAL_CRU_ClkGetFreq(hw, RKX111_CPS_DCLK_RX_PRE_200M);
+        pRate1 = RKX11x_HAL_CRU_ClkGetFreq(hw, RKX110_CLK_D_DSI_1_PATTERN_GEN);
+        freq = HAL_CRU_MuxGetFreq2(hw, clkMux, pRate0, pRate1);
+        break;
+
+    case RKX110_CPS_CLK_PMA2LINK2PCS_CM:
+
+        return _MHZ(200);
+
+    /* gpio: 24M */
+    case RKX110_CPS_DCLK_RX_GPIO0:
+    case RKX110_CPS_DCLK_RX_GPIO1:
+    /* efuse: 24M */
+    case RKX110_CPS_RX_EFUSE:
+    /* pcs_ada: 24M */
+    case RKX110_CPS_PCS0_ADA:
+    case RKX110_CPS_PCS1_ADA:
+
+        return OSC_24M;
+
+    default:
+        CRU_ERR("%s: %s: Unknown clk 0x%08x\n", __func__, hw->name, clockName);
+
+        return HAL_INVAL;
+    }
+
+    if (!clkMux && !clkDiv) {
+        return 0;
+    }
+
+    if (clkDiv) {
+        freq /= (HAL_CRU_ClkGetDiv(hw, clkDiv));
+    }
+
+    return freq;
+}
+
+static HAL_Status RKX11x_HAL_CRU_ClkSetFreq(struct hwclk *hw, uint32_t clockName, uint32_t rate)
+{
+    uint32_t clkMux, clkDiv;
+    uint32_t mux = 0, div = 1;
+    uint32_t pRate = 0;
+    uint32_t maxDiv;
+    uint32_t pll;
+    uint8_t overMax = 0;
+    HAL_Status ret = HAL_OK;
+
+    if (clockName == RKX110_CLK_D_DSI_0_PATTERN_GEN) {
+        clockName = RKX111_CPS_DCLK_D_DSI_0_REC;
+    }
+    if (clockName == RKX110_CLK_D_DSI_1_PATTERN_GEN) {
+        clockName = RKX111_CPS_DCLK_D_DSI_1_REC;
+    }
+
+    clkMux = CLK_GET_MUX(clockName);
+    clkDiv = CLK_GET_DIV(clockName);
+
+    switch (clockName) {
+    case RKX110_CPS_PLL_RXPLL:
+        ret = HAL_CRU_SetPllFreq(hw, &RXPLL_SETUP, rate);
+        if (ret != HAL_OK) {
+            CRU_ERR("%s: RXPLL set rate: %d failed\n", hw->name, rate);
+        } else {
+            hw->pllRate[RKX110_RXPLL] = rate;
+            CRU_MSG("%s: RXPLL set rate: %d\n", hw->name, rate);
+        }
+
+        return ret;
+
+    case RKX110_CPS_PLL_CPLL:
+        ret = HAL_CRU_SetPllFreq(hw, &CPLL_SETUP, rate);
+        if (ret != HAL_OK) {
+            CRU_ERR("%s: CPLL set rate: %d failed\n", hw->name, rate);
+        } else {
+            hw->pllRate[RKX110_CPLL] = rate;
+            CRU_MSG("%s: CPLL set rate: %d\n", hw->name, rate);
+        }
+
+        return ret;
+
+    /* link(dclk): Allowed to change PLL rate if need ! */
+    case RKX111_CPS_DCLK_D_DSI_0_REC:
+    case RKX111_CPS_DCLK_D_DSI_1_REC:
+    case RKX110_CPS_CLK_2X_LVDS_RKLINK_TX:
+    /* i2s */
+    case RKX110_CPS_CLK_I2S_SRC_RKLINK_TX:
+        maxDiv = CLK_DIV_GET_MAXDIV(clkDiv);
+
+        if (DIV_NO_REM(hw->pllRate[RKX110_RXPLL], rate, maxDiv)) {
+            mux = 0;
+            pRate = hw->pllRate[RKX110_RXPLL];
+        } else if (DIV_NO_REM(hw->pllRate[RKX110_CPLL], rate, maxDiv)) {
+            mux = 1;
+            pRate = hw->pllRate[RKX110_CPLL];
+        } else if (DIV_NO_REM(OSC_24M, rate, maxDiv)) {
+            mux = 2;
+            pRate = OSC_24M;
+        } else {
+            if (clockName == RKX110_CPS_CLK_I2S_SRC_RKLINK_TX) {
+                pll = RKX110_CPS_PLL_CPLL;
+                if (DIV_NO_REM(I2S_122888_BEST_PRATE, rate, maxDiv)) {
+                    pRate = I2S_122888_BEST_PRATE;
+                } else if (DIV_NO_REM(I2S_112896_BEST_PRATE, rate, maxDiv)) {
+                    pRate = I2S_112896_BEST_PRATE;
+                }
+            } else {
+                pll = RKX110_CPS_PLL_RXPLL;
+            }
+
+            /* PLL change closest new rate <= 1200M if need */
+            if (!pRate) {
+                pRate = (_MHZ(1200) / rate) * rate;
+            }
+
+            ret = RKX11x_HAL_CRU_ClkSetFreq(hw, pll, pRate);
+            if (ret != HAL_OK) {
+                return ret;
+            }
+
+            /* if success, continue to set divider */
+        }
+        break;
+
+    /* bus */
+    case RKX110_CPS_DCLK_RX_PRE:
+    case RKX111_CPS_DCLK_RX_PRE_200M:
+    /* lvds */
+    case RKX111_CPS_CLK_D_LVDS0_PATTERN_GEN:
+    case RKX111_CPS_CLK_D_LVDS1_PATTERN_GEN:
+    /* camera */
+    case RKX110_CPS_CLK_CAM0_OUT2IO:
+    case RKX110_CPS_CLK_CAM1_OUT2IO:
+    case RKX110_CPS_CLK_CIF_OUT2IO:
+        mux = HAL_CRU_RoundFreqGetMux3(hw, rate, hw->pllRate[RKX110_RXPLL],
+                                       hw->pllRate[RKX110_CPLL], OSC_24M, &pRate);
+        break;
+
+    /* mipi */
+    case RKX110_CPS_CKREF_MIPIRXPHY0:
+    case RKX110_CPS_CKREF_MIPIRXPHY1:
+    case RKX110_CPS_CFGCLK_MIPIRXPHY0:
+    case RKX110_CPS_CFGCLK_MIPIRXPHY1:
+    /* pre-bus */
+    case RKX110_CPS_BUSCLK_RX_PRE0:
+        mux = HAL_CRU_RoundFreqGetMux2(hw, rate, hw->pllRate[RKX110_RXPLL],
+                                       hw->pllRate[RKX110_CPLL], &pRate);
+        break;
+
+    /* lvds0/1 rklink */
+    case RKX111_CPS_CLK_D_LVDS0_RKLINK_TX:
+        if (rate == _MHZ(150)) {    /* input clock: dclk_lvds0/1 */
+            return HAL_CRU_ClkSetMux(hw, clkMux, 0);
+        } else {
+            RKX11x_HAL_CRU_ClkSetFreq(hw, RKX111_CPS_CLK_D_LVDS0_PATTERN_GEN, rate);
+
+            return HAL_CRU_ClkSetMux(hw, clkMux, 1);
+        }
+        break;
+
+    case RKX111_CPS_CLK_D_LVDS1_RKLINK_TX:
+        if (rate == _MHZ(150)) {    /* input clock: dclk_lvds0/1 */
+            return HAL_CRU_ClkSetMux(hw, clkMux, 0);
+        } else {
+            RKX11x_HAL_CRU_ClkSetFreq(hw, RKX111_CPS_CLK_D_LVDS1_PATTERN_GEN, rate);
+
+            return HAL_CRU_ClkSetMux(hw, clkMux, 1);
+        }
+        break;
+
+    case RKX111_CPS_CLK_D_DSI_0_RKLINK_TX:
+        if (rate == _MHZ(400)) {    /* RKX110_CPS_DCLK_RX_PRE */
+            return HAL_CRU_ClkSetMux(hw, clkMux, 0);
+        } else {
+            return HAL_CRU_ClkSetMux(hw, clkMux, 1);
+        }
+        break;
+
+    case RKX111_CPS_CLK_D_DSI_1_RKLINK_TX:
+        if (rate == _MHZ(200)) {    /* RKX111_CPS_DCLK_RX_PRE_200M */
+            return HAL_CRU_ClkSetMux(hw, clkMux, 0);
+        } else {
+            return HAL_CRU_ClkSetMux(hw, clkMux, 1);
+        }
+        break;
+
+    /* bus */
+    case RKX110_CPS_BUSCLK_RX_PRE:
+        if (rate == OSC_24M) {
+            return HAL_CRU_ClkSetMux(hw, clkMux, 0);
+        } else {
+            RKX11x_HAL_CRU_ClkSetFreq(hw, RKX110_CPS_BUSCLK_RX_PRE0, rate);
+
+            return HAL_CRU_ClkSetMux(hw, clkMux, 1);
+        }
+        break;
+
+    /* gpio */
+    case RKX110_CPS_DCLK_RX_GPIO0:
+    case RKX110_CPS_DCLK_RX_GPIO1:
+    /* efuse */
+    case RKX110_CPS_RX_EFUSE:
+    /* pcs_ada */
+    case RKX110_CPS_PCS0_ADA:
+    case RKX110_CPS_PCS1_ADA:
+
+        return rate == OSC_24M ? 0 : HAL_INVAL;
+
+    case RKX110_CPS_CLK_PMA2LINK2PCS_CM:
+        if (rate != _MHZ(200)) {
+            return HAL_INVAL;
+        }
+
+        HAL_CRU_ClkSetMux(hw, clkMux, 0);
+
+        return HAL_OK;
+
+    default:
+        CRU_ERR("%s: %s: Unknown clk 0x%08x\n", __func__, hw->name, clockName);
+
+        return HAL_INVAL;
+    }
+
+    if (!clkMux && !clkDiv) {
+        return HAL_INVAL;
+    }
+
+    if (pRate) {
+        div = HAL_DIV_ROUND_UP(pRate, rate);
+    }
+
+    if (clkDiv) {
+        overMax = div > CLK_DIV_GET_MAXDIV(clkDiv);
+        if (overMax) {
+            CRU_MSG("%s: %s: Clk '0x%08x' req div(%d) over max(%d)!\n",
+                    __func__, hw->name, clockName, div, CLK_DIV_GET_MAXDIV(clkDiv));
+            div = CLK_DIV_GET_MAXDIV(clkDiv);
+        }
+        HAL_CRU_ClkSetDiv(hw, clkDiv, div);
+    }
+
+    if (clkMux) {
+        HAL_CRU_ClkSetMux(hw, clkMux, mux);
+    }
+
+    return HAL_OK;
+}
+
+#if RKX110_SSCG_CPLL_EN || RKX110_SSCG_RXPLL_EN
+static void RKX11x_HAL_CRU_Init_SSCG(struct hwclk *hw)
+{
+    uint8_t down_spread = 1; /* 0: center spread */
+    uint8_t amplitude = 8;   /* range: 0x00 - 0x1f */
+
+#if RKX110_SSCG_CPLL_EN
+    /* down-spread, 0.8%, 37.5khz */
+    HAL_CRU_Write(hw, hw->cru_base + 0x0c, 0x1f000000 | ((amplitude & 0x1f) << 8));
+    HAL_CRU_Write(hw, hw->cru_base + 0x0c, 0x00f00050);
+    HAL_CRU_Write(hw, hw->cru_base + 0x0c, 0x00080000 | ((down_spread & 0x1) << 3));
+    HAL_CRU_Write(hw, hw->cru_base + 0x04, 0x10000000);
+    HAL_CRU_Write(hw, hw->cru_base + 0x0c, 0x00070000);
+    CRU_MSG("%s: CPLL enable SSCG\n", hw->name);
+#endif
+#if RKX110_SSCG_RXPLL_EN
+    /* down-spread, 0.8%, 37.5khz */
+    HAL_CRU_Write(hw, hw->cru_base + 0x2c, 0x1f000000 | ((amplitude & 0x1f) << 8));
+    HAL_CRU_Write(hw, hw->cru_base + 0x2c, 0x00f00050);
+    HAL_CRU_Write(hw, hw->cru_base + 0x2c, 0x00080000 | ((down_spread & 0x1) << 3));
+    HAL_CRU_Write(hw, hw->cru_base + 0x24, 0x10000000);
+    HAL_CRU_Write(hw, hw->cru_base + 0x2c, 0x00070000);
+    CRU_MSG("%s: RXPLL enable SSCG\n", hw->name);
+#endif
+}
+#endif
+
+static HAL_Status RKX11x_HAL_CRU_InitTestout(struct hwclk *hw, uint32_t clockName,
+                                             uint32_t muxValue, uint32_t divValue)
+{
+    uint32_t clkMux = CLK_GET_MUX(clockName);
+    uint32_t clkDiv = CLK_GET_DIV(clockName);
+
+    /* gpio0_b7: iomux to clk_testout */
+    HAL_CRU_Write(hw, RKX110_GRF_GPIO0B_IOMUX_H, GPIO0B7_TEST_CLKOUT);
+
+    /* Enable clock */
+    HAL_CRU_ClkEnable(hw, RKX110_CLK_TESTOUT_TOP_GATE);
+
+    /* Mux, div */
+    HAL_CRU_ClkSetDiv(hw, clkDiv, divValue);
+    HAL_CRU_ClkSetMux(hw, clkMux, muxValue);
+
+    CRU_MSG("%s: Testout div=%d, mux=%d\n", hw->name, divValue, muxValue);
+
+    return HAL_OK;
+}
+
+static HAL_Status RKX11x_HAL_CRU_Init(struct hwclk *hw, struct xferclk *xfer)
+{
+    hw->cru_base = 0x0;
+    hw->sel_con0 = hw->cru_base + 0x100;
+    hw->gate_con0 = hw->cru_base + 0x300;
+    hw->softrst_con0 = hw->cru_base + 0x400;
+    hw->gbl_srst_fst = 0x0614;
+    hw->flags = 0;
+    hw->num_gate = 16 * 12;
+    hw->gate = HAL_KCALLOC(hw->num_gate, sizeof(struct clkGate));
+    if (!hw->gate) {
+        return HAL_NOMEM;
+    }
+    strcat(hw->name, "<CRU.111@");
+    strcat(hw->name, xfer->name);
+    strcat(hw->name, ">");
+
+    /* Don't change order */
+#if RKX110_SSCG_CPLL_EN || RKX110_SSCG_RXPLL_EN
+    RKX11x_HAL_CRU_Init_SSCG(hw);
+#endif
+    RKX11x_HAL_CRU_ClkSetFreq(hw, RKX110_CPS_PLL_CPLL, _MHZ(1200));
+    RKX11x_HAL_CRU_ClkSetFreq(hw, RKX110_CPS_PLL_RXPLL, _MHZ(1188));
+    RKX11x_HAL_CRU_ClkSetFreq(hw, RKX110_CPS_BUSCLK_RX_PRE, _MHZ(100));
+    RKX11x_HAL_CRU_ClkSetFreq(hw, RKX110_CPS_CLK_PMA2LINK2PCS_CM, _MHZ(200));
+    RKX11x_HAL_CRU_ClkSetFreq(hw, RKX110_CPS_DCLK_RX_PRE, _MHZ(400));
+    RKX11x_HAL_CRU_ClkSetFreq(hw, RKX111_CPS_DCLK_RX_PRE_200M, _MHZ(200));
+    RKX11x_HAL_CRU_ClkSetFreq(hw, RKX111_CPS_DCLK_D_DSI_0_REC, _MHZ(200));
+    RKX11x_HAL_CRU_ClkSetFreq(hw, RKX111_CPS_DCLK_D_DSI_1_REC, _MHZ(200));
+    RKX11x_HAL_CRU_ClkSetFreq(hw, RKX111_CPS_CLK_D_LVDS0_PATTERN_GEN, _MHZ(150));
+    RKX11x_HAL_CRU_ClkSetFreq(hw, RKX111_CPS_CLK_D_LVDS1_PATTERN_GEN, _MHZ(150));
+    RKX11x_HAL_CRU_ClkSetFreq(hw, RKX110_CPS_CLK_I2S_SRC_RKLINK_TX, _MHZ(400));
+    RKX11x_HAL_CRU_ClkSetFreq(hw, RKX110_CPS_CFGCLK_MIPIRXPHY0, _MHZ(100));
+
+    HAL_CRU_ClkEnable(hw, RKX110_CLK_TESTOUT_TOP_GATE);
+
+#if RKX110_TESTOUT_MUX >= 0
+    /* clk_testout support max 150M output, so set div=10 by default if not 24M */
+    RKX11x_HAL_CRU_InitTestout(hw, RKX110_CPS_TEST_CLKOUT, RKX110_TESTOUT_MUX,
+                               RKX110_TESTOUT_MUX == 0 ? 1 : 10);
+#endif
+
+    return HAL_OK;
+}
+
+PNAME(mux_24m_p) = { "xin24m" };
+PNAME(mux_rxpll_cpll_p) = { "rxpll", "cpll" };
+PNAME(mux_rxpll_cpll_24m_p) = { "rxpll", "cpll", "xin24m" };
+PNAME(mux_rxpre0_24m_p) = { "xin24m", "busclk_rx_pre0" };
+
+#define CAL_FREQ_REG    0xf00
+#define CAL_FREQ_EN_REG 0xf04
+
+static uint32_t RKX11x_HAL_CRU_ClkGetExtFreq(struct hwclk *hw, uint32_t clk)
+{
+    uint32_t clkMux = CLK_GET_MUX(RKX110_CPS_TEST_CLKOUT);
+    uint32_t clkDiv = CLK_GET_DIV(RKX110_CPS_TEST_CLKOUT);
+    uint32_t freq, mhz;
+    uint8_t div = 10;
+
+    HAL_CRU_ClkSetDiv(hw, clkDiv, div);
+    HAL_CRU_ClkSetMux(hw, clkMux, clk);
+    HAL_CRU_Write(hw, CAL_FREQ_EN_REG, 1); /* NOTE: 1: disable, 0: enable */
+    HAL_CRU_Write(hw, CAL_FREQ_EN_REG, 0);
+    HAL_SleepMs(3);
+    freq = HAL_CRU_Read(hw, CAL_FREQ_REG);
+    HAL_CRU_Write(hw, CAL_FREQ_EN_REG, 1);
+
+    /* Fix accuracy */
+    if ((freq % 10) == 0x9) {
+        freq++;
+    }
+
+    freq *= (1000 * div);
+
+    /* If no external input, freq is close to 24M */
+    mhz = freq / 1000000;
+    if ((clk != 0) && (mhz == 23 || mhz == 24)) {
+        freq = 0;
+    }
+
+    return freq;
+}
+
+static struct clkTable rkx11x_clkTable[] = {
+    /* internal */
+    CLK_DECLARE_INT("rxpll", RKX110_CPS_PLL_RXPLL, mux_24m_p),
+    CLK_DECLARE_INT("cpll", RKX110_CPS_PLL_CPLL, mux_24m_p),
+    CLK_DECLARE_INT("dclk_rx_pre", RKX110_CPS_DCLK_RX_PRE, mux_rxpll_cpll_24m_p),
+    CLK_DECLARE_INT("clk_d_dsi_0_pattern", RKX110_CPS_DCLK_RX_PRE, mux_rxpll_cpll_24m_p),
+    CLK_DECLARE_INT("clk_d_dsi_1_pattern", RKX110_CPS_DCLK_RX_PRE, mux_rxpll_cpll_24m_p),
+    CLK_DECLARE_INT("clk_cam0_out2io", RKX110_CPS_CLK_CAM0_OUT2IO, mux_rxpll_cpll_24m_p),
+    CLK_DECLARE_INT("clk_cam1_out2io", RKX110_CPS_CLK_CAM1_OUT2IO, mux_rxpll_cpll_24m_p),
+    CLK_DECLARE_INT("clk_cif_out2io", RKX110_CPS_CLK_CIF_OUT2IO, mux_rxpll_cpll_24m_p),
+    CLK_DECLARE_INT("dclk_d_dsi_0_rec", RKX111_CPS_DCLK_D_DSI_0_REC, mux_rxpll_cpll_24m_p),
+    CLK_DECLARE_INT("dclk_d_dsi_1_rec", RKX111_CPS_DCLK_D_DSI_1_REC, mux_rxpll_cpll_24m_p),
+    CLK_DECLARE_INT("clk_2x_lvds_rklink_tx", RKX110_CPS_CLK_2X_LVDS_RKLINK_TX, mux_rxpll_cpll_24m_p),
+    CLK_DECLARE_INT("clk_i2s_src_rklink_tx", RKX110_CPS_CLK_I2S_SRC_RKLINK_TX, mux_rxpll_cpll_24m_p),
+    CLK_DECLARE_INT("ckref_mipirxphy0", RKX110_CPS_CKREF_MIPIRXPHY0, mux_rxpll_cpll_p),
+    CLK_DECLARE_INT("ckref_mipirxphy1", RKX110_CPS_CKREF_MIPIRXPHY1, mux_rxpll_cpll_p),
+    CLK_DECLARE_INT("cfgclk_mipirxphy0", RKX110_CPS_CFGCLK_MIPIRXPHY0, mux_rxpll_cpll_p),
+    CLK_DECLARE_INT("cfgclk_mipirxphy1", RKX110_CPS_CFGCLK_MIPIRXPHY1, mux_rxpll_cpll_p),
+    CLK_DECLARE_INT("busclk_rx_pre0", RKX110_CPS_BUSCLK_RX_PRE0, mux_rxpll_cpll_p),
+    CLK_DECLARE_INT("busclk_rx_pre", RKX110_CPS_BUSCLK_RX_PRE, mux_rxpre0_24m_p),
+    CLK_DECLARE_INT("dclk_rx_gpio0", RKX110_CPS_DCLK_RX_GPIO0, mux_24m_p),
+    CLK_DECLARE_INT("dclk_rx_gpio1", RKX110_CPS_DCLK_RX_GPIO1, mux_24m_p),
+    CLK_DECLARE_INT("rx_efuse", RKX110_CPS_RX_EFUSE, mux_24m_p),
+    CLK_DECLARE_INT("pcs0_ada", RKX110_CPS_PCS0_ADA, mux_24m_p),
+    CLK_DECLARE_INT("pcs1_ada", RKX110_CPS_PCS1_ADA, mux_24m_p),
+    CLK_DECLARE_INT("dclk_rx_pre_200m", RKX111_CPS_DCLK_RX_PRE_200M, mux_rxpll_cpll_24m_p),
+    CLK_DECLARE_INT("clk_d_lvds0_pattern_gen", RKX111_CPS_CLK_D_LVDS0_PATTERN_GEN, mux_rxpll_cpll_24m_p),
+    CLK_DECLARE_INT("clk_d_lvds1_pattern_gen", RKX111_CPS_CLK_D_LVDS1_PATTERN_GEN, mux_rxpll_cpll_24m_p),
+
+    /* external */
+    CLK_DECLARE_EXT("xin24m", 0, RKX11x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT("rxpclk_vicap_lvds", 3, RKX11x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT("clk_rxbytehs_csihost0", 4, RKX11x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT("clk_rxbytehs_csihost1", 5, RKX11x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT("clk_d_rgb_rklink_tx", 6, RKX11x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT("dclk_lvds0", 7, RKX11x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT("dclk_lvds1", 8, RKX11x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT("clk_link_pcs0", 9, RKX11x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT("clk_link_pcs1", 10, RKX11x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT("pclkin_vicap_dvp_rx", 21, RKX11x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT("iclk_dsi0", 22, RKX11x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT("iclk_dsi1", 23, RKX11x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT("rxpclk_lvds_align", 24, RKX11x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT("clk_8x_pma2pcs0", 25, RKX11x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT("clk_8x_pma2pcs1", 26, RKX11x_HAL_CRU_ClkGetExtFreq),
+
+    CLK_DECLARE_EXT_PARENT("clk_d_lvds0_rklink_tx_cm", 7, "dclk_lvds0", RKX11x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT_PARENT("clk_d_lvds0_rklink_tx", 7, "clk_d_lvds0_rklink_tx_cm", RKX11x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT_PARENT("clk_d_lvds0_pattern_gen_en", 7, "clk_d_lvds0_rklink_tx_cm", RKX11x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT_PARENT("clk_d_lvds1_rklink_tx_cm", 8, "dclk_lvds1", RKX11x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT_PARENT("clk_d_lvds1_rklink_tx", 8, "clk_d_lvds1_rklink_tx_cm", RKX11x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT_PARENT("clk_d_lvds1_pattern_gen_en", 8, "clk_d_lvds1_rklink_tx_cm", RKX11x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT_PARENT("clk_pma2pcs0", 9, "clk_link_pcs0", RKX11x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT_PARENT("clk_pma2link2pcs_cm", 9, "clk_link_pcs0", RKX11x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT_PARENT("clk_pma2pcs1", 9, "clk_link_pcs1", RKX11x_HAL_CRU_ClkGetExtFreq),
+
+    { /* sentinel */ },
+};
+
+struct clkOps rkx111_clk_ops =
+{
+    .clkTable = rkx11x_clkTable,
+    .clkInit = RKX11x_HAL_CRU_Init,
+    .clkGetFreq = RKX11x_HAL_CRU_ClkGetFreq,
+    .clkSetFreq = RKX11x_HAL_CRU_ClkSetFreq,
+    .clkInitTestout = RKX11x_HAL_CRU_InitTestout,
+};
diff --git a/kernel/drivers/mfd/rkx110_x120/hal/cru_rkx111.h b/kernel/drivers/mfd/rkx110_x120/hal/cru_rkx111.h
new file mode 100644
index 0000000..fc9b493
--- /dev/null
+++ b/kernel/drivers/mfd/rkx110_x120/hal/cru_rkx111.h
@@ -0,0 +1,117 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2023 Rockchip Electronics Co. Ltd.
+ *
+ * Author: Joseph Chen <chenjh@rock-chips.com>
+ */
+
+#ifndef _CRU_RKX111_H
+
+#include "cru_rkx110.h"
+
+// RXCRU_SOFTRST_CON02(Offset:0x408)
+#define RKX111_SRST_DRESETN_VICAP_CSI_LS                     0x0000002E
+
+// RXCRU_SOFTRST_CON05(Offset:0x414)
+#define RKX111_SRST_RESETN_D_DSI_0_REC_RKLINK_TX             0x00000056
+#define RKX111_SRST_RESETN_D_DSI_1_REC_RKLINK_TX             0x00000057
+
+// RXCRU_SOFTRST_CON06(Offset:0x418)
+#define RKX111_SRST_RESETN_D_LVDS0_PATTERN_GEN               0x00000066
+#define RKX111_SRST_RESETN_D_LVDS1_PATTERN_GEN               0x00000067
+
+// RXCRU_SOFTRST_CON11(Offset:0x42C)
+#define RKX111_SRST_PRESETN_LBIST_ADA_RX                     0x000000B1
+
+// RXCRU_GATE_CON02(Offset:0x308)
+#define RKX111_DCLK_RX_PRE_200M_GATE                         0x0000002D
+#define RKX111_DCLK_VICAP_CSI_LS_GATE                        0x0000002E
+
+// RXCRU_GATE_CON04(Offset:0x310)
+#define RKX111_CLK_D_DSI_0_RKLINK_TX_PRE_GATE                0x00000044
+#define RKX111_CLK_D_DSI_1_RKLINK_TX_PRE_GATE                0x00000045
+#define RKX111_CLK_D_LVDS0_RKLINK_TX_GATE                    0x00000047
+#define RKX111_CLK_D_LVDS0_RKLINK_TX_PRE_GATE                0x00000048
+#define RKX111_CLK_D_LVDS1_RKLINK_TX_GATE                    0x00000049
+#define RKX111_CLK_D_LVDS1_RKLINK_TX_PRE_GATE                0x0000004A
+#define RKX111_DCLK_D_DSI_0_REC_GATE                         0x0000004D
+#define RKX111_DCLK_D_DSI_1_REC_GATE                         0x0000004E
+
+// RXCRU_GATE_CON05(Offset:0x314)
+#define RKX111_DCLK_D_DSI_0_REC_RKLINK_TX_GATE               0x00000056
+#define RKX111_DCLK_D_DSI_1_REC_RKLINK_TX_GATE               0x00000057
+#define RKX111_CLK_D_DSI_0_RKLINK_TX_GATE                    0x00000058
+#define RKX111_CLK_D_DSI_1_RKLINK_TX_GATE                    0x00000059
+
+// RXCRU_GATE_CON06(Offset:0x318)
+#define RKX111_CLK_D_LVDS0_PATTERN_GEN_GATE                  0x00000066
+#define RKX111_CLK_D_LVDS1_PATTERN_GEN_GATE                  0x00000067
+
+// RXCRU_GATE_CON11(Offset:0x32C)
+#define RKX111_PCLK_LBIST_ADA_RX_GATE                        0x000000B1
+
+// RXCRU_CLKSEL_CON05(Offset:0x114)
+#define RKX111_DCLK_D_DSI_0_REC_DIV                          0x08000005
+#define RKX111_DCLK_D_DSI_0_REC_SEL                          0x020E0005
+#define RKX111_DCLK_D_DSI_0_REC_SEL_CLK_RXPLL_MUX            0U
+#define RKX111_DCLK_D_DSI_0_REC_SEL_CLK_CPLL_MUX             1U
+#define RKX111_DCLK_D_DSI_0_REC_SEL_XIN_OSC0_FUNC            2U
+
+// RXCRU_CLKSEL_CON06(Offset:0x118)
+#define RKX111_DCLK_D_DSI_1_REC_DIV                          0x08000006
+#define RKX111_DCLK_D_DSI_1_REC_SEL                          0x020E0006
+#define RKX111_DCLK_D_DSI_1_REC_SEL_CLK_RXPLL_MUX            0U
+#define RKX111_DCLK_D_DSI_1_REC_SEL_CLK_CPLL_MUX             1U
+#define RKX111_DCLK_D_DSI_1_REC_SEL_XIN_OSC0_FUNC            2U
+
+// RXCRU_CLKSEL_CON13(Offset:0x134)
+#define RKX111_CLK_D_LVDS0_PATTERN_GEN_DIV                   0x0800000D
+#define RKX111_CLK_D_LVDS0_PATTERN_GEN_SEL                   0x020E000D
+#define RKX111_CLK_D_LVDS0_PATTERN_GEN_SEL_CLK_RXPLL_MUX     0U
+#define RKX111_CLK_D_LVDS0_PATTERN_GEN_SEL_CLK_CPLL_MUX      1U
+#define RKX111_CLK_D_LVDS0_PATTERN_GEN_SEL_XIN_OSC0_FUNC     2U
+
+// RXCRU_CLKSEL_CON14(Offset:0x138)
+#define RKX111_CLK_D_LVDS1_PATTERN_GEN_DIV                   0x0800000E
+#define RKX111_CLK_D_LVDS1_PATTERN_GEN_SEL                   0x020E000E
+#define RKX111_CLK_D_LVDS1_PATTERN_GEN_SEL_CLK_RXPLL_MUX     0U
+#define RKX111_CLK_D_LVDS1_PATTERN_GEN_SEL_CLK_CPLL_MUX      1U
+#define RKX111_CLK_D_LVDS1_PATTERN_GEN_SEL_XIN_OSC0_FUNC     2U
+
+// RXCRU_CLKSEL_CON16(Offset:0x140)
+#define RKX111_DCLK_RX_PRE_200M_DIV                          0x06000010
+#define RKX111_DCLK_RX_PRE_200M_SEL                          0x02060010
+#define RKX111_DCLK_RX_PRE_200M_SEL_CLK_RXPLL_MUX            0U
+#define RKX111_DCLK_RX_PRE_200M_SEL_CLK_CPLL_MUX             1U
+#define RKX111_DCLK_RX_PRE_200M_SEL_XIN_OSC0_FUNC            2U
+
+// RXCRU_CLKSEL_CON17(Offset:0x144)
+#define RKX111_CLK_D_DSI_0_RKLINK_TX_SEL                           0x010C0011
+#define RKX111_CLK_D_DSI_0_RKLINK_TX_SEL_CLK_D_DSI_0_RKLINK_TX_PRE 0U
+#define RKX111_CLK_D_DSI_0_RKLINK_TX_SEL_CLK_D_DSI_0_PATTERN_GEN   1U
+#define RKX111_CLK_D_DSI_1_RKLINK_TX_SEL                           0x010D0011
+#define RKX111_CLK_D_DSI_1_RKLINK_TX_SEL_CLK_D_DSI_1_RKLINK_TX_PRE 0U
+#define RKX111_CLK_D_DSI_1_RKLINK_TX_SEL_CLK_D_DSI_1_PATTERN_GEN   1U
+#define RKX111_CLK_D_LVDS0_RKLINK_TX_SEL                           0x010E0011
+#define RKX111_CLK_D_LVDS0_RKLINK_TX_SEL_CLK_D_LVDS0_RKLINK_TX_PRE 0U
+#define RKX111_CLK_D_LVDS0_RKLINK_TX_SEL_CLK_D_LVDS0_PATTERN_GEN   1U
+#define RKX111_CLK_D_LVDS1_RKLINK_TX_SEL                           0x010F0011
+#define RKX111_CLK_D_LVDS1_RKLINK_TX_SEL_CLK_D_LVDS1_RKLINK_TX_PRE 0U
+#define RKX111_CLK_D_LVDS1_RKLINK_TX_SEL_CLK_D_LVDS1_PATTERN_GEN   1U
+
+/* COMPOSITE */
+#define RKX111_CPS_DCLK_RX_PRE_200M            COMPOSITE_CLK(RKX111_DCLK_RX_PRE_200M_SEL, RKX111_DCLK_RX_PRE_200M_DIV)
+
+/* lvds_pattern_gen => lvds_rklink */
+#define RKX111_CPS_CLK_D_LVDS0_PATTERN_GEN     COMPOSITE_CLK(RKX111_CLK_D_LVDS0_PATTERN_GEN_SEL, RKX111_CLK_D_LVDS0_PATTERN_GEN_DIV)
+#define RKX111_CPS_CLK_D_LVDS1_PATTERN_GEN     COMPOSITE_CLK(RKX111_CLK_D_LVDS1_PATTERN_GEN_SEL, RKX111_CLK_D_LVDS1_PATTERN_GEN_DIV)
+#define RKX111_CPS_CLK_D_LVDS0_RKLINK_TX       COMPOSITE_CLK(RKX111_CLK_D_LVDS0_RKLINK_TX_SEL, 0)
+#define RKX111_CPS_CLK_D_LVDS1_RKLINK_TX       COMPOSITE_CLK(RKX111_CLK_D_LVDS1_RKLINK_TX_SEL, 0)
+
+/* dsi_rec => dsi_rklink */
+#define RKX111_CPS_DCLK_D_DSI_0_REC            COMPOSITE_CLK(RKX111_DCLK_D_DSI_0_REC_SEL, RKX111_DCLK_D_DSI_0_REC_DIV)
+#define RKX111_CPS_DCLK_D_DSI_1_REC            COMPOSITE_CLK(RKX111_DCLK_D_DSI_1_REC_SEL, RKX111_DCLK_D_DSI_1_REC_DIV)
+#define RKX111_CPS_CLK_D_DSI_0_RKLINK_TX       COMPOSITE_CLK(RKX111_CLK_D_DSI_0_RKLINK_TX_SEL, 0)
+#define RKX111_CPS_CLK_D_DSI_1_RKLINK_TX       COMPOSITE_CLK(RKX111_CLK_D_DSI_1_RKLINK_TX_SEL, 0)
+
+#endif
diff --git a/kernel/drivers/mfd/rkx110_x120/hal/cru_rkx120.c b/kernel/drivers/mfd/rkx110_x120/hal/cru_rkx120.c
new file mode 100644
index 0000000..07da30b
--- /dev/null
+++ b/kernel/drivers/mfd/rkx110_x120/hal/cru_rkx120.c
@@ -0,0 +1,599 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2022 Rockchip Electronics Co. Ltd.
+ *
+ * Author: Joseph Chen <chenjh@rock-chips.com>
+ */
+
+#include "cru_core.h"
+#include "cru_rkx120.h"
+
+/*
+ *			[RKX120 CHIP]: TX
+ *
+ * ================= SECITON: Input clock from devices =========================
+ *
+ * ### 300M ###
+ * dclk_c_dvp_src
+ * dclk_d_dsi_src --|-- dclk_d_ds
+ *                  |-- dclk_d_dsi_pattern_gen
+ *
+ * clk_lvds0_src --|-- clk_lvds0
+ *                 |-- clk_lvds0_pattern_gen
+ *
+ * clk_lvds1_src --|-- clk_lvds1
+ *                 |-- clk_lvds1_pattern_gen
+ *
+ * dclk_rgc_src
+ *
+ *
+ * ### 200M ###
+ * clk_link_pcs0 --|-- clk_pma2pcs0                |-- clk_pma2link2pcs_link
+ *                 |                               |
+ *                 |-- clk_pma2link2pcs_cm (MUX) --|-- clk_pma2link2pcs_psc0
+ * clk_link_pcs1 --|                               |
+ *                 |-- clk_pma2pcs1                |-- clk_pma2link2pcs_psc1
+ *
+ * clk_txbytehs_dsitx_csitx0
+ * clk_txbytehs_dsitx_csitx1
+ *
+ *
+ *
+ * ### 150M ###
+ *
+ * rxpclk_vicap_lvds
+ *
+ *
+ * ### 100M ###
+ *
+ * clk_2x_pma2pcs0
+ * clk_2x_pma2pcs1
+ *
+ *
+ * ### 50M ###
+ * clk_txesc_mipitxphy0
+ * clk_rxesc_dsitx
+ *
+ */
+
+#define RKX120_SSCG_TXPLL_EN 0
+#define RKX120_SSCG_CPLL_EN  0
+#define RKX120_TESTOUT_MUX   -1 /* valid options: RKX120_TEST_CLKOUT_IOUT_SEL_? */
+
+#define RKX120_GRF_GPIO0B_IOMUX_H 0x0101000c
+#define GPIO0B7_TEST_CLKOUT       0x01c00080
+
+#define I2S_122888_BEST_PRATE 393216000
+#define I2S_112896_BEST_PRATE 756403200
+
+static struct PLL_CONFIG PLL_TABLE[] = {
+    /* _mhz, _refDiv, _fbDiv, _postdDv1, _postDiv2, _dsmpd, _frac */
+    RK_PLL_RATE(1200000000, 1, 50, 1, 1, 1, 0),
+    RK_PLL_RATE(600000000, 1, 25, 1, 1, 1, 0),
+
+    /* display */
+    RK_PLL_RATE(1188000000, 2, 99, 1, 1, 1, 0),
+
+    /* audio: 12.288M */
+    RK_PLL_RATE(688128000, 1, 86, 3, 1, 0, 268435),   /* div=56, vco=2064.384M */
+    RK_PLL_RATE(393216000, 2, 131, 4, 1, 0, 1207959), /* div=32, vco=1572.864M */
+    RK_PLL_RATE(344064000, 1, 43, 3, 1, 0, 134217),   /* div=28, vco=1032.192M */
+    /* audio: 11.2896M */
+    RK_PLL_RATE(474163200, 1, 79, 4, 1, 0, 456340),   /* div=42, vco=1896.6528M */
+    RK_PLL_RATE(756403200, 1, 63, 2, 1, 0, 563714),   /* div=67, vco=1512.8064M */
+    RK_PLL_RATE(564480000, 1, 47, 2, 1, 0, 671088),   /* div=50, vco=1128.96M */
+
+    { /* sentinel */ },
+};
+
+static struct PLL_SETUP TXPLL_SETUP = {
+    .id = PLL_TXPLL,
+    .conOffset0 = 0x01000020,
+    .conOffset1 = 0x01000024,
+    .conOffset2 = 0x01000028,
+    .conOffset3 = 0x0100002c,
+    .modeOffset = 0x01000600,
+    .modeShift = 0,         /* 0: slow-mode, 1: normal-mode */
+    .lockShift = 10,
+    .modeMask = 0x1,
+    .rateTable = PLL_TABLE,
+
+    .minRefdiv = 1,
+    .maxRefdiv = 2,
+    .minVco = _MHZ(375),
+    .maxVco = _MHZ(2400),
+    .minFout = _MHZ(24),
+    .maxFout = _MHZ(1200),
+    .sscgEn = RKX120_SSCG_TXPLL_EN,
+};
+
+static struct PLL_SETUP CPLL_SETUP = {
+    .id = PLL_CPLL,
+    .conOffset0 = 0x01000000,
+    .conOffset1 = 0x01000004,
+    .conOffset2 = 0x01000008,
+    .conOffset3 = 0x0100000c,
+    .modeOffset = 0x01000600,
+    .modeShift = 2,
+    .lockShift = 10,
+    .modeMask = 0x1 << 2,
+    .rateTable = PLL_TABLE,
+
+    .minRefdiv = 1,
+    .maxRefdiv = 2,
+    .minVco = _MHZ(375),
+    .maxVco = _MHZ(2400),
+    .minFout = _MHZ(24),
+    .maxFout = _MHZ(1200),
+    .sscgEn = RKX120_SSCG_CPLL_EN,
+};
+
+static uint32_t RKX12x_HAL_CRU_ClkGetFreq(struct hwclk *hw, uint32_t clockName)
+{
+    uint32_t clkMux = CLK_GET_MUX(clockName);
+    uint32_t clkDiv = CLK_GET_DIV(clockName);
+    uint32_t pRate = 0, freq = 0;
+
+    switch (clockName) {
+    case RKX120_CPS_PLL_TXPLL:
+        freq = HAL_CRU_GetPllFreq(hw, &TXPLL_SETUP);
+        hw->pllRate[RKX120_TXPLL] = freq;
+
+        return freq;
+
+    case RKX120_CPS_PLL_CPLL:
+        freq = HAL_CRU_GetPllFreq(hw, &CPLL_SETUP);
+        hw->pllRate[RKX120_CPLL] = freq;
+
+        return freq;
+
+    /* link: 300M */
+    case RKX120_CPS_E0_CLK_RKLINK_RX_PRE:
+    case RKX120_CPS_E1_CLK_RKLINK_RX_PRE:
+    /* csi: 50M */
+    case RKX120_CPS_CLK_TXESC_CSITX0:
+    case RKX120_CPS_CLK_TXESC_CSITX1:
+    /* i2s: 600M */
+    case RKX120_CPS_CLK_I2S_SRC_RKLINK_RX:
+    /* pwm: 100M */
+    case RKX120_CPS_CLK_PWM_TX:
+    case RKX120_CPS_PCLKOUT_DVPTX:
+        freq = HAL_CRU_MuxGetFreq3(hw, clkMux,
+                                   hw->pllRate[RKX120_TXPLL],
+                                   hw->pllRate[RKX120_CPLL], OSC_24M);
+        break;
+
+    /* gate clock from TX_E0_CLK_RKLINK_RX_PRE */
+    case RKX120_CPS_ICLK_C_CSI0:
+
+        return RKX12x_HAL_CRU_ClkGetFreq(hw, RKX120_CPS_E0_CLK_RKLINK_RX_PRE);
+
+    /* gate clock from TX_E1_CLK_RKLINK_RX_PRE */
+    case RKX120_CPS_ICLK_C_CSI1:
+
+        return RKX12x_HAL_CRU_ClkGetFreq(hw, RKX120_CPS_E1_CLK_RKLINK_RX_PRE);
+
+    /* pre-bus: 100M */
+    case RKX120_CPS_BUSCLK_TX_PRE0:
+        freq = HAL_CRU_MuxGetFreq2(hw, clkMux,
+                                   hw->pllRate[RKX120_TXPLL],
+                                   hw->pllRate[RKX120_CPLL]);
+        break;
+    /*
+     * bus: 100MHZ
+     *
+     * === TX_BUSCLK_TX_PRE gate children ===
+     *
+     * pclk_tx_cru
+     * pclk_tx_grf
+     * pclk_tx_gpio0/1
+     * pclk_tx_efuse
+     * pclk_mipi_grf_tx0/1
+     * pclk_tx_i2c2apb
+     * pclk_tx_i2c2apb_debug
+     * hclk_dvp_tx
+     * pclk_csitx0/1
+     * pclk_dsitx
+     * pclk_rklink_rx
+     * pclk_d_dsi_pattern_gen
+     * pclk_lvds{0, 1}_pattern_gen
+     * pclk_pcs0/1
+     * pclk_pcs{0,1}_ada
+     * pclk_mipitxphy0/1
+     * pclk_pwm_tx
+     * pclk_dft2apb
+     */
+    case RKX120_CPS_BUSCLK_TX_PRE:
+        pRate = RKX12x_HAL_CRU_ClkGetFreq(hw, RKX120_CPS_BUSCLK_TX_PRE0);
+        freq = HAL_CRU_MuxGetFreq2(hw, clkMux, OSC_24M, pRate);
+        break;
+
+    /* gpio: 24M */
+    case RKX120_CPS_DCLK_TX_GPIO0:
+    case RKX120_CPS_DCLK_TX_GPIO1:
+    /* efuse: 24M */
+    case RKX120_CPS_CLK_TX_EFUSE:
+    /* pcs_ada: 24M */
+    case RKX120_CPS_CLK_PCS0_ADA:
+    case RKX120_CPS_CLK_PCS1_ADA:
+    /* capture_pwm: 24M */
+    case RKX120_CPS_CLK_CAPTURE_PWM_TX:
+
+        return OSC_24M;
+
+    case RKX120_CPS_CLK_PMA2PCS2LINK_CM:
+
+        return _MHZ(200);
+
+    default:
+        CRU_ERR("%s: %s: Unknown clk 0x%08x\n", __func__, hw->name, clockName);
+
+        return HAL_INVAL;
+    }
+
+    if (!clkMux && !clkDiv) {
+        return 0;
+    }
+
+    if (clkDiv) {
+        freq /= (HAL_CRU_ClkGetDiv(hw, clkDiv));
+    }
+
+    return freq;
+}
+
+static HAL_Status RKX12x_HAL_CRU_ClkSetFreq(struct hwclk *hw, uint32_t clockName, uint32_t rate)
+{
+    uint32_t clkMux = CLK_GET_MUX(clockName);
+    uint32_t clkDiv = CLK_GET_DIV(clockName);
+    uint32_t mux = 0, div = 1;
+    uint32_t pRate = 0;
+    uint32_t maxDiv;
+    uint32_t pll;
+    uint8_t overMax;
+    HAL_Status ret = HAL_OK;
+
+    switch (clockName) {
+    case RKX120_CPS_PLL_TXPLL:
+        ret = HAL_CRU_SetPllFreq(hw, &TXPLL_SETUP, rate);
+        hw->pllRate[RKX120_TXPLL] = rate;
+        CRU_MSG("%s: TXPLL set rate: %d\n", hw->name, rate);
+
+        return ret;
+
+    case RKX120_CPS_PLL_CPLL:
+        ret = HAL_CRU_SetPllFreq(hw, &CPLL_SETUP, rate);
+        hw->pllRate[RKX120_CPLL] = rate;
+        CRU_MSG("%s: CPLL set rate: %d\n", hw->name, rate);
+
+        return ret;
+
+    /* link(dclk): Allowed to change PLL rate if need ! */
+    case RKX120_CPS_E0_CLK_RKLINK_RX_PRE:
+    case RKX120_CPS_E1_CLK_RKLINK_RX_PRE:
+    /* i2s */
+    case RKX120_CPS_CLK_I2S_SRC_RKLINK_RX:
+        maxDiv = CLK_DIV_GET_MAXDIV(clkDiv);
+
+        if (DIV_NO_REM(hw->pllRate[RKX120_TXPLL], rate, maxDiv)) {
+            mux = 0;
+            pRate = hw->pllRate[RKX120_TXPLL];
+        } else if (DIV_NO_REM(hw->pllRate[RKX120_CPLL], rate, maxDiv)) {
+            mux = 1;
+            pRate = hw->pllRate[RKX120_CPLL];
+        } else if (DIV_NO_REM(OSC_24M, rate, maxDiv)) {
+            mux = 2;
+            pRate = OSC_24M;
+        } else {
+            if (clockName == RKX120_CPS_CLK_I2S_SRC_RKLINK_RX) {
+                pll = RKX120_CPS_PLL_CPLL;
+                if (DIV_NO_REM(I2S_122888_BEST_PRATE, rate, maxDiv)) {
+                    pRate = I2S_122888_BEST_PRATE;
+                } else if (DIV_NO_REM(I2S_112896_BEST_PRATE, rate, maxDiv)) {
+                    pRate = I2S_112896_BEST_PRATE;
+                }
+            } else {
+                pll = RKX120_CPS_PLL_TXPLL;
+            }
+
+            /* PLL change closest new rate <= 1200M if need */
+            if (!pRate) {
+                pRate = (_MHZ(1200) / rate) * rate;
+            }
+
+            ret = RKX12x_HAL_CRU_ClkSetFreq(hw, pll, pRate);
+            if (ret != HAL_OK) {
+                return ret;
+            }
+
+            /* if success, continue to set divider */
+        }
+        break;
+
+    /* csi */
+    case RKX120_CPS_CLK_TXESC_CSITX0:
+    case RKX120_CPS_CLK_TXESC_CSITX1:
+    /* pwm */
+    case RKX120_CPS_CLK_PWM_TX:
+    case RKX120_CPS_PCLKOUT_DVPTX:
+        mux = HAL_CRU_RoundFreqGetMux3(hw, rate,
+                                       hw->pllRate[RKX120_TXPLL],
+                                       hw->pllRate[RKX120_CPLL], OSC_24M, &pRate);
+        break;
+    /* gate clock from TX_E0_CLK_RKLINK_RX_PRE */
+    case RKX120_CPS_ICLK_C_CSI0:
+
+        return RKX12x_HAL_CRU_ClkSetFreq(hw, RKX120_CPS_E0_CLK_RKLINK_RX_PRE, rate);
+
+    /* gate clock from TX_E1_CLK_RKLINK_RX_PRE */
+    case RKX120_CPS_ICLK_C_CSI1:
+
+        return RKX12x_HAL_CRU_ClkSetFreq(hw, RKX120_CPS_E1_CLK_RKLINK_RX_PRE, rate);
+
+    /* pre-bus */
+    case RKX120_CPS_BUSCLK_TX_PRE0:
+        mux = HAL_CRU_RoundFreqGetMux2(hw, rate,
+                                       hw->pllRate[RKX120_TXPLL],
+                                       hw->pllRate[RKX120_CPLL], &pRate);
+        break;
+
+    case RKX120_CPS_BUSCLK_TX_PRE:
+        if (rate == OSC_24M) {
+            return HAL_CRU_ClkSetMux(hw, clkMux, 0);
+        } else {
+            RKX12x_HAL_CRU_ClkSetFreq(hw, RKX120_CPS_BUSCLK_TX_PRE0, rate);
+
+            return HAL_CRU_ClkSetMux(hw, clkMux, 1);
+        }
+        break;
+
+    /* gpio: 24M */
+    case RKX120_CPS_DCLK_TX_GPIO0:
+    case RKX120_CPS_DCLK_TX_GPIO1:
+    /* efuse: 24M */
+    case RKX120_CPS_CLK_TX_EFUSE:
+    /* pcs_ada: 24M */
+    case RKX120_CPS_CLK_PCS0_ADA:
+    case RKX120_CPS_CLK_PCS1_ADA:
+    /* capture_pwm: 24M */
+    case RKX120_CPS_CLK_CAPTURE_PWM_TX:
+
+        return rate == OSC_24M ? 0 : HAL_INVAL;
+
+    case RKX120_CPS_CLK_PMA2PCS2LINK_CM:
+        if (rate != _MHZ(200)) {
+            return HAL_INVAL;
+        }
+
+        HAL_CRU_ClkSetMux(hw, clkMux, 0);
+
+        return HAL_OK;
+
+    default:
+        CRU_ERR("%s: %s: Unknown clk 0x%08x\n", __func__, hw->name, clockName);
+
+        return HAL_INVAL;
+    }
+
+    if (!clkMux && !clkDiv) {
+        return HAL_INVAL;
+    }
+
+    if (pRate) {
+        div = HAL_DIV_ROUND_UP(pRate, rate);
+    }
+
+    if (clkDiv) {
+        overMax = div > CLK_DIV_GET_MAXDIV(clkDiv);
+        if (overMax) {
+            CRU_MSG("%s: %s: Clk '0x%08x' req div(%d) over max(%d)!\n",
+                    __func__, hw->name, clockName, div, CLK_DIV_GET_MAXDIV(clkDiv));
+            div = CLK_DIV_GET_MAXDIV(clkDiv);
+        }
+        HAL_CRU_ClkSetDiv(hw, clkDiv, div);
+    }
+
+    if (clkMux) {
+        HAL_CRU_ClkSetMux(hw, clkMux, mux);
+    }
+
+    return HAL_OK;
+}
+
+#if RKX120_SSCG_CPLL_EN || RKX120_SSCG_TXPLL_EN
+static void RKX12x_HAL_CRU_Init_SSCG(struct hwclk *hw)
+{
+    uint8_t down_spread = 1; /* 0: center spread */
+    uint8_t amplitude = 8;   /* range: 0x00 - 0x1f */
+
+#if RKX120_SSCG_CPLL_EN
+    /* down-spread, 0.8%, 37.5khz */
+    HAL_CRU_Write(hw, hw->cru_base + 0x0c, 0x1f000000 | ((amplitude & 0x1f) << 8));
+    HAL_CRU_Write(hw, hw->cru_base + 0x0c, 0x00f00050);
+    HAL_CRU_Write(hw, hw->cru_base + 0x0c, 0x00080000 | ((down_spread & 0x1) << 3));
+    HAL_CRU_Write(hw, hw->cru_base + 0x04, 0x10000000);
+    HAL_CRU_Write(hw, hw->cru_base + 0x0c, 0x00070000);
+    CRU_MSG("%s: CPLL enable SSCG\n", hw->name);
+#endif
+#if RKX120_SSCG_TXPLL_EN
+    /* down-spread, 0.8%, 37.5khz */
+    HAL_CRU_Write(hw, hw->cru_base + 0x2c, 0x1f000000 | ((amplitude & 0x1f) << 8));
+    HAL_CRU_Write(hw, hw->cru_base + 0x2c, 0x00f00050);
+    HAL_CRU_Write(hw, hw->cru_base + 0x2c, 0x00080000 | ((down_spread & 0x1) << 3));
+    HAL_CRU_Write(hw, hw->cru_base + 0x24, 0x10000000);
+    HAL_CRU_Write(hw, hw->cru_base + 0x2c, 0x00070000);
+    CRU_MSG("%s: TXPLL enable SSCG\n", hw->name);
+#endif
+}
+#endif
+
+static HAL_Status RKX12x_HAL_CRU_InitTestout(struct hwclk *hw, uint32_t clockName,
+                                             uint32_t muxValue, uint32_t divValue)
+{
+    uint32_t clkMux = CLK_GET_MUX(RKX120_CPS_TEST_CLKOUT);
+    uint32_t clkDiv = CLK_GET_DIV(RKX120_CPS_TEST_CLKOUT);
+
+    /* gpio0_b7: iomux to clk_testout */
+    HAL_CRU_Write(hw, RKX120_GRF_GPIO0B_IOMUX_H, GPIO0B7_TEST_CLKOUT);
+
+    /* Enable clock */
+    HAL_CRU_ClkEnable(hw, RKX120_CLK_TESTOUT_TOP_GATE);
+
+    /* Mux, div */
+    HAL_CRU_ClkSetDiv(hw, clkDiv, divValue);
+    HAL_CRU_ClkSetMux(hw, clkMux, muxValue);
+
+    CRU_MSG("%s: Testout div=%d, mux=%d\n", hw->name, divValue, muxValue);
+
+    return HAL_OK;
+}
+
+static HAL_Status RKX12x_HAL_CRU_Init(struct hwclk *hw, struct xferclk *xfer)
+{
+    hw->cru_base = 0x01000000;
+    hw->sel_con0 = hw->cru_base + 0x100;
+    hw->gate_con0 = hw->cru_base + 0x300;
+    hw->softrst_con0 = hw->cru_base + 0x400;
+    hw->gbl_srst_fst = 0x0614;
+    hw->flags = 0;
+    hw->num_gate = 16 * 12;
+    hw->gate = HAL_KCALLOC(hw->num_gate, sizeof(struct clkGate));
+    if (!hw->gate) {
+        return HAL_NOMEM;
+    }
+    strcat(hw->name, "<CRU.120@");
+    strcat(hw->name, xfer->name);
+    strcat(hw->name, ">");
+
+    /* Don't change order */
+#if RKX120_SSCG_CPLL_EN || RKX120_SSCG_TXPLL_EN
+    RKX12x_HAL_CRU_Init_SSCG(hw);
+#endif
+    RKX12x_HAL_CRU_ClkSetFreq(hw, RKX120_CPS_PLL_CPLL, _MHZ(1200));
+    RKX12x_HAL_CRU_ClkSetFreq(hw, RKX120_CPS_PLL_TXPLL, _MHZ(1188));
+    RKX12x_HAL_CRU_ClkSetFreq(hw, RKX120_CPS_BUSCLK_TX_PRE, _MHZ(24));
+    RKX12x_HAL_CRU_ClkSetFreq(hw, RKX120_CPS_CLK_PMA2PCS2LINK_CM, _MHZ(200));
+    RKX12x_HAL_CRU_ClkSetFreq(hw, RKX120_CPS_CLK_I2S_SRC_RKLINK_RX, _MHZ(300));
+    RKX12x_HAL_CRU_ClkSetFreq(hw, RKX120_CPS_CLK_PWM_TX, _MHZ(24));
+    RKX12x_HAL_CRU_ClkSetFreq(hw, RKX120_CPS_CLK_TXESC_CSITX0, _MHZ(20));
+    RKX12x_HAL_CRU_ClkSetFreq(hw, RKX120_CPS_CLK_TXESC_CSITX1, _MHZ(20));
+
+    /* Must be the same rate as RKX110_CPS_DCLK_RX_PRE when camera mode */
+    RKX12x_HAL_CRU_ClkSetFreq(hw, RKX120_CPS_E0_CLK_RKLINK_RX_PRE, _MHZ(200));
+    RKX12x_HAL_CRU_ClkSetFreq(hw, RKX120_CPS_E1_CLK_RKLINK_RX_PRE, _MHZ(200));
+
+    HAL_CRU_ClkEnable(hw, RKX120_CLK_TESTOUT_TOP_GATE);
+
+#if RKX120_TESTOUT_MUX >= 0
+    /* clk_testout support max 150M output, so set div=10 by default if not 24M */
+    RKX12x_HAL_CRU_InitTestout(hw, RKX120_CPS_TEST_CLKOUT, RKX120_TESTOUT_MUX,
+                               RKX120_TESTOUT_MUX == 0 ? 1 : 10);
+#endif
+
+    return HAL_OK;
+}
+
+PNAME(mux_24m_p) = { "xin24m" };
+PNAME(mux_txpll_cpll_p) = { "txpll", "cpll" };
+PNAME(mux_txpll_cpll_24m_p) = { "txpll", "cpll", "xin24m" };
+PNAME(mux_24m_txpre0_p) = { "xin24m", "busclk_tx_pre0" };
+
+#define CAL_FREQ_REG 0x01000f00
+
+static uint32_t RKX12x_HAL_CRU_ClkGetExtFreq(struct hwclk *hw, uint32_t clk)
+{
+    uint32_t clkMux = CLK_GET_MUX(RKX120_CPS_TEST_CLKOUT);
+    uint32_t clkDiv = CLK_GET_DIV(RKX120_CPS_TEST_CLKOUT);
+    uint32_t freq, mhz;
+    uint8_t div = 10;
+
+    HAL_CRU_ClkSetDiv(hw, clkDiv, div);
+    HAL_CRU_ClkSetMux(hw, clkMux, 0);
+    HAL_SleepMs(2);
+    HAL_CRU_ClkSetMux(hw, clkMux, clk);
+    HAL_SleepMs(2);
+    freq = HAL_CRU_Read(hw, CAL_FREQ_REG);
+
+    /* Fix accuracy */
+    if ((freq % 10) == 0x9) {
+        freq++;
+    }
+
+    freq *= (1000 * div);
+
+    /* If no external input, freq is close to 24M */
+    mhz = freq / 1000000;
+    if ((clk != 0) && (mhz == 23 || mhz == 24)) {
+        freq = 0;
+    }
+
+    return freq;
+}
+
+static struct clkTable rkx12x_clkTable[] = {
+    /* internal */
+    CLK_DECLARE_INT("txpll", RKX120_CPS_PLL_TXPLL, mux_24m_p),
+    CLK_DECLARE_INT("cpll", RKX120_CPS_PLL_CPLL, mux_24m_p),
+    CLK_DECLARE_INT("e0_clk_rklink_rx_pre", RKX120_CPS_E0_CLK_RKLINK_RX_PRE, mux_txpll_cpll_24m_p),
+    CLK_DECLARE_INT("e1_clk_rklink_rx_pre", RKX120_CPS_E1_CLK_RKLINK_RX_PRE, mux_txpll_cpll_24m_p),
+    CLK_DECLARE_INT("iclk_c_csi0", RKX120_CPS_ICLK_C_CSI0, mux_txpll_cpll_24m_p),
+    CLK_DECLARE_INT("iclk_c_csi1", RKX120_CPS_ICLK_C_CSI1, mux_txpll_cpll_24m_p),
+    CLK_DECLARE_INT("clk_txesc_csitx0", RKX120_CPS_CLK_TXESC_CSITX0, mux_txpll_cpll_24m_p),
+    CLK_DECLARE_INT("clk_txesc_csitx1", RKX120_CPS_CLK_TXESC_CSITX1, mux_txpll_cpll_24m_p),
+    CLK_DECLARE_INT("clk_i2s_src_rklink_rx", RKX120_CPS_CLK_I2S_SRC_RKLINK_RX, mux_txpll_cpll_24m_p),
+    CLK_DECLARE_INT("clk_pwm_tx", RKX120_CPS_CLK_PWM_TX, mux_txpll_cpll_24m_p),
+    CLK_DECLARE_INT("pclkout_dvptx", RKX120_CPS_PCLKOUT_DVPTX, mux_txpll_cpll_24m_p),
+    CLK_DECLARE_INT("busclk_tx_pre0", RKX120_CPS_BUSCLK_TX_PRE0, mux_txpll_cpll_p),
+    CLK_DECLARE_INT("busclk_tx_pre", RKX120_CPS_BUSCLK_TX_PRE, mux_24m_txpre0_p),
+    CLK_DECLARE_INT("dclk_tx_gpio0", RKX120_CPS_DCLK_TX_GPIO0, mux_24m_p),
+    CLK_DECLARE_INT("dclk_tx_gpio1", RKX120_CPS_DCLK_TX_GPIO1, mux_24m_p),
+    CLK_DECLARE_INT("clk_tx_efuse", RKX120_CPS_CLK_TX_EFUSE, mux_24m_p),
+    CLK_DECLARE_INT("clk_pcs0_ada", RKX120_CPS_CLK_PCS0_ADA, mux_24m_p),
+    CLK_DECLARE_INT("clk_pcs1_ada", RKX120_CPS_CLK_PCS1_ADA, mux_24m_p),
+    CLK_DECLARE_INT("clk_capture_pwm_tx", RKX120_CPS_CLK_CAPTURE_PWM_TX, mux_24m_p),
+
+    /* external */
+    CLK_DECLARE_EXT("xin24m", 0, RKX12x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT("clk_txbytehs_csitx0", 3, RKX12x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT("clk_txbytehs_csitx1", 5, RKX12x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT("clk_txbytehs_dsitx", 7, RKX12x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT("clk_rxesc_dsitx", 8, RKX12x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT("clk_link_pcs0", 9, RKX12x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT("clk_link_pcs1", 10, RKX12x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT("clk_pmarx0_pixel", 11, RKX12x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT("clk_pmarx1_pixel", 12, RKX12x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT("clk_mipitxphy0_lvds", 13, RKX12x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT("clk_mipitxphy0_pixel", 14, RKX12x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT("clk_mipitxphy1_lvds", 15, RKX12x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT("clk_mipitxphy1_pixel", 16, RKX12x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT("clk_txbytehs_dsitx_csitx0", 23, RKX12x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT("dclk_c_dvp_src", 24, RKX12x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT("dclk_d_dsi_src", 25, RKX12x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT("clk_lvds0_src", 26, RKX12x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT("clk_lvds1_src", 27, RKX12x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT("dclk_rgb_src", 28, RKX12x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT("clk_2x_pma2pcs0", 29, RKX12x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT("clk_2x_pma2pcs1", 30, RKX12x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT("clk_txesc_mipitxphy0", 31, RKX12x_HAL_CRU_ClkGetExtFreq),
+
+    CLK_DECLARE_EXT_PARENT("dclk_d_ds", 25, "dclk_d_dsi_src", RKX12x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT_PARENT("dclk_d_dsi_pattern_gen", 25, "dclk_d_dsi_src", RKX12x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT_PARENT("clk_lvds0", 26, "clk_lvds0_src", RKX12x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT_PARENT("clk_lvds0_pattern_gen", 26, "clk_lvds0_src", RKX12x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT_PARENT("clk_lvds1", 27, "clk_lvds1_src", RKX12x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT_PARENT("clk_lvds1_pattern_gen", 27, "clk_lvds1_src", RKX12x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT_PARENT("clk_pma2pcs0", 9, "clk_link_pcs0", RKX12x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT_PARENT("clk_pma2link2pcs_cm", 9, "clk_link_pcs0", RKX12x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT_PARENT("clk_pma2pcs1", 10, "clk_link_pcs1", RKX12x_HAL_CRU_ClkGetExtFreq),
+
+    { /* sentinel */ },
+};
+
+struct clkOps rkx120_clk_ops =
+{
+    .clkTable = rkx12x_clkTable,
+    .clkInit = RKX12x_HAL_CRU_Init,
+    .clkGetFreq = RKX12x_HAL_CRU_ClkGetFreq,
+    .clkSetFreq = RKX12x_HAL_CRU_ClkSetFreq,
+    .clkInitTestout = RKX12x_HAL_CRU_InitTestout,
+};
diff --git a/kernel/drivers/mfd/rkx110_x120/hal/cru_rkx120.h b/kernel/drivers/mfd/rkx110_x120/hal/cru_rkx120.h
new file mode 100644
index 0000000..e27246b
--- /dev/null
+++ b/kernel/drivers/mfd/rkx110_x120/hal/cru_rkx120.h
@@ -0,0 +1,305 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2022 Rockchip Electronics Co. Ltd.
+ *
+ * Author: Joseph Chen <chenjh@rock-chips.com>
+ */
+
+#ifndef _CRU_RKX120_H
+
+#include "cru_core.h"
+
+// ======================== TXCRU module definition START ======================
+// TXCRU_SOFTRST_CON01(Offset:0x404)
+#define RKX120_SRST_PRESETN_TX_CRU           0x00000010
+#define RKX120_SRST_PRESETN_TX_GRF           0x00000011
+#define RKX120_SRST_PRESETN_TX_GPIO0         0x00000012
+#define RKX120_SRST_DRESETN_TX_GPIO0         0x00000013
+#define RKX120_SRST_PRESETN_TX_GPIO1         0x00000014
+#define RKX120_SRST_DRESETN_TX_GPIO1         0x00000015
+#define RKX120_SRST_PRESETN_TX_EFUSE         0x00000016
+#define RKX120_SRST_RESETN_TX_EFUSE          0x00000017
+#define RKX120_SRST_PRESETN_MIPI_GRF_TX0     0x0000001A
+#define RKX120_SRST_PRESETN_MIPI_GRF_TX1     0x0000001B
+#define RKX120_SRST_PRESETN_TX_I2C2APB       0x0000001E
+#define RKX120_SRST_PRESETN_TX_I2C2APB_DEBUG 0x0000001F
+
+// TXCRU_SOFTRST_CON02(Offset:0x408)
+#define RKX120_SRST_HRESETN_DVP_TX 0x00000020
+
+// TXCRU_SOFTRST_CON03(Offset:0x40C)
+#define RKX120_SRST_PRESETN_CSITX0         0x00000030
+#define RKX120_SRST_RESETN_TXBYTEHS_CSITX0 0x00000031
+#define RKX120_SRST_RESETN_TXESC_CSITX0    0x00000032
+#define RKX120_SRST_PRESETN_CSITX1         0x00000034
+#define RKX120_SRST_RESETN_TXBYTEHS_CSITX1 0x00000035
+#define RKX120_SRST_RESETN_TXESC_CSITX1    0x00000036
+#define RKX120_SRST_PRESETN_DSITX          0x00000038
+
+// TXCRU_SOFTRST_CON04(Offset:0x410)
+#define RKX120_SRST_PRESETN_RKLINK_RX        0x00000040
+#define RKX120_SRST_RESETN_I2S_SRC_RKLINK_RX 0x00000041
+#define RKX120_SRST_RESETN_E0_RKLINK_RX      0x00000045
+#define RKX120_SRST_IRESETN_C_CSI0           0x00000046
+#define RKX120_SRST_RESETN_E1_RKLINK_RX      0x00000049
+#define RKX120_SRST_IRESETN_C_CSI1           0x0000004A
+
+// TXCRU_SOFTRST_CON05(Offset:0x414)
+#define RKX120_SRST_DRESETN_C_DVP 0x00000051
+#define RKX120_SRST_RESETN_LVDS0  0x0000005B
+#define RKX120_SRST_RESETN_LVDS1  0x0000005D
+
+// TXCRU_SOFTRST_CON06(Offset:0x418)
+#define RKX120_SRST_RESETN_PMA2PCS2LINK_LINK  0x00000061
+#define RKX120_SRST_RESETN_PMA2PCS2LINK_PCS0  0x00000062
+#define RKX120_SRST_RESETN_PMA2PCS2LINK_PCS1  0x00000063
+#define RKX120_SRST_PRESETN_D_DSI_PATTERN_GEN 0x00000064
+#define RKX120_SRST_PRESETN_LVDS0_PATTERN_GEN 0x00000065
+#define RKX120_SRST_PRESETN_LVDS1_PATTERN_GEN 0x00000066
+#define RKX120_SRST_DRESETN_D_DSI_PATTERN_GEN 0x00000067
+#define RKX120_SRST_RESETN_LVDS0_PATTERN_GEN  0x00000068
+#define RKX120_SRST_RESETN_LVDS1_PATTERN_GEN  0x00000069
+
+// TXCRU_SOFTRST_CON07(Offset:0x41C)
+#define RKX120_SRST_PRESETN_PCS0       0x00000070
+#define RKX120_SRST_RESETN_2X_PMA2PCS0 0x00000071
+#define RKX120_SRST_RESETN_LINK_PCS0   0x00000073
+#define RKX120_SRST_PRESETN_PCS0_ADA   0x00000074
+#define RKX120_SRST_RESETN_PCS0_ADA    0x00000075
+
+// TXCRU_SOFTRST_CON08(Offset:0x420)
+#define RKX120_SRST_PRESETN_PCS1       0x00000080
+#define RKX120_SRST_RESETN_2X_PMA2PCS1 0x00000081
+#define RKX120_SRST_RESETN_LINK_PCS1   0x00000083
+#define RKX120_SRST_PRESETN_PCS1_ADA   0x00000084
+#define RKX120_SRST_RESETN_PCS1_ADA    0x00000085
+
+// TXCRU_SOFTRST_CON09(Offset:0x424)
+#define RKX120_SRST_PRESETN_DVPTX      0x00000090
+#define RKX120_SRST_PRESETN_MIPITXPHY0 0x00000098
+#define RKX120_SRST_RESETN_MIPITXPHY0  0x00000099
+#define RKX120_SRST_PRESETN_MIPITXPHY1 0x0000009A
+#define RKX120_SRST_RESETN_MIPITXPHY1  0x0000009B
+
+// TXCRU_SOFTRST_CON10(Offset:0x428)
+#define RKX120_SRST_PRESETN_PWM_TX 0x000000A0
+#define RKX120_SRST_RESETN_PWM_TX  0x000000A1
+
+// TXCRU_SOFTRST_CON11(Offset:0x42C)
+#define RKX120_SRST_PRESETN_DFT2APB 0x000000B0
+
+// TXCRU_GATE_CON00(Offset:0x300)
+#define RKX120_CLK_TESTOUT_TOP_GATE 0x00000000
+#define RKX120_BUSCLK_TX_PRE0_GATE  0x00000001
+#define RKX120_BUSCLK_TX_PRE_GATE   0x00000002
+
+// TXCRU_GATE_CON01(Offset:0x304)
+#define RKX120_PCLK_TX_CRU_GATE           0x00000010
+#define RKX120_PCLK_TX_GRF_GATE           0x00000011
+#define RKX120_PCLK_TX_GPIO0_GATE         0x00000012
+#define RKX120_DCLK_TX_GPIO0_GATE         0x00000013
+#define RKX120_PCLK_TX_GPIO1_GATE         0x00000014
+#define RKX120_DCLK_TX_GPIO1_GATE         0x00000015
+#define RKX120_PCLK_TX_EFUSE_GATE         0x00000016
+#define RKX120_CLK_TX_EFUSE_GATE          0x00000017
+#define RKX120_PCLK_MIPI_GRF_TX0_GATE     0x0000001A
+#define RKX120_PCLK_MIPI_GRF_TX1_GATE     0x0000001B
+#define RKX120_PCLK_TX_I2C2APB_GATE       0x0000001E
+#define RKX120_PCLK_TX_I2C2APB_DEBUG_GATE 0x0000001F
+
+// TXCRU_GATE_CON02(Offset:0x308)
+#define RKX120_HCLK_DVP_TX_GATE 0x00000020
+
+// TXCRU_GATE_CON03(Offset:0x30C)
+#define RKX120_PCLK_CSITX0_GATE                   0x00000030
+#define RKX120_CLK_TXBYTEHS_DSITX_CSITX0_DFT_GATE 0x00000031
+#define RKX120_CLK_TXESC_CSITX0_GATE              0x00000032
+#define RKX120_PCLK_CSITX1_GATE                   0x00000034
+#define RKX120_CLK_TXBYTEHS_CSITX1_DFT_GATE       0x00000035
+#define RKX120_CLK_TXESC_CSITX1_GATE              0x00000036
+#define RKX120_PCLK_DSITX_GATE                    0x00000038
+#define RKX120_CLK_RXESC_DSITX_DFT_GATE           0x0000003A
+
+// TXCRU_GATE_CON04(Offset:0x310)
+#define RKX120_PCLK_RKLINK_RX_GATE        0x00000040
+#define RKX120_CLK_I2S_SRC_RKLINK_RX_GATE 0x00000041
+#define RKX120_E0_CLK_RKLINK_RX_PRE_GATE  0x00000044
+#define RKX120_E0_CLK_RKLINK_RX_GATE      0x00000045
+#define RKX120_ICLK_C_CSI0_GATE           0x00000046
+#define RKX120_E1_CLK_RKLINK_RX_PRE_GATE  0x00000048
+#define RKX120_E1_CLK_RKLINK_RX_GATE      0x00000049
+#define RKX120_ICLK_C_CSI1_GATE           0x0000004A
+
+// TXCRU_GATE_CON05(Offset:0x314)
+#define RKX120_DCLK_RGB_GATE      0x00000050
+#define RKX120_DCLK_C_DVP_GATE    0x00000051
+#define RKX120_DCLK_D_DSI_CM_GATE 0x00000058
+#define RKX120_DCLK_D_DSI_GATE    0x00000059
+#define RKX120_CLK_LVDS0_CM_GATE  0x0000005A
+#define RKX120_CLK_LVDS0_GATE     0x0000005B
+#define RKX120_CLK_LVDS1_CM_GATE  0x0000005C
+#define RKX120_CLK_LVDS1_GATE     0x0000005D
+
+// TXCRU_GATE_CON06(Offset:0x318)
+#define RKX120_CLK_PMA2PCS2LINK_CM_GATE    0x00000060
+#define RKX120_CLK_PMA2PCS2LINK_LINK_GATE  0x00000061
+#define RKX120_CLK_PMA2PCS2LINK_PCS0_GATE  0x00000062
+#define RKX120_CLK_PMA2PCS2LINK_PCS1_GATE  0x00000063
+#define RKX120_PCLK_D_DSI_PATTERN_GEN_GATE 0x00000064
+#define RKX120_PCLK_LVDS0_PATTERN_GEN_GATE 0x00000065
+#define RKX120_PCLK_LVDS1_PATTERN_GEN_GATE 0x00000066
+#define RKX120_DCLK_D_DSI_PATTERN_GEN_GATE 0x00000067
+#define RKX120_CLK_LVDS0_PATTERN_GEN_GATE  0x00000068
+#define RKX120_CLK_LVDS1_PATTERN_GEN_GATE  0x00000069
+
+// TXCRU_GATE_CON07(Offset:0x31C)
+#define RKX120_PCLK_PCS0_GATE           0x00000070
+#define RKX120_CLK_2X_PMA2PCS0_DFT_GATE 0x00000071
+#define RKX120_CLK_LINK_PCS0_DFT_GATE   0x00000072
+#define RKX120_CLK_LINK_PCS0_GATE       0x00000073
+#define RKX120_PCLK_PCS0_ADA_GATE       0x00000074
+#define RKX120_CLK_PCS0_ADA_GATE        0x00000075
+
+// TXCRU_GATE_CON08(Offset:0x320)
+#define RKX120_PCLK_PCS1_GATE           0x00000080
+#define RKX120_CLK_2X_PMA2PCS1_DFT_GATE 0x00000081
+#define RKX120_CLK_LINK_PCS1_DFT_GATE   0x00000082
+#define RKX120_CLK_LINK_PCS1_GATE       0x00000083
+#define RKX120_PCLK_PCS1_ADA_GATE       0x00000084
+#define RKX120_CLK_PCS1_ADA_GATE        0x00000085
+
+// TXCRU_GATE_CON09(Offset:0x324)
+#define RKX120_PCLKOUT_DVPTX_GATE   0x00000090
+#define RKX120_PCLK_MIPITXPHY0_GATE 0x00000098
+#define RKX120_PCLK_MIPITXPHY1_GATE 0x0000009A
+
+// TXCRU_GATE_CON10(Offset:0x328)
+#define RKX120_PCLK_PWM_TX_GATE          0x000000A0
+#define RKX120_CLK_PWM_TX_GATE           0x000000A1
+#define RKX120_CLK_CAPTURE_PWM_TX_GATE   0x000000A2
+#define RKX120_CLK_TXESC_MIPITXPHY0_GATE 0x000000A8
+
+// TXCRU_GATE_CON11(Offset:0x32C)
+#define RKX120_PCLK_DFT2APB_GATE 0x000000B0
+
+// TXCRU_CLKSEL_CON00(Offset:0x100)
+#define RKX120_TEST_CLKOUT_IOUT_DIV                           0x08000000
+#define RKX120_TEST_CLKOUT_IOUT_SEL                           0x05080000
+#define RKX120_TEST_CLKOUT_IOUT_SEL_XIN_OSC0_FUNC             0U
+#define RKX120_TEST_CLKOUT_IOUT_SEL_CLK_TXPLL_MUX             1U
+#define RKX120_TEST_CLKOUT_IOUT_SEL_CLK_CPLL_MUX              2U
+#define RKX120_TEST_CLKOUT_IOUT_SEL_CLK_TXBYTEHS_CSITX0       3U
+#define RKX120_TEST_CLKOUT_IOUT_SEL_CLK_TXESC_CSITX0          4U
+#define RKX120_TEST_CLKOUT_IOUT_SEL_CLK_TXBYTEHS_CSITX1       5U
+#define RKX120_TEST_CLKOUT_IOUT_SEL_CLK_TXESC_CSITX1          6U
+#define RKX120_TEST_CLKOUT_IOUT_SEL_CLK_TXBYTEHS_DSITX        7U
+#define RKX120_TEST_CLKOUT_IOUT_SEL_CLK_RXESC_DSITX           8U
+#define RKX120_TEST_CLKOUT_IOUT_SEL_CLK_LINK_PCS0             9U
+#define RKX120_TEST_CLKOUT_IOUT_SEL_CLK_LINK_PCS1             10U
+#define RKX120_TEST_CLKOUT_IOUT_SEL_TESTCLK_PMARX0_PIXEL      11U
+#define RKX120_TEST_CLKOUT_IOUT_SEL_TESTCLK_PMARX1_PIXEL      12U
+#define RKX120_TEST_CLKOUT_IOUT_SEL_TESTCLK_MIPITXPHY0_LVDS   13U
+#define RKX120_TEST_CLKOUT_IOUT_SEL_TESTCLK_MIPITXPHY0_PIXEL  14U
+#define RKX120_TEST_CLKOUT_IOUT_SEL_TESTCLK_MIPITXPHY1_LVDS   15U
+#define RKX120_TEST_CLKOUT_IOUT_SEL_TESTCLK_MIPITXPHY1_PIXEL  16U
+#define RKX120_TEST_CLKOUT_IOUT_SEL_BUSCLK_TX_PRE             17U
+#define RKX120_TEST_CLKOUT_IOUT_SEL_CLK_I2S_SRC_RKLINK_RX     20U
+#define RKX120_TEST_CLKOUT_IOUT_SEL_E0_CLK_RKLINK_RX_PRE      21U
+#define RKX120_TEST_CLKOUT_IOUT_SEL_PCLKOUT_DVPTX             22U
+#define RKX120_TEST_CLKOUT_IOUT_SEL_CLK_TXBYTEHS_DSITX_CSITX0 23U
+#define RKX120_TEST_CLKOUT_IOUT_SEL_DCLK_C_DVP_SRC            24U
+#define RKX120_TEST_CLKOUT_IOUT_SEL_DCLK_D_DSI_SRC            25U
+#define RKX120_TEST_CLKOUT_IOUT_SEL_CLK_LVDS0_SRC             26U
+#define RKX120_TEST_CLKOUT_IOUT_SEL_CLK_LVDS1_SRC             27U
+#define RKX120_TEST_CLKOUT_IOUT_SEL_DCLK_RGB_SRC              28U
+#define RKX120_TEST_CLKOUT_IOUT_SEL_CLK_2X_PMA2PCS0           29U
+#define RKX120_TEST_CLKOUT_IOUT_SEL_CLK_2X_PMA2PCS1           30U
+#define RKX120_TEST_CLKOUT_IOUT_SEL_CLK_TXESC_MIPITXPHY0      31U
+
+// TXCRU_CLKSEL_CON01(Offset:0x104)
+#define RKX120_BUSCLK_TX_PRE0_DIV               0x06000001
+#define RKX120_BUSCLK_TX_PRE0_SEL               0x01070001
+#define RKX120_BUSCLK_TX_PRE0_SEL_CLK_TXPLL_MUX 0U
+#define RKX120_BUSCLK_TX_PRE0_SEL_CLK_CPLL_MUX  1U
+#define RKX120_BUSCLK_TX_PRE_SEL                0x01080001
+#define RKX120_BUSCLK_TX_PRE_SEL_XIN_OSC0_FUNC  0U
+#define RKX120_BUSCLK_TX_PRE_SEL_BUSCLK_TX_PRE0 1U
+
+// TXCRU_CLKSEL_CON03(Offset:0x10C)
+#define RKX120_CLK_TXESC_CSITX0_DIV               0x08000003
+#define RKX120_CLK_TXESC_CSITX0_SEL               0x020E0003
+#define RKX120_CLK_TXESC_CSITX0_SEL_CLK_TXPLL_MUX 0U
+#define RKX120_CLK_TXESC_CSITX0_SEL_CLK_CPLL_MUX  1U
+#define RKX120_CLK_TXESC_CSITX0_SEL_XIN_OSC0_FUNC 2U
+
+// TXCRU_CLKSEL_CON04(Offset:0x110)
+#define RKX120_CLK_TXESC_CSITX1_DIV               0x08000004
+#define RKX120_CLK_TXESC_CSITX1_SEL               0x020E0004
+#define RKX120_CLK_TXESC_CSITX1_SEL_CLK_TXPLL_MUX 0U
+#define RKX120_CLK_TXESC_CSITX1_SEL_CLK_CPLL_MUX  1U
+#define RKX120_CLK_TXESC_CSITX1_SEL_XIN_OSC0_FUNC 2U
+
+// TXCRU_CLKSEL_CON05(Offset:0x114)
+#define RKX120_CLK_I2S_SRC_RKLINK_RX_DIV               0x08000005
+#define RKX120_CLK_I2S_SRC_RKLINK_RX_SEL               0x020E0005
+#define RKX120_CLK_I2S_SRC_RKLINK_RX_SEL_CLK_TXPLL_MUX 0U
+#define RKX120_CLK_I2S_SRC_RKLINK_RX_SEL_CLK_CPLL_MUX  1U
+#define RKX120_CLK_I2S_SRC_RKLINK_RX_SEL_XIN_OSC0_FUNC 2U
+
+// TXCRU_CLKSEL_CON06(Offset:0x118)
+#define RKX120_E0_CLK_RKLINK_RX_PRE_DIV               0x08000006
+#define RKX120_E0_CLK_RKLINK_RX_PRE_SEL               0x020E0006
+#define RKX120_E0_CLK_RKLINK_RX_PRE_SEL_CLK_TXPLL_MUX 0U
+#define RKX120_E0_CLK_RKLINK_RX_PRE_SEL_CLK_CPLL_MUX  1U
+#define RKX120_E0_CLK_RKLINK_RX_PRE_SEL_XIN_OSC0_FUNC 2U
+
+// TXCRU_CLKSEL_CON07(Offset:0x11C)
+#define RKX120_E1_CLK_RKLINK_RX_PRE_DIV               0x08000007
+#define RKX120_E1_CLK_RKLINK_RX_PRE_SEL               0x020E0007
+#define RKX120_E1_CLK_RKLINK_RX_PRE_SEL_CLK_TXPLL_MUX 0U
+#define RKX120_E1_CLK_RKLINK_RX_PRE_SEL_CLK_CPLL_MUX  1U
+#define RKX120_E1_CLK_RKLINK_RX_PRE_SEL_XIN_OSC0_FUNC 2U
+
+// TXCRU_CLKSEL_CON08(Offset:0x120)
+#define RKX120_CLK_PMA2PCS2LINK_CM_SEL                   0x01000008
+#define RKX120_CLK_PMA2PCS2LINK_CM_SEL_CLK_LINK_PCS0_DFT 0U
+#define RKX120_CLK_PMA2PCS2LINK_CM_SEL_CLK_LINK_PCS1_DFT 1U
+
+// TXCRU_CLKSEL_CON10(Offset:0x128)
+#define RKX120_CLK_PWM_TX_DIV               0x0800000A
+#define RKX120_CLK_PWM_TX_SEL               0x020E000A
+#define RKX120_CLK_PWM_TX_SEL_CLK_TXPLL_MUX 0U
+#define RKX120_CLK_PWM_TX_SEL_CLK_CPLL_MUX  1U
+#define RKX120_CLK_PWM_TX_SEL_XIN_OSC0_FUNC 2U
+
+// TXCRU_CLKSEL_CON12(Offset:0x130)
+#define RKX120_PCLKOUT_DVPTX_DIV               0x0800000C
+#define RKX120_PCLKOUT_DVPTX_SEL               0x020E000C
+#define RKX120_PCLKOUT_DVPTX_SEL_CLK_TXPLL_MUX 0U
+#define RKX120_PCLKOUT_DVPTX_SEL_CLK_CPLL_MUX  1U
+#define RKX120_PCLKOUT_DVPTX_SEL_XIN_OSC0_FUNC 2U
+
+// ======================== TXCRU module definition END ========================
+#define RKX120_CPS_INVAL                 0
+#define RKX120_CPS_PLL_CPLL              1
+#define RKX120_CPS_PLL_TXPLL             2
+#define RKX120_CPS_DCLK_TX_GPIO0         3
+#define RKX120_CPS_DCLK_TX_GPIO1         4
+#define RKX120_CPS_CLK_TX_EFUSE          5
+#define RKX120_CPS_CLK_PCS0_ADA          6
+#define RKX120_CPS_CLK_PCS1_ADA          7
+#define RKX120_CPS_CLK_CAPTURE_PWM_TX    8
+#define RKX120_CPS_ICLK_C_CSI0           9
+#define RKX120_CPS_ICLK_C_CSI1           10
+#define RKX120_CPS_CLK_TXESC_CSITX0      COMPOSITE_CLK(RKX120_CLK_TXESC_CSITX0_SEL, RKX120_CLK_TXESC_CSITX0_DIV)
+#define RKX120_CPS_CLK_TXESC_CSITX1      COMPOSITE_CLK(RKX120_CLK_TXESC_CSITX1_SEL, RKX120_CLK_TXESC_CSITX1_DIV)
+#define RKX120_CPS_CLK_I2S_SRC_RKLINK_RX COMPOSITE_CLK(RKX120_CLK_I2S_SRC_RKLINK_RX_SEL, RKX120_CLK_I2S_SRC_RKLINK_RX_DIV)
+#define RKX120_CPS_E0_CLK_RKLINK_RX_PRE  COMPOSITE_CLK(RKX120_E0_CLK_RKLINK_RX_PRE_SEL, RKX120_E0_CLK_RKLINK_RX_PRE_DIV)
+#define RKX120_CPS_E1_CLK_RKLINK_RX_PRE  COMPOSITE_CLK(RKX120_E1_CLK_RKLINK_RX_PRE_SEL, RKX120_E1_CLK_RKLINK_RX_PRE_DIV)
+#define RKX120_CPS_CLK_PMA2PCS2LINK_CM   COMPOSITE_CLK(RKX120_CLK_PMA2PCS2LINK_CM_SEL, 0)
+#define RKX120_CPS_PCLKOUT_DVPTX         COMPOSITE_CLK(RKX120_PCLKOUT_DVPTX_SEL, RKX120_PCLKOUT_DVPTX_DIV)
+#define RKX120_CPS_CLK_PWM_TX            COMPOSITE_CLK(RKX120_CLK_PWM_TX_SEL, RKX120_CLK_PWM_TX_DIV)
+#define RKX120_CPS_BUSCLK_TX_PRE0        COMPOSITE_CLK(RKX120_BUSCLK_TX_PRE0_SEL, RKX120_BUSCLK_TX_PRE0_DIV)
+#define RKX120_CPS_BUSCLK_TX_PRE         COMPOSITE_CLK(RKX120_BUSCLK_TX_PRE_SEL, 0)
+#define RKX120_CPS_TEST_CLKOUT           COMPOSITE_CLK(RKX120_TEST_CLKOUT_IOUT_SEL, RKX120_TEST_CLKOUT_IOUT_DIV)
+#endif
diff --git a/kernel/drivers/mfd/rkx110_x120/hal/cru_rkx121.c b/kernel/drivers/mfd/rkx110_x120/hal/cru_rkx121.c
new file mode 100644
index 0000000..350ce6c
--- /dev/null
+++ b/kernel/drivers/mfd/rkx110_x120/hal/cru_rkx121.c
@@ -0,0 +1,626 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Rockchip Electronics Co. Ltd.
+ *
+ * Author: Joseph Chen <chenjh@rock-chips.com>
+ */
+
+#include "cru_core.h"
+#include "cru_rkx121.h"
+
+/*
+ *			[RKX121 CHIP]: TX
+ *
+ * ================= SECITON: Input clock from devices =========================
+ *
+ * ### 300M ###
+ * dclk_c_dvp_src
+ * dclk_d_dsi_src --|-- dclk_d_ds
+ *                  |-- dclk_d_dsi_pattern_gen
+ *
+ * clk_lvds0_src --|-- clk_lvds0
+ *                 |-- clk_lvds0_pattern_gen
+ *
+ * clk_lvds1_src --|-- clk_lvds1
+ *                 |-- clk_lvds1_pattern_gen
+ *
+ * dclk_rgc_src
+ *
+ *
+ * ### 200M ###
+ * clk_link_pcs0 --|-- clk_pma2pcs0                |-- clk_pma2link2pcs_link
+ *                 |                               |
+ *                 |-- clk_pma2link2pcs_cm (MUX) --|-- clk_pma2link2pcs_psc0
+ * clk_link_pcs1 --|                               |
+ *                 |-- clk_pma2pcs1                |-- clk_pma2link2pcs_psc1
+ *
+ * clk_txbytehs_dsitx_csitx0
+ * clk_txbytehs_dsitx_csitx1
+ *
+ *
+ *
+ * ### 150M ###
+ *
+ * rxpclk_vicap_lvds
+ *
+ *
+ * ### 100M ###
+ *
+ * clk_2x_pma2pcs0
+ * clk_2x_pma2pcs1
+ *
+ *
+ * ### 50M ###
+ * clk_txesc_mipitxphy0
+ * clk_rxesc_dsitx
+ *
+ */
+
+#define RKX120_SSCG_TXPLL_EN 0
+#define RKX120_SSCG_CPLL_EN  0
+#define RKX120_TESTOUT_MUX   -1 /* valid options: RKX120_TEST_CLKOUT_IOUT_SEL_? */
+
+#define RKX120_GRF_GPIO0B_IOMUX_H 0x0101000c
+#define GPIO0B7_TEST_CLKOUT       0x01c00080
+
+#define I2S_122888_BEST_PRATE 393216000
+#define I2S_112896_BEST_PRATE 756403200
+
+static struct PLL_CONFIG PLL_TABLE[] = {
+    /* _mhz, _refDiv, _fbDiv, _postdDv1, _postDiv2, _dsmpd, _frac */
+    RK_PLL_RATE(1200000000, 1, 50, 1, 1, 1, 0),
+    RK_PLL_RATE(600000000, 1, 25, 1, 1, 1, 0),
+
+    /* display */
+    RK_PLL_RATE(1188000000, 2, 99, 1, 1, 1, 0),
+
+    /* audio: 12.288M */
+    RK_PLL_RATE(688128000, 1, 86, 3, 1, 0, 268435),   /* div=56, vco=2064.384M */
+    RK_PLL_RATE(393216000, 2, 131, 4, 1, 0, 1207959), /* div=32, vco=1572.864M */
+    RK_PLL_RATE(344064000, 1, 43, 3, 1, 0, 134217),   /* div=28, vco=1032.192M */
+    /* audio: 11.2896M */
+    RK_PLL_RATE(474163200, 1, 79, 4, 1, 0, 456340),   /* div=42, vco=1896.6528M */
+    RK_PLL_RATE(756403200, 1, 63, 2, 1, 0, 563714),   /* div=67, vco=1512.8064M */
+    RK_PLL_RATE(564480000, 1, 47, 2, 1, 0, 671088),   /* div=50, vco=1128.96M */
+
+    { /* sentinel */ },
+};
+
+static struct PLL_SETUP TXPLL_SETUP = {
+    .id = PLL_TXPLL,
+    .conOffset0 = 0x01000020,
+    .conOffset1 = 0x01000024,
+    .conOffset2 = 0x01000028,
+    .conOffset3 = 0x0100002c,
+    .modeOffset = 0x01000600,
+    .modeShift = 0,         /* 0: slow-mode, 1: normal-mode */
+    .lockShift = 10,
+    .modeMask = 0x1,
+    .rateTable = PLL_TABLE,
+
+    .minRefdiv = 1,
+    .maxRefdiv = 2,
+    .minVco = _MHZ(375),
+    .maxVco = _MHZ(2400),
+    .minFout = _MHZ(24),
+    .maxFout = _MHZ(1200),
+    .sscgEn = RKX120_SSCG_TXPLL_EN,
+};
+
+static struct PLL_SETUP CPLL_SETUP = {
+    .id = PLL_CPLL,
+    .conOffset0 = 0x01000000,
+    .conOffset1 = 0x01000004,
+    .conOffset2 = 0x01000008,
+    .conOffset3 = 0x0100000c,
+    .modeOffset = 0x01000600,
+    .modeShift = 2,
+    .lockShift = 10,
+    .modeMask = 0x1 << 2,
+    .rateTable = PLL_TABLE,
+
+    .minRefdiv = 1,
+    .maxRefdiv = 2,
+    .minVco = _MHZ(375),
+    .maxVco = _MHZ(2400),
+    .minFout = _MHZ(24),
+    .maxFout = _MHZ(1200),
+    .sscgEn = RKX120_SSCG_CPLL_EN,
+};
+
+static uint32_t RKX12x_HAL_CRU_ClkGetFreq(struct hwclk *hw, uint32_t clockName)
+{
+    uint32_t clkMux = CLK_GET_MUX(clockName);
+    uint32_t clkDiv = CLK_GET_DIV(clockName);
+    uint32_t pRate = 0, freq = 0;
+    uint32_t pRate0 = 0, pRate1 = 0;
+
+    switch (clockName) {
+    case RKX120_CPS_PLL_TXPLL:
+        freq = HAL_CRU_GetPllFreq(hw, &TXPLL_SETUP);
+        hw->pllRate[RKX120_TXPLL] = freq;
+
+        return freq;
+
+    case RKX120_CPS_PLL_CPLL:
+        freq = HAL_CRU_GetPllFreq(hw, &CPLL_SETUP);
+        hw->pllRate[RKX120_CPLL] = freq;
+
+        return freq;
+
+    /* lvds: 200M */
+    case RKX121_CPS_CLK_LVDS_C_LVDS_TX:
+    /* link: 300M */
+    case RKX120_CPS_E0_CLK_RKLINK_RX_PRE:
+    case RKX120_CPS_E1_CLK_RKLINK_RX_PRE:
+    /* csi: 50M */
+    case RKX120_CPS_CLK_TXESC_CSITX0:
+    case RKX120_CPS_CLK_TXESC_CSITX1:
+    /* i2s: 600M */
+    case RKX120_CPS_CLK_I2S_SRC_RKLINK_RX:
+    /* pwm: 100M */
+    case RKX120_CPS_CLK_PWM_TX:
+    case RKX120_CPS_PCLKOUT_DVPTX:
+        freq = HAL_CRU_MuxGetFreq3(hw, clkMux,
+                                   hw->pllRate[RKX120_TXPLL],
+                                   hw->pllRate[RKX120_CPLL], OSC_24M);
+        break;
+
+    /* gate clock from TX_E0_CLK_RKLINK_RX_PRE */
+    case RKX120_CPS_ICLK_C_CSI0:
+
+        return RKX12x_HAL_CRU_ClkGetFreq(hw, RKX120_CPS_E0_CLK_RKLINK_RX_PRE);
+
+    /* gate clock from TX_E1_CLK_RKLINK_RX_PRE */
+    case RKX120_CPS_ICLK_C_CSI1:
+
+        return RKX12x_HAL_CRU_ClkGetFreq(hw, RKX120_CPS_E1_CLK_RKLINK_RX_PRE);
+
+    case RKX121_CPS_CLK_LVDS0:
+    case RKX121_CPS_CLK_LVDS1:
+        pRate0 = _MHZ(150); /* input clock: clk_lvds1_cm */
+        pRate1 = RKX12x_HAL_CRU_ClkGetFreq(hw, RKX121_CPS_CLK_LVDS_C_LVDS_TX);
+        freq = HAL_CRU_MuxGetFreq2(hw, clkMux, pRate0, pRate1);
+        break;
+
+    /* pre-bus: 100M */
+    case RKX120_CPS_BUSCLK_TX_PRE0:
+        freq = HAL_CRU_MuxGetFreq2(hw, clkMux,
+                                   hw->pllRate[RKX120_TXPLL],
+                                   hw->pllRate[RKX120_CPLL]);
+        break;
+    /*
+     * bus: 100MHZ
+     *
+     * === TX_BUSCLK_TX_PRE gate children ===
+     *
+     * pclk_tx_cru
+     * pclk_tx_grf
+     * pclk_tx_gpio0/1
+     * pclk_tx_efuse
+     * pclk_mipi_grf_tx0/1
+     * pclk_tx_i2c2apb
+     * pclk_tx_i2c2apb_debug
+     * hclk_dvp_tx
+     * pclk_csitx0/1
+     * pclk_dsitx
+     * pclk_rklink_rx
+     * pclk_d_dsi_pattern_gen
+     * pclk_lvds{0, 1}_pattern_gen
+     * pclk_pcs0/1
+     * pclk_pcs{0,1}_ada
+     * pclk_mipitxphy0/1
+     * pclk_pwm_tx
+     * pclk_dft2apb
+     * pclk_lbist_ada_tx
+     */
+    case RKX120_CPS_BUSCLK_TX_PRE:
+        pRate = RKX12x_HAL_CRU_ClkGetFreq(hw, RKX120_CPS_BUSCLK_TX_PRE0);
+        freq = HAL_CRU_MuxGetFreq2(hw, clkMux, OSC_24M, pRate);
+        break;
+
+    /* gpio: 24M */
+    case RKX120_CPS_DCLK_TX_GPIO0:
+    case RKX120_CPS_DCLK_TX_GPIO1:
+    /* efuse: 24M */
+    case RKX120_CPS_CLK_TX_EFUSE:
+    /* pcs_ada: 24M */
+    case RKX120_CPS_CLK_PCS0_ADA:
+    case RKX120_CPS_CLK_PCS1_ADA:
+    /* capture_pwm: 24M */
+    case RKX120_CPS_CLK_CAPTURE_PWM_TX:
+
+        return OSC_24M;
+
+    case RKX120_CPS_CLK_PMA2PCS2LINK_CM:
+
+        return _MHZ(200);
+
+    default:
+        CRU_ERR("%s: %s: Unknown clk 0x%08x\n", __func__, hw->name, clockName);
+
+        return HAL_INVAL;
+    }
+
+    if (!clkMux && !clkDiv) {
+        return 0;
+    }
+
+    if (clkDiv) {
+        freq /= (HAL_CRU_ClkGetDiv(hw, clkDiv));
+    }
+
+    return freq;
+}
+
+static HAL_Status RKX12x_HAL_CRU_ClkSetFreq(struct hwclk *hw, uint32_t clockName, uint32_t rate)
+{
+    uint32_t clkMux = CLK_GET_MUX(clockName);
+    uint32_t clkDiv = CLK_GET_DIV(clockName);
+    uint32_t mux = 0, div = 1;
+    uint32_t pRate = 0;
+    uint32_t maxDiv;
+    uint32_t pll;
+    uint8_t overMax;
+    HAL_Status ret = HAL_OK;
+
+    switch (clockName) {
+    case RKX120_CPS_PLL_TXPLL:
+        ret = HAL_CRU_SetPllFreq(hw, &TXPLL_SETUP, rate);
+        hw->pllRate[RKX120_TXPLL] = rate;
+        CRU_MSG("%s: TXPLL set rate: %d\n", hw->name, rate);
+
+        return ret;
+
+    case RKX120_CPS_PLL_CPLL:
+        ret = HAL_CRU_SetPllFreq(hw, &CPLL_SETUP, rate);
+        hw->pllRate[RKX120_CPLL] = rate;
+        CRU_MSG("%s: CPLL set rate: %d\n", hw->name, rate);
+
+        return ret;
+
+    /* link(dclk): Allowed to change PLL rate if need ! */
+    case RKX120_CPS_E0_CLK_RKLINK_RX_PRE:
+    case RKX120_CPS_E1_CLK_RKLINK_RX_PRE:
+    /* i2s */
+    case RKX120_CPS_CLK_I2S_SRC_RKLINK_RX:
+        maxDiv = CLK_DIV_GET_MAXDIV(clkDiv);
+
+        if (DIV_NO_REM(hw->pllRate[RKX120_TXPLL], rate, maxDiv)) {
+            mux = 0;
+            pRate = hw->pllRate[RKX120_TXPLL];
+        } else if (DIV_NO_REM(hw->pllRate[RKX120_CPLL], rate, maxDiv)) {
+            mux = 1;
+            pRate = hw->pllRate[RKX120_CPLL];
+        } else if (DIV_NO_REM(OSC_24M, rate, maxDiv)) {
+            mux = 2;
+            pRate = OSC_24M;
+        } else {
+            if (clockName == RKX120_CPS_CLK_I2S_SRC_RKLINK_RX) {
+                pll = RKX120_CPS_PLL_CPLL;
+                if (DIV_NO_REM(I2S_122888_BEST_PRATE, rate, maxDiv)) {
+                    pRate = I2S_122888_BEST_PRATE;
+                } else if (DIV_NO_REM(I2S_112896_BEST_PRATE, rate, maxDiv)) {
+                    pRate = I2S_112896_BEST_PRATE;
+                }
+            } else {
+                pll = RKX120_CPS_PLL_TXPLL;
+            }
+
+            /* PLL change closest new rate <= 1200M if need */
+            if (!pRate) {
+                pRate = (_MHZ(1200) / rate) * rate;
+            }
+
+            ret = RKX12x_HAL_CRU_ClkSetFreq(hw, pll, pRate);
+            if (ret != HAL_OK) {
+                return ret;
+            }
+
+            /* if success, continue to set divider */
+        }
+        break;
+
+    /* lvds */
+    case RKX121_CPS_CLK_LVDS_C_LVDS_TX:
+    /* csi */
+    case RKX120_CPS_CLK_TXESC_CSITX0:
+    case RKX120_CPS_CLK_TXESC_CSITX1:
+    /* pwm */
+    case RKX120_CPS_CLK_PWM_TX:
+    case RKX120_CPS_PCLKOUT_DVPTX:
+        mux = HAL_CRU_RoundFreqGetMux3(hw, rate,
+                                       hw->pllRate[RKX120_TXPLL],
+                                       hw->pllRate[RKX120_CPLL], OSC_24M, &pRate);
+        break;
+    /* gate clock from TX_E0_CLK_RKLINK_RX_PRE */
+    case RKX120_CPS_ICLK_C_CSI0:
+
+        return RKX12x_HAL_CRU_ClkSetFreq(hw, RKX120_CPS_E0_CLK_RKLINK_RX_PRE, rate);
+
+    /* gate clock from TX_E1_CLK_RKLINK_RX_PRE */
+    case RKX120_CPS_ICLK_C_CSI1:
+
+        return RKX12x_HAL_CRU_ClkSetFreq(hw, RKX120_CPS_E1_CLK_RKLINK_RX_PRE, rate);
+
+    /* lvds */
+    case RKX121_CPS_CLK_LVDS0:
+    case RKX121_CPS_CLK_LVDS1:
+        if (rate == _MHZ(150)) {
+            return HAL_CRU_ClkSetMux(hw, clkMux, 0); /* input clock: clk_lvds1_cm */
+        } else {
+            RKX12x_HAL_CRU_ClkSetFreq(hw, RKX121_CPS_CLK_LVDS_C_LVDS_TX, rate);
+
+            return HAL_CRU_ClkSetMux(hw, clkMux, 1);
+        }
+        break;
+
+    /* pre-bus */
+    case RKX120_CPS_BUSCLK_TX_PRE0:
+        mux = HAL_CRU_RoundFreqGetMux2(hw, rate,
+                                       hw->pllRate[RKX120_TXPLL],
+                                       hw->pllRate[RKX120_CPLL], &pRate);
+        break;
+
+    case RKX120_CPS_BUSCLK_TX_PRE:
+        if (rate == OSC_24M) {
+            return HAL_CRU_ClkSetMux(hw, clkMux, 0);
+        } else {
+            RKX12x_HAL_CRU_ClkSetFreq(hw, RKX120_CPS_BUSCLK_TX_PRE0, rate);
+
+            return HAL_CRU_ClkSetMux(hw, clkMux, 1);
+        }
+        break;
+
+    /* gpio: 24M */
+    case RKX120_CPS_DCLK_TX_GPIO0:
+    case RKX120_CPS_DCLK_TX_GPIO1:
+    /* efuse: 24M */
+    case RKX120_CPS_CLK_TX_EFUSE:
+    /* pcs_ada: 24M */
+    case RKX120_CPS_CLK_PCS0_ADA:
+    case RKX120_CPS_CLK_PCS1_ADA:
+    /* capture_pwm: 24M */
+    case RKX120_CPS_CLK_CAPTURE_PWM_TX:
+
+        return rate == OSC_24M ? 0 : HAL_INVAL;
+
+    case RKX120_CPS_CLK_PMA2PCS2LINK_CM:
+        if (rate != _MHZ(200)) {
+            return HAL_INVAL;
+        }
+
+        HAL_CRU_ClkSetMux(hw, clkMux, 0);
+
+        return HAL_OK;
+
+    default:
+        CRU_ERR("%s: %s: Unknown clk 0x%08x\n", __func__, hw->name, clockName);
+
+        return HAL_INVAL;
+    }
+
+    if (!clkMux && !clkDiv) {
+        return HAL_INVAL;
+    }
+
+    if (pRate) {
+        div = HAL_DIV_ROUND_UP(pRate, rate);
+    }
+
+    if (clkDiv) {
+        overMax = div > CLK_DIV_GET_MAXDIV(clkDiv);
+        if (overMax) {
+            CRU_MSG("%s: %s: Clk '0x%08x' req div(%d) over max(%d)!\n",
+                    __func__, hw->name, clockName, div, CLK_DIV_GET_MAXDIV(clkDiv));
+            div = CLK_DIV_GET_MAXDIV(clkDiv);
+        }
+        HAL_CRU_ClkSetDiv(hw, clkDiv, div);
+    }
+
+    if (clkMux) {
+        HAL_CRU_ClkSetMux(hw, clkMux, mux);
+    }
+
+    return HAL_OK;
+}
+
+#if RKX120_SSCG_CPLL_EN || RKX120_SSCG_TXPLL_EN
+static void RKX12x_HAL_CRU_Init_SSCG(struct hwclk *hw)
+{
+    uint8_t down_spread = 1; /* 0: center spread */
+    uint8_t amplitude = 8;   /* range: 0x00 - 0x1f */
+
+#if RKX120_SSCG_CPLL_EN
+    /* down-spread, 0.8%, 37.5khz */
+    HAL_CRU_Write(hw, hw->cru_base + 0x0c, 0x1f000000 | ((amplitude & 0x1f) << 8));
+    HAL_CRU_Write(hw, hw->cru_base + 0x0c, 0x00f00050);
+    HAL_CRU_Write(hw, hw->cru_base + 0x0c, 0x00080000 | ((down_spread & 0x1) << 3));
+    HAL_CRU_Write(hw, hw->cru_base + 0x04, 0x10000000);
+    HAL_CRU_Write(hw, hw->cru_base + 0x0c, 0x00070000);
+    CRU_MSG("%s: CPLL enable SSCG\n", hw->name);
+#endif
+#if RKX120_SSCG_TXPLL_EN
+    /* down-spread, 0.8%, 37.5khz */
+    HAL_CRU_Write(hw, hw->cru_base + 0x2c, 0x1f000000 | ((amplitude & 0x1f) << 8));
+    HAL_CRU_Write(hw, hw->cru_base + 0x2c, 0x00f00050);
+    HAL_CRU_Write(hw, hw->cru_base + 0x2c, 0x00080000 | ((down_spread & 0x1) << 3));
+    HAL_CRU_Write(hw, hw->cru_base + 0x24, 0x10000000);
+    HAL_CRU_Write(hw, hw->cru_base + 0x2c, 0x00070000);
+    CRU_MSG("%s: TXPLL enable SSCG\n", hw->name);
+#endif
+}
+#endif
+
+static HAL_Status RKX12x_HAL_CRU_InitTestout(struct hwclk *hw, uint32_t clockName,
+                                             uint32_t muxValue, uint32_t divValue)
+{
+    uint32_t clkMux = CLK_GET_MUX(RKX120_CPS_TEST_CLKOUT);
+    uint32_t clkDiv = CLK_GET_DIV(RKX120_CPS_TEST_CLKOUT);
+
+    /* gpio0_b7: iomux to clk_testout */
+    HAL_CRU_Write(hw, RKX120_GRF_GPIO0B_IOMUX_H, GPIO0B7_TEST_CLKOUT);
+
+    /* Enable clock */
+    HAL_CRU_ClkEnable(hw, RKX120_CLK_TESTOUT_TOP_GATE);
+
+    /* Mux, div */
+    HAL_CRU_ClkSetDiv(hw, clkDiv, divValue);
+    HAL_CRU_ClkSetMux(hw, clkMux, muxValue);
+
+    CRU_MSG("%s: Testout div=%d, mux=%d\n", hw->name, divValue, muxValue);
+
+    return HAL_OK;
+}
+
+static HAL_Status RKX12x_HAL_CRU_Init(struct hwclk *hw, struct xferclk *xfer)
+{
+    hw->cru_base = 0x01000000;
+    hw->sel_con0 = hw->cru_base + 0x100;
+    hw->gate_con0 = hw->cru_base + 0x300;
+    hw->softrst_con0 = hw->cru_base + 0x400;
+    hw->gbl_srst_fst = 0x0614;
+    hw->flags = 0;
+    hw->num_gate = 16 * 12;
+    hw->gate = HAL_KCALLOC(hw->num_gate, sizeof(struct clkGate));
+    if (!hw->gate) {
+        return HAL_NOMEM;
+    }
+    strcat(hw->name, "<CRU.121@");
+    strcat(hw->name, xfer->name);
+    strcat(hw->name, ">");
+
+    /* Don't change order */
+#if RKX120_SSCG_CPLL_EN || RKX120_SSCG_TXPLL_EN
+    RKX12x_HAL_CRU_Init_SSCG(hw);
+#endif
+    RKX12x_HAL_CRU_ClkSetFreq(hw, RKX120_CPS_PLL_CPLL, _MHZ(1200));
+    RKX12x_HAL_CRU_ClkSetFreq(hw, RKX120_CPS_PLL_TXPLL, _MHZ(1188));
+    RKX12x_HAL_CRU_ClkSetFreq(hw, RKX120_CPS_BUSCLK_TX_PRE, _MHZ(100));
+    RKX12x_HAL_CRU_ClkSetFreq(hw, RKX121_CPS_CLK_LVDS_C_LVDS_TX, _MHZ(200));
+    RKX12x_HAL_CRU_ClkSetFreq(hw, RKX120_CPS_CLK_PMA2PCS2LINK_CM, _MHZ(200));
+    RKX12x_HAL_CRU_ClkSetFreq(hw, RKX120_CPS_CLK_I2S_SRC_RKLINK_RX, _MHZ(400));
+    RKX12x_HAL_CRU_ClkSetFreq(hw, RKX120_CPS_CLK_PWM_TX, _MHZ(24));
+    RKX12x_HAL_CRU_ClkSetFreq(hw, RKX120_CPS_CLK_TXESC_CSITX0, _MHZ(20));
+    RKX12x_HAL_CRU_ClkSetFreq(hw, RKX120_CPS_CLK_TXESC_CSITX1, _MHZ(20));
+
+    /* Must be the same rate as RKX110_CPS_DCLK_RX_PRE when camera mode */
+    RKX12x_HAL_CRU_ClkSetFreq(hw, RKX120_CPS_E0_CLK_RKLINK_RX_PRE, _MHZ(200));
+    RKX12x_HAL_CRU_ClkSetFreq(hw, RKX120_CPS_E1_CLK_RKLINK_RX_PRE, _MHZ(200));
+
+    HAL_CRU_ClkEnable(hw, RKX120_CLK_TESTOUT_TOP_GATE);
+
+#if RKX120_TESTOUT_MUX >= 0
+    /* clk_testout support max 150M output, so set div=10 by default if not 24M */
+    RKX12x_HAL_CRU_InitTestout(hw, RKX120_CPS_TEST_CLKOUT, RKX120_TESTOUT_MUX,
+                               RKX120_TESTOUT_MUX == 0 ? 1 : 10);
+#endif
+
+    return HAL_OK;
+}
+
+PNAME(mux_24m_p) = { "xin24m" };
+PNAME(mux_txpll_cpll_p) = { "txpll", "cpll" };
+PNAME(mux_txpll_cpll_24m_p) = { "txpll", "cpll", "xin24m" };
+PNAME(mux_24m_txpre0_p) = { "xin24m", "busclk_tx_pre0" };
+
+#define CAL_FREQ_REG    0x01000f00
+#define CAL_FREQ_EN_REG 0x01000f04
+
+static uint32_t RKX12x_HAL_CRU_ClkGetExtFreq(struct hwclk *hw, uint32_t clk)
+{
+    uint32_t clkMux = CLK_GET_MUX(RKX120_CPS_TEST_CLKOUT);
+    uint32_t clkDiv = CLK_GET_DIV(RKX120_CPS_TEST_CLKOUT);
+    uint32_t freq, mhz;
+    uint8_t div = 10;
+
+    HAL_CRU_ClkSetDiv(hw, clkDiv, div);
+    HAL_CRU_ClkSetMux(hw, clkMux, clk);
+    HAL_CRU_Write(hw, CAL_FREQ_EN_REG, 1); /* NOTE: 1: enable, 0: disable */
+    HAL_SleepMs(2);
+    freq = HAL_CRU_Read(hw, CAL_FREQ_REG);
+    HAL_CRU_Write(hw, CAL_FREQ_EN_REG, 0x0);
+
+    /* Fix accuracy */
+    if ((freq % 10) == 0x9) {
+        freq++;
+    }
+
+    freq *= (1000 * div);
+
+    /* If no external input, freq is close to 24M */
+    mhz = freq / 1000000;
+    if ((clk != 0) && (mhz == 23 || mhz == 24)) {
+        freq = 0;
+    }
+
+    return freq;
+}
+
+static struct clkTable rkx12x_clkTable[] = {
+    /* internal */
+    CLK_DECLARE_INT("txpll", RKX120_CPS_PLL_TXPLL, mux_24m_p),
+    CLK_DECLARE_INT("cpll", RKX120_CPS_PLL_CPLL, mux_24m_p),
+    CLK_DECLARE_INT("e0_clk_rklink_rx_pre", RKX120_CPS_E0_CLK_RKLINK_RX_PRE, mux_txpll_cpll_24m_p),
+    CLK_DECLARE_INT("e1_clk_rklink_rx_pre", RKX120_CPS_E1_CLK_RKLINK_RX_PRE, mux_txpll_cpll_24m_p),
+    CLK_DECLARE_INT("iclk_c_csi0", RKX120_CPS_ICLK_C_CSI0, mux_txpll_cpll_24m_p),
+    CLK_DECLARE_INT("iclk_c_csi1", RKX120_CPS_ICLK_C_CSI1, mux_txpll_cpll_24m_p),
+    CLK_DECLARE_INT("clk_txesc_csitx0", RKX120_CPS_CLK_TXESC_CSITX0, mux_txpll_cpll_24m_p),
+    CLK_DECLARE_INT("clk_txesc_csitx1", RKX120_CPS_CLK_TXESC_CSITX1, mux_txpll_cpll_24m_p),
+    CLK_DECLARE_INT("clk_i2s_src_rklink_rx", RKX120_CPS_CLK_I2S_SRC_RKLINK_RX, mux_txpll_cpll_24m_p),
+    CLK_DECLARE_INT("clk_pwm_tx", RKX120_CPS_CLK_PWM_TX, mux_txpll_cpll_24m_p),
+    CLK_DECLARE_INT("pclkout_dvptx", RKX120_CPS_PCLKOUT_DVPTX, mux_txpll_cpll_24m_p),
+    CLK_DECLARE_INT("busclk_tx_pre0", RKX120_CPS_BUSCLK_TX_PRE0, mux_txpll_cpll_p),
+    CLK_DECLARE_INT("busclk_tx_pre", RKX120_CPS_BUSCLK_TX_PRE, mux_24m_txpre0_p),
+    CLK_DECLARE_INT("dclk_tx_gpio0", RKX120_CPS_DCLK_TX_GPIO0, mux_24m_p),
+    CLK_DECLARE_INT("dclk_tx_gpio1", RKX120_CPS_DCLK_TX_GPIO1, mux_24m_p),
+    CLK_DECLARE_INT("clk_tx_efuse", RKX120_CPS_CLK_TX_EFUSE, mux_24m_p),
+    CLK_DECLARE_INT("clk_pcs0_ada", RKX120_CPS_CLK_PCS0_ADA, mux_24m_p),
+    CLK_DECLARE_INT("clk_pcs1_ada", RKX120_CPS_CLK_PCS1_ADA, mux_24m_p),
+    CLK_DECLARE_INT("clk_capture_pwm_tx", RKX120_CPS_CLK_CAPTURE_PWM_TX, mux_24m_p),
+
+    /* external */
+    CLK_DECLARE_EXT("xin24m", 0, RKX12x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT("clk_txbytehs_csitx0", 3, RKX12x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT("clk_txbytehs_csitx1", 5, RKX12x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT("clk_txbytehs_dsitx", 7, RKX12x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT("clk_rxesc_dsitx", 8, RKX12x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT("clk_link_pcs0", 9, RKX12x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT("clk_link_pcs1", 10, RKX12x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT("clk_pmarx0_pixel", 11, RKX12x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT("clk_pmarx1_pixel", 12, RKX12x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT("clk_mipitxphy0_lvds", 13, RKX12x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT("clk_mipitxphy0_pixel", 14, RKX12x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT("clk_mipitxphy1_lvds", 15, RKX12x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT("clk_mipitxphy1_pixel", 16, RKX12x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT("clk_txbytehs_dsitx_csitx0", 23, RKX12x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT("dclk_c_dvp_src", 24, RKX12x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT("dclk_d_dsi_src", 25, RKX12x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT("clk_lvds0_src", 26, RKX12x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT("clk_lvds1_src", 27, RKX12x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT("dclk_rgb_src", 28, RKX12x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT("clk_2x_pma2pcs0", 29, RKX12x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT("clk_2x_pma2pcs1", 30, RKX12x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT("clk_txesc_mipitxphy0", 31, RKX12x_HAL_CRU_ClkGetExtFreq),
+
+    CLK_DECLARE_EXT_PARENT("dclk_d_ds", 25, "dclk_d_dsi_src", RKX12x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT_PARENT("dclk_d_dsi_pattern_gen", 25, "dclk_d_dsi_src", RKX12x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT_PARENT("clk_lvds0", 26, "clk_lvds0_src", RKX12x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT_PARENT("clk_lvds0_pattern_gen", 26, "clk_lvds0_src", RKX12x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT_PARENT("clk_lvds1", 27, "clk_lvds1_src", RKX12x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT_PARENT("clk_lvds1_pattern_gen", 27, "clk_lvds1_src", RKX12x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT_PARENT("clk_pma2pcs0", 9, "clk_link_pcs0", RKX12x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT_PARENT("clk_pma2link2pcs_cm", 9, "clk_link_pcs0", RKX12x_HAL_CRU_ClkGetExtFreq),
+    CLK_DECLARE_EXT_PARENT("clk_pma2pcs1", 10, "clk_link_pcs1", RKX12x_HAL_CRU_ClkGetExtFreq),
+
+    { /* sentinel */ },
+};
+
+struct clkOps rkx121_clk_ops =
+{
+    .clkTable = rkx12x_clkTable,
+    .clkInit = RKX12x_HAL_CRU_Init,
+    .clkGetFreq = RKX12x_HAL_CRU_ClkGetFreq,
+    .clkSetFreq = RKX12x_HAL_CRU_ClkSetFreq,
+    .clkInitTestout = RKX12x_HAL_CRU_InitTestout,
+};
diff --git a/kernel/drivers/mfd/rkx110_x120/hal/cru_rkx121.h b/kernel/drivers/mfd/rkx110_x120/hal/cru_rkx121.h
new file mode 100644
index 0000000..96cd659
--- /dev/null
+++ b/kernel/drivers/mfd/rkx110_x120/hal/cru_rkx121.h
@@ -0,0 +1,48 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2023 Rockchip Electronics Co. Ltd.
+ *
+ * Author: Joseph Chen <chenjh@rock-chips.com>
+ */
+
+#ifndef _CRU_RKX121_H
+
+#include "cru_rkx120.h"
+
+// TXCRU_SOFTRST_CON06(Offset:0x418)
+#define RKX121_SRST_DRESETN_C_LVDS_TX     0x0000006A
+#define RKX121_SRST_LVDS_RESETN_C_LVDS_TX 0x0000006B
+#define RKX121_SRST_PRESETN_C_LVDS_TX     0x0000006C
+
+// TXCRU_SOFTRST_CON11(Offset:0x42C)
+#define RKX121_SRST_PRESETN_LBIST_ADA_TX 0x000000B1
+
+// TXCRU_GATE_CON06(Offset:0x318)
+#define RKX121_DCLK_C_LVDS_TX_GATE     0x0000006A
+#define RKX121_CLK_LVDS_C_LVDS_TX_GATE 0x0000006B
+#define RKX121_PCLK_C_LVDS_TX_GATE     0x0000006C
+
+// TXCRU_GATE_CON11(Offset:0x32C)
+#define RKX121_PCLK_LBIST_ADA_TX_GATE 0x000000B1
+
+// TXCRU_CLKSEL_CON08(Offset:0x120)
+#define RKX121_CLK_LVDS1_SEL                    0x01010008
+#define RKX121_CLK_LVDS1_SEL_CLK_LVDS1_CM       0U
+#define RKX121_CLK_LVDS1_SEL_CLK_LVDS_C_LVDS_TX 1U
+#define RKX121_CLK_LVDS0_SEL                    0x01020008
+#define RKX121_CLK_LVDS0_SEL_CLK_LVDS0_CM       0U
+#define RKX121_CLK_LVDS0_SEL_CLK_LVDS_C_LVDS_TX 1U
+
+// TXCRU_CLKSEL_CON09(Offset:0x124)
+#define RKX121_CLK_LVDS_C_LVDS_TX_DIV               0x08000009
+#define RKX121_CLK_LVDS_C_LVDS_TX_SEL               0x020E0009
+#define RKX121_CLK_LVDS_C_LVDS_TX_SEL_CLK_TXPLL_MUX 0U
+#define RKX121_CLK_LVDS_C_LVDS_TX_SEL_CLK_CPLL_MUX  1U
+#define RKX121_CLK_LVDS_C_LVDS_TX_SEL_XIN_OSC0_FUNC 2U
+
+/* COMPOSITE */
+#define RKX121_CPS_CLK_LVDS_C_LVDS_TX COMPOSITE_CLK(RKX121_CLK_LVDS_C_LVDS_TX_SEL, RKX121_CLK_LVDS_C_LVDS_TX_DIV)
+#define RKX121_CPS_CLK_LVDS0          COMPOSITE_CLK(RKX121_CLK_LVDS0_SEL, 0)
+#define RKX121_CPS_CLK_LVDS1          COMPOSITE_CLK(RKX121_CLK_LVDS1_SEL, 0)
+
+#endif
diff --git a/kernel/drivers/mfd/rkx110_x120/hal/hal_def.h b/kernel/drivers/mfd/rkx110_x120/hal/hal_def.h
new file mode 100644
index 0000000..12cfbed
--- /dev/null
+++ b/kernel/drivers/mfd/rkx110_x120/hal/hal_def.h
@@ -0,0 +1,58 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2022 Rockchip Electronics Co. Ltd.
+ *
+ * Author: Steven Liu <steven.liu@rock-chips.com>
+ */
+
+#ifndef _HAL_DEF_H_
+#define _HAL_DEF_H_
+
+#include "hal_os_def.h"
+
+#define HAL_LOGLEVEL 3
+
+#define __hal_print(level, fmt, ...)                                    \
+({                                                                      \
+    level < HAL_LOGLEVEL ? HAL_SYSLOG("[HAL] " fmt, ##__VA_ARGS__) : 0; \
+})
+#define HAL_ERR(fmt, ...)  __hal_print(0, "ERROR: " fmt, ##__VA_ARGS__)
+#define HAL_WARN(fmt, ...) __hal_print(1, "WARN: " fmt, ##__VA_ARGS__)
+#define HAL_MSG(fmt, ...)  __hal_print(2, fmt, ##__VA_ARGS__)
+#define HAL_DBG(fmt, ...)  __hal_print(3, fmt, ##__VA_ARGS__)
+
+#define HAL_NULL		((void *)0)
+#define HAL_BIT(nr)		(1UL << (nr))
+
+/***************************** Structure Definition **************************/
+/** HAL boolean type definition */
+typedef enum {
+    HAL_FALSE = 0x00U,
+    HAL_TRUE  = 0x01U
+} HAL_Check;
+
+/** HAL error code definition */
+typedef enum {
+    HAL_OK      = 0x00U,
+    HAL_ERROR   = (-1),
+    HAL_NOMEM   = (-12),
+    HAL_BUSY    = (-16),
+    HAL_NODEV   = (-19),
+    HAL_INVAL   = (-22),
+    HAL_NOSYS   = (-38),
+    HAL_TIMEOUT = (-110)
+} HAL_Status;
+
+/** HAL functional status definition */
+typedef enum {
+    HAL_DISABLE = 0x00U,
+    HAL_ENABLE  = 0x01U
+} HAL_FuncStatus;
+
+/** HAL lock structures definition */
+typedef enum {
+    HAL_UNLOCKED = 0x00U,
+    HAL_LOCKED   = 0x01U
+} HAL_LockStatus;
+
+#endif /* _HAL_DEF_H_ */
diff --git a/kernel/drivers/mfd/rkx110_x120/hal/hal_os_def.h b/kernel/drivers/mfd/rkx110_x120/hal/hal_os_def.h
new file mode 100644
index 0000000..c93a28b
--- /dev/null
+++ b/kernel/drivers/mfd/rkx110_x120/hal/hal_os_def.h
@@ -0,0 +1,33 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2022 Rockchip Electronics Co. Ltd.
+ *
+ * Author: Joseph Chen <chenjh@rock-chips.com>
+ */
+
+#ifndef _HAL_OS_DEF_H_
+#define _HAL_OS_DEF_H_
+
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+
+#define HAL_BITS_PER_LONG BITS_PER_LONG
+#define HAL_SYSLOG        pr_info
+#define HAL_DelayUs       udelay
+#define HAL_SleepMs       msleep
+#define HAL_ARRAY_SIZE    ARRAY_SIZE
+
+#define HAL_DEFINE_MUTEX DEFINE_MUTEX
+#define HAL_Mutex        struct mutex
+#define HAL_MutexInit    mutex_init
+#define HAL_MutexLock    mutex_lock
+#define HAL_MutexUnlock  mutex_unlock
+
+#define HAL_KCALLOC(n, size) kcalloc(n, size, GFP_KERNEL)
+
+typedef int (HAL_RegRead_t)(struct i2c_client *client, uint32_t addr, uint32_t *value);
+typedef int (HAL_RegWrite_t)(struct i2c_client *client, uint32_t addr, uint32_t value);
+
+#endif /* _HAL_OS_DEF_H_ */
diff --git a/kernel/drivers/mfd/rkx110_x120/hal/pinctrl_api.h b/kernel/drivers/mfd/rkx110_x120/hal/pinctrl_api.h
new file mode 100644
index 0000000..9a15c8c
--- /dev/null
+++ b/kernel/drivers/mfd/rkx110_x120/hal/pinctrl_api.h
@@ -0,0 +1,45 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2023 Rockchip Electronics Co. Ltd.
+ *
+ * Author: Steven Liu <steven.liu@rock-chips.com>
+ */
+
+#ifndef _PINCTRL_API_H_
+#define _PINCTRL_API_H_
+
+#include "hal_def.h"
+#include "hal_os_def.h"
+#include "pinctrl_rkx110_x120.h"
+
+static inline int hwpin_set(struct xferpin xfer)
+{
+	struct hwpin hw;
+	int ret;
+
+	if (!xfer.read || !xfer.write || !xfer.client || !xfer.name[0]) {
+		return HAL_ERROR;
+	}
+
+	if (xfer.type == PIN_UNDEF || xfer.type >= PIN_MAX) {
+		return HAL_INVAL;
+	}
+
+	memset(&hw, 0, sizeof(hw));
+	hw.type = xfer.type;
+	hw.xfer = xfer;
+	hw.bank = xfer.bank;
+	hw.mpins = xfer.mpins;
+	hw.param = xfer.param;
+
+	ret = HAL_PINCTRL_SetParam(&hw, hw.mpins, hw.param);
+
+	return ret;
+}
+
+static inline int hwpin_init(void)
+{
+	return HAL_PINCTRL_Init();
+}
+
+#endif
diff --git a/kernel/drivers/mfd/rkx110_x120/hal/pinctrl_rkx110_x120.c b/kernel/drivers/mfd/rkx110_x120/hal/pinctrl_rkx110_x120.c
new file mode 100644
index 0000000..1709e41
--- /dev/null
+++ b/kernel/drivers/mfd/rkx110_x120/hal/pinctrl_rkx110_x120.c
@@ -0,0 +1,612 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2023 Rockchip Electronics Co. Ltd.
+ *
+ * Author: Steven Liu <steven.liu@rock-chips.com>
+ */
+
+#include "pinctrl_rkx110_x120.h"
+
+/********************* Private MACRO Definition ******************************/
+
+#define _PINCTRL_GENMASK(w)		((1U << (w)) - 1U)
+#define _PINCTRL_OFFSET(gp, w)		((gp) * (w))
+#define _PINCTRL_GENVAL(gp, v, w)	((_PINCTRL_GENMASK(w) << (_PINCTRL_OFFSET(gp, w) + 16)) \
+					| (((v) & _PINCTRL_GENMASK(w)) << _PINCTRL_OFFSET(gp, w)))
+
+#define RKX110_GRF_BASE			0x00010000U
+#define RKX120_GRF_BASE			0x01010000U
+
+#define RKX110_GRF_REG(x)		((x) + RKX110_GRF_BASE)
+#define RKX120_GRF_REG(x)		((x) + RKX120_GRF_BASE)
+
+/************************** SER_GRF Register Define ***************************/
+#define RKX110_GRF_GPIO0A_IOMUX_L	RKX110_GRF_REG(0x0000)
+#define RKX110_GRF_GPIO0A_IOMUX_H	RKX110_GRF_REG(0x0004)
+#define RKX110_GRF_GPIO0B_IOMUX_L	RKX110_GRF_REG(0x0008)
+#define RKX110_GRF_GPIO0B_IOMUX_H	RKX110_GRF_REG(0x000c)
+#define RKX110_GRF_GPIO0C_IOMUX_L	RKX110_GRF_REG(0x0010)
+#define RKX110_GRF_GPIO0C_IOMUX_H	RKX110_GRF_REG(0x0014)
+
+#define RKX110_GRF_GPIO0A_P		RKX110_GRF_REG(0x0020)
+#define RKX110_GRF_GPIO0B_P		RKX110_GRF_REG(0x0024)
+#define RKX110_GRF_GPIO0C_P		RKX110_GRF_REG(0x0028)
+
+#define RKX110_GRF_GPIO1A_IOMUX		RKX110_GRF_REG(0x0080)
+#define RKX110_GRF_GPIO1B_IOMUX		RKX110_GRF_REG(0x0084)
+#define RKX110_GRF_GPIO1C_IOMUX		RKX110_GRF_REG(0x0088)
+
+#define RKX110_GRF_GPIO1A_P		RKX110_GRF_REG(0x0090)
+#define RKX110_GRF_GPIO1B_P		RKX110_GRF_REG(0x0094)
+#define RKX110_GRF_GPIO1C_P		RKX110_GRF_REG(0x0098)
+
+#define RKX110_GRF_GPIO1A_SMT		RKX110_GRF_REG(0x00A0)
+#define RKX110_GRF_GPIO1B_SMT		RKX110_GRF_REG(0x00A4)
+#define RKX110_GRF_GPIO1C_SMT		RKX110_GRF_REG(0x00A8)
+
+#define RKX110_GRF_GPIO1A_E		RKX110_GRF_REG(0x00B0)
+#define RKX110_GRF_GPIO1B_E		RKX110_GRF_REG(0x00B4)
+#define RKX110_GRF_GPIO1C_E		RKX110_GRF_REG(0x00B8)
+
+#define RKX110_GRF_GPIO1A_IE		RKX110_GRF_REG(0x00C0)
+#define RKX110_GRF_GPIO1B_IE		RKX110_GRF_REG(0x00C4)
+#define RKX110_GRF_GPIO1C_IE		RKX110_GRF_REG(0x00C8)
+
+/************************** DES_GRF Register Define ***************************/
+#define RKX120_GRF_GPIO0A_IOMUX_L	RKX120_GRF_REG(0x0000)
+#define RKX120_GRF_GPIO0A_IOMUX_H	RKX120_GRF_REG(0x0004)
+#define RKX120_GRF_GPIO0B_IOMUX_L	RKX120_GRF_REG(0x0008)
+#define RKX120_GRF_GPIO0B_IOMUX_H	RKX120_GRF_REG(0x000C)
+#define RKX120_GRF_GPIO0C_IOMUX_L	RKX120_GRF_REG(0x0010)
+#define RKX120_GRF_GPIO0C_IOMUX_H	RKX120_GRF_REG(0x0014)
+
+#define RKX120_GRF_GPIO0A_P		RKX120_GRF_REG(0x0020)
+#define RKX120_GRF_GPIO0B_P		RKX120_GRF_REG(0x0024)
+#define RKX120_GRF_GPIO0C_P		RKX120_GRF_REG(0x0028)
+
+#define RKX120_GRF_GPIO1A_IOMUX		RKX120_GRF_REG(0x0080)
+#define RKX120_GRF_GPIO1B_IOMUX		RKX120_GRF_REG(0x0084)
+#define RKX120_GRF_GPIO1C_IOMUX		RKX120_GRF_REG(0x0088)
+
+#define RKX120_GRF_GPIO1A_P		RKX120_GRF_REG(0x0090)
+#define RKX120_GRF_GPIO1B_P		RKX120_GRF_REG(0x0094)
+#define RKX120_GRF_GPIO1C_P		RKX120_GRF_REG(0x0098)
+
+#define RKX120_GRF_GPIO1A_SMT		RKX120_GRF_REG(0x00A0)
+#define RKX120_GRF_GPIO1B_SMT		RKX120_GRF_REG(0x00A4)
+#define RKX120_GRF_GPIO1C_SMT		RKX120_GRF_REG(0x00A8)
+
+#define RKX120_GRF_GPIO1A_E		RKX120_GRF_REG(0x00B0)
+#define RKX120_GRF_GPIO1B_E		RKX120_GRF_REG(0x00B4)
+#define RKX120_GRF_GPIO1C_E		RKX120_GRF_REG(0x00B8)
+
+#define RKX120_GRF_GPIO1A_IE		RKX120_GRF_REG(0x00C0)
+#define RKX120_GRF_GPIO1B_IE		RKX120_GRF_REG(0x00C4)
+#define RKX120_GRF_GPIO1C_IE		RKX120_GRF_REG(0x00C8)
+
+#define IOMUX_3_BIT_PER_PIN			(3)
+#define IOMUX_PER_REG				(8)
+#define RKX110_IOMUX_L(__B, __G)		(RKX110_GRF_GPIO##__B##__G##_IOMUX_L)
+#define RKX110_IOMUX_H(__B, __G)		(RKX110_GRF_GPIO##__B##__G##_IOMUX_H)
+#define RKX110_SET_IOMUX_L(_HW, _B, _G, _GP, _V, _W)				\
+{										\
+	HAL_PINCTRL_Write(_HW, RKX110_IOMUX_L(_B, _G), _PINCTRL_GENVAL(_GP, _V, _W));	\
+}
+#define RKX110_SET_IOMUX_H(_HW, _B, _G, _GP, _V, _W)				\
+{										\
+	HAL_PINCTRL_Write(_HW, RKX110_IOMUX_H(_B, _G), _PINCTRL_GENVAL(_GP, _V, _W));	\
+}
+#define HAL_RKX110_SET_IOMUX_L(HW, B, G, BP, V)					\
+	RKX110_SET_IOMUX_L(HW, B, G, BP % IOMUX_PER_REG, V, IOMUX_3_BIT_PER_PIN)
+#define HAL_RKX110_SET_IOMUX_H(HW, B, G, BP, V)					\
+	RKX110_SET_IOMUX_H(HW, B, G, (BP % IOMUX_PER_REG - 5), V, IOMUX_3_BIT_PER_PIN)
+
+#define RKX120_IOMUX_L(__B, __G)		(RKX120_GRF_GPIO##__B##__G##_IOMUX_L)
+#define RKX120_IOMUX_H(__B, __G)		(RKX120_GRF_GPIO##__B##__G##_IOMUX_H)
+#define RKX120_SET_IOMUX_L(_HW, _B, _G, _GP, _V, _W)				\
+{										\
+	HAL_PINCTRL_Write(_HW, RKX120_IOMUX_L(_B, _G), _PINCTRL_GENVAL(_GP, _V, _W));	\
+}
+#define RKX120_SET_IOMUX_H(_HW, _B, _G, _GP, _V, _W)				\
+{										\
+	HAL_PINCTRL_Write(_HW, RKX120_IOMUX_H(_B, _G), _PINCTRL_GENVAL(_GP, _V, _W));	\
+}
+#define HAL_RKX120_SET_IOMUX_L(HW, B, G, BP, V)					\
+	RKX120_SET_IOMUX_L(HW, B, G, BP % IOMUX_PER_REG, V, IOMUX_3_BIT_PER_PIN)
+#define HAL_RKX120_SET_IOMUX_H(HW, B, G, BP, V)					\
+	RKX120_SET_IOMUX_H(HW, B, G, (BP % IOMUX_PER_REG - 5), V, IOMUX_3_BIT_PER_PIN)
+
+#define IOMUX_2_BIT_PER_PIN			(2)
+#define IOMUX_8_PIN_PER_REG			(8)
+#define RKX110_IOMUX_0(__B, __G)		(RKX110_GRF_GPIO##__B##__G##_IOMUX)
+
+#define RKX110_SET_IOMUX_0(_HW, _B, _G, _GP, _V, _W)				\
+{										\
+	HAL_PINCTRL_Write(_HW, RKX110_IOMUX_0(_B, _G), _PINCTRL_GENVAL(_GP, _V, _W));	\
+}
+
+#define HAL_RKX110_SET_IOMUX_0(HW, B, G, BP, V)					\
+	RKX110_SET_IOMUX_0(HW, B, G, BP % IOMUX_8_PIN_PER_REG, V, IOMUX_2_BIT_PER_PIN)
+
+#define RKX120_IOMUX_0(__B, __G)		(RKX120_GRF_GPIO##__B##__G##_IOMUX)
+
+#define RKX120_SET_IOMUX_0(_HW, _B, _G, _GP, _V, _W)				\
+{										\
+	HAL_PINCTRL_Write(_HW, RKX120_IOMUX_0(_B, _G), _PINCTRL_GENVAL(_GP, _V, _W));	\
+}
+
+#define HAL_RKX120_SET_IOMUX_0(HW, B, G, BP, V)					\
+	RKX120_SET_IOMUX_0(HW, B, G, BP % IOMUX_8_PIN_PER_REG, V, IOMUX_2_BIT_PER_PIN)
+
+#define PULL_2_BIT_PER_PIN			(2)
+#define PULL_8_PIN_PER_REG			(8)
+#define RKX110_PULL_0(__B, __G)			(RKX110_GRF_GPIO##__B##__G##_P)
+
+#define RKX110_SET_PULL_0(_HW, _B, _G, _GP, _V, _W)				\
+{										\
+	HAL_PINCTRL_Write(_HW, RKX110_PULL_0(_B, _G), _PINCTRL_GENVAL(_GP, _V, _W));	\
+}
+
+#define HAL_RKX110_SET_PULL_0(HW, B, G, BP, V)					\
+	RKX110_SET_PULL_0(HW, B, G, BP % PULL_8_PIN_PER_REG, V, PULL_2_BIT_PER_PIN)
+
+#define RKX120_PULL_0(__B, __G)			(RKX120_GRF_GPIO##__B##__G##_P)
+
+#define RKX120_SET_PULL_0(_HW, _B, _G, _GP, _V, _W)				\
+{										\
+	HAL_PINCTRL_Write(_HW, RKX120_PULL_0(_B, _G), _PINCTRL_GENVAL(_GP, _V, _W));	\
+}
+
+#define HAL_RKX120_SET_PULL_0(HW, B, G, BP, V)					\
+	RKX120_SET_PULL_0(HW, B, G, BP % PULL_8_PIN_PER_REG, V, PULL_2_BIT_PER_PIN)
+
+#define PULLEN_1_BIT_PER_PIN			(1)
+#define PULLEN_8_PIN_PER_REG			(8)
+#define RKX110_PULLEN_0(__B, __G)		(RKX110_GRF_GPIO##__B##__G##_P)
+
+#define RKX110_SET_PULLEN_0(_HW, _B, _G, _GP, _V, _W)				\
+{										\
+	HAL_PINCTRL_Write(_HW, RKX110_PULLEN_0(_B, _G), _PINCTRL_GENVAL(_GP, _V, _W));	\
+}
+
+#define HAL_RKX110_SET_PULLEN_0(HW, B, G, BP, V)				\
+	RKX110_SET_PULLEN_0(HW, B, G, BP % PULLEN_8_PIN_PER_REG, V, PULLEN_1_BIT_PER_PIN)
+
+#define RKX120_PULLEN_0(__B, __G)		(RKX120_GRF_GPIO##__B##__G##_P)
+
+#define RKX120_SET_PULLEN_0(_HW, _B, _G, _GP, _V, _W)				\
+{										\
+	HAL_PINCTRL_Write(_HW, RKX120_PULLEN_0(_B, _G), _PINCTRL_GENVAL(_GP, _V, _W));	\
+}
+
+#define HAL_RKX120_SET_PULLEN_0(HW, B, G, BP, V)				\
+	RKX120_SET_PULLEN_0(HW, B, G, BP % PULLEN_8_PIN_PER_REG, V, PULLEN_1_BIT_PER_PIN)
+
+#define DRV_2_BIT_PER_PIN			(2)
+#define DRV_8_PIN_PER_REG			(8)
+#define RKX110_DRV_0(__B, __G)			(RKX110_GRF_GPIO##__B##__G##_E)
+
+#define RKX110_SET_DRV_0(_HW, _B, _G, _GP, _V, _W)				\
+{										\
+	HAL_PINCTRL_Write(_HW, RKX110_DRV_0(_B, _G), _PINCTRL_GENVAL(_GP, _V, _W));	\
+}
+
+#define HAL_RKX110_SET_DRV_0(HW, B, G, BP, V)					\
+	RKX110_SET_DRV_0(HW, B, G, BP % DRV_8_PIN_PER_REG, V, DRV_2_BIT_PER_PIN)
+
+#define RKX120_DRV_0(__B, __G)			(RKX120_GRF_GPIO##__B##__G##_E)
+
+#define RKX120_SET_DRV_0(_HW, _B, _G, _GP, _V, _W)				\
+{										\
+	HAL_PINCTRL_Write(_HW, RKX120_DRV_0(_B, _G), _PINCTRL_GENVAL(_GP, _V, _W));	\
+}
+
+#define HAL_RKX120_SET_DRV_0(HW, B, G, BP, V)					\
+	RKX120_SET_DRV_0(HW, B, G, BP % DRV_8_PIN_PER_REG, V, DRV_2_BIT_PER_PIN)
+
+#define SMT_1_BIT_PER_PIN			(1)
+#define SMT_8_PIN_PER_REG			(8)
+#define RKX110_SMT_0(__B, __G)			(RKX110_GRF_GPIO##__B##__G##_SMT)
+
+#define RKX110_SET_SMT_0(_HW, _B, _G, _GP, _V, _W)				\
+{										\
+	HAL_PINCTRL_Write(_HW, RKX110_SMT_0(_B, _G), _PINCTRL_GENVAL(_GP, _V, _W));	\
+}
+
+#define HAL_RKX110_SET_SMT_0(HW, B, G, BP, V)					\
+	RKX110_SET_SMT_0(HW, B, G, BP % SMT_8_PIN_PER_REG, V, SMT_1_BIT_PER_PIN)
+
+#define RKX120_SMT_0(__B, __G)			(RKX120_GRF_GPIO##__B##__G##_SMT)
+
+#define RKX120_SET_SMT_0(_HW, _B, _G, _GP, _V, _W)				\
+{										\
+	HAL_PINCTRL_Write(_HW, RKX120_SMT_0(_B, _G), _PINCTRL_GENVAL(_GP, _V, _W));	\
+}
+
+#define HAL_RKX120_SET_SMT_0(HW, B, G, BP, V)					\
+	RKX120_SET_SMT_0(HW, B, G, BP % SMT_8_PIN_PER_REG, V, SMT_1_BIT_PER_PIN)
+
+/********************* Private Function Definition ***************************/
+
+static HAL_Status RKX110_PINCTRL_SetIOMUX(struct hwpin *hw, uint8_t pin, uint32_t val)
+{
+	switch (hw->bank) {
+	case 0:
+		if (pin < 5) {
+			HAL_RKX110_SET_IOMUX_L(hw, 0, A, pin, val);
+		} else if (pin < 8) {
+			HAL_RKX110_SET_IOMUX_H(hw, 0, A, pin, val);
+		} else if (pin < 13) {
+			HAL_RKX110_SET_IOMUX_L(hw, 0, B, pin, val);
+		} else if (pin < 16) {
+			HAL_RKX110_SET_IOMUX_H(hw, 0, B, pin, val);
+		} else if (pin < 21) {
+			HAL_RKX110_SET_IOMUX_L(hw, 0, C, pin, val);
+		} else if (pin < 24) {
+			HAL_RKX110_SET_IOMUX_H(hw, 0, C, pin, val);
+		} else {
+			HAL_ERR("PINCTRL: iomux unknown gpio pin-%d\n", pin);
+		}
+		break;
+	case 1:
+		if (pin < 8) {
+			HAL_RKX110_SET_IOMUX_0(hw, 1, A, pin, val);
+		} else if (pin < 16) {
+			HAL_RKX110_SET_IOMUX_0(hw, 1, B, pin, val);
+		} else if (pin < 24) {
+			HAL_RKX110_SET_IOMUX_0(hw, 1, C, pin, val);
+		} else {
+			HAL_ERR("PINCTRL: iomux unknown gpio pin-%d\n", pin);
+		}
+		break;
+	default:
+		HAL_ERR("PINCTRL: iomux unknown gpio bank-%d\n", hw->bank);
+		break;
+	}
+
+	return HAL_OK;
+}
+
+static HAL_Status RKX110_PINCTRL_SetPULL(struct hwpin *hw, uint8_t pin, uint32_t val)
+{
+	switch (hw->bank) {
+	case 1:
+		if (pin < 8) {
+			HAL_RKX110_SET_PULL_0(hw, 1, A, pin, val);
+		} else if (pin < 16) {
+			HAL_RKX110_SET_PULL_0(hw, 1, B, pin, val);
+		} else if (pin < 24) {
+			HAL_RKX110_SET_PULL_0(hw, 1, C, pin, val);
+		} else {
+			HAL_ERR("PINCTRL: pull unknown gpio pin-%d\n", pin);
+		}
+		break;
+	default:
+		HAL_ERR("PINCTRL: pull unknown gpio bank-%d\n", hw->bank);
+		break;
+	}
+
+	return HAL_OK;
+}
+
+static HAL_Status RKX110_PINCTRL_SetPULLEN(struct hwpin *hw, uint8_t pin, uint32_t val)
+{
+	switch (hw->bank) {
+	case 0:
+		if (pin < 8) {
+			HAL_RKX110_SET_PULLEN_0(hw, 0, A, pin, val);
+		} else if (pin < 16) {
+			HAL_RKX110_SET_PULLEN_0(hw, 0, B, pin, val);
+		} else if (pin < 24) {
+			HAL_RKX110_SET_PULLEN_0(hw, 0, C, pin, val);
+		} else {
+			HAL_ERR("PINCTRL: pull enable unknown gpio pin-%d\n", pin);
+		}
+		break;
+	default:
+		HAL_ERR("PINCTRL: pull enable unknown gpio bank-%d\n", hw->bank);
+		break;
+	}
+
+	return HAL_OK;
+}
+
+static HAL_Status RKX110_PINCTRL_SetDRV(struct hwpin *hw, uint8_t pin, uint32_t val)
+{
+	switch (hw->bank) {
+	case 1:
+		if (pin < 8) {
+			HAL_RKX110_SET_DRV_0(hw, 1, A, pin, val);
+		} else if (pin < 16) {
+			HAL_RKX110_SET_DRV_0(hw, 1, B, pin, val);
+		} else if (pin < 24) {
+			HAL_RKX110_SET_DRV_0(hw, 1, C, pin, val);
+		} else {
+			HAL_ERR("PINCTRL: drive strengh unknown gpio pin-%d\n", pin);
+		}
+		break;
+	default:
+		HAL_ERR("PINCTRL: drive strengh gpio bank-%d\n", hw->bank);
+		break;
+	}
+
+	return HAL_OK;
+}
+
+static HAL_Status RKX110_PINCTRL_SetSMT(struct hwpin *hw, uint8_t pin, uint32_t val)
+{
+	switch (hw->bank) {
+	case 1:
+		if (pin < 8) {
+			HAL_RKX110_SET_SMT_0(hw, 1, A, pin, val);
+		} else if (pin < 16) {
+			HAL_RKX110_SET_SMT_0(hw, 1, B, pin, val);
+		} else if (pin < 24) {
+			HAL_RKX110_SET_SMT_0(hw, 1, C, pin, val);
+		} else {
+			HAL_ERR("PINCTRL: smt gpio pin-%d\n", pin);
+		}
+		break;
+	default:
+		HAL_ERR("PINCTRL: smt gpio bank-%d\n", hw->bank);
+		break;
+	}
+
+	return HAL_OK;
+}
+
+static HAL_Status RKX120_PINCTRL_SetIOMUX(struct hwpin *hw, uint8_t pin, uint32_t val)
+{
+	switch (hw->bank) {
+	case 5:
+		if (pin < 5) {
+			HAL_RKX120_SET_IOMUX_L(hw, 0, A, pin, val);
+		} else if (pin < 8) {
+			HAL_RKX120_SET_IOMUX_H(hw, 0, A, pin, val);
+		} else if (pin < 13) {
+			HAL_RKX120_SET_IOMUX_L(hw, 0, B, pin, val);
+		} else if (pin < 16) {
+			HAL_RKX120_SET_IOMUX_H(hw, 0, B, pin, val);
+		} else if (pin < 21) {
+			HAL_RKX120_SET_IOMUX_L(hw, 0, C, pin, val);
+		} else if (pin < 24) {
+			HAL_RKX120_SET_IOMUX_H(hw, 0, C, pin, val);
+		} else {
+			HAL_ERR("PINCTRL: iomux unknown gpio pin-%d\n", pin);
+		}
+		break;
+	case 6:
+		if (pin < 8) {
+			HAL_RKX120_SET_IOMUX_0(hw, 1, A, pin, val);
+		} else if (pin < 16) {
+			HAL_RKX120_SET_IOMUX_0(hw, 1, B, pin, val);
+		} else if (pin < 24) {
+			HAL_RKX120_SET_IOMUX_0(hw, 1, C, pin, val);
+		} else {
+			HAL_ERR("PINCTRL: iomux unknown gpio pin-%d\n", pin);
+		}
+		break;
+	default:
+		HAL_ERR("PINCTRL: iomux unknown gpio bank-%d\n", hw->bank);
+		break;
+	}
+
+	return HAL_OK;
+}
+
+static HAL_Status RKX120_PINCTRL_SetPULL(struct hwpin *hw, uint8_t pin, uint32_t val)
+{
+	switch (hw->bank) {
+	case 6:
+		if (pin < 8) {
+			HAL_RKX120_SET_PULL_0(hw, 1, A, pin, val);
+		} else if (pin < 16) {
+			HAL_RKX120_SET_PULL_0(hw, 1, B, pin, val);
+		} else if (pin < 24) {
+			HAL_RKX120_SET_PULL_0(hw, 1, C, pin, val);
+		} else {
+			HAL_ERR("PINCTRL: pull unknown gpio pin-%d\n", pin);
+		}
+		break;
+	default:
+		HAL_ERR("PINCTRL: pull unknown gpio bank-%d\n", hw->bank);
+		break;
+	}
+
+	return HAL_OK;
+}
+
+static HAL_Status RKX120_PINCTRL_SetPULLEN(struct hwpin *hw, uint8_t pin, uint32_t val)
+{
+	switch (hw->bank) {
+	case 5:
+		if (pin < 8) {
+			HAL_RKX120_SET_PULLEN_0(hw, 0, A, pin, val);
+		} else if (pin < 16) {
+			HAL_RKX120_SET_PULLEN_0(hw, 0, B, pin, val);
+		} else if (pin < 24) {
+			HAL_RKX120_SET_PULLEN_0(hw, 0, C, pin, val);
+		} else {
+			HAL_ERR("PINCTRL: pull enable unknown gpio pin-%d\n", pin);
+		}
+		break;
+	default:
+		HAL_ERR("PINCTRL: pull enable unknown gpio bank-%d\n", hw->bank);
+		break;
+	}
+
+	return HAL_OK;
+}
+
+static HAL_Status RKX120_PINCTRL_SetDRV(struct hwpin *hw, uint8_t pin, uint32_t val)
+{
+	switch (hw->bank) {
+	case 6:
+		if (pin < 8) {
+			HAL_RKX120_SET_DRV_0(hw, 1, A, pin, val);
+		} else if (pin < 16) {
+			HAL_RKX120_SET_DRV_0(hw, 1, B, pin, val);
+		} else if (pin < 24) {
+			HAL_RKX120_SET_DRV_0(hw, 1, C, pin, val);
+		} else {
+			HAL_ERR("PINCTRL: drive strengh unknown gpio pin-%d\n", pin);
+		}
+		break;
+	default:
+		HAL_ERR("PINCTRL: drive strengh gpio bank-%d\n", hw->bank);
+		break;
+	}
+
+	return HAL_OK;
+}
+
+static HAL_Status RKX120_PINCTRL_SetSMT(struct hwpin *hw, uint8_t pin, uint32_t val)
+{
+	switch (hw->bank) {
+	case 6:
+		if (pin < 8) {
+			HAL_RKX120_SET_SMT_0(hw, 1, A, pin, val);
+		} else if (pin < 16) {
+			HAL_RKX120_SET_SMT_0(hw, 1, B, pin, val);
+		} else if (pin < 24) {
+			HAL_RKX120_SET_SMT_0(hw, 1, C, pin, val);
+		} else {
+			HAL_ERR("PINCTRL: smt gpio pin-%d\n", pin);
+		}
+		break;
+	default:
+		HAL_ERR("PINCTRL: smt gpio bank-%d\n", hw->bank);
+		break;
+	}
+
+	return HAL_OK;
+}
+
+static HAL_Status PINCTRL_SetPinParam(struct hwpin *hw, uint8_t pin, uint32_t param)
+{
+	HAL_Status rc = HAL_OK;
+	uint8_t val;
+
+	if (hw->type == PIN_RKX110) {
+		if (param & RK_SERDES_FLAG_MUX) {
+			val = (param & RK_SERDES_MASK_MUX) >> RK_SERDES_SHIFT_MUX;
+			rc |= RKX110_PINCTRL_SetIOMUX(hw, pin, val);
+		}
+
+		if (param & RK_SERDES_FLAG_PUL) {
+			val = (param & RK_SERDES_MASK_PUL) >> RK_SERDES_SHIFT_PUL;
+			rc |= RKX110_PINCTRL_SetPULL(hw, pin, val);
+		}
+
+		if (param & RK_SERDES_FLAG_PEN) {
+			val = (param & RK_SERDES_MASK_PEN) >> RK_SERDES_SHIFT_PEN;
+			rc |= RKX110_PINCTRL_SetPULLEN(hw, pin, val);
+		}
+
+		if (param & RK_SERDES_FLAG_DRV) {
+			val = (param & RK_SERDES_MASK_DRV) >> RK_SERDES_SHIFT_DRV;
+			rc |= RKX110_PINCTRL_SetDRV(hw, pin, val);
+		}
+
+		if (param & RK_SERDES_FLAG_SMT) {
+			val = (param & RK_SERDES_MASK_SMT) >> RK_SERDES_SHIFT_SMT;
+			rc |= RKX110_PINCTRL_SetSMT(hw, pin, val);
+		}
+	} else if (hw->type == PIN_RKX120) {
+		if (param & RK_SERDES_FLAG_MUX) {
+			val = (param & RK_SERDES_MASK_MUX) >> RK_SERDES_SHIFT_MUX;
+			rc |= RKX120_PINCTRL_SetIOMUX(hw, pin, val);
+		}
+
+		if (param & RK_SERDES_FLAG_PUL) {
+			val = (param & RK_SERDES_MASK_PUL) >> RK_SERDES_SHIFT_PUL;
+			rc |= RKX120_PINCTRL_SetPULL(hw, pin, val);
+		}
+
+		if (param & RK_SERDES_FLAG_PEN) {
+			val = (param & RK_SERDES_MASK_PEN) >> RK_SERDES_SHIFT_PEN;
+			rc |= RKX120_PINCTRL_SetPULLEN(hw, pin, val);
+		}
+
+		if (param & RK_SERDES_FLAG_DRV) {
+			val = (param & RK_SERDES_MASK_DRV) >> RK_SERDES_SHIFT_DRV;
+			rc |= RKX120_PINCTRL_SetDRV(hw, pin, val);
+		}
+
+		if (param & RK_SERDES_FLAG_SMT) {
+			val = (param & RK_SERDES_MASK_SMT) >> RK_SERDES_SHIFT_SMT;
+			rc |= RKX120_PINCTRL_SetSMT(hw, pin, val);
+		}
+	} else {
+		HAL_ERR("PINCTRL: error pin type %d\n", hw->type);
+	}
+
+	return rc;
+}
+
+/********************* Public Function Definition ***************************/
+
+uint32_t HAL_PINCTRL_Read(struct hwpin *hw, uint32_t reg)
+{
+	uint32_t val;
+	int ret;
+
+	ret = hw->xfer.read(hw->xfer.client, reg, &val);
+	if (ret) {
+		HAL_ERR("%s: read reg=0x%08x failed, ret=%d\n", hw->name, reg, ret);
+	}
+
+	HAL_DBG("%s: %s: reg=0x%08x, val=0x%08x\n", __func__, hw->name, reg, val);
+
+	return val;
+}
+
+uint32_t HAL_PINCTRL_Write(struct hwpin *hw, uint32_t reg, uint32_t val)
+{
+	int ret;
+
+	HAL_DBG("%s: %s: reg=0x%08x, val=0x%08x\n", __func__, hw->name, reg, val);
+
+	ret = hw->xfer.write(hw->xfer.client, reg, val);
+	if (ret) {
+		HAL_ERR("%s: write reg=0x%08x, val=0x%08x failed, ret=%d\n",
+			hw->name, reg, val, ret);
+	}
+
+	return ret;
+}
+
+HAL_Status HAL_PINCTRL_Init(void)
+{
+	return HAL_OK;
+}
+
+HAL_Status HAL_PINCTRL_SetParam(struct hwpin *hw, uint32_t mPins, uint32_t param)
+{
+	uint8_t pin;
+	HAL_Status rc;
+
+	if (!(param & (RK_SERDES_FLAG_MUX | RK_SERDES_FLAG_PUL | RK_SERDES_FLAG_PEN |
+		       RK_SERDES_FLAG_DRV | RK_SERDES_FLAG_SMT))) {
+		HAL_ERR("PINCTRL: no parameter!\n");
+
+		return HAL_ERROR;
+	}
+
+	for (pin = 0; pin < 32; pin++) {
+		if (mPins & (1 << pin)) {
+			rc = PINCTRL_SetPinParam(hw, pin, param);
+			if (rc) {
+				return rc;
+			}
+		}
+	}
+
+	return HAL_OK;
+}
+
+HAL_Status HAL_PINCTRL_SetIOMUX(struct hwpin *hw, uint32_t mPins, uint32_t param)
+{
+	return HAL_PINCTRL_SetParam(hw, mPins, param);
+}
+
diff --git a/kernel/drivers/mfd/rkx110_x120/hal/pinctrl_rkx110_x120.h b/kernel/drivers/mfd/rkx110_x120/hal/pinctrl_rkx110_x120.h
new file mode 100644
index 0000000..0f28be9
--- /dev/null
+++ b/kernel/drivers/mfd/rkx110_x120/hal/pinctrl_rkx110_x120.h
@@ -0,0 +1,166 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2023 Rockchip Electronics Co. Ltd.
+ *
+ * Author: Steven Liu <steven.liu@rock-chips.com>
+ */
+
+#ifndef _PINCTRL_CORE_H_
+#define _PINCTRL_CORE_H_
+
+#include <dt-bindings/mfd/rockchip-serdes.h>
+
+#include "hal_def.h"
+#include "hal_os_def.h"
+
+typedef enum {
+	PIN_UNDEF,
+	PIN_RKX110,
+	PIN_RKX120,
+	PIN_ALL,
+	PIN_MAX,
+} HAL_PinType;
+
+struct hwpin;
+
+struct xferpin {
+	HAL_PinType type;
+	char *name; /* slave addr is expected */
+	void *client;
+	uint32_t bank;
+	uint32_t mpins;
+	uint32_t param;
+	HAL_RegRead_t *read;
+	HAL_RegWrite_t *write;
+};
+
+struct hwpin {
+	char name[32];
+	HAL_PinType type;
+	uint32_t grf_base;
+	uint32_t bank;
+	uint32_t mpins;
+	uint32_t param;
+	struct xferpin xfer;
+};
+
+typedef enum {
+	GPIO0_A0 = 0,
+	GPIO0_A1,
+	GPIO0_A2,
+	GPIO0_A3,
+	GPIO0_A4,
+	GPIO0_A5,
+	GPIO0_A6,
+	GPIO0_A7,
+	GPIO0_B0 = 8,
+	GPIO0_B1,
+	GPIO0_B2,
+	GPIO0_B3,
+	GPIO0_B4,
+	GPIO0_B5,
+	GPIO0_B6,
+	GPIO0_B7,
+	GPIO0_C0 = 16,
+	GPIO0_C1,
+	GPIO0_C2,
+	GPIO0_C3,
+	GPIO0_C4,
+	GPIO0_C5,
+	GPIO0_C6,
+	GPIO0_C7,
+	GPIO0_D0 = 24,
+	GPIO0_D1,
+	GPIO0_D2,
+	GPIO0_D3,
+	GPIO0_D4,
+	GPIO0_D5,
+	GPIO0_D6,
+	GPIO0_D7,
+	GPIO1_A0 = 32,
+	GPIO1_A1,
+	GPIO1_A2,
+	GPIO1_A3,
+	GPIO1_A4,
+	GPIO1_A5,
+	GPIO1_A6,
+	GPIO1_A7,
+	GPIO1_B0 = 40,
+	GPIO1_B1,
+	GPIO1_B2,
+	GPIO1_B3,
+	GPIO1_B4,
+	GPIO1_B5,
+	GPIO1_B6,
+	GPIO1_B7,
+	GPIO1_C0 = 48,
+	GPIO1_C1,
+	GPIO1_C2,
+	GPIO1_C3,
+	GPIO1_C4,
+	GPIO1_C5,
+	GPIO1_C6,
+	GPIO1_C7,
+	GPIO1_D0 = 56,
+	GPIO1_D1,
+	GPIO1_D2,
+	GPIO1_D3,
+	GPIO1_D4,
+	GPIO1_D5,
+	GPIO1_D6,
+	GPIO1_D7,
+	GPIO_NUM_MAX
+} ePINCTRL_PIN;
+
+typedef enum {
+	PINCTRL_IOMUX_FUNC0,
+	PINCTRL_IOMUX_FUNC1,
+	PINCTRL_IOMUX_FUNC2,
+	PINCTRL_IOMUX_FUNC3,
+	PINCTRL_IOMUX_FUNC4,
+	PINCTRL_IOMUX_FUNC5,
+	PINCTRL_IOMUX_FUNC6,
+	PINCTRL_IOMUX_FUNC7,
+} ePINCTRL_iomuxFunc;
+
+typedef enum {
+	PINCTRL_PULL_NORMAL,
+	PINCTRL_PULL_UP,
+	PINCTRL_PULL_DOWN,
+	PINCTRL_PULL_KEEP
+} ePINCTRL_pullMode;
+
+/*
+ * Special pull configuration.
+ * Only enable and disable.
+ * The specific pull-up or pull-down can not be configured.
+ */
+typedef enum {
+	PINCTRL_PULL_DIS,
+	PINCTRL_PULL_EN
+} ePINCTRL_pullEnable;
+
+typedef enum {
+	PINCTRL_DRIVE_LEVEL0,
+	PINCTRL_DRIVE_LEVEL1,
+	PINCTRL_DRIVE_LEVEL2,
+	PINCTRL_DRIVE_LEVEL3,
+	PINCTRL_DRIVE_LEVEL4,
+	PINCTRL_DRIVE_LEVEL5,
+	PINCTRL_DRIVE_LEVEL6,
+	PINCTRL_DRIVE_LEVEL7
+} ePINCTRL_driveLevel;
+
+typedef enum {
+	PINCTRL_SCHMITT_DIS,
+	PINCTRL_SCHMITT_EN
+} ePINCTRL_schmitt;
+
+uint32_t HAL_PINCTRL_Read(struct hwpin *hw, uint32_t reg);
+uint32_t HAL_PINCTRL_Write(struct hwpin *hw, uint32_t reg, uint32_t val);
+
+HAL_Status HAL_PINCTRL_Init(void);
+HAL_Status HAL_PINCTRL_SetParam(struct hwpin *hw, uint32_t mPins, uint32_t param);
+HAL_Status HAL_PINCTRL_SetIOMUX(struct hwpin *hw, uint32_t mPins, uint32_t param);
+
+#endif /* _PINCTRL_CORE_H_ */
diff --git a/kernel/drivers/mfd/rkx110_x120/pattern_gen.c b/kernel/drivers/mfd/rkx110_x120/pattern_gen.c
new file mode 100644
index 0000000..488d372
--- /dev/null
+++ b/kernel/drivers/mfd/rkx110_x120/pattern_gen.c
@@ -0,0 +1,141 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2022 Rockchip Electronics Co. Ltd.
+ *
+ */
+
+#include <linux/bitfield.h>
+#include <linux/debugfs.h>
+
+#include "rkx110_x120.h"
+
+#define PATTERN_GEN_PATTERN_CTRL	0x0000
+#define PATTERN_START_PCLK		BIT(31)
+#define PATTERN_START			BIT(30)
+#define PATTERN_RECTANGLE_H		GENMASK(29, 16)
+#define PATTERN_RECTANGLE_V		GENMASK(13, 0)
+#define PATTERN_GEN_PATERN_VH_CFG0	0x0004
+#define PATTERN_HACT			GENMASK(29, 16)
+#define PATTERN_VACT			GENMASK(13, 0)
+#define PATTERN_GEN_PATERN_VH_CFG1	0x0008
+#define PATTERN_VFP			GENMASK(29, 20)
+#define PATTERN_VBP			GENMASK(19, 10)
+#define PATTERN_VSA			GENMASK(9, 0)
+#define PATTERN_GEN_PATERN_VH_CFG2	0x000C
+#define PATTERN_HFP			GENMASK(27, 16)
+#define PATTERN_HBP			GENMASK(11, 0)
+#define PATTERN_GEN_PATERN_VH_CFG3	0x0010
+#define PATTERN_HSA			GENMASK(11, 0)
+#define PATTERN_GEN_VALUE0		0x0014
+#define PATTERN_GEN_VALUE1		0x0018
+
+static void pattern_gen_enable(struct pattern_gen *pattern_gen)
+{
+	struct i2c_client *client = pattern_gen->chip->client;
+	struct rk_serdes *serdes = pattern_gen->chip->serdes;
+	const struct videomode *vm = serdes->vm;
+
+	serdes->i2c_update_bits(client, pattern_gen->base + PATTERN_GEN_PATTERN_CTRL,
+				PATTERN_RECTANGLE_H | PATTERN_RECTANGLE_V,
+				FIELD_PREP(PATTERN_RECTANGLE_H, 128) |
+				FIELD_PREP(PATTERN_RECTANGLE_V, 128));
+
+	serdes->i2c_write_reg(client, pattern_gen->base + PATTERN_GEN_PATERN_VH_CFG0,
+			      FIELD_PREP(PATTERN_HACT, vm->hactive) |
+			      FIELD_PREP(PATTERN_VACT, vm->vactive));
+	serdes->i2c_write_reg(client, pattern_gen->base + PATTERN_GEN_PATERN_VH_CFG1,
+			      FIELD_PREP(PATTERN_VFP, vm->vfront_porch) |
+			      FIELD_PREP(PATTERN_VBP, vm->vback_porch) |
+			      FIELD_PREP(PATTERN_VSA, vm->vsync_len));
+	serdes->i2c_write_reg(client, pattern_gen->base + PATTERN_GEN_PATERN_VH_CFG2,
+			      FIELD_PREP(PATTERN_HFP, vm->hfront_porch) |
+			      FIELD_PREP(PATTERN_HBP, vm->hback_porch));
+	serdes->i2c_write_reg(client, pattern_gen->base + PATTERN_GEN_PATERN_VH_CFG3,
+			      FIELD_PREP(PATTERN_HSA, vm->hsync_len));
+
+	serdes->i2c_update_bits(client, pattern_gen->base + PATTERN_GEN_PATTERN_CTRL,
+				PATTERN_START_PCLK,
+				FIELD_PREP(PATTERN_START_PCLK, 1));
+	serdes->i2c_write_reg(client, pattern_gen->link_src_reg,
+			      BIT(pattern_gen->link_src_offset + 16) |
+			      BIT(pattern_gen->link_src_offset));
+}
+
+static void pattern_gen_disable(struct pattern_gen *pattern_gen)
+{
+	struct i2c_client *client = pattern_gen->chip->client;
+	struct rk_serdes *serdes = pattern_gen->chip->serdes;
+
+	serdes->i2c_write_reg(client, pattern_gen->link_src_reg,
+			      BIT(pattern_gen->link_src_offset + 16));
+	serdes->i2c_update_bits(client, pattern_gen->base + PATTERN_GEN_PATTERN_CTRL,
+				PATTERN_START_PCLK,
+				FIELD_PREP(PATTERN_START_PCLK, 0));
+}
+
+static ssize_t pattern_gen_write(struct file *file, const char __user *ubuf,
+				 size_t len, loff_t *offp)
+{
+	struct seq_file *m = file->private_data;
+	struct pattern_gen *pattern_gen = m->private;
+	char buf[5];
+
+	if (len > sizeof(buf) - 1)
+		return -EINVAL;
+
+	if (copy_from_user(buf, ubuf, len))
+		return -EFAULT;
+
+	buf[len] = '\0';
+
+	if (sysfs_streq(buf, "on"))
+		pattern_gen_enable(pattern_gen);
+	else if (sysfs_streq(buf, "off"))
+		pattern_gen_disable(pattern_gen);
+	else
+		return -EINVAL;
+
+	return len;
+}
+
+static int pattern_gen_show(struct seq_file *m, void *data)
+{
+	struct pattern_gen *pattern_gen = m->private;
+	struct i2c_client *client = pattern_gen->chip->client;
+	struct rk_serdes *serdes = pattern_gen->chip->serdes;
+	u32 reg = 0;
+
+	serdes->i2c_read_reg(client, pattern_gen->link_src_reg, &reg);
+	if (reg & BIT(pattern_gen->link_src_offset))
+		seq_printf(m, "%s\n", "on");
+	else
+		seq_printf(m, "%s\n", "off");
+
+	return 0;
+}
+
+static int pattern_gen_open(struct inode *inode, struct file *file)
+{
+	struct pattern_gen *pattern_gen = inode->i_private;
+
+	return single_open(file, pattern_gen_show, pattern_gen);
+}
+
+static const struct file_operations pattern_gen_fops = {
+	.owner = THIS_MODULE,
+	.open = pattern_gen_open,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = single_release,
+	.write = pattern_gen_write,
+};
+
+void rkx110_x120_pattern_gen_debugfs_create_file(struct pattern_gen *pattern_gen,
+						 struct rk_serdes_chip *chip,
+						 struct dentry *dentry)
+{
+	pattern_gen->chip = chip;
+
+	debugfs_create_file(pattern_gen->name, 0600, dentry, pattern_gen,
+			    &pattern_gen_fops);
+}
diff --git a/kernel/drivers/mfd/rkx110_x120/rkx110.c b/kernel/drivers/mfd/rkx110_x120/rkx110.c
new file mode 100644
index 0000000..0e3fc4f
--- /dev/null
+++ b/kernel/drivers/mfd/rkx110_x120/rkx110.c
@@ -0,0 +1,304 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2022 Rockchip Electronics Co. Ltd.
+ */
+#include <linux/debugfs.h>
+#include "hal/pinctrl_api.h"
+#include "rkx110_x120.h"
+#include "rkx110_reg.h"
+#include "serdes_combphy.h"
+
+#if defined(CONFIG_DEBUG_FS)
+static struct pattern_gen rkx110_pattern_gen[] = {
+	{
+		.name = "dsi0",
+		.base = RKX110_PATTERN_GEN_DSI0_BASE,
+		.link_src_reg = SER_GRF_SOC_CON4,
+		.link_src_offset = 12,
+	}, {
+		.name = "dsi1",
+		.base = RKX110_PATTERN_GEN_DSI1_BASE,
+		.link_src_reg = SER_GRF_SOC_CON4,
+		.link_src_offset = 13,
+	}, {
+		.name = "lvds0",
+		.base = RKX110_PATTERN_GEN_LVDS0_BASE,
+		.link_src_reg = SER_GRF_SOC_CON4,
+		.link_src_offset = 14,
+	}, {
+		.name = "lvds1",
+		.base = RKX110_PATTERN_GEN_LVDS1_BASE,
+		.link_src_reg = SER_GRF_SOC_CON4,
+		.link_src_offset = 15,
+	},
+	{ /* sentinel */ }
+};
+
+static const struct rk_serdes_reg rkx110_regs[] = {
+	{
+		.name = "cru",
+		.reg_base = RKX110_SER_CRU_BASE,
+		.reg_len = 0xF04,
+	},
+	{
+		.name = "grf",
+		.reg_base = RKX110_SER_GRF_BASE,
+		.reg_len = 0x220,
+
+	},
+	{
+		.name = "grf_mipi0",
+		.reg_base = RKX110_GRF_MIPI0_BASE,
+		.reg_len = 0x600,
+	},
+	{
+		.name = "grf_mipi1",
+		.reg_base = RKX110_GRF_MIPI1_BASE,
+		.reg_len = 0x600,
+	},
+	{
+		.name = "mipi_lvds_phy0",
+		.reg_base = RKX110_MIPI_LVDS_RX_PHY0_BASE,
+		.reg_len = 0xb0,
+	},
+	{
+		.name = "mipi_lvds_phy1",
+		.reg_base = RKX110_MIPI_LVDS_RX_PHY1_BASE,
+		.reg_len = 0xb0,
+	},
+
+	{
+		.name = "host0",
+		.reg_base = RKX110_CSI2HOST0_BASE,
+		.reg_len = 0x60,
+	},
+	{
+		.name = "host1",
+		.reg_base = RKX110_CSI2HOST1_BASE,
+		.reg_len = 0x60,
+	},
+	{
+		.name = "vicap",
+		.reg_base = RKX110_VICAP_BASE,
+		.reg_len = 0x220,
+	},
+	{
+		.name = "gpio0",
+		.reg_base = RKX110_GPIO0_BASE,
+		.reg_len = 0x80,
+	},
+	{
+		.name = "gpio1",
+		.reg_base = RKX110_GPIO1_BASE,
+		.reg_len = 0x80,
+	},
+	{
+		.name = "dsi0",
+		.reg_base = RKX110_DSI_RX0_BASE,
+		.reg_len = 0x1D0,
+	},
+	{
+		.name = "dsi1",
+		.reg_base = RKX110_DSI_RX1_BASE,
+		.reg_len = 0x1D0,
+	},
+	{
+		.name = "rklink",
+		.reg_base = RKX110_SER_RKLINK_BASE,
+		.reg_len = 0xD4,
+	},
+	{
+		.name = "pcs0",
+		.reg_base = RKX110_SER_PCS0_BASE,
+		.reg_len = 0x1c0,
+	},
+	{
+		.name = "pcs1",
+		.reg_base = RKX110_SER_PCS1_BASE,
+		.reg_len = 0x1c0,
+	},
+	{
+		.name = "pma0",
+		.reg_base = RKX110_SER_PMA0_BASE,
+		.reg_len = 0x100,
+	},
+	{
+		.name = "pma1",
+		.reg_base = RKX110_SER_PMA1_BASE,
+		.reg_len = 0x100,
+	},
+	{
+		.name = "dsi0_pattern_gen",
+		.reg_base = RKX110_PATTERN_GEN_DSI0_BASE,
+		.reg_len = 0x18,
+	},
+	{
+		.name = "dsi1_pattern_gen",
+		.reg_base = RKX110_PATTERN_GEN_DSI1_BASE,
+		.reg_len = 0x18,
+	},
+	{
+		.name = "lvds0_pattern_gen",
+		.reg_base = RKX110_PATTERN_GEN_LVDS0_BASE,
+		.reg_len = 0x18,
+	},
+	{
+		.name = "lvds1_pattern_gen",
+		.reg_base = RKX110_PATTERN_GEN_LVDS1_BASE,
+		.reg_len = 0x18,
+	},
+	{ /* sentinel */ }
+};
+
+static int rkx110_reg_show(struct seq_file *s, void *v)
+{
+	const struct rk_serdes_reg *regs = rkx110_regs;
+	struct rk_serdes_chip *chip = s->private;
+	struct rk_serdes *serdes = chip->serdes;
+	struct i2c_client *client = chip->client;
+	int i;
+	u32 val = 0;
+
+	seq_printf(s, "rkx110_%s:\n", file_dentry(s->file)->d_iname);
+
+	while (regs->name) {
+		if (!strcmp(regs->name, file_dentry(s->file)->d_iname))
+			break;
+		regs++;
+	}
+
+	if (!regs->name)
+		return -ENODEV;
+
+	for (i = 0; i <= regs->reg_len; i += 4) {
+		serdes->i2c_read_reg(client, regs->reg_base + i, &val);
+
+		if (i % 16 == 0)
+			seq_printf(s, "\n0x%04x:", i);
+		seq_printf(s, " %08x", val);
+	}
+	seq_puts(s, "\n");
+
+	return 0;
+}
+
+static ssize_t rkx110_reg_write(struct file *file, const char __user *buf,
+				size_t count, loff_t *ppos)
+{
+	const struct rk_serdes_reg *regs = rkx110_regs;
+	struct rk_serdes_chip *chip = file->f_path.dentry->d_inode->i_private;
+	struct rk_serdes *serdes = chip->serdes;
+	struct i2c_client *client = chip->client;
+	u32 addr;
+	u32 val;
+	char kbuf[25];
+	int ret;
+
+	if (count >= sizeof(kbuf))
+		return -ENOSPC;
+
+	if (copy_from_user(kbuf, buf, count))
+		return -EFAULT;
+
+	kbuf[count] = '\0';
+
+	ret = sscanf(kbuf, "%x%x", &addr, &val);
+	if (ret != 2)
+		return -EINVAL;
+
+	while (regs->name) {
+		if (!strcmp(regs->name, file_dentry(file)->d_iname))
+			break;
+		regs++;
+	}
+
+	if (!regs->name)
+		return -ENODEV;
+
+	addr += regs->reg_base;
+
+	serdes->i2c_write_reg(client, addr, val);
+
+	return count;
+}
+
+static int rkx110_reg_open(struct inode *inode, struct file *file)
+{
+	struct rk_serdes_chip *chip = inode->i_private;
+
+	return single_open(file, rkx110_reg_show, chip);
+}
+
+static const struct file_operations rkx110_reg_fops = {
+	.owner          = THIS_MODULE,
+	.open           = rkx110_reg_open,
+	.read           = seq_read,
+	.write          = rkx110_reg_write,
+	.llseek         = seq_lseek,
+	.release        = single_release,
+};
+
+void rkx110_debugfs_init(struct rk_serdes_chip *chip, struct dentry *dentry)
+{
+	struct pattern_gen *pattern_gen = rkx110_pattern_gen;
+	const struct rk_serdes_reg *regs = rkx110_regs;
+	struct dentry *dir;
+
+	dir = debugfs_create_dir("registers", dentry);
+	if (!IS_ERR(dir)) {
+		while (regs->name) {
+			debugfs_create_file(regs->name, 0600, dir, chip, &rkx110_reg_fops);
+			regs++;
+		}
+	}
+
+	dir = debugfs_create_dir("pattern_gen", dentry);
+	if (!IS_ERR(dir)) {
+		while (pattern_gen->name) {
+			rkx110_x120_pattern_gen_debugfs_create_file(pattern_gen, chip, dir);
+			pattern_gen++;
+		}
+	}
+}
+#endif
+
+static int rkx110_rgb_rx_iomux_cfg(struct rk_serdes *serdes, struct rk_serdes_route *route)
+{
+	struct i2c_client *client = serdes->chip[DEVICE_LOCAL].client;
+	uint32_t pins;
+
+	pins = RK_SERDES_GPIO_PIN_C0 | RK_SERDES_GPIO_PIN_C1 | RK_SERDES_GPIO_PIN_C2 |
+	       RK_SERDES_GPIO_PIN_C3 | RK_SERDES_GPIO_PIN_C4 | RK_SERDES_GPIO_PIN_C5 |
+	       RK_SERDES_GPIO_PIN_C6 | RK_SERDES_GPIO_PIN_C7;
+	serdes->set_hwpin(serdes, client, PIN_RKX110, RK_SERDES_SER_GPIO_BANK0, pins,
+			  RK_SERDES_PIN_CONFIG_MUX_FUNC1);
+
+	pins = RK_SERDES_GPIO_PIN_A0 | RK_SERDES_GPIO_PIN_A1 | RK_SERDES_GPIO_PIN_A2 |
+	       RK_SERDES_GPIO_PIN_A3 | RK_SERDES_GPIO_PIN_A4 | RK_SERDES_GPIO_PIN_A5 |
+	       RK_SERDES_GPIO_PIN_A6 | RK_SERDES_GPIO_PIN_A7 | RK_SERDES_GPIO_PIN_B0 |
+	       RK_SERDES_GPIO_PIN_B1 | RK_SERDES_GPIO_PIN_B2 | RK_SERDES_GPIO_PIN_B3 |
+	       RK_SERDES_GPIO_PIN_B4 | RK_SERDES_GPIO_PIN_B5 | RK_SERDES_GPIO_PIN_B6 |
+	       RK_SERDES_GPIO_PIN_B7 | RK_SERDES_GPIO_PIN_C0 | RK_SERDES_GPIO_PIN_C1 |
+	       RK_SERDES_GPIO_PIN_C2 | RK_SERDES_GPIO_PIN_C3;
+	serdes->set_hwpin(serdes, client, PIN_RKX110, RK_SERDES_SER_GPIO_BANK1, pins,
+			  RK_SERDES_PIN_CONFIG_MUX_FUNC1);
+
+	return 0;
+}
+
+int rkx110_rgb_rx_enable(struct rk_serdes *serdes, struct rk_serdes_route *route)
+{
+	rkx110_rgb_rx_iomux_cfg(serdes, route);
+
+	return 0;
+}
+
+int rkx110_lvds_rx_enable(struct rk_serdes *serdes, struct rk_serdes_route *route, int id)
+{
+	rkx110_combrxphy_set_mode(serdes, COMBRX_PHY_MODE_VIDEO_LVDS);
+
+	rkx110_combrxphy_power_on(serdes, id ? COMBPHY_1 : COMBPHY_0);
+
+	return 0;
+}
+
diff --git a/kernel/drivers/mfd/rkx110_x120/rkx110_combrxphy.c b/kernel/drivers/mfd/rkx110_x120/rkx110_combrxphy.c
new file mode 100644
index 0000000..4152728
--- /dev/null
+++ b/kernel/drivers/mfd/rkx110_x120/rkx110_combrxphy.c
@@ -0,0 +1,280 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2022 Rockchip Electronics Co. Ltd.
+ *
+ * Author: Guochun Huang <hero.huang@rock-chips.com>
+ */
+
+#include "hal/cru_api.h"
+#include <linux/kernel.h>
+#include <linux/iopoll.h>
+#include "rkx110_x120.h"
+#include "rkx110_reg.h"
+#include "serdes_combphy.h"
+
+#define SOFT_RST		0x0000
+#define HS_CLK_SOFT_RST		BIT(1)
+#define CFG_CLK_SOFT_RST	BIT(0)
+#define PHY_RCAL		0x0004
+#define RCAL_DONE		BIT(17)
+#define RCAL_OUT(x)		UPDATE(x, 16, 13)
+#define RCAL_CTL(x)		UPDATE(x, 12, 5)
+#define RCAL_TRIM(x)		UPDATE(x, 4, 1)
+#define RCAL_EN			BIT(0)
+#define ULP_RX_EN		0x0008
+#define VOFFCAL_OUT		0x000c
+#define CSI_CLK_VOFFCAL_DONE	BIT(29)
+#define CSI_CLK_VOFFCAL_OUT(x)	UPDATE(x, 28, 24)
+#define CSI_0_VOFFCAL_DONE	BIT(23)
+#define CSI_0_VOFFCAL_OUT(x)	UPDATE(x, 22, 18)
+#define CSI_1_VOFFCAL_DONE	BIT(17)
+#define CSI_1_VOFFCAL_OUT(x)	UPDATE(x, 16, 12)
+#define CSI_2_VOFFCAL_DONE	BIT(11)
+#define CSI_2_VOFFCAL_OUT(x)	UPDATE(x, 10, 6)
+#define CSI_3_VOFFCAL_DONE	BIT(5)
+#define CSI_3_VOFFCAL_OUT(x)	UPDATE(x, 4, 0)
+#define CSI_CTL01		0x0010
+#define CSI_CTL1(x)		UPDATE(x, 31, 16)
+#define CSI_CTL0(x)		UPDATE(x, 15, 0)
+#define CSI_CTL23		0x0014
+#define CSI_CTL3(x)		UPDATE(x, 31, 16)
+#define CSI_CTL2(x)		UPDATE(x, 15, 0)
+#define CSI_VINIT		0x001c
+#define CSI_LPRX_VREF_TRIM	UPDATE(x, 23, 20)
+#define CSI_CLK_LPRX_VINIT(x)	UPDATE(x, 19, 16)
+#define CSI_3_LPRX_VINIT(x)	UPDATE(x, 15, 12)
+#define	CSI_2_LPRX_VINIT(x)	UPDATE(x, 11, 8)
+#define CSI_1_LPRX_VINIT(x)	UPDATE(x, 7, 4)
+#define CSI_0_LPRX_VINIT(x)	UPDATE(x, 3, 0)
+#define CLANE_PARA		0x0020
+#define T_CLK_TERM_EN(x)	UPDATE(x, 15, 8)
+#define T_CLK_SETTLE(x)		UPDATE(x, 7, 0)
+#define T_HS_TERMEN		0x0024
+#define T_D3_TERMEN(x)		UPDATE(x, 31, 24)
+#define T_D2_TERMEN(x)		UPDATE(x, 23, 16)
+#define T_D1_TERMEN(x)		UPDATE(x, 15, 8)
+#define T_D0_TERMEN(x)		UPDATE(x, 7, 0)
+#define T_HS_SETTLE		0x0028
+#define T_D3_SETTLE(x)		UPDATE(x, 31, 24)
+#define T_D2_SETTLE(x)		UPDATE(x, 23, 16)
+#define T_D1_SETTLE(x)		UPDATE(x, 15, 8)
+#define T_D0_SETTLE(x)		UPDATE(x, 7, 0)
+#define T_CLANE_INIT		0x0030
+#define T_CLK_INIT(x)		UPDATE(x, 23, 0)
+#define T_LANE0_INIT		0x0034
+#define T_D0_INIT(x)		UPDATE(x, 23, 0)
+#define T_LANE1_INIT		0x0038
+#define T_D1_INIT(x)		UPDATE(x, 23, 0)
+#define T_LANE2_INIT		0x003c
+#define T_D2_INIT(x)		UPDATE(x, 23, 0)
+#define T_LANE3_INIT		0x0040
+#define T_D3_INIT(x)		UPDATE(x, 23, 0)
+#define TLPX_CTRL		0x0044
+#define EN_TLPX_CHECK		BIT(8)
+#define TLPX(x)			UPDATE(x, 7, 0)
+#define NE_SWAP			0x0048
+#define DPDN_SWAP_LANE(x)	UPDATE(1 << x, 11, 8)
+#define LANE_SWAP_LAN3(x)	UPDATE(x, 7, 6)
+#define LANE_SWAP_LANE2(x)	UPDATE(x, 5, 4)
+#define LANE_SWAP_LANE1(x)	UPDATE(x, 3, 2)
+#define LANE_SWAP_LANE0(x)	UPDATE(x, 1, 0)
+#define MISC_INFO		0x004c
+#define ULPS_LP10_SEL		BIT(1)
+#define LONG_SOTSYNC_EN		BIT(0)
+
+#define GRF_MIPI_RX_CON0	0x0000
+#define RXCK_RTRM(x)		HIWORD_UPDATE(x, GENMASK(15, 12), 12)
+#define LVDS_RX3_PD(x)		HIWORD_UPDATE(x, BIT(10), 10)
+#define LVDS_RX2_PD(x)		HIWORD_UPDATE(x, BIT(9), 9)
+#define LVDS_RX1_PD(x)		HIWORD_UPDATE(x, BIT(8), 8)
+#define LVDS_RX0_PD(x)		HIWORD_UPDATE(x, BIT(7), 7)
+#define LVDS_RXCK_PD(x)		HIWORD_UPDATE(x, BIT(6), 6)
+#define LANE3_ENABLE(x)		HIWORD_UPDATE(x, BIT(5), 5)
+#define LANE2_ENABLE(x)		HIWORD_UPDATE(x, BIT(4), 4)
+#define LANE1_ENABLE(x)		HIWORD_UPDATE(x, BIT(3), 3)
+#define LANE0_ENABLE(x)		HIWORD_UPDATE(x, BIT(2), 2)
+#define PHY_MODE(x)		HIWORD_UPDATE(x, GENMASK(1, 0), 0)
+
+#define GRF_MIPI_RX_CON2	0x0008
+#define RXCK_CTL_5(x)		HIWORD_UPDATE(x, BIT(11), 11)
+#define DDR_CLK_DUTY_CYCLE(x)	HIWORD_UPDATE(x, GENMASK(10, 8), 8)
+#define BUS_WIDTH_SELECTION(x)	HIWORD_UPDATE(x, GENMASK(7, 5), 5)
+#define RXCK_CTL_2(x)		HIWORD_UPDATE(x, BIT(4), 4)
+#define RXCK_CTL_1(x)		HIWORD_UPDATE(x, GENMASK(2, 1), 1)
+#define RXCK_CTL_0(x)		HIWORD_UPDATE(x, BIT(0), 0)
+
+#define GRF_MIPI_RX_CON4	0x0010
+#define GRF_MIPI_RX_CON5	0x0014
+#define GRF_MIPI_RX_CON6	0x0018
+#define GRF_MIPI_RX_CON7	0x001c
+#define GRF_SOC_STATUS		0x0080
+#define DLL_LOCK		BIT(24)
+
+#define LVDS1_MSBSEL(x)		HIWORD_UPDATE(x, BIT(5), 5)
+#define LVDS0_MSBSEL(x)		HIWORD_UPDATE(x, BIT(2), 2)
+
+static void
+rkx110_combrxphy_dsi_timing_init(struct rk_serdes *ser, enum comb_phy_id id)
+{
+}
+
+static void rkx110_combrxphy_dsi_power_on(struct rk_serdes *ser, enum comb_phy_id id)
+{
+	struct hwclk *hwclk = ser->chip[DEVICE_LOCAL].hwclk;
+	struct rkx110_combrxphy *combrxphy = &ser->combrxphy;
+	struct i2c_client *client = ser->chip[DEVICE_LOCAL].client;
+	u32 val = 0;
+	u32 grf_base;
+
+	switch (id) {
+	case COMBPHY_0:
+		hwclk_set_rate(hwclk, RKX110_CPS_CFGCLK_MIPIRXPHY0, 100 * USEC_PER_SEC);
+		dev_info(ser->dev, "RKX110_CPS_CFGCLK_MIPIRXPHY0:%d\n",
+			 hwclk_get_rate(hwclk, RKX110_CPS_CFGCLK_MIPIRXPHY0));
+		break;
+	case COMBPHY_1:
+		hwclk_set_rate(hwclk, RKX110_CPS_CFGCLK_MIPIRXPHY1, 100 * USEC_PER_SEC);
+		dev_info(ser->dev, "RKX110_CPS_CFGCLK_MIPIRXPHY1:%d\n",
+			 hwclk_get_rate(hwclk, RKX110_CPS_CFGCLK_MIPIRXPHY1));
+		break;
+	default:
+		break;
+	}
+
+	serdes_combphy_get_default_config(combrxphy->rate,
+					  &combrxphy->mipi_dphy_cfg);
+
+	switch (ser->dsi_rx.lanes) {
+	case 4:
+		val |= LANE3_ENABLE(1);
+		fallthrough;
+	case 3:
+		val |= LANE2_ENABLE(1);
+		fallthrough;
+	case 2:
+		val |= LANE1_ENABLE(1);
+		fallthrough;
+	case 1:
+	default:
+		val |= LANE0_ENABLE(1);
+		break;
+	}
+
+	grf_base = id ? RKX110_GRF_MIPI1_BASE : RKX110_GRF_MIPI0_BASE;
+	ser->i2c_write_reg(client, grf_base + GRF_MIPI_RX_CON0,
+			   PHY_MODE(COMBRX_PHY_MODE_VIDEO_MIPI) | val);
+
+	rkx110_combrxphy_dsi_timing_init(ser, id);
+}
+
+static void rkx110_combrxphy_dsi_power_off(struct rk_serdes *ser, enum comb_phy_id id)
+{
+	struct i2c_client *client = ser->chip[DEVICE_LOCAL].client;
+	u32 grf_base;
+
+	grf_base = id ? RKX110_GRF_MIPI1_BASE : RKX110_GRF_MIPI0_BASE;
+	ser->i2c_write_reg(client, grf_base + GRF_MIPI_RX_CON0,
+			    LANE3_ENABLE(0) | LANE2_ENABLE(0) |
+			    LANE1_ENABLE(0) | LANE0_ENABLE(0));
+}
+
+static void rkx110_combrxphy_lvds_power_on(struct rk_serdes *ser, enum comb_phy_id id)
+{
+	struct i2c_client *client = ser->chip[DEVICE_LOCAL].client;
+	u32 grf_base = id ? RKX110_GRF_MIPI1_BASE : RKX110_GRF_MIPI0_BASE;
+	u32 val;
+	int ret;
+
+	ser->i2c_write_reg(client, grf_base + GRF_MIPI_RX_CON0,
+			   LVDS_RXCK_PD(0) | PHY_MODE(COMBRX_PHY_MODE_VIDEO_LVDS));
+	ser->i2c_write_reg(client, grf_base + GRF_MIPI_RX_CON2,
+			   BUS_WIDTH_SELECTION(7) | RXCK_CTL_2(1) |
+			   RXCK_CTL_1(1) | RXCK_CTL_0(0));
+	ser->i2c_write_reg(client, SER_GRF_SOC_CON2,
+			   id ? LVDS1_MSBSEL(0) : LVDS0_MSBSEL(0));
+	ser->i2c_write_reg(client, grf_base + GRF_MIPI_RX_CON4, 0xffff0f08);
+	ser->i2c_write_reg(client, grf_base + GRF_MIPI_RX_CON5, 0xffff0f08);
+	ser->i2c_write_reg(client, grf_base + GRF_MIPI_RX_CON6, 0xffff0f08);
+	ser->i2c_write_reg(client, grf_base + GRF_MIPI_RX_CON7, 0xffff0f08);
+
+	ret = read_poll_timeout(ser->i2c_read_reg, ret,
+				!(ret < 0) && (val & DLL_LOCK),
+				0, MSEC_PER_SEC, false, client,
+				grf_base + GRF_SOC_STATUS, &val);
+	if (ret < 0)
+		dev_err(ser->dev, "DLL is not locked\n");
+
+	ser->i2c_write_reg(client, grf_base + GRF_MIPI_RX_CON0,
+			   LVDS_RX3_PD(0) | LVDS_RX2_PD(0) |
+			   LVDS_RX1_PD(0) | LVDS_RX0_PD(0));
+}
+
+static void rkx110_combrxphy_lvds_power_off(struct rk_serdes *ser, enum comb_phy_id id)
+{
+	struct i2c_client *client = ser->chip[DEVICE_LOCAL].client;
+	u32 grf_base = id ? RKX110_GRF_MIPI1_BASE : RKX110_GRF_MIPI0_BASE;
+
+	ser->i2c_write_reg(client, grf_base + GRF_MIPI_RX_CON0,
+			   LVDS_RX3_PD(1) | LVDS_RX2_PD(1) |
+			   LVDS_RX1_PD(1) | LVDS_RX0_PD(1));
+}
+
+static void rkx110_combrxphy_lvds_camera_power_on(struct rk_serdes *ser, enum comb_phy_id id)
+{
+}
+
+static void rkx110_combrxphy_lvds_camera_power_off(struct rk_serdes *ser, enum comb_phy_id id)
+{
+}
+
+void rkx110_combrxphy_power_on(struct rk_serdes *ser, enum comb_phy_id id)
+{
+	struct rkx110_combrxphy *combrxphy = &ser->combrxphy;
+
+	switch (combrxphy->mode) {
+	case COMBRX_PHY_MODE_VIDEO_MIPI:
+		rkx110_combrxphy_dsi_power_on(ser, id);
+		break;
+	case COMBRX_PHY_MODE_VIDEO_LVDS:
+		rkx110_combrxphy_lvds_power_on(ser, id);
+		break;
+	case COMBRX_PHY_MODE_LVDS_CAMERA:
+		rkx110_combrxphy_lvds_camera_power_on(ser, id);
+		break;
+	default:
+		break;
+	}
+}
+
+void rkx110_combrxphy_power_off(struct rk_serdes *ser, enum comb_phy_id id)
+{
+	struct rkx110_combrxphy *combrxphy = &ser->combrxphy;
+
+	switch (combrxphy->mode) {
+	case COMBRX_PHY_MODE_VIDEO_MIPI:
+		rkx110_combrxphy_dsi_power_off(ser, id);
+		break;
+	case COMBRX_PHY_MODE_VIDEO_LVDS:
+		rkx110_combrxphy_lvds_power_off(ser, id);
+		break;
+	case COMBRX_PHY_MODE_LVDS_CAMERA:
+		rkx110_combrxphy_lvds_camera_power_off(ser, id);
+		break;
+	default:
+		break;
+	}
+}
+
+void rkx110_combrxphy_set_rate(struct rk_serdes *ser, u64 rate)
+{
+	struct rkx110_combrxphy *combrxphy = &ser->combrxphy;
+
+	combrxphy->rate = rate;
+}
+
+void rkx110_combrxphy_set_mode(struct rk_serdes *ser, enum combrx_phy_mode mode)
+{
+	struct rkx110_combrxphy *combrxphy = &ser->combrxphy;
+
+	combrxphy->mode = mode;
+}
diff --git a/kernel/drivers/mfd/rkx110_x120/rkx110_dsi_rx.c b/kernel/drivers/mfd/rkx110_x120/rkx110_dsi_rx.c
new file mode 100644
index 0000000..8c012ca
--- /dev/null
+++ b/kernel/drivers/mfd/rkx110_x120/rkx110_dsi_rx.c
@@ -0,0 +1,124 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2022 Rockchip Electronics Co. Ltd.
+ *
+ * Author: Guochun Huang <hero.huang@rock-chips.com>
+ */
+
+#include <asm/unaligned.h>
+#include <dt-bindings/mfd/rockchip-serdes.h>
+#include "rkx110_reg.h"
+#include "rkx110_x120.h"
+#include "rkx110_dsi_rx.h"
+#include "serdes_combphy.h"
+
+#define DSI_RX_MIPI_IDX_CTRL0(x)	(0x0100 + x * 8)
+#define SW_COMMAND_MODE_EN		BIT(26)
+#define SW_DT(x)			UPDATE(x, 15, 10)
+#define SW_VC(x)			UPDATE(x, 9, 8)
+#define SW_CAP_EN			BIT(0)
+#define DSI_RX_MIPI_IDX_CTRL1(x)	(0X0104 + x * 8)
+#define SW_HEIGHT(x)			UPDATE(x, 29, 16)
+#define SW_WIDTH(x)			UPDATE(x, 13, 0)
+#define DSI_RX_MIPI_INTEN		0x0174
+#define DSI_RX_MIPI_INTSTAT		0x0178
+#define DSI_RX_MIPI_SIZE_NUM(x)		(0x01c0 + x * 4)
+
+enum {
+	VSYNC_START = 0x01,
+	VSYNC_END = 0x11,
+	HSYNC_START = 0x21,
+	HSYNC_END = 0x31,
+};
+
+static inline int dsi_rx_write(struct rk_serdes *ser, u32 reg, u32 val)
+{
+	struct i2c_client *client = ser->chip[DEVICE_LOCAL].client;
+
+	return ser->i2c_write_reg(client, reg, val);
+}
+
+static inline int dsi_rx_read(struct rk_serdes *ser, u32 reg, u32 *val)
+{
+	struct i2c_client *client = ser->chip[DEVICE_LOCAL].client;
+
+	return ser->i2c_read_reg(client, reg, val);
+}
+
+static inline int dsi_rx_update_bits(struct rk_serdes *ser,
+				     u32 reg, u32 mask, u32 val)
+{
+	struct i2c_client *client = ser->chip[DEVICE_LOCAL].client;
+
+	return ser->i2c_update_bits(client, reg, mask, val);
+}
+
+void rkx110_dsi_rx_enable(struct rk_serdes *ser, struct rk_serdes_route *route, int id)
+{
+	struct rkx110_dsi_rx *dsi = &ser->dsi_rx;
+	const struct videomode *vm = &route->vm;
+	unsigned long pixelclock;
+	u32 hactive, vactive;
+	u64 rate;
+	u32 val = 0;
+	u32 csi_base, dsirx_base;
+
+	switch (route->frame_mode) {
+	case SERDES_SP_PIXEL_INTERLEAVED:
+		fallthrough;
+	case SERDES_SP_LEFT_RIGHT_SPLIT:
+		pixelclock = vm->pixelclock * 2;
+		hactive = vm->hactive * 2;
+		vactive = vm->vactive;
+		break;
+	case SERDES_SP_LINE_INTERLEAVED:
+		pixelclock = vm->pixelclock * 2;
+		vactive = vm->vactive * 2;
+		hactive = vm->hactive;
+		break;
+	case SERDES_FRAME_NORMAL_MODE:
+		fallthrough;
+	default:
+		pixelclock = vm->pixelclock;
+		hactive = vm->hactive;
+		vactive = vm->vactive;
+		break;
+	}
+
+	rate = DIV_ROUND_CLOSEST_ULL(pixelclock, dsi->lanes);
+
+	rkx110_combrxphy_set_mode(ser, COMBRX_PHY_MODE_VIDEO_MIPI);
+	rkx110_combrxphy_set_rate(ser, rate * MSEC_PER_SEC);
+	rkx110_combrxphy_power_on(ser, id ? COMBPHY_1 : COMBPHY_0);
+
+	csi_base = id ? RKX110_CSI2HOST1_BASE : RKX110_CSI2HOST0_BASE;
+	dsirx_base = id ? RKX110_DSI_RX1_BASE : RKX110_DSI_RX0_BASE;
+
+
+	dsi_rx_write(ser, csi_base + CSI2HOST_N_LANES, dsi->lanes - 1);
+	dsi_rx_write(ser, csi_base + CSI2HOST_RESETN, 0);
+
+	val |= SW_DSI_EN | SW_DATETYPE_FE(VSYNC_END) | SW_DATETYPE_FS(VSYNC_START);
+	dsi_rx_update_bits(ser, csi_base + CSI2HOST_CONTROL,
+			   SW_DATETYPE_FE_MASK |
+			   SW_DATETYPE_FS_MASK |
+			   SW_DSI_EN, val);
+
+	dsi_rx_write(ser, csi_base + CSI2HOST_RESETN, 1);
+
+	val = SW_CAP_EN | SW_VC(0);
+	/*
+	 * video mode only support rgb888(0x3e), command mode
+	 * only support DCS Long Write(0x39)
+	 */
+	val |= (dsi->mode_flags & SERDES_MIPI_DSI_MODE_VIDEO) ?
+	       (0 | SW_DT(0x3e)) : (SW_COMMAND_MODE_EN | SW_DT(0x39));
+	dsi_rx_write(ser, dsirx_base + DSI_RX_MIPI_IDX_CTRL0(0), val);
+	dsi_rx_write(ser, dsirx_base + DSI_RX_MIPI_IDX_CTRL1(0),
+		     SW_HEIGHT(vactive) | SW_WIDTH(hactive));
+}
+
+void rkx110_dsi_rx_disable(struct rk_serdes *ser, struct rk_serdes_route *route, int id)
+{
+	rkx110_combrxphy_power_off(ser, id ? COMBPHY_1 : COMBPHY_0);
+}
diff --git a/kernel/drivers/mfd/rkx110_x120/rkx110_dsi_rx.h b/kernel/drivers/mfd/rkx110_x120/rkx110_dsi_rx.h
new file mode 100644
index 0000000..8eaf455
--- /dev/null
+++ b/kernel/drivers/mfd/rkx110_x120/rkx110_dsi_rx.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2022 Rockchip Electronics Co. Ltd.
+ *
+ * Author: Guochun Huang <hero.huang@rock-chips.com>
+ */
+
+#ifndef _RKX110_DSI_RX_H
+#define _RKX110_DSI_RX_H
+
+void rkx110_dsi_rx_enable(struct rk_serdes *ser, struct rk_serdes_route *route, int id);
+void rkx110_dsi_rx_disable(struct rk_serdes *ser, struct rk_serdes_route *route, int id);
+
+#endif
diff --git a/kernel/drivers/mfd/rkx110_x120/rkx110_linktx.c b/kernel/drivers/mfd/rkx110_x120/rkx110_linktx.c
new file mode 100644
index 0000000..007a825
--- /dev/null
+++ b/kernel/drivers/mfd/rkx110_x120/rkx110_linktx.c
@@ -0,0 +1,738 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2022 Rockchip Electronics Co. Ltd.
+ *
+ * Author: Zhang Yubing <yubing.zhang@rock-chips.com>
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/iopoll.h>
+#include <linux/mfd/core.h>
+#include <dt-bindings/mfd/rockchip-serdes.h>
+
+#include "hal/cru_api.h"
+#include "hal/pinctrl_api.h"
+#include "rkx110_x120.h"
+#include "rkx110_reg.h"
+
+#define LINK_REG(x)			((x) + RKX110_SER_RKLINK_BASE)
+
+#define RKLINK_TX_SERDES_CTRL		LINK_REG(0x0000)
+
+#define VIDEO_CH0_EN_MASK		BIT(31)
+#define VIDEO_CH1_EN_MASK		BIT(30)
+#define STOP_AUDIO			BIT(18)
+#define STOP_VIDEO_CH1			BIT(17)
+#define STOP_VIDEO_CH0			BIT(16)
+#define CH1_LVDS_SEL_EN			BIT(15)
+#define TRAIN_CLK_SEL_MASK		GENMASK(14, 13)
+#define TRAIN_CLK_SEL_CH0		UPDATE(0, 14, 13)
+#define TRAIN_CLK_SEL_CH1		UPDATE(1, 14, 13)
+#define TRAIN_CLK_SEL_I2S		UPDATE(2, 14, 13)
+
+#define STREAM_TYPE_MASK		BIT(12)
+
+#define SER_STREAM_CAMERA		UPDATE(0, 12, 12)
+#define SER_STREAM_DISPLAY		UPDATE(1, 12, 12)
+
+#define VIDEO_EN			BIT(11)
+#define SERDES_LANE_SWAP_EN		BIT(10)
+#define SERDES_MIRROR_EN		BIT(9)
+
+#define SER_CH1_EN			BIT(8)
+
+#define CH1_DSOURCE_ID(x)		UPDATE(x, 7, 6)
+#define CH0_DSOURCE_ID(x)		UPDATE(x, 5, 4)
+
+#define SERDES_DATA_WIDTH_MASK		GENMASK(3, 2)
+#define SERDES_DATA_WIDTH_8BIT		UPDATE(0, 3, 2)
+#define SERDES_DATA_WIDTH_16BIT		UPDATE(1, 3, 2)
+#define SERDES_DATA_WIDTH_24BIT		UPDATE(2, 3, 2)
+#define SERDES_DATA_WIDTH_32BIT		UPDATE(3, 3, 2)
+
+#define SERDES_DUAL_LANE_EN		BIT(1)
+#define SER_EN				BIT(0)
+
+#define RKLINK_TX_VIDEO_CTRL		LINK_REG(0x0004)
+#define VIDEO_REPKT_LENGTH(x)		UPDATE(x, 29, 16)
+#define DUAL_LVDS_CYCLE_DIFF(x)		UPDATE(x, 13, 4)
+#define PIXEL_VSYNC_SEL			BIT(3)
+#define SOURCE_ID_MASK			UPDATE(7, 2, 0)
+#define CAMERA_SRC_CSI			UPDATE(0, 2, 0)
+#define CAMERA_SRC_LVDS			UPDATE(1, 2, 0)
+#define CAMERA_SRC_DVP			UPDATE(2, 2, 0)
+#define DISPLAY_SRC_DSI			UPDATE(0, 2, 0)
+#define DISPLAY_SRC_DUAL_LDVS		UPDATE(1, 2, 0)
+#define DISPLAY_SRC_LVDS0		UPDATE(2, 2, 0)
+#define DISPLAY_SRC_LVDS1		UPDATE(3, 2, 0)
+#define DISPLAY_SRC_RGB			UPDATE(5, 2, 0)
+
+#define SER_RKLINK_DSI_REC0(x)		LINK_REG(0x0008 + 0x10 * x)
+ #define DSI_REC_START			BIT(31)
+ #define DSI_CMD_TYPE			BIT(30)
+ #define DSI_HACT(x)			UPDATE(x, 29, 16)
+ #define DSI_VACT(x)			UPDATE(x, 13, 0)
+
+#define SER_RKLINK_DSI_REC1(x)		LINK_REG(0x000C + 0x10 * x)
+ #define DSI_SPLIT_LR_SWAP		BIT(30)
+#define DSI_FRAME_MODE(x)		UPDATE(x, 29, 28)
+ #define DSI_HFP(x)			UPDATE(x, 27, 16)
+ #define DSI_HBP(x)			UPDATE(x, 11, 0)
+
+#define SER_RKLINK_DSI_REC2(x)		LINK_REG(0x0010 + 0x10 * x)
+#define DSI_0_DST_MASK			GENMASK(31, 30)
+#define DSI_0_DST(x)			UPDATE(x, 31, 30)
+ #define DSI_CHANNEL_SWAP		UPDATE(2, 31, 30)
+ #define DSI0_SPLIT_MODE		UPDATE(0, 31, 30)
+ #define DSI1_SPLIT_MODE		UPDATE(3, 31, 30)
+ #define DSI_VFP(x)			UPDATE(x, 29, 20)
+ #define DSI_VBP(x)			UPDATE(x, 19, 10)
+ #define DSI_VSA(x)			UPDATE(x, 9, 0)
+
+#define SER_RKLINK_DSI_REC3(x)		LINK_REG(0x0014 + 0x10 * x)
+ #define DSI_DELAY_LENGTH(x)		UPDATE(x, 31, 12)
+ #define DSI_HSA(x)			UPDATE(x, 11, 0)
+
+#define SER_RKLINK_AUDIO_CRTL		LINK_REG(0x0028)
+#define SER_RKLINK_AUDIO_CTRL		LINK_REG(0x0028)
+#define SER_RKLINK_AUDIO_FDIV		LINK_REG(0x002C)
+#define SER_RKLINK_AUDIO_LRCK		LINK_REG(0x0030)
+#define SER_RKLINK_AUDIO_RECOVER	LINK_REG(0x0034)
+#define SER_RKLINK_AUDIO_FM_STATUS	LINK_REG(0x0038)
+#define SER_RKLINK_FIFO_STATUS		LINK_REG(0x003C)
+#define SER_RKLINK_SOURCE_IRQ_EN	LINK_REG(0x0040)
+
+#define SER_RKLINK_TRAIN_CTRL		LINK_REG(0x0044)
+#define SER_RKLINK_I2C_CFG		LINK_REG(0x00C0)
+#define SER_RKLINK_SPI_CFG		LINK_REG(0x00C4)
+#define SER_RKLINK_UART_CFG		LINK_REG(0x00C8)
+
+#define SER_RKLINK_GPIO_CFG		LINK_REG(0x00CC)
+ #define GPIO_GROUP1_EN			BIT(17)
+ #define GPIO_GROUP0_EN			BIT(16)
+
+#define SER_RKLINK_IO_DEF_INV_CFG	LINK_REG(0x00D0)
+
+#define OTHER_LANE_ACTIVE(x)		HIWORD_UPDATE(x, 12, 12)
+#define FORWARD_NON_ACK(x)		HIWORD_UPDATE(x, 11, 11)
+
+#define SER_RKLINK_DISP_DSI_SPLIT	BIT(1)
+#define SER_RKLINK_DISP_MIRROR		BIT(2)
+#define SER_RKLINK_DISP_DUAL_CHANNEL	BIT(3)
+
+#define PCS_REG(id, x)			((x) + RKX110_SER_PCS0_BASE + (id) * RKX110_SER_PCS_OFFSET)
+
+#define PCS_REG00(id)			PCS_REG(id, 0x00)
+ #define PCS_DUAL_LANE_MODE_EN		HIWORD_UPDATE(1, GENMASK(12, 12), 12)
+ #define PCS_AUTO_START_EN		HIWORD_UPDATE(1, GENMASK(3, 3), 3)
+ #define PCS_EN_MASK			HIWORD_MASK(2, 2)
+ #define PCS_EN				HIWORD_UPDATE(1, GENMASK(2, 2), 2)
+ #define PCS_DISABLE			HIWORD_UPDATE(0, GENMASK(2, 2), 2)
+ #define PCS_ECU_MODE			HIWORD_UPDATE(0, GENMASK(2, 2), 1)
+ #define PCS_REMOTE_MODE		HIWORD_UPDATE(1, GENMASK(2, 2), 1)
+ #define PCS_SAFE_MODE_EN		HIWORD_UPDATE(1, GENMASK(0, 0), 0)
+
+#define PCS_REG04(id)			PCS_REG(id, 0x04)
+#define PCS_REG08(id)			PCS_REG(id, 0x08)
+ #define VIDEO_SUS_EN(x)		HIWORD_UPDATE(x, GENMASK(15, 15), 15)
+#define PCS_REG10(id)			PCS_REG(id, 0x10)
+#define PCS_REG14(id)			PCS_REG(id, 0x14)
+#define PCS_REG18(id)			PCS_REG(id, 0x18)
+#define PCS_REG1C(id)			PCS_REG(id, 0x1C)
+#define PCS_REG20(id)			PCS_REG(id, 0x20)
+#define PCS_REG24(id)			PCS_REG(id, 0x24)
+#define PCS_REG28(id)			PCS_REG(id, 0x28)
+#define PCS_REG30(id)			PCS_REG(id, 0x30)
+#define PCS_REG34(id)			PCS_REG(id, 0x34)
+#define PCS_REG40(id)			PCS_REG(id, 0x40)
+
+#define PMA_REG(id, x)			((x) + RKX110_SER_PMA0_BASE + (id) * RKX110_SER_PMA_OFFSET)
+
+#define SER_PMA_STATUS(id)		PMA_REG(id, 0x00)
+ #define SER_PMA_FORCE_INIT_STA		BIT(8)
+
+#define SER_PMA_CTRL(id)		PMA_REG(id, 0x04)
+ #define SER_PMA_FORCE_INIT_MASK	HIWORD_MASK(8, 8)
+ #define SER_PMA_FORCE_INIT_EN		HIWORD_UPDATE(1, BIT(8), 8)
+ #define SER_PMA_FORCE_INIT_DISABLE	HIWORD_UPDATE(0, BIT(8), 8)
+ #define SER_PMA_DUAL_CHANNEL		HIWORD_UPDATE(1, BIT(3), 3)
+ #define SER_PMA_INIT_CNT_CLR_MASK	HIWORD_MASK(2, 2)
+ #define SER_PMA_INIT_CNT_CLR		HIWORD_UPDATE(1, BIT(2), 2)
+
+#define SER_PMA_LOAD00(id)		PMA_REG(id, 0x10)
+ #define PMA_RATE_MODE_MASK		HIWORD_MASK(2, 0)
+ #define PMA_FDR_MODE			HIWORD_UPDATE(1, GENMASK(2, 0), 0)
+ #define PMA_HDR_MODE			HIWORD_UPDATE(2, GENMASK(2, 0), 0)
+ #define PMA_QDR_MODE			HIWORD_UPDATE(4, GENMASK(2, 0), 0)
+
+#define SER_PMA_LOAD01(id)		PMA_REG(id, 0x14)
+#define SER_PMA_LOAD02(id)		PMA_REG(id, 0x18)
+#define SER_PMA_LOAD03(id)		PMA_REG(id, 0x1C)
+#define SER_PMA_LOAD04(id)		PMA_REG(id, 0x20)
+ #define PMA_PLL_DIV_MASK		HIWORD_MASK(14, 0)
+ #define PLL_I_POST_DIV(x)		HIWORD_UPDATE(x, GENMASK(14, 10), 10)
+ #define PLL_F_POST_DIV(x)		HIWORD_UPDATE(x, GENMASK(9, 0), 0)
+ #define PMA_PLL_DIV(x)			HIWORD_UPDATE(x, GENMASK(14, 0), 0)
+
+#define SER_PMA_LOAD05(id)		PMA_REG(id, 0x2C)
+ #define PMA_PLL_REFCLK_DIV_MASK	HIWORD_MASK(3, 0)
+ #define PMA_PLL_REFCLK_DIV(x)		HIWORD_UPDATE(x, GENMASK(3, 0), 0)
+
+#define SER_PMA_LOAD06(id)		PMA_REG(id, 0x28)
+#define SER_PMA_LOAD07(id)		PMA_REG(id, 0x2C)
+#define SER_PMA_LOAD08(id)		PMA_REG(id, 0x30)
+ #define PMA_FCK_VCO_MASK		HIWORD_MASK(15, 15)
+ #define PMA_FCK_VCO			HIWORD_UPDATE(1, BIT(15), 15)
+ #define PMA_FCK_VCO_DIV2		HIWORD_UPDATE(0, BIT(15), 15)
+
+#define SER_PMA_LOAD09(id)		PMA_REG(id, 0x34)
+ #define PMA_PLL_DIV4_MASK		HIWORD_MASK(12, 12)
+ #define PMA_PLL_DIV4			HIWORD_UPDATE(1, GENMASK(12, 12), 12)
+ #define PMA_PLL_DIV8			HIWORD_UPDATE(0, GENMASK(12, 12), 12)
+
+#define SER_PMA_LOAD0A(id)		PMA_REG(id, 0x38)
+ #define PMA_CLK_8X_DIV_MASK		HIWORD_MASK(7, 1)
+ #define PMA_CLK_8X_DIV(x)		HIWORD_UPDATE(x, GENMASK(7, 1), 1)
+
+enum {
+	SER_LINK_CH_ID0 = 0,
+	SER_LINK_CH_ID1,
+	SER_LINK_CH_ID2,
+	SER_LINK_CH_ID3,
+};
+
+static const struct rk_serdes_pt ser_pt[] = {
+	{
+		/* gpi_gpo_0 */
+		.en_reg = SER_RKLINK_GPIO_CFG,
+		.en_mask = 0x10001,
+		.en_val = 0x10001,
+		.dir_reg = SER_RKLINK_GPIO_CFG,
+		.dir_mask = 0x10,
+		.dir_val = 0x10,
+		.configs = 1,
+		{
+			{
+				.bank = RK_SERDES_SER_GPIO_BANK0,
+				.pin = RK_SERDES_GPIO_PIN_A5,
+				.incfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC6,
+				.outcfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC5,
+			},
+		},
+	}, {
+		/* gpi_gpo_1 */
+		.en_reg = SER_RKLINK_GPIO_CFG,
+		.en_mask = 0x10002,
+		.en_val = 0x10002,
+		.dir_reg = SER_RKLINK_GPIO_CFG,
+		.dir_mask = 0x20,
+		.dir_val = 0x20,
+		.configs = 1,
+		{
+			{
+				.bank = RK_SERDES_SER_GPIO_BANK0,
+				.pin = RK_SERDES_GPIO_PIN_A6,
+				.incfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC6,
+				.outcfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC5,
+			},
+		},
+	}, {
+		/* gpi_gpo_2 */
+		.en_reg = SER_RKLINK_GPIO_CFG,
+		.en_mask = 0x10004,
+		.en_val = 0x10004,
+		.dir_reg = SER_RKLINK_GPIO_CFG,
+		.dir_mask = 0x40,
+		.dir_val = 0x40,
+		.configs = 1,
+		{
+			{
+				.bank = RK_SERDES_SER_GPIO_BANK0,
+				.pin = RK_SERDES_GPIO_PIN_A7,
+				.incfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC6,
+				.outcfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC5,
+			},
+		},
+	}, {
+		/* gpi_gpo_3 */
+		.en_reg = SER_RKLINK_GPIO_CFG,
+		.en_mask = 0x10008,
+		.en_val = 0x10008,
+		.dir_reg = SER_RKLINK_GPIO_CFG,
+		.dir_mask = 0x80,
+		.dir_val = 0x80,
+		.configs = 1,
+		{
+			{
+				.bank = RK_SERDES_SER_GPIO_BANK0,
+				.pin = RK_SERDES_GPIO_PIN_B0,
+				.incfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC6,
+				.outcfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC5,
+			},
+		},
+	}, {
+		/* gpi_gpo_4 */
+		.en_reg = SER_RKLINK_GPIO_CFG,
+		.en_mask = 0x20100,
+		.en_val = 0x20100,
+		.dir_reg = SER_RKLINK_GPIO_CFG,
+		.dir_mask = 0x1000,
+		.dir_val = 0x1000,
+		.configs = 1,
+		{
+			{
+				.bank = RK_SERDES_SER_GPIO_BANK0,
+				.pin = RK_SERDES_GPIO_PIN_B3,
+				.incfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC2,
+				.outcfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC3,
+			},
+		},
+	}, {
+		/* gpi_gpo_5 */
+		.en_reg = SER_RKLINK_GPIO_CFG,
+		.en_mask = 0x20200,
+		.en_val = 0x20200,
+		.dir_reg = SER_RKLINK_GPIO_CFG,
+		.dir_mask = 0x2000,
+		.dir_val = 0x2000,
+		.configs = 1,
+		{
+			{
+				.bank = RK_SERDES_SER_GPIO_BANK0,
+				.pin = RK_SERDES_GPIO_PIN_B4,
+				.incfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC2,
+				.outcfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC3,
+			},
+		},
+	}, {
+		/* gpi_gpo_6 */
+		.en_reg = SER_RKLINK_GPIO_CFG,
+		.en_mask = 0x20400,
+		.en_val = 0x20400,
+		.dir_reg = SER_RKLINK_GPIO_CFG,
+		.dir_mask = 0x4000,
+		.dir_val = 0x4000,
+		.configs = 1,
+		{
+			{
+				.bank = RK_SERDES_SER_GPIO_BANK0,
+				.pin = RK_SERDES_GPIO_PIN_B5,
+				.incfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC2,
+				.outcfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC3,
+			},
+		},
+	}, {
+		/* passthrough irq */
+		.en_reg = SER_RKLINK_GPIO_CFG,
+		.en_mask = 0x20800,
+		.en_val = 0x20800,
+		.dir_reg = SER_RKLINK_GPIO_CFG,
+		.dir_mask = 0x8000,
+		.dir_val = 0x8000,
+		.configs = 1,
+		{
+			{
+				.bank = RK_SERDES_SER_GPIO_BANK0,
+				.pin = RK_SERDES_GPIO_PIN_A4,
+				.incfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC1,
+				.outcfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC2,
+			},
+		},
+	}, {
+		/* passthrough uart0 */
+		.en_reg = SER_RKLINK_UART_CFG,
+		.en_mask = 0x1,
+		.en_val = 0x1,
+		.configs = 1,
+		{
+			{
+				.bank = RK_SERDES_SER_GPIO_BANK0,
+				.pin = RK_SERDES_GPIO_PIN_A5 | RK_SERDES_GPIO_PIN_A6,
+				.incfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC4,
+				.outcfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC3,
+			},
+		},
+	}, {
+		/* passthrough uart1 */
+		.en_reg = SER_RKLINK_UART_CFG,
+		.en_mask = 0x2,
+		.en_val = 0x2,
+		.configs = 1,
+		{
+			{
+				.bank = RK_SERDES_SER_GPIO_BANK0,
+				.pin = RK_SERDES_GPIO_PIN_A7 | RK_SERDES_GPIO_PIN_B0,
+				.incfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC4,
+				.outcfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC3,
+			},
+		},
+	}, {
+		/* passthrough spi */
+		.en_reg = SER_RKLINK_SPI_CFG,
+		.en_mask = 0x4,
+		.en_val = 0x4,
+		.dir_reg = SER_RKLINK_SPI_CFG,
+		.dir_mask = 0x1,
+		.dir_val = 0x0,
+		.configs = 1,
+		{
+			{
+				.bank = RK_SERDES_SER_GPIO_BANK0,
+				.pin = RK_SERDES_GPIO_PIN_A5 | RK_SERDES_GPIO_PIN_A6 |
+				       RK_SERDES_GPIO_PIN_A7 | RK_SERDES_GPIO_PIN_B0,
+				.incfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC2,
+				.outcfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC1,
+			},
+		},
+	},
+};
+
+static int rk_serdes_get_stream_source(struct rk_serdes *serdes, int local_port)
+{
+	if (serdes->stream_type == STREAM_DISPLAY) {
+		if (local_port & RK_SERDES_RGB_RX)
+			return DISPLAY_SRC_RGB;
+		else if (local_port & RK_SERDES_LVDS_RX0)
+			return DISPLAY_SRC_LVDS0;
+		else if (local_port & RK_SERDES_LVDS_RX1)
+			return DISPLAY_SRC_LVDS1;
+		else if (local_port & RK_SERDES_DUAL_LVDS_RX)
+			return DISPLAY_SRC_DUAL_LDVS;
+		else if (local_port & RK_SERDES_DSI_RX0)
+			return DISPLAY_SRC_DSI;
+		else if (local_port & RK_SERDES_DSI_RX1)
+			return DISPLAY_SRC_DSI;
+	} else {
+		return CAMERA_SRC_CSI;
+	}
+
+	return 0;
+}
+
+void rkx110_set_stream_source(struct rk_serdes *serdes, int local_port, u8 dev_id)
+{
+	struct i2c_client *client = serdes->chip[dev_id].client;
+	u32 val, rx_src;
+
+	serdes->i2c_read_reg(client, RKLINK_TX_VIDEO_CTRL, &val);
+	val &= ~SOURCE_ID_MASK;
+	rx_src = rk_serdes_get_stream_source(serdes, local_port);
+	val |= rx_src;
+	serdes->i2c_write_reg(client, RKLINK_TX_VIDEO_CTRL, val);
+}
+
+static int rk_serdes_link_tx_ctrl_enable(struct rk_serdes *serdes,
+					 struct rk_serdes_route *route,
+					 u8 remote_id)
+{
+	struct hwclk *hwclk = serdes->chip[remote_id].hwclk;
+	struct i2c_client *client;
+	u32 ctrl_val, val;
+	u32 rx_src;
+	u32 stream_type;
+
+	if (route->stream_type == STREAM_DISPLAY) {
+		client = serdes->chip[DEVICE_LOCAL].client;
+		stream_type = SER_STREAM_DISPLAY;
+	} else {
+		client = serdes->chip[remote_id].client;
+		stream_type = SER_STREAM_CAMERA;
+	}
+
+	serdes->i2c_read_reg(client, RKLINK_TX_SERDES_CTRL, &ctrl_val);
+
+	ctrl_val &= ~(SER_EN | SERDES_DUAL_LANE_EN | SER_CH1_EN | SERDES_MIRROR_EN |
+		      CH1_LVDS_SEL_EN);
+	ctrl_val |= stream_type;
+	if (serdes->route_flag & ROUTE_MULTI_LANE)
+		ctrl_val |= SERDES_DUAL_LANE_EN;
+	if (serdes->route_flag & ROUTE_MULTI_CHANNEL)
+		ctrl_val |= SER_CH1_EN;
+	if (serdes->route_flag & ROUTE_MULTI_MIRROR)
+		ctrl_val |= SERDES_MIRROR_EN;
+	if (serdes->route_flag & ROUTE_MULTI_LVDS_INPUT)
+		ctrl_val |= CH1_LVDS_SEL_EN;
+
+	serdes->i2c_write_reg(client, RKLINK_TX_SERDES_CTRL, ctrl_val);
+
+	serdes->i2c_read_reg(client, RKLINK_TX_VIDEO_CTRL, &val);
+	rx_src = rk_serdes_get_stream_source(serdes, route->local_port0);
+	val |= rx_src;
+	serdes->i2c_write_reg(client, RKLINK_TX_VIDEO_CTRL, val);
+
+	if (route->local_port0 & RK_SERDES_DUAL_LVDS_RX) {
+		hwclk_set_rate(hwclk, RKX110_CPS_CLK_2X_LVDS_RKLINK_TX, route->vm.pixelclock);
+		dev_info(serdes->dev, "RKX110_CPS_CLK_2X_LVDS_RKLINK_TX:%d\n",
+			 hwclk_get_rate(hwclk, RKX110_CPS_CLK_2X_LVDS_RKLINK_TX));
+	}
+
+	ctrl_val |= SER_EN;
+	serdes->i2c_write_reg(client, RKLINK_TX_SERDES_CTRL, ctrl_val);
+
+	return 0;
+}
+
+static int rk_serdes_link_tx_dsi_enable(struct rk_serdes *serdes, struct rk_serdes_route *route,
+					int id)
+{
+	struct videomode *vm = &route->vm;
+	struct i2c_client *client = serdes->chip[DEVICE_LOCAL].client;
+	struct hwclk *hwclk = serdes->chip[DEVICE_LOCAL].hwclk;
+	int delay_length;
+	u32 value, type;
+
+	if (id == 0) {
+		hwclk_set_rate(hwclk, RKX110_CPS_DCLK_D_DSI_0_REC_RKLINK_TX,
+			       route->vm.pixelclock);
+		dev_info(serdes->dev, "RKX110_CPS_DCLK_D_DSI_0_REC_RKLINK_TX:%d\n",
+			 hwclk_get_rate(hwclk, RKX110_CPS_DCLK_D_DSI_0_REC_RKLINK_TX));
+	} else if (id == 1) {
+		hwclk_set_rate(hwclk, RKX110_CPS_DCLK_D_DSI_1_REC_RKLINK_TX,
+			       route->vm.pixelclock);
+		dev_info(serdes->dev, "RKX110_CPS_DCLK_D_DSI_1_REC_RKLINK_TX:%d\n",
+			 hwclk_get_rate(hwclk, RKX110_CPS_DCLK_D_DSI_1_REC_RKLINK_TX));
+
+	} else {
+		return 0;
+	}
+
+	/* config SER_RKLINK_DSI_REC1 */
+	value = DSI_FRAME_MODE(route->frame_mode);
+	value |= DSI_HFP(vm->hfront_porch);
+	value |= DSI_HBP(vm->hback_porch);
+	serdes->i2c_write_reg(client, SER_RKLINK_DSI_REC1(id), value);
+
+	/* config SER_RKLINK_DSI_REC2 */
+	value = DSI_VFP(vm->vfront_porch);
+	value |= DSI_VBP(vm->vback_porch);
+	value |= DSI_VSA(vm->vsync_len);
+	serdes->i2c_write_reg(client, SER_RKLINK_DSI_REC2(id), value);
+
+	if (id)
+		type = (serdes->route_flag & ROUTE_MULTI_CHANNEL) ? DSI_0_DST(0) : DSI_0_DST(2);
+	else
+		type = (serdes->route_flag & ROUTE_MULTI_CHANNEL) ? DSI_0_DST(3) : DSI_0_DST(1);
+
+	serdes->i2c_update_bits(client, SER_RKLINK_DSI_REC2(0), DSI_0_DST_MASK, type);
+
+	/* config SER_RKLINK_DSI_REC3 */
+	if (serdes->dsi_rx.mode_flags & SERDES_MIPI_DSI_MODE_VIDEO)
+		delay_length = vm->hsync_len + vm->hback_porch +
+			       vm->hactive + vm->hfront_porch;
+	else
+		delay_length = (vm->vfront_porch + 1) * (vm->hsync_len +
+				vm->hback_porch + vm->hactive + vm->hfront_porch);
+
+	value = DSI_DELAY_LENGTH(delay_length);
+	value |= DSI_HSA(vm->hsync_len);
+
+	serdes->i2c_write_reg(client, SER_RKLINK_DSI_REC3(id), value);
+
+	/* config SER_RKLINK_DSI_REC0 */
+	value = DSI_REC_START;
+	if (!(serdes->dsi_rx.mode_flags & SERDES_MIPI_DSI_MODE_VIDEO))
+		value |= DSI_CMD_TYPE;
+
+	value |= DSI_HACT(vm->hactive);
+	value |= DSI_VACT(vm->vactive);
+	serdes->i2c_write_reg(client, SER_RKLINK_DSI_REC0(id), value);
+
+	return 0;
+}
+
+static int rk110_linktx_cfg(struct rk_serdes *serdes, struct rk_serdes_route *route)
+{
+	u8 remote_id = 0;
+
+	rk_serdes_link_tx_ctrl_enable(serdes, route, remote_id);
+
+	if (route->local_port0 & RK_SERDES_DSI_RX0) {
+		rk_serdes_link_tx_dsi_enable(serdes, route, 0);
+		if (serdes->route_flag & ROUTE_MULTI_DSI_INPUT)
+			rk_serdes_link_tx_dsi_enable(serdes, route, 1);
+	}
+
+	if (route->local_port0 & RK_SERDES_DSI_RX1) {
+		rk_serdes_link_tx_dsi_enable(serdes, route, 1);
+		if (serdes->route_flag & ROUTE_MULTI_DSI_INPUT)
+			rk_serdes_link_tx_dsi_enable(serdes, route, 0);
+	}
+
+	return 0;
+}
+
+static int rk110_ser_pcs_cfg(struct rk_serdes *serdes, struct rk_serdes_route *route, u8 pcs_id)
+{
+	struct i2c_client *client;
+	u8 remote_id = 0;
+
+	if (route->stream_type == STREAM_DISPLAY)
+		client = serdes->chip[DEVICE_LOCAL].client;
+	else
+		client = serdes->chip[remote_id].client;
+
+	if (serdes->version == SERDES_V1)
+		serdes->i2c_write_reg(client, PCS_REG08(pcs_id), VIDEO_SUS_EN(0));
+
+	return 0;
+}
+
+static int rk110_ser_pma_cfg(struct rk_serdes *serdes, struct rk_serdes_route *route, u8 pcs_id)
+{
+	return 0;
+}
+
+int rkx110_linktx_enable(struct rk_serdes *serdes, struct rk_serdes_route *route)
+{
+	rk110_linktx_cfg(serdes, route);
+	rk110_ser_pcs_cfg(serdes, route, 0);
+	rk110_ser_pma_cfg(serdes, route, 0);
+	if (serdes->route_flag & ROUTE_MULTI_LANE) {
+		rk110_ser_pcs_cfg(serdes, route, 1);
+		rk110_ser_pma_cfg(serdes, route, 1);
+	}
+
+	return 0;
+}
+
+void rkx110_linktx_video_enable(struct rk_serdes *serdes, u8 dev_id, bool enable)
+{
+	struct i2c_client *client = serdes->chip[dev_id].client;
+
+	serdes->i2c_update_bits(client, RKLINK_TX_SERDES_CTRL, VIDEO_EN, enable ? VIDEO_EN : 0);
+}
+
+void rkx110_linktx_channel_enable(struct rk_serdes *serdes, u8 ch_id, u8 dev_id, bool enable)
+{
+	struct i2c_client *client = serdes->chip[dev_id].client;
+
+	if (ch_id)
+		serdes->i2c_update_bits(client, RKLINK_TX_SERDES_CTRL, STOP_VIDEO_CH1,
+					enable ? 0 : STOP_VIDEO_CH1);
+	else
+		serdes->i2c_update_bits(client, RKLINK_TX_SERDES_CTRL, STOP_VIDEO_CH0,
+					enable ? 0 : STOP_VIDEO_CH0);
+}
+
+void rkx110_linktx_passthrough_cfg(struct rk_serdes *serdes, u32 client_id, u32 func_id,
+				   bool is_rx)
+{
+	struct i2c_client *client = serdes->chip[client_id].client;
+	const struct rk_serdes_pt_pin *pt_pin = ser_pt[func_id].pt_pins;
+	int i;
+
+	/* config link passthrough */
+	serdes->i2c_update_bits(client, ser_pt[func_id].en_reg, ser_pt[func_id].en_mask,
+				ser_pt[func_id].en_val);
+	if (ser_pt[func_id].en_reg)
+		serdes->i2c_update_bits(client, ser_pt[func_id].dir_reg, ser_pt[func_id].dir_mask,
+					is_rx ? ser_pt[func_id].dir_val : ~ser_pt[func_id].dir_val);
+
+	/* config passthrough pinctrl */
+	for (i = 0; i < ser_pt[func_id].configs; i++) {
+		serdes->set_hwpin(serdes, client, PIN_RKX110, pt_pin[i].bank, pt_pin[i].pin,
+				  is_rx ? pt_pin[i].incfgs : pt_pin[i].outcfgs);
+	}
+}
+
+void rkx110_linktx_wait_link_ready(struct rk_serdes *serdes, u8 id)
+{
+	struct i2c_client *client = serdes->chip[DEVICE_LOCAL].client;
+	u32 val;
+	int ret;
+	int sta;
+
+	if (id)
+		sta = SER_PCS1_READY;
+	else
+		sta = SER_PCS0_READY;
+
+	ret = read_poll_timeout(serdes->i2c_read_reg, ret,
+				!(ret < 0) && (val & sta),
+				1000, USEC_PER_SEC, false, client,
+				SER_GRF_SOC_STATUS0, &val);
+	if (ret < 0)
+		dev_err(&client->dev, "wait link ready timeout: 0x%08x\n", val);
+	else
+		dev_info(&client->dev, "link success: 0x%08x\n", val);
+}
+
+void rkx110_pma_set_rate(struct rk_serdes *serdes, struct rk_serdes_pma_pll *pll,
+			 u8 pcs_id, u8 dev_id)
+{
+	struct i2c_client *client = serdes->chip[dev_id].client;
+	u32 val;
+
+	serdes->i2c_read_reg(client, SER_PMA_STATUS(pcs_id), &val);
+	if (val & SER_PMA_FORCE_INIT_STA)
+		serdes->i2c_update_bits(client, SER_PMA_CTRL(pcs_id), SER_PMA_INIT_CNT_CLR_MASK,
+					SER_PMA_INIT_CNT_CLR);
+
+	if (pll->force_init_en)
+		serdes->i2c_update_bits(client, SER_PMA_CTRL(pcs_id), SER_PMA_FORCE_INIT_MASK,
+					SER_PMA_FORCE_INIT_EN);
+	else
+		serdes->i2c_update_bits(client, SER_PMA_CTRL(pcs_id), SER_PMA_FORCE_INIT_MASK,
+					SER_PMA_FORCE_INIT_DISABLE);
+
+	if (pll->rate_mode == FDR_RATE_MODE)
+		serdes->i2c_update_bits(client, SER_PMA_LOAD00(pcs_id), PMA_RATE_MODE_MASK,
+					PMA_FDR_MODE);
+	else if (pll->rate_mode == HDR_RATE_MODE)
+		serdes->i2c_update_bits(client, SER_PMA_LOAD00(pcs_id), PMA_RATE_MODE_MASK,
+					PMA_HDR_MODE);
+	else
+		serdes->i2c_update_bits(client, SER_PMA_LOAD00(pcs_id), PMA_RATE_MODE_MASK,
+					PMA_QDR_MODE);
+
+	serdes->i2c_update_bits(client, SER_PMA_LOAD04(pcs_id), PMA_PLL_DIV_MASK,
+				PMA_PLL_DIV(pll->pll_div));
+	serdes->i2c_update_bits(client, SER_PMA_LOAD05(pcs_id), PMA_PLL_REFCLK_DIV_MASK,
+				PMA_PLL_REFCLK_DIV(pll->pll_refclk_div));
+
+	if (pll->pll_fck_vco_div2)
+		serdes->i2c_update_bits(client, SER_PMA_LOAD08(pcs_id), PMA_FCK_VCO_MASK,
+					PMA_FCK_VCO_DIV2);
+	else
+		serdes->i2c_update_bits(client, SER_PMA_LOAD08(pcs_id), PMA_FCK_VCO_MASK,
+					PMA_FCK_VCO);
+
+	if (pll->pll_div4)
+		serdes->i2c_update_bits(client, SER_PMA_LOAD09(pcs_id), PMA_PLL_DIV4_MASK,
+					PMA_PLL_DIV4);
+	else
+		serdes->i2c_update_bits(client, SER_PMA_LOAD09(pcs_id), PMA_PLL_DIV4_MASK,
+					PMA_PLL_DIV8);
+
+	serdes->i2c_update_bits(client, SER_PMA_LOAD0A(pcs_id), PMA_CLK_8X_DIV_MASK,
+				PMA_CLK_8X_DIV(pll->clk_div));
+}
+
+void rkx110_pcs_enable(struct rk_serdes *serdes, bool enable, u8 pcs_id, u8 dev_id)
+{
+	struct i2c_client *client = serdes->chip[dev_id].client;
+
+	dev_info(serdes->dev, "%s: %d\n", __func__, enable);
+
+	if (enable)
+		serdes->i2c_update_bits(client, PCS_REG00(pcs_id), PCS_EN_MASK, PCS_EN);
+	else
+		serdes->i2c_update_bits(client, PCS_REG00(pcs_id), PCS_EN_MASK, PCS_DISABLE);
+}
+
+void rkx110_ser_pma_enable(struct rk_serdes *serdes, bool enable, u8 pma_id, u8 dev_id)
+{
+	struct i2c_client *client = serdes->chip[dev_id].client;
+	u32 mask, val;
+
+	if (pma_id) {
+		mask = PMA1_EN_MASK;
+		val = enable ? PMA1_EN : PMA1_DISABLE;
+	} else {
+		mask = PMA0_EN_MASK;
+		val = enable ? PMA0_EN : PMA0_DISABLE;
+	}
+
+	serdes->i2c_update_bits(client, SER_GRF_SOC_CON7, mask, val);
+}
diff --git a/kernel/drivers/mfd/rkx110_x120/rkx110_reg.h b/kernel/drivers/mfd/rkx110_x120/rkx110_reg.h
new file mode 100644
index 0000000..4d6db3d
--- /dev/null
+++ b/kernel/drivers/mfd/rkx110_x120/rkx110_reg.h
@@ -0,0 +1,515 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2022 Rockchip Electronics Co. Ltd.
+ *
+ * Author: Zhang Yubing <yubing.zhang@rock-chips.com>
+ */
+
+#ifndef _RKX110_REG_H
+#define _RKX110_REG_H
+
+#include <linux/bits.h>
+
+#define HIWORD_MASK(h, l)	(GENMASK(h, l) | GENMASK(h, l) << 16)
+#define UPDATE(x, h, l)		(((x) << (l)) & GENMASK((h), (l)))
+#define HIWORD_UPDATE(v, m, l)	(((v) << (l)) | (m << 16))
+
+/************** RKX110 SER TX ***************/
+#define RKX110_SER_CRU_BASE		0x00000000
+
+#define RKX110_SER_GRF_BASE		0x00010000
+#define GRF_REG(x)			 ((x) + RKX110_SER_GRF_BASE)
+#define SER_GRF_GPIO0A_IOMUX_L		GRF_REG(0x0)
+#define SER_GRF_GPIO0A_IOMUX_H		GRF_REG(0x4)
+#define SER_GRF_GPIO0B_IOMUX_L		GRF_REG(0x8)
+#define SER_GRF_GPIO0B_IOMUX_H		GRF_REG(0xC)
+#define SER_GRF_GPIO0C_IOMUX_L		GRF_REG(0x10)
+#define SER_GRF_GPIO0C_IOMUX_H		GRF_REG(0x14)
+#define SER_GRF_GPIO0A_PULL_EN		GRF_REG(0x20)
+#define SER_GRF_GPIO0B_PULL_EN		GRF_REG(0x24)
+#define SER_GRF_GPIO0C_PULL_EN		GRF_REG(0x28)
+#define SER_GRF_GPIO1A_IOMUX		GRF_REG(0x80)
+#define SER_GRF_GPIO1B_IOMUX		GRF_REG(0x84)
+#define SER_GRF_GPIO1C_IOMUX		GRF_REG(0x88)
+#define SER_GRF_GPIO1A_PULL_CFG		GRF_REG(0x90)
+#define SER_GRF_GPIO1B_PULL_CFG		GRF_REG(0x94)
+#define SER_GRF_GPIO1C_PULL_CFG		GRF_REG(0x98)
+
+enum {
+	/* GPIO0A_IOMUX_H */
+	GPIO0A7_SHIFT	= 12,
+	GPIO0A7_MASK	= GENMASK(14, 12),
+	GPIO0A7_GPIO	= 0,
+	GPIO0A7_SPI_MISO_M,
+	GPIO0A7_SPI_MISO_S,
+	GPIO0A7_UART1_TX_M,
+	GPIO0A7_UART1_TX_S,
+	GPIO0A7_GPO_2,
+	GPIO0A7_GPI_2,
+	GPIO0A7_TP15,
+
+	GPIO0A6_SHIFT	= 8,
+	GPIO0A6_MASK	= GENMASK(10, 8),
+	GPIO0A6_GPIO	= 0,
+	GPIO0A6_SPI_MOSI_M,
+	GPIO0A6_SPI_MOSI_S,
+	GPIO0A6_UART0_RX_M,
+	GPIO0A6_UART0_RX_S,
+	GPIO0A6_GPO_1,
+	GPIO0A6_GPI_1,
+	GPIO0A6_I2C_DEBUG_SDA,
+
+	GPIO0A5_SHIFT	= 4,
+	GPIO0A5_MASK	= GENMASK(6, 4),
+	GPIO0A5_GPIO	= 0,
+	GPIO0A5_SPI_CLK_M,
+	GPIO0A5_SPI_CLK_S,
+	GPIO0A5_UART0_TX_M,
+	GPIO0A5_UART0_TX_S,
+	GPIO0A5_GPO_0,
+	GPIO0A5_GPI_0,
+	GPIO0A5_I2C_DEBUG_SCL,
+
+	GPIO0A4_SHIFT	= 0,
+	GPIO0A4_MASK	= GENMASK(2, 0),
+	GPIO0A4_GPIO	= 0,
+	GPIO0A4_INT_RX,
+	GPIO0A4_INT_TX,
+
+	/* GPIO0B_IOMUX_L */
+	GPIO0B3_SHIFT	= 12,
+	GPIO0B3_MASK	= GENMASK(14, 12),
+	GPIO0B3_GPIO	= 0,
+	GPIO0B3_I2S_SDI0,
+	GPIO0B3_GPI_4,
+	GPIO0B3_GPO_4,
+	GPIO0B3_TP2,
+
+	GPIO0B2_SHIFT	= 8,
+	GPIO0B2_MASK	= GENMASK(10, 8),
+	GPIO0B2_GPIO	= 0,
+	GPIO0B2_I2S_LRCK_M,
+	GPIO0B2_I2S_LRCK_S,
+	GPIO0B2_TP1,
+
+	GPIO0B1_SHIFT	= 4,
+	GPIO0B1_MASK	= GENMASK(6, 4),
+	GPIO0B1_GPIO	= 0,
+	GPIO0B1_I2S_SCLK_M,
+	GPIO0B1_I2S_SCLK_S,
+	GPIO0B1_TP0,
+
+	GPIO0B0_SHIFT	= 0,
+	GPIO0B0_MASK	= GENMASK(2, 0),
+	GPIO0B0_GPIO	= 0,
+	GPIO0B0_SPI_CSN_M,
+	GPIO0B0_SPI_CSN_S,
+	GPIO0B0_UART1_RX_M,
+	GPIO0B0_UART1_RX_S,
+	GPIO0B0_GPO_3,
+	GPIO0B0_GPI_3,
+	GPIO0B0_TP16,
+
+	/* GPIO0B_IOMUX_H */
+	GPIO0B7_SHIFT	= 12,
+	GPIO0B7_MASK	= GENMASK(14, 12),
+	GPIO0B7_GPIO	= 0,
+	GPIO0B7_I2S_MCLK,
+	GPIO0B7_TEST_CLK_OUT,
+	GPIO0B7_MIPI_MCLK0,
+	GPIO0B7_TP6,
+
+	GPIO0B6_SHIFT	= 8,
+	GPIO0B6_MASK	= GENMASK(10, 8),
+	GPIO0B6_GPIO	= 0,
+	GPIO0B6_I2S_SDI3,
+	GPIO0B6_I2S_SDO0,
+	GPIO0B6_TP5,
+
+	GPIO0B5_SHIFT	= 4,
+	GPIO0B5_MASK	= GENMASK(6, 4),
+	GPIO0B5_GPIO	= 0,
+	GPIO0B5_I2S_SDI2,
+	GPIO0B5_GPI_6,
+	GPIO0B5_GPO_6,
+	GPIO0B5_I2C1_SDA_M,
+	GPIO0B5_I2C1_SDA_S,
+	GPIO0B5_TP4,
+
+	GPIO0B4_SHIFT	= 0,
+	GPIO0B4_MASK	= GENMASK(2, 0),
+	GPIO0B4_GPIO	= 0,
+	GPIO0B4_I2S_SDI1,
+	GPIO0B4_GPI_5,
+	GPIO0B4_GPO_5,
+	GPIO0B5_I2C1_SCL_M,
+	GPIO0B5_I2C1_SCL_S,
+	GPIO0B5_TP3,
+
+	/* GPIO0C_IOMUX_L */
+	GPIO0C4_SHIFT	= 12,
+	GPIO0C4_MASK	= GENMASK(14, 12),
+	GPIO0C4_GPIO	= 0,
+	GPIO0C4_LCDC_D0,
+	GPIO0C4_CIF_D0,
+	GPIO0C4_TP11,
+
+	GPIO0C3_SHIFT	= 9,
+	GPIO0C3_MASK	= GENMASK(11, 9),
+	GPIO0C3_GPIO	= 0,
+	GPIO0C3_LCDC_DEN,
+	GPIO0C3_CIF_CLK_OUT,
+	GPIO0C3_MIPI_CLK1,
+	GPIO0C3_TP10,
+
+	GPIO0C2_SHIFT	= 6,
+	GPIO0C2_MASK	= GENMASK(8, 6),
+	GPIO0C2_GPIO	= 0,
+	GPIO0C2_LCDC_HSYNC,
+	GPIO0C2_CIF_HSYNC,
+	GPIO0C2_TP9,
+
+	GPIO0C1_SHIFT	= 3,
+	GPIO0C1_MASK	= GENMASK(5, 3),
+	GPIO0C1_GPIO	= 0,
+	GPIO0C1_LCDC_VSYNC,
+	GPIO0C1_CIF_VSYNC,
+	GPIO0C1_TP8,
+
+	GPIO0C0_SHIFT	= 0,
+	GPIO0C0_MASK	= GENMASK(2, 0),
+	GPIO0C0_GPIO	= 0,
+	GPIO0C0_LCDC_CLK,
+	GPIO0C0_CIF_CLK,
+	GPIO0C0_TP7,
+
+	/* GPIO0C_IOMUX_H */
+	GPIO0C7_SHIFT	= 6,
+	GPIO0C7_MASK	= GENMASK(8, 6),
+	GPIO0C7_GPIO	= 0,
+	GPIO0C7_LCDC_D3,
+	GPIO0C7_CIF_D3,
+	GPIO0C7_TP14,
+
+	GPIO0C6_SHIFT	= 3,
+	GPIO0C6_MASK	= GENMASK(5, 3),
+	GPIO0C6_GPIO	= 0,
+	GPIO0C6_LCDC_D2,
+	GPIO0C6_CIF_D2,
+	GPIO0C6_TP13,
+
+	GPIO0C5_SHIFT	= 0,
+	GPIO0C5_MASK	= GENMASK(2, 0),
+	GPIO0C5_GPIO	= 0,
+	GPIO0C5_LCDC_D1,
+	GPIO0C5_CIF_D1,
+	GPIO0C5_TP12,
+
+	/* GPIO1A_IOMUX */
+	GPIO1A7_SHIFT	= 14,
+	GPIO1A7_MASK	= GENMASK(15, 14),
+	GPIO1A7_GPIO	= 0,
+	GPIO1A7_LCDC_D11,
+	GPIO1A7_CIF_D11,
+	GPIO1A7_MIPI0_RX2_P,
+
+	GPIO1A6_SHIFT	= 12,
+	GPIO1A6_MASK	= GENMASK(13, 12),
+	GPIO1A6_GPIO	= 0,
+	GPIO1A6_LCDC_D10,
+	GPIO1A6_CIF_D10,
+	GPIO1A6_MIPI0_RX2_N,
+
+	GPIO1A5_SHIFT	= 10,
+	GPIO1A5_MASK	= GENMASK(11, 10),
+	GPIO1A5_GPIO	= 0,
+	GPIO1A5_LCDC_D9,
+	GPIO1A5_CIF_D9,
+	GPIO1A5_MIPI0_TCK_P,
+
+	GPIO1A4_SHIFT	= 8,
+	GPIO1A4_MASK	= GENMASK(9, 8),
+	GPIO1A4_GPIO	= 0,
+	GPIO1A4_LCDC_D8,
+	GPIO1A4_CIF_D8,
+	GPIO1A4_MIPI0_TCK_N,
+
+	GPIO1A3_SHIFT	= 6,
+	GPIO1A3_MASK	= GENMASK(7, 6),
+	GPIO1A3_GPIO	= 0,
+	GPIO1A3_LCDC_D7,
+	GPIO1A3_CIF_D7,
+	GPIO1A3_MIPI0_RX1_P,
+
+	GPIO1A2_SHIFT	= 4,
+	GPIO1A2_MASK	= GENMASK(5, 4),
+	GPIO1A2_GPIO	= 0,
+	GPIO1A2_LCDC_D6,
+	GPIO1A2_CIF_D6,
+	GPIO1A2_MIPI0_RX1_N,
+
+	GPIO1A1_SHIFT	= 2,
+	GPIO1A1_MASK	= GENMASK(3, 2),
+	GPIO1A1_GPIO	= 0,
+	GPIO1A1_LCDC_D5,
+	GPIO1A1_CIF_D5,
+	GPIO1A1_MIPI0_RX0_P,
+
+	GPIO1A0_SHIFT	= 0,
+	GPIO1A0_MASK	= GENMASK(1, 0),
+	GPIO1A0_GPIO	= 0,
+	GPIO1A0_LCDC_D4,
+	GPIO1A0_CIF_D4,
+	GPIO1A0_MIPI0_RX0_N,
+
+	/* GPIO1B_IOMUX */
+	GPIO1B7_SHIFT	= 14,
+	GPIO1B7_MASK	= GENMASK(15, 14),
+	GPIO1B7_GPIO	= 0,
+	GPIO1B7_LCDC_D19,
+	GPIO1B7_CIF_D19,
+	GPIO1B7_MIPI1_TCK_P,
+
+	GPIO1B6_SHIFT	= 12,
+	GPIO1B6_MASK	= GENMASK(13, 12),
+	GPIO1B6_GPIO	= 0,
+	GPIO1B6_LCDC_D18,
+	GPIO1B6_CIF_D18,
+	GPIO1B6_MIPI1_TCK_N,
+
+	GPIO1B5_SHIFT	= 10,
+	GPIO1B5_MASK	= GENMASK(11, 10),
+	GPIO1B5_GPIO	= 0,
+	GPIO1B5_LCDC_D17,
+	GPIO1B5_CIF_D17,
+	GPIO1B5_MIPI1_RX1_P,
+
+	GPIO1B4_SHIFT	= 8,
+	GPIO1B4_MASK	= GENMASK(9, 8),
+	GPIO1B4_GPIO	= 0,
+	GPIO1B4_LCDC_D16,
+	GPIO1B4_CIF_D16,
+	GPIO1B4_MIPI1_RX1_N,
+
+	GPIO1B3_SHIFT	= 6,
+	GPIO1B3_MASK	= GENMASK(7, 6),
+	GPIO1B3_GPIO	= 0,
+	GPIO1B3_LCDC_D15,
+	GPIO1B3_CIF_D15,
+	GPIO1B3_MIPI1_RX0_P,
+
+	GPIO1B2_SHIFT	= 4,
+	GPIO1B2_MASK	= GENMASK(5, 4),
+	GPIO1B2_GPIO	= 0,
+	GPIO1B2_LCDC_D14,
+	GPIO1B2_CIF_D14,
+	GPIO1B2_MIPI1_RX0_N,
+
+	GPIO1B1_SHIFT	= 2,
+	GPIO1B1_MASK	= GENMASK(3, 2),
+	GPIO1B1_GPIO	= 0,
+	GPIO1B1_LCDC_D13,
+	GPIO1B1_CIF_D13,
+	GPIO1B1_MIPI0_RX3_P,
+
+	GPIO1B0_SHIFT	= 0,
+	GPIO1B0_MASK	= GENMASK(1, 0),
+	GPIO1B0_GPIO	= 0,
+	GPIO1B0_LCDC_D12,
+	GPIO1B0_CIF_D12,
+	GPIO1B0_MIPI0_RX3_N,
+
+	/* GPIO1C_IOMUX */
+	GPIO1C3_SHIFT	= 6,
+	GPIO1C3_MASK	= GENMASK(7, 6),
+	GPIO1C3_GPIO	= 0,
+	GPIO1C3_LCDC_D23,
+	GPIO1C3_CIF_D23,
+	GPIO1C3_MIPI1_RX3_P,
+
+	GPIO1C2_SHIFT	= 4,
+	GPIO1C2_MASK	= GENMASK(5, 4),
+	GPIO1C2_GPIO	= 0,
+	GPIO1C2_LCDC_D22,
+	GPIO1C2_CIF_D22,
+	GPIO1C2_MIPI1_RX3_N,
+
+	GPIO1C1_SHIFT	= 2,
+	GPIO1C1_MASK	= GENMASK(3, 2),
+	GPIO1C1_GPIO	= 0,
+	GPIO1C1_LCDC_D21,
+	GPIO1C1_CIF_D21,
+	GPIO1C1_MIPI1_RX2_P,
+
+	GPIO1C0_SHIFT	= 0,
+	GPIO1C0_MASK	= GENMASK(1, 0),
+	GPIO1C0_GPIO	= 0,
+	GPIO1C0_LCDC_D20,
+	GPIO1C0_CIF_D20,
+	GPIO1C0_MIPI1_RX2_N,
+};
+
+#define SER_GRF_SOC_CON0		GRF_REG(0x100)
+#define SER_GRF_SOC_CON1		GRF_REG(0x104)
+#define SER_GRF_SOC_CON2		GRF_REG(0x108)
+#define SER_GRF_SOC_CON3		GRF_REG(0x10C)
+#define SER_GRF_SOC_CON4		GRF_REG(0x110)
+#define SER_GRF_SOC_CON5		GRF_REG(0x114)
+#define SER_GRF_SOC_CON6		GRF_REG(0x118)
+#define SER_GRF_SOC_CON7		GRF_REG(0x11C)
+#define SER_GRF_SOC_STATUS0		GRF_REG(0x160)
+
+enum {
+	/* SOC_CON0 */
+	LVDS_ALIGN_MODE_SHIFT	= 13,
+	LVDS_ALIGN_MODE_MASK	= GENMASK(14, 13),
+	LVDS_ALIGN_8BIT		= 0,
+	LVDS_ALIGN_10BIT	= 0,
+	LVDS_ALIGN_12BIT	= 0,
+
+	LVDS_ALIGN_EN_SHIFT	= 12,
+	LVDS_ALIGN_EN_MASK	= GENMASK(12, 12),
+	LVDS_ALIGN_DISABLE	= 0,
+	LVDS_ALIGN_EN,
+
+	/* SOC_CON2 */
+	LVDS1_MSB_SHIFT		= 5,
+	LVDS1_MSB_MASK		= GENMASK(5, 5),
+	LVDS_LSB		= 0,
+	LVDS_MSB,
+
+	LVDS1_FORMAT_SHIFT	= 3,
+	LVDS1_FORMAT_MASK	= GENMASK(4, 3),
+	LVDS_FORMAT_VESA_24BIT	= 0,
+	LVDS_FORMAT_JEIDA_24BIT,
+	LVDS_FORMAT_JEIDA_18BIT,
+	LVDS_FORMAT_VESA_18BIT,
+
+	LVDS0_MSB_SHIFT		= 2,
+	LVDS0_MSB_MASK		= GENMASK(2, 2),
+
+	LVDS0_FORMAT_SHIFT	= 0,
+	LVDS0_FORMAT_MASK	= GENMASK(1, 0),
+
+	/* SOC_CON3 */
+	AUDIO_PCS_SEL_SHIFT	= 15,
+	AUDIO_PCS_SEL_MASK	= GENMASK(15, 15),
+	AUDIO_SEL_PCS0		= 0,
+	AUDIO_SEL_PCS1		= 1,
+
+	CMD_PCS_SEL_SHIFT	= 14,
+	CMD_PCS_SEL_MASK	= GENMASK(14, 14),
+	CMD_SEL_PCS0		= 0,
+	CMD_SEL_PCS1		= 1,
+
+	/* SOC_CON4 */
+	LVDS1_LINK_SEL_SHIFT	= 15,
+	LVDS1_LINK_SEL_MASK	= GENMASK(15, 15),
+	/* enable lvds source from pattern generation */
+	LINK_SEL_PG_DISABLE	= 0,
+	LINK_SEL_PG_EN		= 1,
+
+	LVDS0_LINK_SEL_SHIFT	= 14,
+	LVDS0_LINK_SEL_MASK	= GENMASK(14, 14),
+
+	DSI1_LINK_SEL_SHIFT	= 13,
+	DSI1_LINK_SEL_MASK	= GENMASK(13, 13),
+
+	DSI0_LINK_SEL_SHIFT	= 12,
+	DSI0_LINK_SEL_MASK	= GENMASK(12, 12),
+
+	RGB_DCLK_BYPASS_SHIFT	= 9,
+	RGB_DCLK_BYPASS_MASK	= GENMASK(9, 9),
+
+	RGB_DCLK_DCLK_DLY_SHIFT	= 1,
+	RGB_DCLK_DCLK_DLY_MASK	= GENMASK(8, 1),
+
+	RGB_DCLK_INV_SHIFT	= 0,
+	RGB_DCLK_INV_MASK	= GENMASK(0, 0),
+
+	/* SOC_CON5 */
+	LDO_PLC_SEL_SHIFT	= 8,
+	LDO_PLC_SEL_MASK	= GENMASK(8, 8),
+	LDO_PLC_170		= 0,
+	LDO_PLC_270,
+
+	LDO_VOL_SEL_SHIFT	= 4,
+	LDO_VOL_SEL_MASK	= HIWORD_MASK(7, 4),
+	LDO_VOL_110		= HIWORD_UPDATE(0, GENMASK(7, 4), 4),
+	LDO_VOL_115		= HIWORD_UPDATE(1, GENMASK(7, 4), 4),
+	LDO_VOL_120		= HIWORD_UPDATE(2, GENMASK(7, 4), 4),
+	LDO_VOL_125		= HIWORD_UPDATE(3, GENMASK(7, 4), 4),
+	LDO_VOL_130		= HIWORD_UPDATE(4, GENMASK(7, 4), 4),
+	LDO_VOL_135		= HIWORD_UPDATE(5, GENMASK(7, 4), 4),
+	LDO_VOL_140		= HIWORD_UPDATE(6, GENMASK(7, 4), 4),
+	LDO_VOL_145		= HIWORD_UPDATE(4, GENMASK(7, 4), 4),
+
+	LDO_BG_TRIM_SHIFT	= 4,
+	LDO_BG_TRIM_MASK	= GENMASK(7, 4),
+	LDO_BG_TRIM_OUT		= 0,
+	LDO_BG_TRIM_OUT_55_N	= 0,
+	LDO_BG_TRIM_OUT_110_N,
+
+	/* SOC_CON7 */
+	PMA_PLL_CTRL_SEL_SHIFT	= 15,
+	PMA_PLL_CTRL_SEL_MASK	= GENMASK(15, 15),
+	PMA_PLL_CTRL_BY_PMA0	= 0,
+	PMA_PLL_CTRL_BY_PMA1,
+
+	PMA1_EN_SHIFT		= 9,
+	PMA1_EN_MASK		= HIWORD_MASK(9, 9),
+	PMA1_EN			= HIWORD_UPDATE(1, BIT(9), 9),
+	PMA1_DISABLE		= HIWORD_UPDATE(0, BIT(9), 9),
+
+	PMA0_EN_SHIFT		= 8,
+	PMA0_EN_MASK		= HIWORD_MASK(8, 8),
+	PMA0_EN			= HIWORD_UPDATE(1, BIT(8), 8),
+	PMA0_DISABLE		= HIWORD_UPDATE(0, BIT(8), 8),
+
+	/* SER_GRF_IRQ_EN */
+
+	/* SER_GRF_SOC_STATUS0 */
+	SER_PCS1_READY		= BIT(21),
+	SER_PCS0_READY		= BIT(20),
+};
+
+#define RKX110_CSI2HOST0_BASE		0x00020000
+#define RKX110_CSI2HOST1_BASE		0x00028000
+#define CSI2HOST_N_LANES		0x0004
+#define CSI2HOST_RESETN			0x0010
+#define CSI2HOST_CONTROL		0x0040
+#define SW_DATATYPE_LE(x)		UPDATE(x, 31, 26)
+#define SW_DATETYPE_LS(x)		UPDATE(x, 25, 20)
+#define SW_DATETYPE_FE_MASK		GENMASK(19, 14)
+#define SW_DATETYPE_FE(x)		UPDATE(x, 19, 14)
+#define SW_DATETYPE_FS_MASK		GENMASK(13, 8)
+#define SW_DATETYPE_FS(x)		UPDATE(x, 13, 8)
+#define SW_DSI_EN			BIT(4)
+
+#define RKX110_VICAP_BASE		0x00030000
+#define RKX110_GPIO0_BASE		0x00040000
+
+
+#define RKX110_DSI_RX0_BASE		0x00060000
+#define RKX110_DSI_RX1_BASE		0x00068000
+#define RKX110_SER_RKLINK_BASE		0x00070000
+#define RKX110_SER_PCS0_BASE		0x00074000
+#define RKX110_SER_PCS1_BASE		0x00075000
+#define RKX110_SER_PCS_OFFSET		0x00001000
+#define RKX110_EFUSE_BASE		0x00800000
+#define RKX110_MIPI_LVDS_RX_PHY0_BASE	0x00090000
+#define RKX110_GRF_MIPI0_BASE		0x000A0000
+#define RKX110_MIPI_LVDS_RX_PHY1_BASE	0x000B0000
+#define RKX110_GRF_MIPI1_BASE		0x000C0000
+#define RKX110_SER_PMA0_BASE		0x000D0000
+#define RKX110_SER_PMA1_BASE		0x000E0000
+#define RKX110_SER_PMA_OFFSET		0x00010000
+
+#define RKX110_GPIO1_BASE		0x000F0000
+
+#define RKX110_PATTERN_GEN_DSI0_BASE	0x00100000
+#define RKX110_PATTERN_GEN_DSI1_BASE	0x00110000
+#define RKX110_PATTERN_GEN_LVDS0_BASE	0x00120000
+#define RKX110_PATTERN_GEN_LVDS1_BASE	0x00130000
+
+#endif
diff --git a/kernel/drivers/mfd/rkx110_x120/rkx110_x120.h b/kernel/drivers/mfd/rkx110_x120/rkx110_x120.h
new file mode 100644
index 0000000..67c2d1f
--- /dev/null
+++ b/kernel/drivers/mfd/rkx110_x120/rkx110_x120.h
@@ -0,0 +1,366 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2022 Rockchip Electronics Co. Ltd.
+ *
+ * Author: Zhang Yubing <yubing.zhang@rock-chips.com>
+ */
+
+#ifndef _RKX110_X120_H
+#define _RKX110_X120_H
+
+#include <drm/drm_panel.h>
+#include <dt-bindings/mfd/rockchip-serdes.h>
+#include <linux/i2c.h>
+#include <video/videomode.h>
+
+#define MAX_PANEL 2
+#define RK_SERDES_PASSTHROUGH_CNT	11
+
+#define SERDES_VERSION_V0(type)		0x2201
+#define SERDES_VERSION_V1(type)		(type ? 0x1200001 : 0x1100001)
+
+#define SER_GRF_CHIP_ID			0x10800
+#define DES_GRF_CHIP_ID			0x1010400
+#define HIWORD_MASK(h, l)		(GENMASK(h, l) | GENMASK(h, l) << 16)
+
+enum {
+	SERDES_V0 = 0,
+	SERDES_V1,
+};
+
+enum {
+	LOCAL_MODE = 0,
+	REMOTE_MODE,
+};
+
+enum {
+	STREAM_DISPLAY = 0,
+	STREAM_CAMERA,
+};
+
+enum {
+	DEVICE_LOCAL = 0,
+	DEVICE_REMOTE0,
+	DEVICE_REMOTE1,
+	DEVICE_MAX,
+};
+
+enum {
+	PORT_REMOTE0,
+	PORT_REMOTE1,
+	PORT_REMOTE_MAX,
+};
+
+enum {
+	LINK_LANE0,
+	LINK_LANE1,
+	LINK_LANE_DUAL,
+};
+
+enum combtx_phy_mode {
+	COMBTX_PHY_MODE_GPIO,
+	COMBTX_PHY_MODE_VIDEO_LVDS,
+	COMBTX_PHY_MODE_VIDEO_MIPI,
+	COMBTX_PHY_MODE_VIDEO_MINI_LVDS,
+};
+
+enum comb_phy_id {
+	COMBPHY_0,
+	COMBPHY_1,
+	COMBPHY_MAX,
+};
+
+enum combrx_phy_mode {
+	COMBRX_PHY_MODE_RGB,
+	COMBRX_PHY_MODE_VIDEO_LVDS,
+	COMBRX_PHY_MODE_VIDEO_MIPI,
+	COMBRX_PHY_MODE_LVDS_CAMERA,
+};
+
+enum serdes_dsi_mode_flags {
+	SERDES_MIPI_DSI_MODE_VIDEO = 1,
+	SERDES_MIPI_DSI_MODE_VIDEO_BURST = 2,
+	SERDES_MIPI_DSI_MODE_VIDEO_SYNC_PULSE = 4,
+	SERDES_MIPI_DSI_MODE_VIDEO_HFP = 8,
+	SERDES_MIPI_DSI_MODE_VIDEO_HBP = 16,
+	SERDES_MIPI_DSI_MODE_EOT_PACKET = 32,
+	SERDES_MIPI_DSI_CLOCK_NON_CONTINUOUS = 64,
+	SERDES_MIPI_DSI_MODE_LPM = 128,
+};
+
+enum serdes_dsi_bus_format {
+	SERDES_MIPI_DSI_FMT_RGB888,
+	SERDES_MIPI_DSI_FMT_RGB666,
+	SERDES_MIPI_DSI_FMT_RGB666_PACKED,
+	SERDES_MIPI_DSI_FMT_RGB565,
+};
+
+enum serdes_frame_mode {
+	SERDES_FRAME_NORMAL_MODE,
+	SERDES_SP_PIXEL_INTERLEAVED,
+	SERDES_SP_LEFT_RIGHT_SPLIT,
+	SERDES_SP_LINE_INTERLEAVED,
+};
+
+struct configure_opts_combphy {
+	unsigned int clk_miss;
+	unsigned int clk_post;
+	unsigned int clk_pre;
+	unsigned int clk_prepare;
+	unsigned int clk_settle;
+	unsigned int clk_term_en;
+	unsigned int clk_trail;
+	unsigned int clk_zero;
+	unsigned int d_term_en;
+	unsigned int eot;
+	unsigned int hs_exit;
+	unsigned int hs_prepare;
+	unsigned int hs_settle;
+	unsigned int hs_skip;
+	unsigned int hs_trail;
+	unsigned int hs_zero;
+	unsigned int init;
+	unsigned int lpx;
+	unsigned int ta_get;
+	unsigned int ta_go;
+	unsigned int ta_sure;
+	unsigned int wakeup;
+	unsigned long hs_clk_rate;
+	unsigned long lp_clk_rate;
+	unsigned char lanes;
+};
+
+struct rkx120_combtxphy {
+	enum combtx_phy_mode mode;
+	unsigned int flags;
+	u8 ref_div;
+	u16 fb_div;
+	u8 rate_factor;
+	u64 rate;
+	struct configure_opts_combphy mipi_dphy_cfg;
+};
+
+struct rkx110_combrxphy {
+	enum combrx_phy_mode mode;
+	u64 rate;
+	struct configure_opts_combphy mipi_dphy_cfg;
+};
+
+struct rkx120_dsi_tx {
+	int bpp; /* 24/18/16*/
+	enum serdes_dsi_bus_format bus_format;
+	enum serdes_dsi_mode_flags mode_flags;
+	struct videomode *vm;
+	uint8_t channel;
+	uint8_t lanes;
+};
+
+struct rkx110_dsi_rx {
+	enum serdes_dsi_mode_flags mode_flags;
+	struct videomode *vm;
+	uint8_t channel;
+	uint8_t lanes;
+};
+
+enum {
+	OUTPUT,
+	INPUT,
+};
+
+enum rk_serdes_rate {
+	RATE_2GBPS_83M,
+	RATE_4GBPS_83M,
+	RATE_4GBPS_125M,
+	RATE_4GBPS_250M,
+	RATE_4_5GBPS_140M,
+	RATE_4_8GBPS_150M,
+	RATE_5GBPS_156M,
+	RATE_6GBPS_187M,
+};
+
+enum {
+	FDR_RATE_MODE,
+	HDR_RATE_MODE,
+	QDR_RATE_MODE,
+};
+
+enum rk_serdes_route_type {
+	ROUTE_MULTI_SOURCE = BIT(0),
+	ROUTE_MULTI_LANE = BIT(1),
+	ROUTE_MULTI_CHANNEL = BIT(2),
+	ROUTE_MULTI_REMOTE = BIT(3),
+	ROUTE_MULTI_DSI_INPUT = BIT(20),
+	ROUTE_MULTI_LVDS_INPUT = BIT(21),
+	ROUTE_MULTI_MIRROR = BIT(22),
+	ROUTE_MULTI_SPLIT = BIT(23),
+};
+
+struct rk_serdes_pma_pll {
+	uint32_t rate_mode;
+	uint32_t pll_refclk_div;
+	uint32_t pll_div;
+	uint32_t clk_div;
+	bool pll_div4;
+	bool pll_fck_vco_div2;
+	bool force_init_en;
+};
+
+struct rk_serdes_reg {
+	const char *name;
+	uint32_t reg_base;
+	uint32_t reg_len;
+};
+
+struct rk_serdes_route {
+	u32 stream_type;
+	struct videomode vm;
+	enum serdes_frame_mode frame_mode;
+	u32 local_port0;
+	u32 local_port1;
+	u32 remote0_port0;
+	u32 remote0_port1;
+	u32 remote1_port0;
+	u32 remote1_port1;
+};
+
+struct rk_serdes_chip {
+	bool is_remote;
+	struct i2c_client *client;
+	struct hwclk *hwclk;
+	struct rk_serdes *serdes;
+};
+
+struct pattern_gen {
+	const char *name;
+	struct rk_serdes_chip *chip;
+	u32 base;
+	u32 link_src_reg;
+	u8 link_src_offset;
+};
+
+struct rk_serdes_pt_pin {
+	u32 bank;
+	u32 pin;
+	u32 incfgs;
+	u32 outcfgs;
+};
+
+struct rk_serdes_pt {
+	u32 en_reg;
+	u32 en_mask;
+	u32 en_val;
+	u32 dir_reg;
+	u32 dir_mask;
+	u32 dir_val;
+	int configs;
+	struct rk_serdes_pt_pin pt_pins[4];
+};
+
+struct rk_serdes {
+	struct device *dev;
+	struct rk_serdes_chip chip[DEVICE_MAX];
+	struct gpio_desc *reset;
+	struct gpio_desc *enable;
+
+	/*
+	 * Control by I2C-Debug
+	 */
+	bool rkx110_debug;
+	bool rkx120_debug;
+
+	enum rk_serdes_rate rate;
+
+	struct dentry *debugfs_root;
+	struct dentry *debugfs_local;
+	struct dentry *debugfs_remote0;
+	struct dentry *debugfs_remote1;
+	struct dentry *debugfs_rate;
+
+	struct videomode *vm;
+	u32 stream_type;
+	u32 version;
+	u32 route_flag;
+	u8 remote_nr;
+	struct rkx110_combrxphy combrxphy;
+	struct rkx110_dsi_rx dsi_rx;
+	struct rkx120_combtxphy combtxphy;
+	struct rkx120_dsi_tx dsi_tx;
+
+	int (*i2c_read_reg)(struct i2c_client *client, u32 addr, u32 *value);
+	int (*i2c_write_reg)(struct i2c_client *client, u32 addr, u32 value);
+	int (*i2c_update_bits)(struct i2c_client *client, u32 reg, u32 mask, u32 val);
+	int (*route_prepare)(struct rk_serdes *serdes, struct rk_serdes_route *route);
+	int (*route_enable)(struct rk_serdes *serdes, struct rk_serdes_route *route);
+	int (*route_disable)(struct rk_serdes *serdes, struct rk_serdes_route *route);
+	int (*route_unprepare)(struct rk_serdes *serdes, struct rk_serdes_route *route);
+	int (*set_hwpin)(struct rk_serdes *serdes, struct i2c_client *client,
+			 int pintype, int bank, uint32_t mpins, uint32_t param);
+};
+
+struct cmd_ctrl_hdr {
+	u8 dtype;       /* data type */
+	u8 wait;        /* ms */
+	u8 dlen;        /* payload len */
+} __packed;
+
+struct cmd_desc {
+	struct cmd_ctrl_hdr dchdr;
+	u8 *payload;
+};
+
+struct panel_cmds {
+	u8 *buf;
+	int blen;
+	struct cmd_desc *cmds;
+	int cmd_cnt;
+};
+
+struct rk_serdes_panel {
+	struct drm_panel panel;
+	struct device *dev;
+	struct rk_serdes *parent;
+	struct rk_serdes_route route;
+	unsigned int bus_format;
+	int link_mode;
+
+	struct panel_cmds *on_cmds;
+	struct panel_cmds *off_cmds;
+
+	struct regulator *supply;
+	struct gpio_desc *enable_gpio;
+	struct gpio_desc *reset_gpio;
+};
+
+int rkx110_linktx_enable(struct rk_serdes *serdes, struct rk_serdes_route *route);
+void rkx110_linktx_video_enable(struct rk_serdes *serdes, u8 dev_id, bool enable);
+void rkx110_linktx_channel_enable(struct rk_serdes *serdes, u8 ch_id, u8 dev_id, bool enable);
+void rkx120_linkrx_engine_enable(struct rk_serdes *serdes, u8 en_id, u8 dev_id, bool enable);
+void rkx110_set_stream_source(struct rk_serdes *serdes, int local_port, u8 dev_id);
+int rkx120_linkrx_enable(struct rk_serdes *serdes, struct rk_serdes_route *route, u8 remote_id);
+int rkx120_rgb_tx_enable(struct rk_serdes *serdes, struct rk_serdes_route *route, u8 remote_id);
+int rkx120_lvds_tx_enable(struct rk_serdes *serdes, struct rk_serdes_route *route, u8 remote_id,
+			  u8 phy_id);
+void rkx120_linkrx_gpi_gpo_mux_cfg(struct rk_serdes *serdes, u32 mux, u8 remote_id);
+void rkx110_linktx_gpi_gpo_mux_cfg(struct rk_serdes *serdes, u32 mux, u8 remote_id);
+int rkx110_rgb_rx_enable(struct rk_serdes *serdes, struct rk_serdes_route *route);
+int rkx110_lvds_rx_enable(struct rk_serdes *serdes, struct rk_serdes_route *route, int id);
+void rkx110_debugfs_init(struct rk_serdes_chip *chip, struct dentry *dentry);
+void rkx120_debugfs_init(struct rk_serdes_chip *chip, struct dentry *dentry);
+void rkx110_pma_set_rate(struct rk_serdes *serdes, struct rk_serdes_pma_pll *pll,
+			 u8 pcs_id, u8 dev_id);
+void rkx120_pma_set_rate(struct rk_serdes *serdes, struct rk_serdes_pma_pll *pll,
+			 u8 pcs_id, u8 dev_id);
+void rkx110_pcs_enable(struct rk_serdes *serdes, bool enable, u8 pcs_id, u8 dev_id);
+void rkx120_pcs_enable(struct rk_serdes *serdes, bool enable, u8 pcs_id, u8 dev_id);
+void rkx110_ser_pma_enable(struct rk_serdes *serdes, bool enable, u8 pma_id, u8 remote_id);
+void rkx120_des_pma_enable(struct rk_serdes *serdes, bool enable, u8 pma_id, u8 remote_id);
+void rkx110_linktx_wait_link_ready(struct rk_serdes *serdes, u8 id);
+void rkx120_linkrx_wait_link_ready(struct rk_serdes *serdes, u8 id);
+void rkx110_x120_pattern_gen_debugfs_create_file(struct pattern_gen *pattern_gen,
+						 struct rk_serdes_chip *chip,
+						 struct dentry *dentry);
+void rkx110_linktx_passthrough_cfg(struct rk_serdes *serdes, u32 client_id, u32 func_id,
+				   bool is_rx);
+void rkx120_linkrx_passthrough_cfg(struct rk_serdes *serdes, u32 client_id, u32 func_id,
+				   bool is_rx);
+#endif
diff --git a/kernel/drivers/mfd/rkx110_x120/rkx110_x120_core.c b/kernel/drivers/mfd/rkx110_x120/rkx110_x120_core.c
new file mode 100644
index 0000000..95d875b
--- /dev/null
+++ b/kernel/drivers/mfd/rkx110_x120/rkx110_x120_core.c
@@ -0,0 +1,1100 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2022 Rockchip Electronics Co. Ltd.
+ *
+ * Author: Zhang Yubing <yubing.zhang@rock-chips.com>
+ */
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/debugfs.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/gpio/consumer.h>
+#include <linux/mfd/core.h>
+#include "rkx110_x120.h"
+#include "rkx110_reg.h"
+#include "rkx110_dsi_rx.h"
+#include "rkx120_dsi_tx.h"
+#include "hal/cru_api.h"
+#include "hal/pinctrl_api.h"
+
+static const struct mfd_cell rkx110_x120_devs[] = {
+	/* 2 panel device for rkx110_x120 drm panel */
+	{
+		.name = "serdes-panel",
+		.of_compatible = "rockchip,serdes-panel",
+	},
+};
+
+static int rk_serdes_i2c_read(struct i2c_client *client, u32 addr, u32 *value)
+{
+	struct i2c_msg xfer[2];
+	u32 reg;
+	u32 data;
+	int ret;
+
+	reg = cpu_to_le32(addr);
+	/* Write register */
+	xfer[0].addr = client->addr;
+	xfer[0].flags = 0;
+	xfer[0].len = 4;
+	xfer[0].buf = (u8 *)&reg;
+
+	/* Read data */
+	xfer[1].addr = client->addr;
+	xfer[1].flags = I2C_M_RD;
+	xfer[1].len = 4;
+	xfer[1].buf = (u8 *)&data;
+
+	ret = i2c_transfer(client->adapter, xfer, 2);
+	if (ret == 2)
+		ret = 0;
+	else if (ret >= 0)
+		ret = -EIO;
+
+	*value = le32_to_cpu(data);
+	dev_dbg(&client->dev, "read: 0x%08x: 0x%08x\n", addr, *value);
+
+	return ret;
+}
+
+static int rk_serdes_i2c_write(struct i2c_client *client, u32 addr, u32 value)
+{
+	struct i2c_msg xfer;
+	u32 reg;
+	u32 data;
+	u8 buf[8];
+	int ret;
+
+	reg = cpu_to_le32(addr);
+	data = cpu_to_le32(value);
+	memcpy(&buf[0], &reg, 4);
+	memcpy(&buf[4], &data, 4);
+
+	/* Write address & data */
+	xfer.addr = client->addr;
+	xfer.flags = 0;
+	xfer.len = 8;
+	xfer.buf = buf;
+
+	dev_dbg(&client->dev, "write: 0x%08x: 0x%08x\n", addr, value);
+	ret = i2c_transfer(client->adapter, &xfer, 1);
+	if (ret == 1)
+		return 0;
+	else if (ret < 0)
+		return ret;
+	else
+		return -EIO;
+}
+
+static int rk_serdes_i2c_update_bits(struct i2c_client *client, u32 reg, u32 mask, u32 val)
+{
+	u32 value;
+	int ret;
+
+	ret = rk_serdes_i2c_read(client, reg, &value);
+	if (ret)
+		return ret;
+
+	value &= ~mask;
+	value |= (val & mask);
+	ret = rk_serdes_i2c_write(client, reg, value);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static bool rk_serdes_debug_mode(struct rk_serdes *serdes)
+{
+	return serdes->rkx110_debug || serdes->rkx120_debug;
+}
+
+static void rk_serdes_wait_link_ready(struct rk_serdes *serdes)
+{
+	if (serdes->stream_type == STREAM_DISPLAY) {
+		rkx110_linktx_wait_link_ready(serdes, 0);
+		if (serdes->route_flag & ROUTE_MULTI_LANE) {
+			rkx110_ser_pma_enable(serdes, true, 1, DEVICE_LOCAL);
+			if (!(serdes->route_flag & ROUTE_MULTI_REMOTE))
+				rkx120_des_pma_enable(serdes, true, 1, DEVICE_REMOTE0);
+			rkx110_linktx_wait_link_ready(serdes, 1);
+		}
+
+	} else {
+		rkx120_linkrx_wait_link_ready(serdes, 0);
+		if (serdes->route_flag & ROUTE_MULTI_LANE) {
+			rkx120_des_pma_enable(serdes, true, 1, DEVICE_LOCAL);
+			if (!(serdes->route_flag & ROUTE_MULTI_REMOTE))
+				rkx110_ser_pma_enable(serdes, true, 1, DEVICE_REMOTE0);
+			rkx120_linkrx_wait_link_ready(serdes, 1);
+		}
+	}
+}
+
+static void rk_serdes_print_rate(struct rk_serdes *serdes, enum rk_serdes_rate rate)
+{
+	switch (rate) {
+	case RATE_4GBPS_83M:
+		dev_info(serdes->dev, "serdes set rate: 4Gbps, backward: 83Mbps\n");
+		break;
+	case RATE_4GBPS_125M:
+		dev_info(serdes->dev, "serdes set rate: 4Gbps, backward: 125Mbps\n");
+		break;
+	case RATE_4GBPS_250M:
+		dev_info(serdes->dev, "serdes set rate: 4Gbps, backward: 250Mbps\n");
+		break;
+	case RATE_4_5GBPS_140M:
+		dev_info(serdes->dev, "serdes set rate: 4.5Gbps, backward: 140Mbps\n");
+		break;
+	case RATE_4_8GBPS_150M:
+		dev_info(serdes->dev, "serdes set rate: 4.8Gbps, backward: 150Mbps\n");
+		break;
+#if 0
+	case RATE_5GBPS_156M:
+		dev_info(serdes->dev, "serdes set rate: 5Gbps, backward: 156Mbps\n");
+		break;
+	case RATE_6GBPS_187M:
+		dev_info(serdes->dev, "serdes set rate: 6Gbps, backward: 187Mbps\n");
+		break;
+#endif
+	case RATE_2GBPS_83M:
+		dev_info(serdes->dev, "serdes set rate: 2Gbps, backward: 83Mbps\n");
+		break;
+	default:
+		dev_info(serdes->dev, "serdes set rate: Unknown rate\n");
+		break;
+	}
+}
+
+static void rk_serdes_set_rate(struct rk_serdes *serdes, enum rk_serdes_rate rate)
+{
+	struct rk_serdes_pma_pll rkx110_pll, rkx120_pll;
+
+	if (serdes->rate == rate)
+		return;
+
+	memset(&rkx110_pll, 0, sizeof(rkx110_pll));
+	memset(&rkx120_pll, 0, sizeof(rkx120_pll));
+
+	rk_serdes_print_rate(serdes, rate);
+
+	switch (rate) {
+	case RATE_4GBPS_83M:
+		rkx110_pll.rate_mode = FDR_RATE_MODE;
+		rkx110_pll.pll_div4 = true;
+		rkx110_pll.pll_div = 21330;
+		rkx110_pll.clk_div = 5;
+		rkx110_pll.pll_refclk_div = 0;
+		rkx110_pll.pll_fck_vco_div2 = false;
+
+		rkx120_pll.pll_div4 = true;
+		rkx120_pll.pll_div = 21330;
+		rkx120_pll.clk_div = 23;
+		rkx120_pll.pll_refclk_div = 0;
+		rkx120_pll.pll_fck_vco_div2 = true;
+		break;
+	case RATE_4GBPS_125M:
+		rkx110_pll.rate_mode = FDR_RATE_MODE;
+		rkx110_pll.pll_div4 = true;
+		rkx110_pll.pll_div = 21330;
+		rkx110_pll.clk_div = 1;
+		rkx110_pll.pll_refclk_div = 0;
+		rkx110_pll.pll_fck_vco_div2 = true;
+
+		rkx120_pll.pll_div4 = true;
+		rkx120_pll.pll_div = 21330;
+		rkx120_pll.clk_div = 15;
+		rkx120_pll.pll_refclk_div = 0;
+		rkx120_pll.pll_fck_vco_div2 = true;
+		break;
+	case RATE_4GBPS_250M:
+		rkx110_pll.rate_mode = FDR_RATE_MODE;
+		rkx110_pll.pll_div4 = true;
+		rkx110_pll.pll_div = 21330;
+		rkx110_pll.clk_div = 0;
+		rkx110_pll.pll_refclk_div = 0;
+		rkx110_pll.pll_fck_vco_div2 = true;
+
+		rkx120_pll.pll_div4 = true;
+		rkx120_pll.pll_div = 21330;
+		rkx120_pll.clk_div = 7;
+		rkx120_pll.pll_refclk_div = 0;
+		rkx120_pll.pll_fck_vco_div2 = true;
+		break;
+	case RATE_4_5GBPS_140M:
+		rkx110_pll.rate_mode = FDR_RATE_MODE;
+		rkx110_pll.pll_div4 = true;
+		rkx110_pll.pll_div = 24000;
+		rkx110_pll.clk_div = 1;
+		rkx110_pll.pll_refclk_div = 0;
+		rkx110_pll.pll_fck_vco_div2 = true;
+
+		rkx120_pll.pll_div4 = true;
+		rkx120_pll.pll_div = 12000;
+		rkx120_pll.clk_div = 7;
+		rkx120_pll.pll_refclk_div = 0;
+		rkx120_pll.pll_fck_vco_div2 = true;
+		break;
+	case RATE_4_8GBPS_150M:
+		rkx110_pll.rate_mode = FDR_RATE_MODE;
+		rkx110_pll.pll_div4 = true;
+		rkx110_pll.pll_div = 26000;
+		rkx110_pll.clk_div = 1;
+		rkx110_pll.pll_refclk_div = 0;
+		rkx110_pll.pll_fck_vco_div2 = true;
+
+		rkx120_pll.pll_div4 = true;
+		rkx120_pll.pll_div = 13000;
+		rkx120_pll.clk_div = 7;
+		rkx120_pll.pll_refclk_div = 0;
+		rkx120_pll.pll_fck_vco_div2 = true;
+		break;
+#if 0
+	case RATE_5GBPS_156M:
+		rkx110_pll.rate_mode = FDR_RATE_MODE;
+		rkx110_pll.pll_div4 = true;
+		rkx110_pll.pll_div = 26667;
+		rkx110_pll.clk_div = 3;
+		rkx110_pll.pll_refclk_div = 0;
+		rkx110_pll.pll_fck_vco_div2 = true;
+
+		rkx120_pll.pll_div4 = 1;
+		rkx120_pll.pll_div = 26667;
+		rkx120_pll.clk_div = 31;
+		rkx120_pll.pll_refclk_div = 0;
+		rkx120_pll.pll_fck_vco_div2 = true;
+		break;
+	case RATE_6GBPS_187M:
+		rkx110_pll.rate_mode = QDR_RATE_MODE;
+		rkx110_pll.pll_div4 = true;
+		rkx110_pll.pll_div = 29000;
+		rkx110_pll.clk_div = 1;
+		rkx110_pll.pll_refclk_div = 0;
+		rkx110_pll.pll_fck_vco_div2 = true;
+
+		rkx120_pll.pll_div4 = true;
+		rkx120_pll.pll_div = 29000;
+		rkx120_pll.clk_div = 15;
+		rkx120_pll.pll_refclk_div = 0;
+		rkx120_pll.pll_fck_vco_div2 = true;
+		break;
+#endif
+	case RATE_2GBPS_83M:
+		rkx110_pll.rate_mode = HDR_RATE_MODE;
+		rkx110_pll.pll_div4 = true;
+		rkx110_pll.pll_div = 21330;
+		rkx110_pll.clk_div = 2;
+		rkx110_pll.pll_refclk_div = 0;
+		rkx110_pll.pll_fck_vco_div2 = true;
+
+		rkx120_pll.pll_div4 = true;
+		rkx120_pll.pll_div = 21330;
+		rkx120_pll.clk_div = 23;
+		rkx120_pll.pll_refclk_div = 0;
+		rkx120_pll.pll_fck_vco_div2 = true;
+		break;
+	default:
+		return;
+	}
+
+	if (serdes->stream_type == STREAM_DISPLAY) {
+		rkx110_pma_set_rate(serdes, &rkx110_pll, 0, DEVICE_LOCAL);
+		rkx120_pma_set_rate(serdes, &rkx120_pll, 0, DEVICE_REMOTE0);
+		if (serdes->route_flag & ROUTE_MULTI_LANE) {
+			rkx110_pma_set_rate(serdes, &rkx110_pll, 1, DEVICE_LOCAL);
+			if (serdes->route_flag & ROUTE_MULTI_REMOTE)
+				rkx120_pma_set_rate(serdes, &rkx120_pll, 0, DEVICE_REMOTE1);
+			else
+				rkx120_pma_set_rate(serdes, &rkx120_pll, 1, DEVICE_REMOTE0);
+		}
+		rkx110_pcs_enable(serdes, 0, 0, DEVICE_LOCAL);
+		usleep_range(1000, 2000);
+		rkx110_pcs_enable(serdes, 1, 0, DEVICE_LOCAL);
+	} else {
+		rkx120_pma_set_rate(serdes, &rkx120_pll, 0, DEVICE_LOCAL);
+		rkx110_pma_set_rate(serdes, &rkx110_pll, 0, DEVICE_REMOTE0);
+		if (serdes->route_flag & ROUTE_MULTI_LANE) {
+			rkx120_pma_set_rate(serdes, &rkx120_pll, 1, DEVICE_LOCAL);
+			if (serdes->route_flag & ROUTE_MULTI_REMOTE)
+				rkx110_pma_set_rate(serdes, &rkx110_pll, 0, DEVICE_REMOTE1);
+			else
+				rkx110_pma_set_rate(serdes, &rkx110_pll, 1, DEVICE_REMOTE0);
+		}
+		rkx120_pcs_enable(serdes, 0, 0, DEVICE_LOCAL);
+		usleep_range(1000, 2000);
+		rkx120_pcs_enable(serdes, 1, 0, DEVICE_LOCAL);
+	}
+
+	rk_serdes_wait_link_ready(serdes);
+
+	serdes->rate = rate;
+}
+
+static int rk_serdes_route_prepare(struct rk_serdes *serdes, struct rk_serdes_route *route)
+{
+	if (rk_serdes_debug_mode(serdes))
+		return 0;
+
+	if (route->stream_type == STREAM_DISPLAY) {
+		switch (route->local_port0) {
+		case RK_SERDES_RGB_RX:
+			rkx110_rgb_rx_enable(serdes, route);
+			break;
+		case RK_SERDES_LVDS_RX0:
+			rkx110_lvds_rx_enable(serdes, route, 0);
+			if (serdes->route_flag & ROUTE_MULTI_LVDS_INPUT)
+				rkx110_lvds_rx_enable(serdes, route, 1);
+			break;
+		case RK_SERDES_LVDS_RX1:
+			rkx110_lvds_rx_enable(serdes, route, 1);
+			if (serdes->route_flag & ROUTE_MULTI_LVDS_INPUT)
+				rkx110_lvds_rx_enable(serdes, route, 0);
+			break;
+		case RK_SERDES_DUAL_LVDS_RX:
+			rkx110_lvds_rx_enable(serdes, route, 0);
+			rkx110_lvds_rx_enable(serdes, route, 1);
+			break;
+		case RK_SERDES_DSI_RX0:
+			rkx110_dsi_rx_enable(serdes, route, 0);
+			if (serdes->route_flag & ROUTE_MULTI_DSI_INPUT)
+				rkx110_dsi_rx_enable(serdes, route, 1);
+			break;
+		case RK_SERDES_DSI_RX1:
+			rkx110_dsi_rx_enable(serdes, route, 1);
+			if (serdes->route_flag & ROUTE_MULTI_DSI_INPUT)
+				rkx110_dsi_rx_enable(serdes, route, 0);
+			break;
+		default:
+			dev_info(serdes->dev, "undefined local port0");
+			return -EINVAL;
+		}
+
+		rkx110_linktx_enable(serdes, route);
+
+		rkx120_linkrx_enable(serdes, route, DEVICE_REMOTE0);
+		if (serdes->route_flag & ROUTE_MULTI_REMOTE)
+			rkx120_linkrx_enable(serdes, route, DEVICE_REMOTE1);
+
+		if (route->remote0_port0 & RK_SERDES_DSI_TX0)
+			rkx120_dsi_tx_pre_enable(serdes, route, DEVICE_REMOTE0);
+		if (route->remote1_port0 & RK_SERDES_DSI_TX0)
+			rkx120_dsi_tx_pre_enable(serdes, route, DEVICE_REMOTE1);
+	} else {
+		/* for camera stream */
+	}
+
+	return 0;
+}
+
+static int rk_serdes_route_enable(struct rk_serdes *serdes, struct rk_serdes_route *route)
+{
+	if (rk_serdes_debug_mode(serdes))
+		return 0;
+
+	if (route->stream_type == STREAM_DISPLAY) {
+		switch (route->remote0_port0) {
+		case RK_SERDES_RGB_TX:
+			rkx120_rgb_tx_enable(serdes, route, DEVICE_REMOTE0);
+			break;
+		case RK_SERDES_LVDS_TX0:
+			rkx120_lvds_tx_enable(serdes, route, DEVICE_REMOTE0, 0);
+			break;
+		case RK_SERDES_LVDS_TX1:
+			rkx120_lvds_tx_enable(serdes, route, DEVICE_REMOTE0, 1);
+			break;
+		case RK_SERDES_DUAL_LVDS_TX:
+			rkx120_lvds_tx_enable(serdes, route, DEVICE_REMOTE0, 0);
+			rkx120_lvds_tx_enable(serdes, route, DEVICE_REMOTE0, 1);
+			break;
+		case RK_SERDES_DSI_TX0:
+			rkx120_dsi_tx_enable(serdes, route, DEVICE_REMOTE0);
+			break;
+		default:
+			dev_err(serdes->dev, "undefined remote0_port0\n");
+			return -EINVAL;
+		}
+
+		if (serdes->route_flag & ROUTE_MULTI_REMOTE) {
+			switch (route->remote1_port0) {
+			case RK_SERDES_RGB_TX:
+				rkx120_rgb_tx_enable(serdes, route, DEVICE_REMOTE1);
+				break;
+			case RK_SERDES_LVDS_TX0:
+				rkx120_lvds_tx_enable(serdes, route, DEVICE_REMOTE1, 0);
+				break;
+			case RK_SERDES_LVDS_TX1:
+				rkx120_lvds_tx_enable(serdes, route, DEVICE_REMOTE1, 1);
+				break;
+			case RK_SERDES_DUAL_LVDS_TX:
+				rkx120_lvds_tx_enable(serdes, route, DEVICE_REMOTE1, 0);
+				rkx120_lvds_tx_enable(serdes, route, DEVICE_REMOTE1, 1);
+				break;
+			case RK_SERDES_DSI_TX0:
+				rkx120_dsi_tx_enable(serdes, route, DEVICE_REMOTE1);
+				break;
+			default:
+				dev_err(serdes->dev, "undefined remote1_port0\n");
+				return -EINVAL;
+			}
+		} else if (serdes->route_flag & ROUTE_MULTI_CHANNEL) {
+			if (route->remote0_port1 & RK_SERDES_LVDS_TX0) {
+				rkx120_lvds_tx_enable(serdes, route, DEVICE_REMOTE0, 0);
+			} else if (route->remote0_port1 & RK_SERDES_LVDS_TX1) {
+				rkx120_lvds_tx_enable(serdes, route, DEVICE_REMOTE0, 1);
+			} else {
+				dev_err(serdes->dev, "undefined remote0_port1\n");
+				return -EINVAL;
+			}
+		}
+
+		if (serdes->version == SERDES_V1) {
+			rkx120_linkrx_engine_enable(serdes, 0, DEVICE_REMOTE0, true);
+			rkx110_linktx_channel_enable(serdes, 0, DEVICE_LOCAL, true);
+		}
+
+		rkx110_linktx_video_enable(serdes, DEVICE_LOCAL, true);
+	} else {
+		/* for camera stream */
+	}
+
+	return 0;
+}
+
+static int rk_serdes_route_disable(struct rk_serdes *serdes, struct rk_serdes_route *route)
+{
+	if (route->stream_type == STREAM_DISPLAY) {
+		if (route->remote0_port0 & RK_SERDES_DSI_TX0)
+			rkx120_dsi_tx_disable(serdes, route, DEVICE_REMOTE0);
+
+		if (serdes->version == SERDES_V1) {
+			rkx120_linkrx_engine_enable(serdes, 0, DEVICE_REMOTE0, false);
+			rkx110_linktx_channel_enable(serdes, 0, DEVICE_LOCAL, false);
+
+			if (route->local_port0 == RK_SERDES_DUAL_LVDS_RX) {
+				rkx110_set_stream_source(serdes, RK_SERDES_RGB_RX,
+							 DEVICE_LOCAL);
+				hwclk_reset(serdes->chip[DEVICE_LOCAL].hwclk,
+					    RKX110_SRST_RESETN_2X_LVDS_RKLINK_TX);
+				hwclk_reset(serdes->chip[DEVICE_LOCAL].hwclk,
+					    RKX110_SRST_RESETN_D_LVDS0_RKLINK_TX);
+				hwclk_reset(serdes->chip[DEVICE_LOCAL].hwclk,
+					    RKX110_SRST_RESETN_D_LVDS1_RKLINK_TX);
+			}
+
+			if ((route->local_port0 == RK_SERDES_DSI_RX0) ||
+			    (route->local_port1 == RK_SERDES_DSI_RX0)) {
+				serdes->i2c_write_reg(serdes->chip[DEVICE_LOCAL].client, 0x0314,
+						      0x1400140);
+				hwclk_reset(serdes->chip[DEVICE_LOCAL].hwclk,
+					    RKX111_SRST_RESETN_D_DSI_0_REC_RKLINK_TX);
+				hwclk_reset(serdes->chip[DEVICE_LOCAL].hwclk,
+					    RKX110_SRST_RESETN_D_DSI_0_RKLINK_TX);
+			}
+
+			if ((route->local_port0 == RK_SERDES_DSI_RX1) ||
+			    (route->local_port1 == RK_SERDES_DSI_RX1)) {
+				serdes->i2c_write_reg(serdes->chip[DEVICE_LOCAL].client, 0x0314,
+						      0x2800280);
+				hwclk_reset(serdes->chip[DEVICE_LOCAL].hwclk,
+					    RKX111_SRST_RESETN_D_DSI_1_REC_RKLINK_TX);
+				hwclk_reset(serdes->chip[DEVICE_LOCAL].hwclk,
+					    RKX110_SRST_RESETN_D_DSI_1_RKLINK_TX);
+			}
+		}
+	}
+
+	return 0;
+}
+
+static int rk_serdes_route_unprepare(struct rk_serdes *serdes, struct rk_serdes_route *route)
+{
+	if (route->stream_type == STREAM_DISPLAY) {
+		if (route->remote0_port0 & RK_SERDES_DSI_TX0)
+			rkx120_dsi_tx_post_disable(serdes, route, DEVICE_REMOTE0);
+
+		if (serdes->version == SERDES_V1) {
+			if (route->local_port0 == RK_SERDES_DUAL_LVDS_RX) {
+				hwclk_reset_deassert(serdes->chip[DEVICE_LOCAL].hwclk,
+						     RKX110_SRST_RESETN_2X_LVDS_RKLINK_TX);
+				hwclk_reset_deassert(serdes->chip[DEVICE_LOCAL].hwclk,
+						     RKX110_SRST_RESETN_D_LVDS0_RKLINK_TX);
+				hwclk_reset_deassert(serdes->chip[DEVICE_LOCAL].hwclk,
+						     RKX110_SRST_RESETN_D_LVDS1_RKLINK_TX);
+				rkx110_set_stream_source(serdes, RK_SERDES_DUAL_LVDS_RX,
+							    DEVICE_LOCAL);
+			}
+
+			if ((route->local_port0 == RK_SERDES_DSI_RX0) ||
+			    (route->local_port1 == RK_SERDES_DSI_RX0)) {
+				hwclk_reset_deassert(serdes->chip[DEVICE_LOCAL].hwclk,
+						     RKX110_SRST_RESETN_D_DSI_0_RKLINK_TX);
+				hwclk_reset_deassert(serdes->chip[DEVICE_LOCAL].hwclk,
+						     RKX111_SRST_RESETN_D_DSI_0_REC_RKLINK_TX);
+				serdes->i2c_write_reg(serdes->chip[DEVICE_LOCAL].client, 0x0314,
+						      0x1400000);
+			}
+
+			if ((route->local_port0 == RK_SERDES_DSI_RX1) ||
+			    (route->local_port1 == RK_SERDES_DSI_RX1)) {
+				hwclk_reset_deassert(serdes->chip[DEVICE_LOCAL].hwclk,
+						     RKX110_SRST_RESETN_D_DSI_1_RKLINK_TX);
+				hwclk_reset_deassert(serdes->chip[DEVICE_LOCAL].hwclk,
+						     RKX111_SRST_RESETN_D_DSI_1_REC_RKLINK_TX);
+				serdes->i2c_write_reg(serdes->chip[DEVICE_LOCAL].client, 0x0314,
+						      0x2800000);
+			}
+		}
+	}
+
+	return 0;
+}
+
+static int rk_serdes_set_hwpin(struct rk_serdes *serdes, struct i2c_client *client,
+			       int pintype, int bank, uint32_t mpins, uint32_t param)
+{
+	struct xferpin xfer;
+	char name[16];
+
+	snprintf(name, sizeof(name), "0x%x", client->addr);
+
+	xfer.name = name;
+	xfer.client = client;
+	xfer.type = pintype;
+	xfer.bank = bank;
+	xfer.mpins = mpins;
+	xfer.param = param;
+	xfer.read = serdes->i2c_read_reg;
+	xfer.write = serdes->i2c_write_reg;
+
+	return hwpin_set(xfer);
+}
+
+static void rk_serdes_add_callback(struct rk_serdes *serdes)
+{
+	serdes->i2c_read_reg = rk_serdes_i2c_read;
+	serdes->i2c_write_reg = rk_serdes_i2c_write;
+	serdes->i2c_update_bits = rk_serdes_i2c_update_bits;
+	serdes->route_prepare = rk_serdes_route_prepare;
+	serdes->route_enable = rk_serdes_route_enable;
+	serdes->route_disable = rk_serdes_route_disable;
+	serdes->route_unprepare = rk_serdes_route_unprepare;
+	serdes->set_hwpin = rk_serdes_set_hwpin;
+}
+
+static int rk_serdes_passthrough_init(struct rk_serdes *serdes)
+{
+	struct device_node *np;
+	u32 *configs;
+	char name[30] = "rk-serdes,pt";
+	int length, i, ret;
+	u32 devicerx_id, devicetx_id, func_id;
+
+	/* rk-serdes,passthrough = <devicerx_id devicetx_id passthrough_func>; */
+	for_each_child_of_node(serdes->dev->of_node, np) {
+		length = of_property_count_u32_elems(np, name);
+		if (length < 0)
+			continue;
+		if (length % 3) {
+			dev_err(serdes->dev, "Invalid count for passthrough %s\n", np->name);
+			return -EINVAL;
+		}
+		configs = kmalloc_array(length, sizeof(u32), GFP_KERNEL);
+		if (!configs)
+			return -ENOMEM;
+
+		ret = of_property_read_u32_array(np, name, configs, length);
+		if (ret) {
+			dev_err(serdes->dev, "get %s passthrough configs data error\n", np->name);
+			kfree(configs);
+			return -EINVAL;
+		}
+		for (i = 0; i < length; i += 3) {
+			devicerx_id = configs[i];
+			devicetx_id = configs[i + 1];
+			func_id = configs[i + 2];
+
+			if (serdes->stream_type == STREAM_DISPLAY) {
+				if (devicerx_id == DEVICE_LOCAL) {
+					/* soc out->rkx110 in->rkx120 out->device in */
+					rkx110_linktx_passthrough_cfg(serdes, devicerx_id, func_id,
+								      true);
+					rkx120_linkrx_passthrough_cfg(serdes, devicetx_id, func_id,
+								      false);
+				} else {
+					/* device out->rkx120 in->rkx110 out->soc in */
+					rkx110_linktx_passthrough_cfg(serdes, devicetx_id, func_id,
+								      false);
+					rkx120_linkrx_passthrough_cfg(serdes, devicerx_id, func_id,
+								      true);
+				}
+			} else {
+				if (devicerx_id == DEVICE_LOCAL) {
+					/* soc out->rkx120 in->rkx110 out->device in */
+					rkx110_linktx_passthrough_cfg(serdes, devicetx_id, func_id,
+								      false);
+					rkx120_linkrx_passthrough_cfg(serdes, devicerx_id, func_id,
+								      true);
+				} else {
+					/* device out->rkx110 in->rkx120 out->soc in */
+					rkx110_linktx_passthrough_cfg(serdes, devicerx_id, func_id,
+								      true);
+					rkx120_linkrx_passthrough_cfg(serdes, devicetx_id, func_id,
+								      false);
+				}
+			}
+			dev_info(serdes->dev, "%s: devicerx_id %x, devicetx_id %x, func_id %x\n",
+				 np->name, devicerx_id, devicetx_id, func_id);
+		}
+
+		kfree(configs);
+	}
+
+	return 0;
+}
+
+static int rk_serdes_clk_show(struct seq_file *s, void *v)
+{
+	hwclk_dump_tree(CLK_ALL);
+
+	return 0;
+}
+
+static int rk_serdes_clk_open(struct inode *inode, struct file *file)
+{
+
+	return single_open(file, rk_serdes_clk_show, NULL);
+}
+
+static const struct file_operations rk_serdes_clk_fops = {
+	.owner          = THIS_MODULE,
+	.open           = rk_serdes_clk_open,
+	.read           = seq_read,
+	.llseek         = seq_lseek,
+	.release        = single_release,
+};
+
+static int rk_serdes_rate_show(struct seq_file *s, void *v)
+{
+	struct rk_serdes *serdes = s->private;
+
+	seq_printf(s, "serdes current rate: %d\n", serdes->rate);
+
+	seq_printf(s, "%d: 2Gbps, backward: 83Mbps\n", RATE_2GBPS_83M);
+	seq_printf(s, "%d: 4Gbps, backward: 83Mbps\n", RATE_4GBPS_83M);
+	seq_printf(s, "%d: 4Gbps, backward: 125Mbps\n", RATE_4GBPS_125M);
+	seq_printf(s, "%d: 4Gbps, backward: 250Mbps\n", RATE_4GBPS_250M);
+	seq_printf(s, "%d: 4.5Gbps, backward: 140Mbps\n", RATE_4_5GBPS_140M);
+	seq_printf(s, "%d: 4.8Gbps, backward: 150Mbps\n", RATE_4_8GBPS_150M);
+	seq_printf(s, "%d: 5Gbps, backward: 156Mbps\n", RATE_5GBPS_156M);
+//	seq_printf(s, "%d: 6Gbps, backward: 187Mbps\n", RATE_6GBPS_187M);
+
+	return 0;
+}
+
+static ssize_t rk_serdes_rate_write(struct file *file, const char __user *buf,
+				size_t count, loff_t *ppos)
+{
+	struct rk_serdes *serdes = file->f_path.dentry->d_inode->i_private;
+	u32 rate;
+	char kbuf[25];
+	int ret;
+
+	if (count >= sizeof(kbuf))
+		return -ENOSPC;
+
+	if (copy_from_user(kbuf, buf, count))
+		return -EFAULT;
+
+	kbuf[count] = '\0';
+
+	ret = kstrtou32(kbuf, 10, &rate);
+	if (ret < 0)
+		return ret;
+
+	rk_serdes_set_rate(serdes, rate);
+
+	return count;
+}
+
+static int rk_serdes_rate_open(struct inode *inode, struct file *file)
+{
+	struct rk_serdes *serdes = inode->i_private;
+
+	return single_open(file, rk_serdes_rate_show, serdes);
+}
+
+static const struct file_operations rk_serdes_rate_fops = {
+	.owner          = THIS_MODULE,
+	.open           = rk_serdes_rate_open,
+	.read           = seq_read,
+	.write          = rk_serdes_rate_write,
+	.llseek         = seq_lseek,
+	.release        = single_release,
+};
+
+static void rk_serdes_function_debugfs_init(struct rk_serdes *serdes)
+{
+	serdes->debugfs_rate = debugfs_create_file("rate", 0400, serdes->debugfs_root,
+						   serdes, &rk_serdes_rate_fops);
+	serdes->debugfs_rate = debugfs_create_file("clk", 0400, serdes->debugfs_root,
+						   NULL, &rk_serdes_clk_fops);
+}
+
+static void rk_serdes_debugfs_init(struct rk_serdes *serdes)
+{
+#if defined(CONFIG_DEBUG_FS)
+	serdes->debugfs_root =
+		debugfs_create_dir(dev_name(serdes->dev), debugfs_lookup("rkserdes", NULL));
+	serdes->debugfs_local = debugfs_create_dir("local", serdes->debugfs_root);
+
+	if (rk_serdes_debug_mode(serdes)) {
+		if (serdes->rkx110_debug)
+			rkx110_debugfs_init(&serdes->chip[DEVICE_LOCAL], serdes->debugfs_local);
+		else
+			rkx120_debugfs_init(&serdes->chip[DEVICE_LOCAL], serdes->debugfs_local);
+	} else {
+		serdes->debugfs_remote0 = debugfs_create_dir("remote0", serdes->debugfs_root);
+		serdes->debugfs_remote1 = debugfs_create_dir("remote1", serdes->debugfs_root);
+
+		if (serdes->stream_type == STREAM_DISPLAY) {
+			rkx110_debugfs_init(&serdes->chip[DEVICE_LOCAL], serdes->debugfs_local);
+			rkx120_debugfs_init(&serdes->chip[DEVICE_REMOTE0], serdes->debugfs_remote0);
+			if (serdes->remote_nr == 2)
+				rkx120_debugfs_init(&serdes->chip[DEVICE_REMOTE1],
+						    serdes->debugfs_remote1);
+		} else {
+			rkx120_debugfs_init(&serdes->chip[DEVICE_LOCAL], serdes->debugfs_local);
+			rkx110_debugfs_init(&serdes->chip[DEVICE_REMOTE0], serdes->debugfs_remote0);
+			if (serdes->remote_nr == 2)
+				rkx110_debugfs_init(&serdes->chip[DEVICE_REMOTE1],
+						    serdes->debugfs_remote1);
+		}
+
+		rk_serdes_function_debugfs_init(serdes);
+	}
+#endif
+}
+
+static void rk_serdes_read_chip_id(struct rk_serdes *serdes)
+{
+	struct i2c_client *client;
+	int i;
+	u32 chip_id, local_id_reg, remote_id_reg, reg;
+	u32 version = 0;
+
+	if (serdes->stream_type == STREAM_DISPLAY) {
+		local_id_reg = SER_GRF_CHIP_ID;
+		remote_id_reg = DES_GRF_CHIP_ID;
+	} else {
+		local_id_reg = DES_GRF_CHIP_ID;
+		remote_id_reg = SER_GRF_CHIP_ID;
+	}
+
+	for (i = 0; i <= serdes->remote_nr; i++) {
+		client = serdes->chip[i].client;
+		reg = i > 0 ? remote_id_reg : local_id_reg;
+
+		serdes->i2c_read_reg(client, reg, &chip_id);
+		if (i == 0)
+			version = chip_id;
+		dev_info(&client->dev, "device%d chip_id: 0x%x\n", i, chip_id);
+	}
+
+	if (version == SERDES_VERSION_V1(serdes->stream_type))
+		serdes->version = SERDES_V1;
+	else
+		serdes->version = SERDES_V0;
+}
+
+static struct hwclk *rk_serdes_register_hwclk(struct rk_serdes *serdes, struct i2c_client *client,
+					      int idx, int clktype)
+{
+	struct xferclk xfer;
+	char name[16];
+
+	snprintf(name, sizeof(name), "0x%x", client->addr);
+
+	xfer.name = name;
+	xfer.type = clktype;
+	xfer.client = client;
+	xfer.read = serdes->i2c_read_reg;
+	xfer.write = serdes->i2c_write_reg;
+	xfer.version = serdes->version;
+
+	return hwclk_register(xfer);
+}
+
+static int rk_serdes_add_hwclk(struct rk_serdes *serdes)
+{
+	struct i2c_client *client;
+	struct hwclk *hwclk;
+	int clktype, local_clktype, remote_clktype;
+	int i;
+
+	if (serdes->stream_type == STREAM_DISPLAY) {
+		local_clktype = CLK_RKX110;
+		remote_clktype = CLK_RKX120;
+	} else {
+		local_clktype = CLK_RKX120;
+		remote_clktype = CLK_RKX110;
+	}
+
+	for (i = 0; i <= serdes->remote_nr; i++) {
+		client = serdes->chip[i].client;
+		clktype = i > 0 ? remote_clktype : local_clktype;
+		hwclk = rk_serdes_register_hwclk(serdes, client, i, clktype);
+		if (!hwclk) {
+			dev_err(serdes->dev, "Register hwclk for device%d clktype:%d failed:\n",
+				i, clktype);
+			return -EINVAL;
+		}
+		serdes->chip[i].hwclk = hwclk;
+	}
+
+	return 0;
+}
+
+static int rk_serdes_add_remote_i2c_device(struct rk_serdes *serdes)
+{
+	struct i2c_client *local_client = serdes->chip[DEVICE_LOCAL].client;
+	struct i2c_client *client;
+	int ret;
+	u32 i2c_addr;
+
+	ret = of_property_read_u32(serdes->dev->of_node, "remote0-addr", &i2c_addr);
+	if (!ret) {
+		client = devm_i2c_new_dummy_device(serdes->dev, local_client->adapter, i2c_addr);
+		if (IS_ERR(client)) {
+			dev_err(serdes->dev,
+				"failed to alloc i2c client for remote0 i2c_addr:0x%x\n", i2c_addr);
+			return -PTR_ERR(client);
+		}
+
+		serdes->chip[DEVICE_REMOTE0].client = client;
+		serdes->chip[DEVICE_REMOTE0].is_remote = true;
+		serdes->chip[DEVICE_REMOTE0].serdes = serdes;
+		serdes->remote_nr++;
+		i2c_set_clientdata(client, serdes);
+	}
+
+	ret = of_property_read_u32(serdes->dev->of_node, "remote1-addr", &i2c_addr);
+	if (!ret) {
+		if (serdes->remote_nr > 0) {
+			u32 remote0_addr = serdes->chip[DEVICE_REMOTE0].client->addr;
+
+			if (i2c_addr == remote0_addr) {
+				dev_err(serdes->dev, "remote devices i2c addr must be different\n");
+				return -EINVAL;
+			}
+		}
+
+		client = devm_i2c_new_dummy_device(serdes->dev, local_client->adapter, i2c_addr);
+		if (IS_ERR(client)) {
+			dev_err(serdes->dev, "failed to alloc i2c device\n");
+			return -PTR_ERR(client);
+		}
+
+		serdes->chip[DEVICE_REMOTE1].client = client;
+		serdes->chip[DEVICE_REMOTE1].is_remote = true;
+		serdes->chip[DEVICE_REMOTE1].serdes = serdes;
+		serdes->remote_nr++;
+		i2c_set_clientdata(client, serdes);
+	}
+
+	if (serdes->remote_nr == 0)
+		return -ENODEV;
+
+	return 0;
+}
+
+static int rk_serdes_pinctrl_init(struct rk_serdes *serdes)
+{
+	struct device_node *np;
+	struct i2c_client *client;
+	u32 *configs;
+	char name[20] = "rk-serdes,pins";
+	int length, i, ret;
+	u32 client_id, bank, pins, pin_configs, pin_type;
+
+	/* rk-serdes,pins = <client_id bank_id pins pin_configs>; */
+	for_each_child_of_node(serdes->dev->of_node, np) {
+		length = of_property_count_u32_elems(np, name);
+		if (length < 0)
+			continue;
+		if (length % 4) {
+			dev_err(serdes->dev, "Invalid count for pinctrl %s\n", np->name);
+			return -EINVAL;
+		}
+		configs = kmalloc_array(length, sizeof(u32), GFP_KERNEL);
+		if (!configs)
+			return -ENOMEM;
+
+		ret = of_property_read_u32_array(np, name, configs, length);
+		if (ret) {
+			dev_err(serdes->dev, "get %s configs data error\n", np->name);
+			kfree(configs);
+			return -EINVAL;
+		}
+		for (i = 0; i < length; i += 4) {
+			client_id = configs[i];
+			bank = configs[i + 1];
+			pins = configs[i + 2];
+			pin_configs = configs[i + 3];
+			pin_type = client_id ? PIN_RKX120 : PIN_RKX110;
+			client = serdes->chip[client_id].client;
+			serdes->set_hwpin(serdes, client, pin_type, bank, pins, pin_configs);
+			dev_dbg(serdes->dev, "%s: client_id %x, bank %x, pins %x pin_configs %x\n",
+				np->name, client_id, bank, pins, pin_configs);
+		}
+
+		kfree(configs);
+	}
+
+	return 0;
+}
+
+static int rk_serdes_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+	struct device *dev = &client->dev;
+	struct rk_serdes *serdes;
+	int ret;
+
+	serdes = devm_kzalloc(dev, sizeof(*serdes), GFP_KERNEL);
+	if (!serdes)
+		return -ENOMEM;
+
+	serdes->dev = dev;
+	serdes->chip[DEVICE_LOCAL].client = client;
+	serdes->chip[DEVICE_LOCAL].is_remote = false;
+	serdes->chip[DEVICE_LOCAL].serdes = serdes;
+	i2c_set_clientdata(client, serdes);
+
+	rk_serdes_add_callback(serdes);
+
+	if (of_device_is_compatible(dev->of_node, "rockchip,rkx110-debug")) {
+		serdes->rkx110_debug = true;
+		dev_info(dev, "rkx110 debug mode");
+	}
+
+	if (of_device_is_compatible(dev->of_node, "rockchip,rkx120-debug")) {
+		serdes->rkx120_debug = true;
+		dev_info(dev, "rkx120 debug mode");
+	}
+
+	if (rk_serdes_debug_mode(serdes))
+		goto out;
+
+	serdes->rate = RATE_2GBPS_83M;
+
+	serdes->enable = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_LOW);
+	if (IS_ERR(serdes->enable)) {
+		ret = PTR_ERR(serdes->enable);
+		dev_err(dev, "failed to request enable GPIO: %d\n", ret);
+		return ret;
+	}
+
+	serdes->reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
+	if (IS_ERR(serdes->reset)) {
+		ret = PTR_ERR(serdes->reset);
+		dev_err(dev, "failed to request reset GPIO: %d\n", ret);
+		return ret;
+	}
+
+	gpiod_set_value(serdes->enable, 1);
+
+	gpiod_set_value(serdes->reset, 1);
+	usleep_range(10000, 11000);
+	gpiod_set_value(serdes->reset, 0);
+
+	if (of_get_child_by_name(dev->of_node, "serdes-panel")) {
+		serdes->stream_type = STREAM_DISPLAY;
+		of_node_put(dev->of_node);
+		dev_info(dev, "serdes display stream");
+	} else {
+		serdes->stream_type = STREAM_CAMERA;
+		dev_info(dev, "serdes camera stream");
+	}
+
+	ret = rk_serdes_add_remote_i2c_device(serdes);
+	if (ret)
+		return ret;
+
+	msleep(20);
+
+	ret = mfd_add_devices(dev, -1, rkx110_x120_devs, ARRAY_SIZE(rkx110_x120_devs),
+			      NULL, 0, NULL);
+	if (ret) {
+		dev_err(dev, "failed to add subdev: %d\n", ret);
+		return ret;
+	}
+
+	rk_serdes_wait_link_ready(serdes);
+
+	rk_serdes_read_chip_id(serdes);
+
+	ret = rk_serdes_add_hwclk(serdes);
+	if (ret < 0)
+		return ret;
+
+	rk_serdes_set_rate(serdes, RATE_4GBPS_83M);
+	rk_serdes_pinctrl_init(serdes);
+	rk_serdes_passthrough_init(serdes);
+out:
+	rk_serdes_debugfs_init(serdes);
+
+	return 0;
+}
+
+static int rk_serdes_i2c_remove(struct i2c_client *client)
+{
+	struct rk_serdes *rk_serdes = i2c_get_clientdata(client);
+
+	mfd_remove_devices(rk_serdes->dev);
+
+	return 0;
+}
+
+static const struct of_device_id rk_serdes_of_match[] = {
+	{ .compatible = "rockchip,rkx110", },
+	{ .compatible = "rockchip,rkx110-debug", },
+	{ .compatible = "rockchip,rkx120", },
+	{ .compatible = "rockchip,rkx120-debug", },
+	{}
+};
+MODULE_DEVICE_TABLE(of, rk_serdes_of_match);
+
+static const struct i2c_device_id rk_serdes_i2c_id[] = {
+	{ "rk_serdes", 0 },
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, rk_serdes_i2c_id);
+
+static struct i2c_driver rk_serdes_i2c_driver = {
+	.driver = {
+		.name = "rk_serdes",
+		.of_match_table = of_match_ptr(rk_serdes_of_match),
+	},
+	.probe = rk_serdes_i2c_probe,
+	.remove = rk_serdes_i2c_remove,
+	.id_table = rk_serdes_i2c_id,
+};
+
+static int __init rk_serdes_i2c_driver_init(void)
+{
+	debugfs_create_dir("rkserdes", NULL);
+	return i2c_add_driver(&rk_serdes_i2c_driver);
+}
+module_init(rk_serdes_i2c_driver_init);
+
+static void __exit rk_serdes_i2c_driver_exit(void)
+{
+	debugfs_remove_recursive(debugfs_lookup("rkserdes", NULL));
+	i2c_del_driver(&rk_serdes_i2c_driver);
+}
+module_exit(rk_serdes_i2c_driver_exit);
+
+MODULE_AUTHOR("Zhang Yubing <yubing.zhang@rock-chips.com>");
+MODULE_DESCRIPTION("Rockchip RK Parus MFD driver");
+MODULE_LICENSE("GPL");
diff --git a/kernel/drivers/mfd/rkx110_x120/rkx110_x120_panel.c b/kernel/drivers/mfd/rkx110_x120/rkx110_x120_panel.c
new file mode 100644
index 0000000..38b76f0
--- /dev/null
+++ b/kernel/drivers/mfd/rkx110_x120/rkx110_x120_panel.c
@@ -0,0 +1,642 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * rockchip SerDes Panele driver for drm platform
+ *
+ * Copyright (C) 2022 Rockchip Electronics Co. Ltd.
+ */
+
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/of_graph.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/gpio/consumer.h>
+#include <linux/regulator/consumer.h>
+#include <linux/delay.h>
+
+#include <video/display_timing.h>
+#include <video/of_display_timing.h>
+#include <video/videomode.h>
+
+#include <drm/drm_crtc.h>
+#include <drm/drm_mipi_dsi.h>
+#include <drm/drm_panel.h>
+
+#include "rkx110_x120.h"
+#include "rkx120_dsi_tx.h"
+
+static inline struct rk_serdes_panel *to_serdes_panel(struct drm_panel *panel)
+{
+	return container_of(panel, struct rk_serdes_panel, panel);
+}
+
+static int serdes_panel_prepare(struct drm_panel *panel)
+{
+	struct rk_serdes_panel *sd_panel = to_serdes_panel(panel);
+	struct rk_serdes_route *route = &sd_panel->route;
+	struct rk_serdes *serdes = sd_panel->parent;
+
+	if (sd_panel->supply) {
+		int err;
+
+		err = regulator_enable(sd_panel->supply);
+		if (err < 0) {
+			dev_err(sd_panel->dev, "failed to enable supply: %d\n",
+				err);
+			return err;
+		}
+		mdelay(20);
+	}
+
+	if (sd_panel->enable_gpio) {
+		gpiod_set_value_cansleep(sd_panel->enable_gpio, 1);
+		mdelay(20);
+	}
+
+	if (sd_panel->reset_gpio) {
+		gpiod_set_value_cansleep(sd_panel->reset_gpio, 1);
+		mdelay(20);
+	}
+
+	if (serdes->route_prepare)
+		serdes->route_prepare(serdes, &sd_panel->route);
+
+	if (route->remote0_port0 == RK_SERDES_DSI_TX0 && !!(sd_panel->on_cmds))
+		rkx120_dsi_tx_cmd_seq_xfer(serdes, DEVICE_REMOTE0,
+					   sd_panel->on_cmds);
+
+	if (route->remote1_port0 == RK_SERDES_DSI_TX0 && !!(sd_panel->on_cmds))
+		rkx120_dsi_tx_cmd_seq_xfer(serdes, DEVICE_REMOTE1,
+					   sd_panel->on_cmds);
+
+	return 0;
+}
+
+static int serdes_panel_enable(struct drm_panel *panel)
+{
+	struct rk_serdes_panel *sd_panel = to_serdes_panel(panel);
+	struct rk_serdes *serdes = sd_panel->parent;
+
+	if (serdes->route_enable)
+		serdes->route_enable(serdes, &sd_panel->route);
+
+	return 0;
+}
+
+static int serdes_panel_disable(struct drm_panel *panel)
+{
+	struct rk_serdes_panel *sd_panel = to_serdes_panel(panel);
+	struct rk_serdes *serdes = sd_panel->parent;
+
+
+	if (serdes->route_disable)
+		serdes->route_disable(serdes, &sd_panel->route);
+
+	return 0;
+}
+
+static int serdes_panel_unprepare(struct drm_panel *panel)
+{
+	struct rk_serdes_panel *sd_panel = to_serdes_panel(panel);
+	struct rk_serdes_route *route = &sd_panel->route;
+	struct rk_serdes *serdes = sd_panel->parent;
+
+	if (route->remote0_port0 == RK_SERDES_DSI_TX0 && !!(sd_panel->on_cmds))
+		rkx120_dsi_tx_cmd_seq_xfer(serdes, DEVICE_REMOTE0,
+					   sd_panel->off_cmds);
+
+	if (route->remote1_port0 == RK_SERDES_DSI_TX0 && !!(sd_panel->on_cmds))
+		rkx120_dsi_tx_cmd_seq_xfer(serdes, DEVICE_REMOTE1,
+					   sd_panel->off_cmds);
+	if (serdes->route_unprepare)
+		serdes->route_unprepare(serdes, &sd_panel->route);
+
+	if (sd_panel->reset_gpio) {
+		gpiod_set_value_cansleep(sd_panel->reset_gpio, 0);
+		mdelay(20);
+	}
+
+	if (sd_panel->enable_gpio) {
+		gpiod_set_value_cansleep(sd_panel->enable_gpio, 0);
+		mdelay(20);
+	}
+
+	if (sd_panel->supply)
+		regulator_disable(sd_panel->supply);
+
+	return 0;
+}
+
+static int serdes_panel_of_get_native_mode(struct rk_serdes_panel *sd_panel,
+					   struct drm_connector *connector)
+{
+	struct rk_serdes_route *route = &sd_panel->route;
+	struct device_node *timings_np;
+	struct drm_display_mode *mode;
+	struct drm_device *drm = connector->dev;
+	u32 bus_flags;
+	int ret;
+
+	timings_np = of_get_child_by_name(sd_panel->dev->of_node, "display-timings");
+	if (!timings_np) {
+		dev_err(sd_panel->dev, "failed to find display-timings node\n");
+		return 0;
+	}
+
+	of_node_put(timings_np);
+
+	mode = drm_mode_create(drm);
+	if (!mode)
+		return 0;
+
+	ret = of_get_drm_display_mode(sd_panel->dev->of_node, mode,
+				      &bus_flags, OF_USE_NATIVE_MODE);
+	if (ret) {
+		dev_err(sd_panel->dev, "failed to find dts display timings\n");
+		drm_mode_destroy(drm, mode);
+		return 0;
+	}
+
+	drm_mode_set_name(mode);
+	connector->display_info.bus_flags = bus_flags;
+	mode->type |= DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
+	drm_mode_probed_add(connector, mode);
+	drm_display_mode_to_videomode(mode, &sd_panel->route.vm);
+
+	if (route->frame_mode == SERDES_SP_LEFT_RIGHT_SPLIT ||
+	    route->frame_mode == SERDES_SP_PIXEL_INTERLEAVED) {
+		sd_panel->route.vm.pixelclock /= 2;
+		sd_panel->route.vm.hactive /= 2;
+		sd_panel->route.vm.hfront_porch /= 2;
+		sd_panel->route.vm.hback_porch /= 2;
+		sd_panel->route.vm.hsync_len /= 2;
+	} else if (route->frame_mode == SERDES_SP_LINE_INTERLEAVED) {
+		sd_panel->route.vm.pixelclock /= 2;
+		sd_panel->route.vm.vactive /= 2;
+		sd_panel->route.vm.vfront_porch /= 2;
+		sd_panel->route.vm.vback_porch /= 2;
+		sd_panel->route.vm.vsync_len /= 2;
+	}
+
+	return 1;
+}
+
+static int serdes_panel_get_modes(struct drm_panel *panel,
+				struct drm_connector *connector)
+{
+	struct rk_serdes_panel *sd_panel = to_serdes_panel(panel);
+	int num = 0;
+
+	num += serdes_panel_of_get_native_mode(sd_panel, connector);
+	drm_display_info_set_bus_formats(&connector->display_info,
+					 &sd_panel->bus_format, 1);
+
+	return num;
+}
+
+static const struct drm_panel_funcs serdes_panel_funcs = {
+	.prepare = serdes_panel_prepare,
+	.enable = serdes_panel_enable,
+	.disable = serdes_panel_disable,
+	.unprepare = serdes_panel_unprepare,
+	.get_modes = serdes_panel_get_modes,
+};
+
+static int
+dsi_panel_parse_cmds(struct device *dev, const u8 *data,
+		     int blen, struct panel_cmds *pcmds)
+{
+	unsigned int len;
+	char *buf, *bp;
+	struct cmd_ctrl_hdr *dchdr;
+	int i, cnt;
+
+	if (!pcmds)
+		return -EINVAL;
+
+	buf = devm_kmemdup(dev, data, blen, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	/* scan init commands */
+	bp = buf;
+	len = blen;
+	cnt = 0;
+	while (len > sizeof(*dchdr)) {
+		dchdr = (struct cmd_ctrl_hdr *)bp;
+
+		if (dchdr->dlen > len) {
+			dev_err(dev, "%s: error, len=%d", __func__, dchdr->dlen);
+			return -EINVAL;
+		}
+
+		bp += sizeof(*dchdr);
+		len -= sizeof(*dchdr);
+		bp += dchdr->dlen;
+		len -= dchdr->dlen;
+		cnt++;
+	}
+
+	if (len != 0) {
+		dev_err(dev, "%s: dcs_cmd=%x len=%d error!", __func__, buf[0], blen);
+		return -EINVAL;
+	}
+
+	pcmds->cmds = devm_kcalloc(dev, cnt, sizeof(struct cmd_desc), GFP_KERNEL);
+	if (!pcmds->cmds)
+		return -ENOMEM;
+
+	pcmds->cmd_cnt = cnt;
+	pcmds->buf = buf;
+	pcmds->blen = blen;
+
+	bp = buf;
+	len = blen;
+	for (i = 0; i < cnt; i++) {
+		dchdr = (struct cmd_ctrl_hdr *)bp;
+		len -= sizeof(*dchdr);
+		bp += sizeof(*dchdr);
+		pcmds->cmds[i].dchdr = *dchdr;
+		pcmds->cmds[i].payload = bp;
+		bp += dchdr->dlen;
+		len -= dchdr->dlen;
+	}
+
+	return 0;
+}
+
+static int serdes_dsi_panel_get_cmds(struct rk_serdes_panel *sd_panel)
+{
+	struct device_node *np = sd_panel->dev->of_node;
+	struct device *dev = sd_panel->dev;
+	const void *data;
+	int len, err;
+
+	data = of_get_property(np, "panel-init-sequence", &len);
+	if (data) {
+		sd_panel->on_cmds = devm_kzalloc(dev, sizeof(*sd_panel->on_cmds), GFP_KERNEL);
+		if (!sd_panel->on_cmds)
+			return -ENOMEM;
+
+		err = dsi_panel_parse_cmds(dev, data, len, sd_panel->on_cmds);
+		if (err) {
+			dev_err(dev, "failed to parse dsi panel init sequence\n");
+			return err;
+		}
+	}
+
+	data = of_get_property(np, "panel-exit-sequence", &len);
+	if (data) {
+		sd_panel->off_cmds = devm_kzalloc(dev, sizeof(*sd_panel->off_cmds), GFP_KERNEL);
+		if (!sd_panel->off_cmds)
+			return -ENOMEM;
+
+		err = dsi_panel_parse_cmds(dev, data, len, sd_panel->off_cmds);
+		if (err) {
+			dev_err(dev, "failed to parse dsi panel exit sequence\n");
+			return err;
+		}
+	}
+
+	return 0;
+}
+
+static struct mipi_dsi_device *serdes_attach_dsi(struct rk_serdes_panel *sd_panel,
+						 struct device_node *dsi_node)
+{
+	const struct mipi_dsi_device_info info = { "serdes", 0, NULL };
+	struct mipi_dsi_device *dsi;
+	struct mipi_dsi_host *host;
+	int ret;
+
+	host = of_find_mipi_dsi_host_by_node(dsi_node);
+	if (!host) {
+		dev_err(sd_panel->dev, "failed to find dsi host\n");
+		return ERR_PTR(-EPROBE_DEFER);
+	}
+
+	dsi = mipi_dsi_device_register_full(host, &info);
+	if (IS_ERR(dsi)) {
+		dev_err(sd_panel->dev, "failed to create dsi device\n");
+		return dsi;
+	}
+
+	dsi->lanes = 4;
+	dsi->format = MIPI_DSI_FMT_RGB888;
+	dsi->mode_flags = MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_EOT_PACKET |
+			  MIPI_DSI_CLOCK_NON_CONTINUOUS;
+
+	ret = mipi_dsi_attach(dsi);
+	if (ret < 0) {
+		dev_err(sd_panel->dev, "failed to attach dsi to host\n");
+		mipi_dsi_device_unregister(dsi);
+		return ERR_PTR(ret);
+	}
+
+	return dsi;
+}
+
+static int rkx120_dsi_rx_parse(struct rk_serdes_panel *sd_panel)
+{
+	struct device_node *np = sd_panel->dev->of_node;
+	struct rk_serdes *serdes = sd_panel->parent;
+	struct rkx110_dsi_rx *dsi_rx = &serdes->dsi_rx;
+	struct mipi_dsi_device *dsi;
+	struct device_node *dsi_node;
+	u32 val;
+
+	if (of_property_read_u32(np, "dsi-rx,lanes", &val))
+		dsi_rx->lanes = 4;
+	else
+		dsi_rx->lanes = val;
+
+	if (of_property_read_bool(np, "dsi-rx,video-mode"))
+		dsi_rx->mode_flags |= SERDES_MIPI_DSI_MODE_LPM | SERDES_MIPI_DSI_MODE_VIDEO |
+				      SERDES_MIPI_DSI_MODE_VIDEO_BURST;
+	else
+		dsi_rx->mode_flags |= SERDES_MIPI_DSI_MODE_LPM;
+
+	dsi_node = of_graph_get_remote_node(np, 0, -1);
+	if (!dsi_node)
+		dev_err(sd_panel->dev, "failed to get remote node for primary dsi\n");
+
+	dsi = serdes_attach_dsi(sd_panel, dsi_node);
+	if (IS_ERR(dsi))
+		return -EPROBE_DEFER;
+
+	return 0;
+}
+
+static int rkx120_dsi_tx_parse(struct rk_serdes_panel *sd_panel)
+{
+	struct device_node *np = sd_panel->dev->of_node;
+	struct rk_serdes *serdes = sd_panel->parent;
+	struct rkx120_dsi_tx *dsi_tx = &serdes->dsi_tx;
+	const char *string;
+	int ret;
+	u32 val;
+
+	if (of_property_read_u32(np, "dsi-tx,lanes", &val))
+		dsi_tx->lanes = 4;
+	else
+		dsi_tx->lanes = val;
+
+	if (of_property_read_bool(np, "dsi-tx,video-mode"))
+		dsi_tx->mode_flags |= SERDES_MIPI_DSI_MODE_LPM | SERDES_MIPI_DSI_MODE_VIDEO |
+				      SERDES_MIPI_DSI_MODE_VIDEO_BURST;
+	else
+		dsi_tx->mode_flags |= SERDES_MIPI_DSI_MODE_LPM;
+
+	if (of_property_read_bool(np, "dsi-tx,eotp"))
+		dsi_tx->mode_flags |= SERDES_MIPI_DSI_MODE_EOT_PACKET;
+
+	if (!of_property_read_string(np, "dsi-tx,format", &string)) {
+		if (!strcmp(string, "rgb666")) {
+			dsi_tx->bus_format = SERDES_MIPI_DSI_FMT_RGB666;
+			dsi_tx->bpp = 24;
+		} else if (!strcmp(string, "rgb666-packed")) {
+			dsi_tx->bus_format = SERDES_MIPI_DSI_FMT_RGB666_PACKED;
+			dsi_tx->bpp = 18;
+		} else if (!strcmp(string, "rgb565")) {
+			dsi_tx->bus_format = SERDES_MIPI_DSI_FMT_RGB565;
+			dsi_tx->bpp = 16;
+		} else {
+			dsi_tx->bus_format = SERDES_MIPI_DSI_FMT_RGB888;
+			dsi_tx->bpp = 24;
+		}
+	} else {
+		dsi_tx->bus_format = SERDES_MIPI_DSI_FMT_RGB888;
+		dsi_tx->bpp = 24;
+	}
+
+	ret = serdes_dsi_panel_get_cmds(sd_panel);
+	if (ret) {
+		dev_err(sd_panel->dev, "failed to get cmds\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int serdes_panel_parse_dt(struct rk_serdes_panel *sd_panel)
+{
+	struct rk_serdes_route *route = &sd_panel->route;
+	struct rk_serdes *serdes = sd_panel->parent;
+	u32 lanes;
+	int ret;
+
+	device_property_read_u32(sd_panel->dev, "local-port0", &route->local_port0);
+	device_property_read_u32(sd_panel->dev, "local-port1", &route->local_port1);
+	device_property_read_u32(sd_panel->dev, "remote0-port0", &route->remote0_port0);
+	device_property_read_u32(sd_panel->dev, "remote0-port1", &route->remote0_port1);
+	device_property_read_u32(sd_panel->dev, "remote1-port0", &route->remote1_port0);
+	device_property_read_u32(sd_panel->dev, "remote1-port1", &route->remote1_port1);
+	device_property_read_u32(sd_panel->dev, "num-lanes", &lanes);
+
+	serdes->route_flag = 0;
+
+	if (!route->local_port0) {
+		dev_err(sd_panel->dev, "local_port0 should set\n");
+		return -EINVAL;
+	}
+
+	if (!route->remote0_port0) {
+		dev_err(sd_panel->dev, "remote0_port0 should set\n");
+		return -EINVAL;
+	}
+
+	if (route->remote1_port0 && route->remote0_port1) {
+		dev_err(sd_panel->dev, "too many output\n");
+		return -EINVAL;
+	}
+
+	route->frame_mode = SERDES_FRAME_NORMAL_MODE;
+
+	/* 2 video stream output */
+	if (route->remote1_port0 || route->remote0_port1) {
+		if (route->remote1_port0)
+			serdes->route_flag |= ROUTE_MULTI_REMOTE | ROUTE_MULTI_CHANNEL |
+					     ROUTE_MULTI_LANE;
+
+		if (route->remote0_port1) {
+			if ((route->remote0_port0 == RK_SERDES_LVDS_TX0) &&
+			    (route->remote0_port1 == RK_SERDES_LVDS_TX1)) {
+				serdes->route_flag |= ROUTE_MULTI_CHANNEL;
+			} else if ((route->remote0_port0 == RK_SERDES_LVDS_TX1) &&
+				    (route->remote0_port1 == RK_SERDES_LVDS_TX0)) {
+				serdes->route_flag |= ROUTE_MULTI_CHANNEL;
+			} else {
+				dev_err(sd_panel->dev, "invalid multi output type\n");
+				return -EINVAL;
+			}
+
+			if (lanes == 2)
+				serdes->route_flag |= ROUTE_MULTI_LANE;
+		}
+
+		if (route->local_port1) {
+			if ((route->local_port0 == RK_SERDES_DSI_RX0) &&
+			    (route->local_port1 == RK_SERDES_DSI_RX1))
+				serdes->route_flag |= ROUTE_MULTI_DSI_INPUT;
+			else if ((route->local_port0 == RK_SERDES_DSI_RX1) &&
+				 (route->local_port1 == RK_SERDES_DSI_RX0))
+				serdes->route_flag |= ROUTE_MULTI_DSI_INPUT;
+			else if ((route->local_port0 == RK_SERDES_LVDS_RX0) &&
+				 (route->local_port1 == RK_SERDES_LVDS_RX1))
+				serdes->route_flag |= ROUTE_MULTI_LVDS_INPUT;
+			else if ((route->local_port0 == RK_SERDES_LVDS_RX1) &&
+				 (route->local_port1 == RK_SERDES_LVDS_RX0))
+				serdes->route_flag |= ROUTE_MULTI_LVDS_INPUT;
+			else {
+				dev_err(sd_panel->dev, "invalid multi input type\n");
+				return -EINVAL;
+			}
+			serdes->route_flag |= ROUTE_MULTI_SOURCE;
+		} else {
+			if (device_property_read_bool(sd_panel->dev, "split-mode")) {
+				/* only dsi input support split mode */
+				if ((route->local_port0 != RK_SERDES_DSI_RX0) &&
+				    (route->local_port0 != RK_SERDES_DSI_RX1)) {
+					dev_err(sd_panel->dev,
+						"local_port should be dsi in split mode\n");
+					return -EINVAL;
+				}
+				if (device_property_read_bool(sd_panel->dev,
+							      "sf-pixel-interleaved"))
+					route->frame_mode = SERDES_SP_PIXEL_INTERLEAVED;
+				else if (device_property_read_bool(sd_panel->dev,
+								   "sf-line-interleaved"))
+					route->frame_mode = SERDES_SP_LINE_INTERLEAVED;
+				else
+					route->frame_mode = SERDES_SP_LEFT_RIGHT_SPLIT;
+
+				serdes->route_flag |= ROUTE_MULTI_SPLIT;
+
+			} else  {
+				serdes->route_flag |= ROUTE_MULTI_MIRROR;
+			}
+		}
+	} else {
+		if (lanes == 2)
+			serdes->route_flag |= ROUTE_MULTI_LANE;
+	}
+
+	if (route->remote0_port0 & RK_SERDES_DSI_TX0 ||
+	    route->remote1_port0 & RK_SERDES_DSI_TX0) {
+		ret = rkx120_dsi_tx_parse(sd_panel);
+		if (ret) {
+			dev_err(sd_panel->dev, "failed to get cmds\n");
+			return ret;
+		}
+	}
+
+	if (route->local_port0 & RK_SERDES_DSI_RX0 ||
+	    route->local_port0 & RK_SERDES_DSI_TX1) {
+		ret = rkx120_dsi_rx_parse(sd_panel);
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int serdes_panel_probe(struct platform_device *pdev)
+{
+	struct rk_serdes *serdes = dev_get_drvdata(pdev->dev.parent);
+	struct rk_serdes_panel *sd_panel;
+	int ret;
+
+	sd_panel = devm_kzalloc(&pdev->dev, sizeof(*sd_panel), GFP_KERNEL);
+	if (!sd_panel)
+		return -ENOMEM;
+
+	sd_panel->dev = &pdev->dev;
+	sd_panel->parent = serdes;
+	sd_panel->route.stream_type = STREAM_DISPLAY;
+	serdes->vm = &sd_panel->route.vm;
+
+	sd_panel->supply = devm_regulator_get_optional(sd_panel->dev, "power");
+	if (IS_ERR(sd_panel->supply)) {
+		ret = PTR_ERR(sd_panel->supply);
+
+		if (ret != -ENODEV) {
+			if (ret != -EPROBE_DEFER)
+				dev_err(sd_panel->dev, "failed to request regulator: %d\n",
+					ret);
+			return ret;
+		}
+
+		sd_panel->supply = NULL;
+	}
+
+	/* Get GPIOs and backlight controller. */
+	sd_panel->enable_gpio = devm_gpiod_get_optional(sd_panel->dev, "enable",
+							GPIOD_OUT_LOW);
+	if (IS_ERR(sd_panel->enable_gpio)) {
+		ret = PTR_ERR(sd_panel->enable_gpio);
+		dev_err(sd_panel->dev, "failed to request %s GPIO: %d\n",
+			"enable", ret);
+		return ret;
+	}
+
+	sd_panel->reset_gpio = devm_gpiod_get_optional(sd_panel->dev, "reset",
+						       GPIOD_OUT_HIGH);
+	if (IS_ERR(sd_panel->reset_gpio)) {
+		ret = PTR_ERR(sd_panel->reset_gpio);
+		dev_err(sd_panel->dev, "failed to request %s GPIO: %d\n",
+			"reset", ret);
+		return ret;
+	}
+
+	/* Register the panel. */
+	drm_panel_init(&sd_panel->panel, sd_panel->dev, &serdes_panel_funcs, 0);
+
+	ret = drm_panel_of_backlight(&sd_panel->panel);
+	if (ret)
+		return ret;
+
+	drm_panel_add(&sd_panel->panel);
+
+	dev_set_drvdata(sd_panel->dev, sd_panel);
+
+	ret = serdes_panel_parse_dt(sd_panel);
+	if (ret < 0) {
+		drm_panel_remove(&sd_panel->panel);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int serdes_panel_remove(struct platform_device *pdev)
+{
+	struct rk_serdes_panel *sd_panel = dev_get_drvdata(&pdev->dev);
+
+	drm_panel_remove(&sd_panel->panel);
+
+	drm_panel_disable(&sd_panel->panel);
+
+	return 0;
+}
+
+static const struct of_device_id serdes_panel_of_table[] = {
+	{ .compatible = "rockchip,serdes-panel", },
+	{ /* Sentinel */ },
+};
+
+MODULE_DEVICE_TABLE(of, serdes_panel_of_table);
+
+static struct platform_driver serdes_panel_driver = {
+	.probe		= serdes_panel_probe,
+	.remove		= serdes_panel_remove,
+	.driver		= {
+		.name	= "serdes-panel",
+		.of_match_table = serdes_panel_of_table,
+	},
+};
+
+module_platform_driver(serdes_panel_driver);
+
+MODULE_AUTHOR("Zhang Yubing <yubing.zhang@rock-chips.com>");
+MODULE_DESCRIPTION("RKx110 RKx120 Panel Driver");
+MODULE_LICENSE("GPL");
diff --git a/kernel/drivers/mfd/rkx110_x120/rkx120.c b/kernel/drivers/mfd/rkx110_x120/rkx120.c
new file mode 100644
index 0000000..75150fe
--- /dev/null
+++ b/kernel/drivers/mfd/rkx110_x120/rkx120.c
@@ -0,0 +1,309 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2022 Rockchip Electronics Co. Ltd.
+ *
+ * Author: Andy Yan <Andy.yan@rock-chips.com>
+ */
+
+#include <dt-bindings/mfd/rockchip-serdes.h>
+#include <linux/debugfs.h>
+
+#include "hal/cru_api.h"
+#include "hal/pinctrl_api.h"
+#include "rkx110_x120.h"
+#include "rkx120_reg.h"
+#include "serdes_combphy.h"
+
+#if defined(CONFIG_DEBUG_FS)
+static struct pattern_gen rkx120_pattern_gen[] = {
+	{
+		.name = "dsi",
+		.base = RKX120_PATTERN_GEN_DSI_BASE,
+		.link_src_reg = DES_GRF_SOC_CON2,
+		.link_src_offset = 12,
+	}, {
+		.name = "lvds0",
+		.base = RKX120_PATTERN_GEN_LVDS0_BASE,
+		.link_src_reg = DES_GRF_SOC_CON2,
+		.link_src_offset = 13,
+	}, {
+		.name = "lvds1",
+		.base = RKX120_PATTERN_GEN_LVDS1_BASE,
+		.link_src_reg = DES_GRF_SOC_CON2,
+		.link_src_offset = 14,
+	},
+	{ /* sentinel */ }
+};
+
+static const struct rk_serdes_reg rkx120_regs[] = {
+	{
+		.name = "grf",
+		.reg_base = RKX120_DES_GRF_BASE,
+		.reg_len = 0x410,
+	},
+	{
+		.name = "cru",
+		.reg_base = RKX120_DES_CRU_BASE,
+		.reg_len = 0xF04,
+	},
+	{
+		.name = "dvp",
+		.reg_base = RKX120_DVP_TX_BASE,
+		.reg_len = 0x1d0,
+	},
+
+	{
+		.name = "grf_mipi0",
+		.reg_base = RKX120_GRF_MIPI0_BASE,
+		.reg_len = 0x600,
+	},
+	{
+		.name = "grf_mipi1",
+		.reg_base = RKX120_GRF_MIPI1_BASE,
+		.reg_len = 0x600,
+	},
+	{
+		.name = "mipi_lvds_phy0",
+		.reg_base = RKX120_MIPI_LVDS_TX_PHY0_BASE,
+		.reg_len = 0x70,
+	},
+	{
+		.name = "mipi_lvds_phy1",
+		.reg_base = RKX120_MIPI_LVDS_TX_PHY1_BASE,
+		.reg_len = 0x70,
+	},
+	{
+		.name = "dsihost",
+		.reg_base = RKX120_DSI_TX_BASE,
+		.reg_len = 0x190,
+	},
+	{
+		.name = "gpio0",
+		.reg_base = RKX120_GPIO0_TX_BASE,
+		.reg_len = 0x80,
+	},
+	{
+		.name = "gpio1",
+		.reg_base = RKX120_GPIO1_TX_BASE,
+		.reg_len = 0x80,
+	},
+	{
+		.name = "csi_tx0",
+		.reg_base = RKX120_CSI_TX0_BASE,
+		.reg_len = 0x1D0,
+	},
+	{
+		.name = "csi_tx1",
+		.reg_base = RKX120_CSI_TX1_BASE,
+		.reg_len = 0x1D0,
+	},
+	{
+		.name = "rklink",
+		.reg_base = RKX120_DES_RKLINK_BASE,
+		.reg_len = 0xD4,
+	},
+	{
+		.name = "pcs0",
+		.reg_base = RKX120_DES_PCS0_BASE,
+		.reg_len = 0x1c0,
+	},
+	{
+		.name = "pcs1",
+		.reg_base = RKX120_DES_PCS1_BASE,
+		.reg_len = 0x1c0,
+	},
+	{
+		.name = "pma0",
+		.reg_base = RKX120_DES_PMA0_BASE,
+		.reg_len = 0x100,
+	},
+	{
+		.name = "pma1",
+		.reg_base = RKX120_DES_PMA1_BASE,
+		.reg_len = 0x100,
+	},
+	{
+		.name = "dsi_pattern_gen",
+		.reg_base = RKX120_PATTERN_GEN_DSI_BASE,
+		.reg_len = 0x18,
+	},
+	{
+		.name = "lvds0_pattern_gen",
+		.reg_base = RKX120_PATTERN_GEN_LVDS0_BASE,
+		.reg_len = 0x18,
+	},
+	{
+		.name = "lvds1_pattern_gen",
+		.reg_base = RKX120_PATTERN_GEN_LVDS1_BASE,
+		.reg_len = 0x18,
+	},
+	{ /* sentinel */ }
+};
+
+static int rkx120_reg_show(struct seq_file *s, void *v)
+{
+	const struct rk_serdes_reg *regs = rkx120_regs;
+	struct rk_serdes_chip *chip = s->private;
+	struct rk_serdes *serdes = chip->serdes;
+	struct i2c_client *client = chip->client;
+	int i;
+	u32 val = 0;
+
+	seq_printf(s, "rkx120_%s:\n", file_dentry(s->file)->d_iname);
+
+	while (regs->name) {
+		if (!strcmp(regs->name, file_dentry(s->file)->d_iname))
+			break;
+		regs++;
+	}
+
+	if (!regs->name)
+		return -ENODEV;
+
+	for (i = 0; i <= regs->reg_len; i += 4) {
+		serdes->i2c_read_reg(client, regs->reg_base + i, &val);
+
+		if (i % 16 == 0)
+			seq_printf(s, "\n0x%04x:", i);
+		seq_printf(s, " %08x", val);
+	}
+	seq_puts(s, "\n");
+
+	return 0;
+}
+
+static ssize_t rkx120_reg_write(struct file *file, const char __user *buf,
+				size_t count, loff_t *ppos)
+{
+	const struct rk_serdes_reg *regs = rkx120_regs;
+	struct rk_serdes_chip *chip = file->f_path.dentry->d_inode->i_private;
+	struct rk_serdes *serdes = chip->serdes;
+	struct i2c_client *client = chip->client;
+	u32 addr;
+	u32 val;
+	char kbuf[25];
+	int ret;
+
+	if (count >= sizeof(kbuf))
+		return -ENOSPC;
+
+	if (copy_from_user(kbuf, buf, count))
+		return -EFAULT;
+
+	kbuf[count] = '\0';
+
+	ret = sscanf(kbuf, "%x%x", &addr, &val);
+	if (ret != 2)
+		return -EINVAL;
+
+	while (regs->name) {
+		if (!strcmp(regs->name, file_dentry(file)->d_iname))
+			break;
+		regs++;
+	}
+
+	if (!regs->name)
+		return -ENODEV;
+
+	addr += regs->reg_base;
+
+	serdes->i2c_write_reg(client, addr, val);
+
+	return count;
+}
+
+static int rkx120_reg_open(struct inode *inode, struct file *file)
+{
+	struct rk_serdes_chip *chip = inode->i_private;
+
+	return single_open(file, rkx120_reg_show, chip);
+}
+
+static const struct file_operations rkx120_reg_fops = {
+	.owner          = THIS_MODULE,
+	.open           = rkx120_reg_open,
+	.read           = seq_read,
+	.write          = rkx120_reg_write,
+	.llseek         = seq_lseek,
+	.release        = single_release,
+};
+
+void rkx120_debugfs_init(struct rk_serdes_chip *chip, struct dentry *dentry)
+{
+	const struct rk_serdes_reg *regs = rkx120_regs;
+	struct pattern_gen *pattern_gen = rkx120_pattern_gen;
+	struct dentry *dir;
+
+	dir = debugfs_create_dir("registers", dentry);
+	if (!IS_ERR(dir)) {
+		while (regs->name) {
+			debugfs_create_file(regs->name, 0600, dir, chip, &rkx120_reg_fops);
+			regs++;
+		}
+	}
+
+	dir = debugfs_create_dir("pattern_gen", dentry);
+	if (!IS_ERR(dir)) {
+		while (pattern_gen->name) {
+			rkx110_x120_pattern_gen_debugfs_create_file(pattern_gen, chip, dir);
+			pattern_gen++;
+		}
+	}
+}
+#endif
+
+static int rkx120_rgb_tx_iomux_cfg(struct rk_serdes *serdes, struct rk_serdes_route *route,
+				   u8 remote_id)
+{
+	struct i2c_client *client = serdes->chip[remote_id].client;
+	uint32_t pins;
+
+	pins = RK_SERDES_GPIO_PIN_C0 | RK_SERDES_GPIO_PIN_C1 | RK_SERDES_GPIO_PIN_C2 |
+	       RK_SERDES_GPIO_PIN_C3 | RK_SERDES_GPIO_PIN_C4 | RK_SERDES_GPIO_PIN_C5 |
+	       RK_SERDES_GPIO_PIN_C6 | RK_SERDES_GPIO_PIN_C7;
+	serdes->set_hwpin(serdes, client, PIN_RKX120, RK_SERDES_DES_GPIO_BANK0, pins,
+			  RK_SERDES_PIN_CONFIG_MUX_FUNC1);
+
+	pins = RK_SERDES_GPIO_PIN_A0 | RK_SERDES_GPIO_PIN_A1 | RK_SERDES_GPIO_PIN_A2 |
+	       RK_SERDES_GPIO_PIN_A3 | RK_SERDES_GPIO_PIN_A4 | RK_SERDES_GPIO_PIN_A5 |
+	       RK_SERDES_GPIO_PIN_A6 | RK_SERDES_GPIO_PIN_A7 | RK_SERDES_GPIO_PIN_B0 |
+	       RK_SERDES_GPIO_PIN_B1 | RK_SERDES_GPIO_PIN_B2 | RK_SERDES_GPIO_PIN_B3 |
+	       RK_SERDES_GPIO_PIN_B4 | RK_SERDES_GPIO_PIN_B5 | RK_SERDES_GPIO_PIN_B6 |
+	       RK_SERDES_GPIO_PIN_B7 | RK_SERDES_GPIO_PIN_C0 | RK_SERDES_GPIO_PIN_C1 |
+	       RK_SERDES_GPIO_PIN_C2 | RK_SERDES_GPIO_PIN_C3;
+	serdes->set_hwpin(serdes, client, PIN_RKX120, RK_SERDES_DES_GPIO_BANK1, pins,
+			  RK_SERDES_PIN_CONFIG_MUX_FUNC1);
+
+	return 0;
+}
+
+int rkx120_rgb_tx_enable(struct rk_serdes *serdes, struct rk_serdes_route *route,
+			 u8 remote_id)
+{
+	rkx120_rgb_tx_iomux_cfg(serdes, route, remote_id);
+
+	return 0;
+}
+
+int rkx120_lvds_tx_enable(struct rk_serdes *serdes, struct rk_serdes_route *route, u8 remote_id,
+			  u8 phy_id)
+{
+	struct i2c_client *client = serdes->chip[remote_id].client;
+
+	if (phy_id) {
+		serdes->i2c_write_reg(client, DES_GRF_SOC_CON7, 0xfff00630);
+		serdes->i2c_write_reg(client, DES_GRF_SOC_CON2, 0x00200020);
+	} else {
+		serdes->i2c_write_reg(client, DES_GRF_SOC_CON6, 0x0fff0063);
+		serdes->i2c_write_reg(client, DES_GRF_SOC_CON2, 0x00040004);
+	}
+
+	rkx120_combtxphy_set_mode(serdes, COMBTX_PHY_MODE_VIDEO_LVDS);
+	if (route->remote0_port0 & RK_SERDES_DUAL_LVDS_TX)
+		rkx120_combtxphy_set_rate(serdes, route->vm.pixelclock * 7 / 2);
+	else
+		rkx120_combtxphy_set_rate(serdes, route->vm.pixelclock * 7);
+	rkx120_combtxphy_power_on(serdes, remote_id, phy_id);
+
+	return 0;
+}
diff --git a/kernel/drivers/mfd/rkx110_x120/rkx120_combtxphy.c b/kernel/drivers/mfd/rkx110_x120/rkx120_combtxphy.c
new file mode 100644
index 0000000..7a44e71
--- /dev/null
+++ b/kernel/drivers/mfd/rkx110_x120/rkx120_combtxphy.c
@@ -0,0 +1,416 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2022 Rockchip Electronics Co. Ltd.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/iopoll.h>
+#include "rkx110_x120.h"
+#include "rkx120_reg.h"
+#include "serdes_combphy.h"
+
+#define CLANE_PARA0		0x0000
+#define CLANE_PARA1		0x0004
+#define T_INITTIME_C(x)		UPDATE(x, 31, 0)
+#define CLANE_PARA2		0x0008
+#define T_CLKPREPARE_C(x)	UPDATE(x, 23, 16)
+#define T_CLKZERO_C(x)		UPDATE(x, 15, 8)
+#define T_CLKPRE_C(x)		UPDATE(x, 7, 0)
+#define CLANE_PARA3		0x000c
+#define T_CLKPOST_C_MASK	GENMASK(23, 16)
+#define T_CLKPOST_C(x)		UPDATE(x, 23, 16)
+#define T_CLKTRAIL_C_MASK	GENMASK(15, 8)
+#define T_CLKTRAIL_C(x)		UPDATE(x, 15, 8)
+#define T_HSEXIT_C_MASK		GENMASK(7, 0)
+#define T_HSEXIT_C(x)		UPDATE(x, 7, 0)
+#define DLANE0_PARA0		0x0010
+#define T_RST2ENLPTX_D(x)	UPDATE(x, 15, 0)
+#define DLANE0_PARA1		0x0014
+#define T_INITTIME_D(x)		UPDATE(x, 31, 0)
+#define DLANE0_PARA2		0x0018
+#define T_HSPREPARE_D(x)	UPDATE(x, 31, 24)
+#define T_HSZERO_D(x)		UPDATE(x, 23, 16)
+#define T_HSTRAIL_D(x)		UPDATE(x, 15, 8)
+#define T_HSEXIT_D(x)		UPDATE(x, 7, 0)
+#define DLANE0_PARA3		0x001c
+#define T_WAKEUP_D(x)		UPDATE(x, 31, 0)
+#define DLANE0_PARA4		0x0020
+#define T_TAGO_D0(x)		UPDATE(x, 23, 16)
+#define T_TASURE_D0(x)		UPDATE(x, 15, 8)
+#define T_TAGET_D0(x)		UPDATE(x, 7, 0)
+#define DLANE_PARA0(x)		(0x0014 + (x * 0x0010))
+#define DLANE_PARA1(x)		(0x0018 + (x * 0x0010))
+#define DLANE_PARA2(x)		(0x001C + (x * 0x0010))
+#define DLANE_PARA3(x)		(0x0020 + (x * 0x0010))
+#define COMMON_PARA0		(0x0054)
+#define T_LPX(x)		UPDATE(x, 7, 0)
+#define CTRL_PARA0		0x0058
+#define PWON_SEL		BIT(3)
+#define PWON_DSI		BIT(1)
+#define SU_IDDQ_EN		BIT(0)
+#define PLL_CTRL_PARA0		0x005c
+#define PLL_LOCK		BIT(27)
+#define RATE_MASK		GENMASK(26, 24)
+#define RATE(x)			UPDATE(x, 26, 24)
+#define REFCLK_DIV_MASK		GENMASK(23, 19)
+#define REFCLK_DIV(x)		UPDATE(x, 23, 19)
+#define PLL_DIV_MASK		GENMASK(18, 4)
+#define PLL_DIV(x)		UPDATE(x, 18, 4)
+#define DSI_PIXELCLK_DIV(x)	UPDATE(x, 3, 0)
+#define PLL_CTRL_PARA1		0x0060
+#define PLL_CTRL(x)		UPDATE(x, 31, 0)
+#define RCAL_CTRL		0x0064
+#define RCAL_EN			BIT(13)
+#define RCAL_TRIM(x)		UPDATE(x, 12, 9)
+#define RCAL_DONE		BIT(0)
+#define TRIM_PARA		0x0068
+#define HSTX_AMP_TRIM(x)	UPDATE(x, 13, 11)
+#define LPTX_SR_TRIM(x)		UPDATE(x, 10, 8)
+#define LPRX_VREF_TRIM(x)	UPDATE(x, 7, 4)
+#define LPCD_VREF_TRIM(x)	UPDATE(x, 3, 0)
+#define TEST_PARA0		0x006c
+#define FSET_EN			BIT(3)
+
+#define CLANE_PARA4		0x0078
+#define INTERFACE_PARA		0x007c
+#define TXREADYESC_VLD(x)	UPDATE(x, 15, 8)
+#define RXVALIDESC_VLD(x)	UPDATE(x, 7, 0)
+
+#define GRF_MIPITX_CON0		0x0000
+#define PHYSHUTDWN(x)		HIWORD_UPDATE(x, GENMASK(10, 10), 10)
+#define LVDS_REFCLK_DIV(x)	HIWORD_UPDATE(x, GENMASK(9, 6), 6)
+#define PHY_MODE(x)		HIWORD_UPDATE(x, GENMASK(4, 3), 3)
+#define RATE_LVDS(x)		HIWORD_UPDATE(x, GENMASK(2, 1), 1)
+#define GRF_MIPITX_CON1		0x0004
+#define PWON_PLL(x)		HIWORD_UPDATE(x, GENMASK(15, 15), 15)
+#define LVDS_PLL_DIV(x)		HIWORD_UPDATE(x, GENMASK(14, 0), 0)
+
+#define GRF_MIPITX_CON5		0x0014
+#define TXCLK_BUS_WIDTH(x)	HIWORD_UPDATE(x, GENMASK(14, 12), 12)
+#define TX3_BUS_WIDTH(x)	HIWORD_UPDATE(x, GENMASK(11, 9), 9)
+#define TX2_BUS_WIDTH(x)	HIWORD_UPDATE(x, GENMASK(8, 6), 6)
+#define TX1_BUS_WIDTH(x)	HIWORD_UPDATE(x, GENMASK(5, 3), 3)
+#define TX0_BUS_WIDTH(x)	HIWORD_UPDATE(x, GENMASK(2, 0), 0)
+#define GRF_MIPITX_CON6		0x0018
+#define TX0_CTL_LOW(x)		HIWORD_UPDATE(x, GENMASK(15, 0), 0)
+#define GRF_MIPITX_CON7		0x001c
+#define TX1_CTL_LOW(x)		HIWORD_UPDATE(x, GENMASK(15, 0), 0)
+#define GRF_MIPITX_CON8		0x0020
+#define TX2_CTL_LOW(x)		HIWORD_UPDATE(x, GENMASK(15, 0), 0)
+#define GRF_MIPITX_CON9		0x0024
+#define TX3_CTL_LOW(x)		HIWORD_UPDATE(x, GENMASK(15, 0), 0)
+#define GRF_MIPITX_CON10	0x0028
+#define TXCK_CTL_LOW(x)		HIWORD_UPDATE(x, GENMASK(15, 0), 0)
+
+#define GRF_MIPITX_CON13	0x0034
+#define TX_IDLE(x)		HIWORD_UPDATE(x, GENMASK(4, 0), 0)
+#define GRF_MIPITX_CON14	0x0038
+#define TX_PD(x)		HIWORD_UPDATE(x, GENMASK(14, 10), 10)
+
+#define GRF_MIPI_STATUS		0x0080
+#define PHYLOCK			BIT(0)
+
+static void rkx120_combtxphy_dsi_timing_init(struct rk_serdes *des, u8 remote_id)
+{
+	struct rkx120_combtxphy *combtxphy = &des->combtxphy;
+	const struct configure_opts_combphy cfg = combtxphy->mipi_dphy_cfg;
+	u32 byte_clk = DIV_ROUND_CLOSEST_ULL(combtxphy->rate, 8);
+	u32 esc_div = DIV_ROUND_UP(byte_clk, 20 * USEC_PER_SEC);
+	u64 t_byte_clk = DIV_ROUND_CLOSEST_ULL(NSEC_PER_SEC, byte_clk);
+	u32 t_clkprepare, t_clkzero, t_clkpre, t_clkpost, t_clktrail;
+	u32 t_init, t_hsexit, t_hsprepare, t_hszero, t_wakeup, t_hstrail;
+	u32 t_tago, t_tasure, t_taget;
+	u32 base = RKX120_MIPI_LVDS_TX_PHY0_BASE;
+
+	serdes_combphy_write(des, remote_id, base + INTERFACE_PARA,
+			     TXREADYESC_VLD(esc_div - 2) |
+			     RXVALIDESC_VLD(esc_div - 2));
+	serdes_combphy_write(des, remote_id, base + COMMON_PARA0, esc_div);
+	serdes_combphy_update_bits(des, remote_id, base + TEST_PARA0, FSET_EN, FSET_EN);
+
+	t_init = DIV_ROUND_UP(cfg.init, t_byte_clk) - 1;
+	serdes_combphy_write(des, remote_id, base + CLANE_PARA1, T_INITTIME_C(t_init));
+	serdes_combphy_write(des, remote_id, base + DLANE0_PARA1, T_INITTIME_D(t_init));
+	serdes_combphy_write(des, remote_id, base + DLANE_PARA1(1), T_INITTIME_D(t_init));
+	serdes_combphy_write(des, remote_id, base + DLANE_PARA1(2), T_INITTIME_D(t_init));
+	serdes_combphy_write(des, remote_id, base + DLANE_PARA1(3), T_INITTIME_D(t_init));
+
+	t_clkprepare = DIV_ROUND_UP(cfg.clk_prepare, t_byte_clk) - 1;
+	t_clkzero = DIV_ROUND_UP(cfg.clk_zero, t_byte_clk) - 1;
+	t_clkpre = DIV_ROUND_UP(cfg.clk_pre, t_byte_clk) - 1;
+	serdes_combphy_write(des, remote_id, base + CLANE_PARA2,
+			     T_CLKPREPARE_C(t_clkprepare) |
+			     T_CLKZERO_C(t_clkzero) | T_CLKPRE_C(t_clkpre));
+
+	t_clkpost = DIV_ROUND_UP(cfg.clk_post, t_byte_clk) - 1;
+	t_clktrail = DIV_ROUND_UP(cfg.clk_trail, t_byte_clk) - 1;
+	t_hsexit = DIV_ROUND_UP(cfg.hs_exit, t_byte_clk) - 1;
+	serdes_combphy_write(des, remote_id, base + CLANE_PARA3,
+			     T_CLKPOST_C(t_clkpost) |
+			     T_CLKTRAIL_C(t_clktrail) |
+			     T_HSEXIT_C(t_hsexit));
+
+	t_hsprepare = DIV_ROUND_UP(cfg.hs_prepare, t_byte_clk) - 1;
+	t_hszero = DIV_ROUND_UP(cfg.hs_zero, t_byte_clk) - 1;
+	t_hstrail = DIV_ROUND_UP(cfg.hs_trail, t_byte_clk) - 1;
+	serdes_combphy_write(des, remote_id, base + DLANE0_PARA2,
+			     T_HSPREPARE_D(t_hsprepare) |
+			     T_HSZERO_D(t_hszero) |
+			     T_HSTRAIL_D(t_hstrail) |
+			     T_HSEXIT_D(t_hsexit));
+
+	serdes_combphy_write(des, remote_id, base + DLANE_PARA2(1),
+			     T_HSPREPARE_D(t_hsprepare) |
+			     T_HSZERO_D(t_hszero) |
+			     T_HSTRAIL_D(t_hstrail) |
+			     T_HSEXIT_D(t_hsexit));
+
+	serdes_combphy_write(des, remote_id, base + DLANE_PARA2(2),
+			     T_HSPREPARE_D(t_hsprepare) |
+			     T_HSZERO_D(t_hszero) |
+			     T_HSTRAIL_D(t_hstrail) |
+			     T_HSEXIT_D(t_hsexit));
+
+	serdes_combphy_write(des, remote_id, base + DLANE_PARA2(3),
+			     T_HSPREPARE_D(t_hsprepare) |
+			     T_HSZERO_D(t_hszero) |
+			     T_HSTRAIL_D(t_hstrail) |
+			     T_HSEXIT_D(t_hsexit));
+
+	t_wakeup = DIV_ROUND_UP(cfg.wakeup, t_byte_clk) - 1;
+	serdes_combphy_write(des, remote_id, base + DLANE0_PARA3, T_WAKEUP_D(t_wakeup));
+	serdes_combphy_write(des, remote_id, base + DLANE_PARA3(1), T_WAKEUP_D(t_wakeup));
+	serdes_combphy_write(des, remote_id, base + DLANE_PARA3(2), T_WAKEUP_D(t_wakeup));
+	serdes_combphy_write(des, remote_id, base + DLANE_PARA3(3), T_WAKEUP_D(t_wakeup));
+	serdes_combphy_write(des, remote_id, base + CLANE_PARA4, T_WAKEUP_D(t_wakeup));
+
+	t_tago = DIV_ROUND_UP(cfg.ta_go, t_byte_clk) - 1;
+	t_tasure = DIV_ROUND_UP(cfg.ta_sure, t_byte_clk) - 1;
+	t_taget = DIV_ROUND_UP(cfg.ta_get, t_byte_clk) - 1;
+	serdes_combphy_write(des, remote_id, base + DLANE0_PARA4,
+			     T_TAGO_D0(t_tago) |
+			     T_TASURE_D0(t_tasure) |
+			     T_TAGET_D0(t_taget));
+}
+
+static void rkx120_combtxphy_dsi_pll_set(struct rk_serdes *des, u8 remote_id)
+{
+	struct rkx120_combtxphy *combtxphy = &des->combtxphy;
+	u32 base = RKX120_MIPI_LVDS_TX_PHY0_BASE;
+
+	serdes_combphy_update_bits(des, remote_id, base + PLL_CTRL_PARA0,
+				   RATE_MASK | REFCLK_DIV_MASK | PLL_DIV_MASK,
+				   RATE(combtxphy->rate_factor) |
+				   REFCLK_DIV(combtxphy->ref_div - 1) |
+				   PLL_DIV(combtxphy->fb_div));
+}
+
+static void rkx120_combtxphy_dsi_power_on(struct rk_serdes *des, u8 remote_id)
+{
+	struct rkx120_combtxphy *combtxphy = &des->combtxphy;
+	struct i2c_client *client = des->chip[remote_id].client;
+	u32 grf_base = RKX120_GRF_MIPI0_BASE;
+	u32 val;
+	int ret;
+
+	des->i2c_write_reg(client, grf_base + GRF_MIPITX_CON0,
+			   PHY_MODE(COMBTX_PHY_MODE_VIDEO_MIPI));
+
+	serdes_combphy_get_default_config(combtxphy->rate, &combtxphy->mipi_dphy_cfg);
+	rkx120_combtxphy_dsi_timing_init(des, remote_id);
+	rkx120_combtxphy_dsi_pll_set(des, remote_id);
+
+	des->i2c_write_reg(client, grf_base + GRF_MIPITX_CON0, PHYSHUTDWN(1));
+	des->i2c_write_reg(client, grf_base + GRF_MIPITX_CON1, PWON_PLL(1));
+
+	ret = read_poll_timeout(des->i2c_read_reg, ret,
+				!(ret < 0) && (val & PLL_LOCK),
+				0, MSEC_PER_SEC, false, client,
+				RKX120_MIPI_LVDS_TX_PHY0_BASE + PLL_CTRL_PARA0,
+				&val);
+	if (ret < 0)
+		dev_err(des->dev, "PLL is not locked\n");
+}
+
+static void rkx120_combtxphy_dsi_power_off(struct rk_serdes *des, u8 remote_id)
+{
+	struct i2c_client *client = des->chip[remote_id].client;
+	u32 grf_base = RKX120_GRF_MIPI0_BASE;
+
+	des->i2c_write_reg(client, grf_base + GRF_MIPITX_CON0, PHYSHUTDWN(0));
+	des->i2c_write_reg(client, grf_base + GRF_MIPITX_CON1, PWON_PLL(0));
+}
+
+static void rkx120_combtxphy_lvds_power_on(struct rk_serdes *des, u8 remote_id, u8 phy_id)
+{
+	struct rkx120_combtxphy *combtxphy = &des->combtxphy;
+	struct i2c_client *client = des->chip[remote_id].client;
+	u32 grf_base = (phy_id == 0) ?
+			RKX120_GRF_MIPI0_BASE : RKX120_GRF_MIPI1_BASE;
+	const struct {
+		unsigned long max_lane_mbps;
+		u8 rate_lvds;
+		u8 refclk_div;
+		u16 pll_div;
+	} hsfreqrange_table[] = {
+		{250, 2, 0, 0x3800},
+		{500, 1, 1, 0x3800},
+		{1000, 0, 3, 0x3800},
+	};
+	int ret;
+	u32 val;
+	u8 index;
+
+	for (index = 0; index < ARRAY_SIZE(hsfreqrange_table); index++)
+		if (combtxphy->rate < hsfreqrange_table[index].max_lane_mbps * USEC_PER_SEC)
+			break;
+
+	if (index == ARRAY_SIZE(hsfreqrange_table))
+		--index;
+
+	des->i2c_write_reg(client, grf_base + GRF_MIPITX_CON13, TX_IDLE(0x1f));
+	des->i2c_write_reg(client, grf_base + GRF_MIPITX_CON14, TX_PD(0x1f));
+	des->i2c_write_reg(client, grf_base + GRF_MIPITX_CON0,
+			   LVDS_REFCLK_DIV(hsfreqrange_table[index].refclk_div) |
+			   RATE_LVDS(hsfreqrange_table[index].rate_lvds));
+
+	des->i2c_write_reg(client, grf_base + GRF_MIPITX_CON1,
+			   LVDS_PLL_DIV(hsfreqrange_table[index].pll_div));
+
+	des->i2c_write_reg(client, grf_base + GRF_MIPITX_CON5,
+			   TXCLK_BUS_WIDTH(2) | TX3_BUS_WIDTH(2) |
+			   TX2_BUS_WIDTH(2) | TX1_BUS_WIDTH(2) |
+			   TX0_BUS_WIDTH(2));
+
+	des->i2c_write_reg(client, grf_base + GRF_MIPITX_CON6, TX0_CTL_LOW(0x80));
+	des->i2c_write_reg(client, grf_base + GRF_MIPITX_CON7, TX0_CTL_LOW(0x80));
+	des->i2c_write_reg(client, grf_base + GRF_MIPITX_CON8, TX0_CTL_LOW(0x80));
+	des->i2c_write_reg(client, grf_base + GRF_MIPITX_CON9, TX0_CTL_LOW(0x80));
+	des->i2c_write_reg(client, grf_base + GRF_MIPITX_CON10, TX0_CTL_LOW(0x80));
+
+	des->i2c_write_reg(client, grf_base + GRF_MIPITX_CON14, TX_PD(0));
+	des->i2c_write_reg(client, grf_base + GRF_MIPITX_CON0, PHYSHUTDWN(1) |
+			   PHY_MODE(COMBTX_PHY_MODE_VIDEO_LVDS));
+	des->i2c_write_reg(client, grf_base + GRF_MIPITX_CON1, PWON_PLL(1));
+
+	ret = read_poll_timeout(des->i2c_read_reg, ret,
+				!(ret < 0) && (val & PHYLOCK),
+				0, MSEC_PER_SEC, false, client,
+				grf_base + GRF_MIPI_STATUS, &val);
+	if (ret < 0)
+		dev_err(des->dev, "PLL is not locked\n");
+
+	des->i2c_write_reg(client, grf_base + GRF_MIPITX_CON13, TX_IDLE(0));
+}
+
+void rkx120_combtxphy_power_on(struct rk_serdes *des, u8 remote_id, u8 phy_id)
+{
+	struct rkx120_combtxphy *combtxphy = &des->combtxphy;
+
+	switch (combtxphy->mode) {
+	case COMBTX_PHY_MODE_VIDEO_MIPI:
+		rkx120_combtxphy_dsi_power_on(des, remote_id);
+		break;
+	case COMBTX_PHY_MODE_VIDEO_LVDS:
+		rkx120_combtxphy_lvds_power_on(des, remote_id, phy_id);
+		break;
+	default:
+		break;
+	}
+}
+
+void rkx120_combtxphy_power_off(struct rk_serdes *des, u8 remote_id)
+{
+	struct rkx120_combtxphy *combtxphy = &des->combtxphy;
+
+	switch (combtxphy->mode) {
+	case COMBTX_PHY_MODE_VIDEO_MIPI:
+		rkx120_combtxphy_dsi_power_off(des, remote_id);
+		break;
+	case COMBTX_PHY_MODE_VIDEO_LVDS:
+		break;
+	case COMBTX_PHY_MODE_GPIO:
+		break;
+	default:
+		break;
+	}
+}
+
+static void rkx120_combtxphy_dsi_pll_calc_rate(struct rkx120_combtxphy *combtxphy, u64 rate)
+{
+	const struct {
+		unsigned long max_lane_mbps;
+		u8 refclk_div;
+		u8 post_factor;
+	} hsfreqrange_table[] = {
+		{ 100, 1, 5 },
+		{ 200, 1, 4 },
+		{ 400, 1, 3 },
+		{ 800, 1, 2},
+		{ 1600, 1, 1},
+	};
+	u64 ref_clk = 24 * USEC_PER_SEC;
+	u64 fvco;
+	u16 fb_div;
+	u8 ref_div, post_div, index;
+
+	for (index = 0; index < ARRAY_SIZE(hsfreqrange_table); index++)
+		if (rate <= hsfreqrange_table[index].max_lane_mbps * USEC_PER_SEC)
+			break;
+
+	if (index == ARRAY_SIZE(hsfreqrange_table))
+		--index;
+
+	/*
+	 * FVCO = Fckref * 8 * ( PI_POST_DIV + PF_POST_DIV) / R
+	 * data_rate = FVCO / post_div;
+	 */
+
+	ref_div = hsfreqrange_table[index].refclk_div;
+	post_div = 1 << hsfreqrange_table[index].post_factor;
+	fvco = rate * post_div;
+	fb_div = DIV_ROUND_CLOSEST_ULL((fvco * ref_div) << 10, ref_clk * 8);
+
+	rate = DIV_ROUND_CLOSEST_ULL(ref_clk * 8 * fb_div,
+				     (u64)(ref_div * post_div) << 10);
+
+	combtxphy->ref_div = ref_div;
+	combtxphy->fb_div = fb_div;
+	combtxphy->rate_factor = hsfreqrange_table[index].post_factor;
+	combtxphy->rate = rate;
+}
+
+static void rkx120_combtxphy_lvds_pll_calc_rate(struct rkx120_combtxphy *combtxphy, u64 rate)
+{
+}
+
+void rkx120_combtxphy_set_rate(struct rk_serdes *des, u64 rate)
+{
+	struct rkx120_combtxphy *combtxphy = &des->combtxphy;
+
+	switch (combtxphy->mode) {
+	case COMBTX_PHY_MODE_VIDEO_MIPI:
+		rkx120_combtxphy_dsi_pll_calc_rate(combtxphy, rate);
+		break;
+	case COMBTX_PHY_MODE_VIDEO_LVDS:
+		rkx120_combtxphy_lvds_pll_calc_rate(combtxphy, rate);
+		break;
+	default:
+		break;
+	}
+
+	combtxphy->rate = rate;
+}
+
+u64 rkx120_combtxphy_get_rate(struct rk_serdes *des)
+{
+	return des->combtxphy.rate;
+}
+
+void rkx120_combtxphy_set_mode(struct rk_serdes *des, enum combtx_phy_mode mode)
+{
+	struct rkx120_combtxphy *combtxphy = &des->combtxphy;
+
+	combtxphy->mode = mode;
+}
diff --git a/kernel/drivers/mfd/rkx110_x120/rkx120_dsi_tx.c b/kernel/drivers/mfd/rkx110_x120/rkx120_dsi_tx.c
new file mode 100644
index 0000000..70a122e
--- /dev/null
+++ b/kernel/drivers/mfd/rkx110_x120/rkx120_dsi_tx.c
@@ -0,0 +1,1182 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2022 Rockchip Electronics Co. Ltd.
+ *
+ * Author: Guochun Huang <hero.huang@rock-chips.com>
+ */
+
+#include <asm/unaligned.h>
+#include <linux/delay.h>
+#include <linux/iopoll.h>
+#include "rkx120_reg.h"
+#include "rkx120_dsi_tx.h"
+#include "serdes_combphy.h"
+
+#define REG(x)				(x + RKX120_DSI_TX_BASE)
+#define DSI_VERSION			REG(0x0000)
+#define DSI_PWR_UP			REG(0x0004)
+#define RESET				0
+#define POWER_UP			BIT(0)
+#define DSI_CLKMGR_CFG			REG(0x0008)
+#define TO_CLK_DIVISION(x)		UPDATE(x, 15,  8)
+#define TX_ESC_CLK_DIVISION(x)		UPDATE(x,  7,  0)
+#define DSI_DPI_VCID			REG(0x000c)
+#define DPI_VID(x)			UPDATE(x,  1,  0)
+#define DSI_DPI_COLOR_CODING		REG(0x0010)
+#define LOOSELY18_EN			BIT(8)
+#define DPI_COLOR_CODING(x)		UPDATE(x,  3,  0)
+#define DSI_DPI_CFG_POL			REG(0x0014)
+#define COLORM_ACTIVE_LOW		BIT(4)
+#define SHUTD_ACTIVE_LOW		BIT(3)
+#define HSYNC_ACTIVE_LOW		BIT(2)
+#define VSYNC_ACTIVE_LOW		BIT(1)
+#define DATAEN_ACTIVE_LOW		BIT(0)
+#define DSI_DPI_LP_CMD_TIM		REG(0x0018)
+#define OUTVACT_LPCMD_TIME(x)		UPDATE(x, 23, 16)
+#define INVACT_LPCMD_TIME(x)		UPDATE(x,  7,  0)
+#define DSI_PCKHDL_CFG			REG(0x002c)
+#define CRC_RX_EN			BIT(4)
+#define ECC_RX_EN			BIT(3)
+#define BTA_EN				BIT(2)
+#define EOTP_RX_EN			BIT(1)
+#define EOTP_TX_EN			BIT(0)
+#define DSI_GEN_VCID			REG(0x0030)
+#define DSI_MODE_CFG			REG(0x0034)
+#define CMD_VIDEO_MODE(x)		UPDATE(x,  0,  0)
+#define DSI_VID_MODE_CFG		REG(0x0038)
+#define VPG_EN				BIT(16)
+#define LP_CMD_EN			BIT(15)
+#define FRAME_BTA_ACK_EN		BIT(14)
+#define LP_HFP_EN			BIT(13)
+#define LP_HBP_EN			BIT(12)
+#define LP_VACT_EN			BIT(11)
+#define LP_VFP_EN			BIT(10)
+#define LP_VBP_EN			BIT(9)
+#define LP_VSA_EN			BIT(8)
+#define VID_MODE_TYPE(x)		UPDATE(x,  1,  0)
+#define DSI_VID_PKT_SIZE		REG(0x003c)
+#define VID_PKT_SIZE(x)			UPDATE(x, 13,  0)
+#define DSI_VID_NUM_CHUNKS		REG(0x0040)
+#define DSI_VID_NULL_SIZE		REG(0x0044)
+#define DSI_VID_HSA_TIME		REG(0x0048)
+#define VID_HSA_TIME(x)			UPDATE(x, 11,  0)
+#define DSI_VID_HBP_TIME		REG(0x004c)
+#define VID_HBP_TIME(x)			UPDATE(x, 11,  0)
+#define DSI_VID_HLINE_TIME		REG(0x0050)
+#define VID_HLINE_TIME(x)		UPDATE(x, 14,  0)
+#define DSI_VID_VSA_LINES		REG(0x0054)
+#define VSA_LINES(x)			UPDATE(x,  9,  0)
+#define DSI_VID_VBP_LINES		REG(0x0058)
+#define VBP_LINES(x)			UPDATE(x,  9,  0)
+#define DSI_VID_VFP_LINES		REG(0x005c)
+#define VFP_LINES(x)			UPDATE(x,  9,  0)
+#define DSI_VID_VACTIVE_LINES		REG(0x0060)
+#define V_ACTIVE_LINES(x)		UPDATE(x, 13,  0)
+#define DSI_EDPI_CMD_SIZE		REG(0x0064)
+#define EDPI_ALLOWED_CMD_SIZE(x)	UPDATE(x, 15,  0)
+#define DSI_CMD_MODE_CFG		REG(0x0068)
+#define MAX_RD_PKT_SIZE			BIT(24)
+#define DCS_LW_TX			BIT(19)
+#define DCS_SR_0P_TX			BIT(18)
+#define DCS_SW_1P_TX			BIT(17)
+#define DCS_SW_0P_TX			BIT(16)
+#define GEN_LW_TX			BIT(14)
+#define GEN_SR_2P_TX			BIT(13)
+#define GEN_SR_1P_TX			BIT(12)
+#define GEN_SR_0P_TX			BIT(11)
+#define GEN_SW_2P_TX			BIT(10)
+#define GEN_SW_1P_TX			BIT(9)
+#define GEN_SW_0P_TX			BIT(8)
+#define ACK_RQST_EN			BIT(1)
+#define TEAR_FX_EN			BIT(0)
+#define DSI_GEN_HDR			REG(0x006c)
+#define GEN_WC_MSBYTE(x)		UPDATE(x, 23, 16)
+#define GEN_WC_LSBYTE(x)		UPDATE(x, 15,  8)
+#define GEN_VC(x)			UPDATE(x,  7,  6)
+#define GEN_DT(x)			UPDATE(x,  5,  0)
+#define DSI_GEN_PLD_DATA		REG(0x0070)
+#define DSI_CMD_PKT_STATUS		REG(0x0074)
+#define GEN_RD_CMD_BUSY			BIT(6)
+#define GEN_PLD_R_FULL			BIT(5)
+#define GEN_PLD_R_EMPTY			BIT(4)
+#define GEN_PLD_W_FULL			BIT(3)
+#define GEN_PLD_W_EMPTY			BIT(2)
+#define GEN_CMD_FULL			BIT(1)
+#define GEN_CMD_EMPTY			BIT(0)
+#define DSI_TO_CNT_CFG			REG(0x0078)
+#define HSTX_TO_CNT(x)			UPDATE(x, 31, 16)
+#define LPRX_TO_CNT(x)			UPDATE(x, 15,  0)
+#define DSI_HS_RD_TO_CNT		REG(0x007c)
+#define HS_RD_TO_CNT(x)			UPDATE(x, 15,  0)
+#define DSI_LP_RD_TO_CNT		REG(0x0080)
+#define LP_RD_TO_CNT(x)			UPDATE(x, 15,  0)
+#define DSI_HS_WR_TO_CNT		REG(0x0084)
+#define HS_WR_TO_CNT(x)			UPDATE(x, 15,  0)
+#define DSI_LP_WR_TO_CNT		REG(0x0088)
+#define LP_WR_TO_CNT(x)			UPDATE(x, 15,  0)
+#define DSI_BTA_TO_CNT			REG(0x008c)
+#define BTA_TO_CNT(x)			UPDATE(x, 15,  0)
+#define DSI_SDF_3D			REG(0x0090)
+#define DSI_LPCLK_CTRL			REG(0x0094)
+#define AUTO_CLKLANE_CTRL		BIT(1)
+#define PHY_TXREQUESTCLKHS		BIT(0)
+#define DSI_PHY_TMR_LPCLK_CFG		REG(0x0098)
+#define PHY_CLKHS2LP_TIME(x)		UPDATE(x, 25, 16)
+#define PHY_CLKLP2HS_TIME(x)		UPDATE(x,  9,  0)
+#define DSI_PHY_TMR_CFG			REG(0x009c)
+#define PHY_HS2LP_TIME(x)		UPDATE(x, 31, 24)
+#define PHY_LP2HS_TIME(x)		UPDATE(x, 23, 16)
+#define MAX_RD_TIME(x)			UPDATE(x, 14,  0)
+#define DSI_PHY_RSTZ			REG(0x00a0)
+#define PHY_FORCEPLL			BIT(3)
+#define PHY_ENABLECLK			BIT(2)
+#define PHY_RSTZ			BIT(1)
+#define PHY_SHUTDOWNZ			BIT(0)
+#define DSI_PHY_IF_CFG			REG(0x00a4)
+#define PHY_STOP_WAIT_TIME(x)		UPDATE(x, 15,  8)
+#define N_LANES(x)			UPDATE(x,  1,  0)
+#define DSI_PHY_STATUS			REG(0x00b0)
+#define PHY_STOPSTATE3LANE		BIT(11)
+#define PHY_STOPSTATE2LANE		BIT(9)
+#define PHY_STOPSTATE1LANE		BIT(7)
+#define PHY_STOPSTATE0LANE		BIT(4)
+#define PHY_STOPSTATECLKLANE		BIT(2)
+#define PHY_LOCK			BIT(0)
+#define PHY_STOPSTATELANE		(PHY_STOPSTATE0LANE | \
+					 PHY_STOPSTATECLKLANE)
+#define DSI_INT_ST0			REB(0x00bc)
+#define DSI_INT_ST1			REB(0x00c0)
+#define DSI_INT_MSK0			REB(0x00c4)
+#define DSI_INT_MSK1			REB(0x00c8)
+#define DSI_INT_FORCE0			REB(0x00d8)
+#define DSI_INT_FORCE1			REB(0x00dc)
+#define DSI_MAX_REGISTER		DSI_INT_FORCE1
+
+/* request ACK from peripheral */
+#define MIPI_DSI_MSG_REQ_ACK	BIT(0)
+/* use Low Power Mode to transmit message */
+#define MIPI_DSI_MSG_USE_LPM	BIT(1)
+
+#define DISPLAY_FLAGS_HSYNC_LOW			BIT(0)
+#define DISPLAY_FLAGS_HSYNC_HIGH		BIT(1)
+#define DISPLAY_FLAGS_VSYNC_LOW			BIT(2)
+#define DISPLAY_FLAGS_VSYNC_HIGH		BIT(3)
+
+static u64 lane_kbps;
+
+enum vid_mode_type {
+	VIDEO_MODE,
+	COMMAND_MODE,
+};
+
+enum dpi_color_coding {
+	DPI_COLOR_CODING_16BIT_1,
+	DPI_COLOR_CODING_16BIT_2,
+	DPI_COLOR_CODING_16BIT_3,
+	DPI_COLOR_CODING_18BIT_1,
+	DPI_COLOR_CODING_18BIT_2,
+	DPI_COLOR_CODING_24BIT,
+};
+
+enum {
+	VID_MODE_TYPE_NON_BURST_SYNC_PULSES,
+	VID_MODE_TYPE_NON_BURST_SYNC_EVENTS,
+	VID_MODE_TYPE_BURST,
+};
+
+/* MIPI DSI Processor-to-Peripheral transaction types */
+enum {
+	MIPI_DSI_V_SYNC_START				= 0x01,
+	MIPI_DSI_V_SYNC_END				= 0x11,
+	MIPI_DSI_H_SYNC_START				= 0x21,
+	MIPI_DSI_H_SYNC_END				= 0x31,
+
+	MIPI_DSI_COLOR_MODE_OFF				= 0x02,
+	MIPI_DSI_COLOR_MODE_ON				= 0x12,
+	MIPI_DSI_SHUTDOWN_PERIPHERAL			= 0x22,
+	MIPI_DSI_TURN_ON_PERIPHERAL			= 0x32,
+
+	MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM		= 0x03,
+	MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM		= 0x13,
+	MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM		= 0x23,
+
+	MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM		= 0x04,
+	MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM		= 0x14,
+	MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM		= 0x24,
+
+	MIPI_DSI_DCS_SHORT_WRITE			= 0x05,
+	MIPI_DSI_DCS_SHORT_WRITE_PARAM			= 0x15,
+
+	MIPI_DSI_DCS_READ				= 0x06,
+
+	MIPI_DSI_DCS_COMPRESSION_MODE                   = 0x07,
+	MIPI_DSI_PPS_LONG_WRITE                         = 0x0A,
+
+	MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE		= 0x37,
+
+	MIPI_DSI_END_OF_TRANSMISSION			= 0x08,
+
+	MIPI_DSI_NULL_PACKET				= 0x09,
+	MIPI_DSI_BLANKING_PACKET			= 0x19,
+	MIPI_DSI_GENERIC_LONG_WRITE			= 0x29,
+	MIPI_DSI_DCS_LONG_WRITE				= 0x39,
+
+	MIPI_DSI_LOOSELY_PACKED_PIXEL_STREAM_YCBCR20	= 0x0c,
+	MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR24		= 0x1c,
+	MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR16		= 0x2c,
+
+	MIPI_DSI_PACKED_PIXEL_STREAM_30			= 0x0d,
+	MIPI_DSI_PACKED_PIXEL_STREAM_36			= 0x1d,
+	MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR12		= 0x3d,
+
+	MIPI_DSI_PACKED_PIXEL_STREAM_16			= 0x0e,
+	MIPI_DSI_PACKED_PIXEL_STREAM_18			= 0x1e,
+	MIPI_DSI_PIXEL_STREAM_3BYTE_18			= 0x2e,
+	MIPI_DSI_PACKED_PIXEL_STREAM_24			= 0x3e,
+};
+
+/* MIPI DSI Peripheral-to-Processor transaction types */
+enum {
+	MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT	= 0x02,
+	MIPI_DSI_RX_END_OF_TRANSMISSION			= 0x08,
+	MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_1BYTE	= 0x11,
+	MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_2BYTE	= 0x12,
+	MIPI_DSI_RX_GENERIC_LONG_READ_RESPONSE		= 0x1a,
+	MIPI_DSI_RX_DCS_LONG_READ_RESPONSE		= 0x1c,
+	MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_1BYTE	= 0x21,
+	MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_2BYTE	= 0x22,
+};
+
+/* MIPI DCS commands */
+enum {
+	MIPI_DCS_NOP			= 0x00,
+	MIPI_DCS_SOFT_RESET		= 0x01,
+	MIPI_DCS_GET_DISPLAY_ID		= 0x04,
+	MIPI_DCS_GET_RED_CHANNEL	= 0x06,
+	MIPI_DCS_GET_GREEN_CHANNEL	= 0x07,
+	MIPI_DCS_GET_BLUE_CHANNEL	= 0x08,
+	MIPI_DCS_GET_DISPLAY_STATUS	= 0x09,
+	MIPI_DCS_GET_POWER_MODE		= 0x0A,
+	MIPI_DCS_GET_ADDRESS_MODE	= 0x0B,
+	MIPI_DCS_GET_PIXEL_FORMAT	= 0x0C,
+	MIPI_DCS_GET_DISPLAY_MODE	= 0x0D,
+	MIPI_DCS_GET_SIGNAL_MODE	= 0x0E,
+	MIPI_DCS_GET_DIAGNOSTIC_RESULT	= 0x0F,
+	MIPI_DCS_ENTER_SLEEP_MODE	= 0x10,
+	MIPI_DCS_EXIT_SLEEP_MODE	= 0x11,
+	MIPI_DCS_ENTER_PARTIAL_MODE	= 0x12,
+	MIPI_DCS_ENTER_NORMAL_MODE	= 0x13,
+	MIPI_DCS_EXIT_INVERT_MODE	= 0x20,
+	MIPI_DCS_ENTER_INVERT_MODE	= 0x21,
+	MIPI_DCS_SET_GAMMA_CURVE	= 0x26,
+	MIPI_DCS_SET_DISPLAY_OFF	= 0x28,
+	MIPI_DCS_SET_DISPLAY_ON		= 0x29,
+	MIPI_DCS_SET_COLUMN_ADDRESS	= 0x2A,
+	MIPI_DCS_SET_PAGE_ADDRESS	= 0x2B,
+	MIPI_DCS_WRITE_MEMORY_START	= 0x2C,
+	MIPI_DCS_WRITE_LUT		= 0x2D,
+	MIPI_DCS_READ_MEMORY_START	= 0x2E,
+	MIPI_DCS_SET_PARTIAL_AREA	= 0x30,
+	MIPI_DCS_SET_SCROLL_AREA	= 0x33,
+	MIPI_DCS_SET_TEAR_OFF		= 0x34,
+	MIPI_DCS_SET_TEAR_ON		= 0x35,
+	MIPI_DCS_SET_ADDRESS_MODE	= 0x36,
+	MIPI_DCS_SET_SCROLL_START	= 0x37,
+	MIPI_DCS_EXIT_IDLE_MODE		= 0x38,
+	MIPI_DCS_ENTER_IDLE_MODE	= 0x39,
+	MIPI_DCS_SET_PIXEL_FORMAT	= 0x3A,
+	MIPI_DCS_WRITE_MEMORY_CONTINUE	= 0x3C,
+	MIPI_DCS_READ_MEMORY_CONTINUE	= 0x3E,
+	MIPI_DCS_SET_TEAR_SCANLINE	= 0x44,
+	MIPI_DCS_GET_SCANLINE		= 0x45,
+	MIPI_DCS_SET_DISPLAY_BRIGHTNESS = 0x51,		/* MIPI DCS 1.3 */
+	MIPI_DCS_GET_DISPLAY_BRIGHTNESS = 0x52,		/* MIPI DCS 1.3 */
+	MIPI_DCS_WRITE_CONTROL_DISPLAY  = 0x53,		/* MIPI DCS 1.3 */
+	MIPI_DCS_GET_CONTROL_DISPLAY	= 0x54,		/* MIPI DCS 1.3 */
+	MIPI_DCS_WRITE_POWER_SAVE	= 0x55,		/* MIPI DCS 1.3 */
+	MIPI_DCS_GET_POWER_SAVE		= 0x56,		/* MIPI DCS 1.3 */
+	MIPI_DCS_SET_CABC_MIN_BRIGHTNESS = 0x5E,	/* MIPI DCS 1.3 */
+	MIPI_DCS_GET_CABC_MIN_BRIGHTNESS = 0x5F,	/* MIPI DCS 1.3 */
+	MIPI_DCS_READ_DDB_START		= 0xA1,
+	MIPI_DCS_READ_DDB_CONTINUE	= 0xA8,
+};
+
+/**
+ * struct mipi_dsi_msg - read/write DSI buffer
+ * @channel: virtual channel id
+ * @type: payload data type
+ * @flags: flags controlling this message transmission
+ * @tx_len: length of @tx_buf
+ * @tx_buf: data to be written
+ * @rx_len: length of @rx_buf
+ * @rx_buf: data to be read, or NULL
+ */
+struct mipi_dsi_msg {
+	u8 channel;
+	u8 type;
+	u16 flags;
+
+	size_t tx_len;
+	const void *tx_buf;
+
+	size_t rx_len;
+	void *rx_buf;
+};
+
+/**
+ * struct mipi_dsi_packet - represents a MIPI DSI packet in protocol format
+ * @size: size (in bytes) of the packet
+ * @header: the four bytes that make up the header (Data ID, Word Count or
+ * Packet Data, and ECC)
+ * @payload_length: number of bytes in the payload
+ * @payload: a pointer to a buffer containing the payload, if any
+ */
+struct mipi_dsi_packet {
+	size_t size;
+	u8 header[4];
+	size_t payload_length;
+	const u8 *payload;
+};
+
+static inline int
+dsi_write(struct rk_serdes *des, u8 remote_id, u32 reg, u32 val)
+{
+	struct i2c_client *client = des->chip[remote_id].client;
+
+	return des->i2c_write_reg(client, reg, val);
+}
+
+static inline int
+dsi_read(struct rk_serdes *des, u8 remote_id, u32 reg, u32 *val)
+{
+	struct i2c_client *client = des->chip[remote_id].client;
+
+	return des->i2c_read_reg(client, reg, val);
+}
+
+static inline int dsi_update_bits(struct rk_serdes *des, u8 remote_id,
+				  u32 reg, u32 mask, u32 val)
+{
+	struct i2c_client *client = des->chip[remote_id].client;
+
+	return des->i2c_update_bits(client, reg, mask, val);
+}
+
+static int genif_wait_w_pld_fifo_not_full(struct rk_serdes *des, u8 remote_id,
+					  const struct rkx120_dsi_tx *dsi)
+{
+	struct i2c_client *client = des->chip[remote_id].client;
+	u32 sts;
+	int ret;
+
+	ret = read_poll_timeout(des->i2c_read_reg, ret,
+				!(ret < 0) && !(sts & GEN_PLD_W_FULL),
+				0, MSEC_PER_SEC, false, client,
+				DSI_CMD_PKT_STATUS, &sts);
+	if (ret < 0) {
+		dev_err(des->dev, "generic write payload fifo is full\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int genif_wait_cmd_fifo_not_full(struct rk_serdes *des, u8 remote_id,
+					const struct rkx120_dsi_tx *dsi)
+{
+	struct i2c_client *client = des->chip[remote_id].client;
+	u32 sts;
+	int ret = 0;
+
+	ret = read_poll_timeout(des->i2c_read_reg, ret,
+				!(ret < 0) && !(sts & GEN_CMD_FULL),
+				0, MSEC_PER_SEC, false, client,
+				DSI_CMD_PKT_STATUS, &sts);
+	if (ret < 0) {
+		dev_err(des->dev, "generic write cmd fifo is full\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int
+genif_wait_write_fifo_empty(struct rk_serdes *des, u8 remote_id,
+			    const struct rkx120_dsi_tx *dsi)
+{
+	struct i2c_client *client = des->chip[remote_id].client;
+	u32 sts;
+	u32 mask;
+	int ret;
+
+	mask = GEN_CMD_EMPTY | GEN_PLD_W_EMPTY;
+
+	ret = read_poll_timeout(des->i2c_read_reg, ret,
+				!(ret < 0) && (sts & mask) == mask,
+				0, MSEC_PER_SEC, false, client,
+				DSI_CMD_PKT_STATUS, &sts);
+	if (ret < 0) {
+		dev_err(des->dev, "generic write fifo is full\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int rkx120_dsi_tx_read_from_fifo(struct rk_serdes *des, u8 remote_id,
+				    const struct rkx120_dsi_tx *dsi,
+				    const struct mipi_dsi_msg *msg)
+{
+	struct i2c_client *client = des->chip[remote_id].client;
+	u8 *payload = msg->rx_buf;
+	unsigned int vrefresh = 60;
+	u16 length;
+	u32 val;
+	int ret;
+
+	ret = read_poll_timeout(des->i2c_read_reg, ret,
+				!(ret < 0) && !(val & GEN_RD_CMD_BUSY),
+				0, DIV_ROUND_UP(MSEC_PER_SEC, vrefresh),
+				false, client, DSI_CMD_PKT_STATUS, &val);
+	if (ret < 0) {
+		dev_err(des->dev, "entire response isn't stored in the FIFO\n");
+		return ret;
+	}
+
+	/* Receive payload */
+	for (length = msg->rx_len; length; length -= 4) {
+		dsi_read(des, remote_id, DSI_CMD_PKT_STATUS, &val);
+		if (val & GEN_PLD_R_EMPTY)
+			ret = -ETIMEDOUT;
+		if (ret) {
+			dev_err(des->dev, "dsi Read payload FIFO is empty\n");
+			return ret;
+		}
+
+		dsi_read(des, remote_id, DSI_GEN_PLD_DATA, &val);
+
+		switch (length) {
+		case 3:
+			payload[2] = (val >> 16) & 0xff;
+			fallthrough;
+		case 2:
+			payload[1] = (val >> 8) & 0xff;
+			fallthrough;
+		case 1:
+			payload[0] = val & 0xff;
+			return 0;
+		}
+
+		payload[0] = (val >>  0) & 0xff;
+		payload[1] = (val >>  8) & 0xff;
+		payload[2] = (val >> 16) & 0xff;
+		payload[3] = (val >> 24) & 0xff;
+		payload += 4;
+	}
+
+	return 0;
+}
+
+/**
+ * mipi_dsi_packet_format_is_short - check if a packet is of the short format
+ * @type: MIPI DSI data type of the packet
+ *
+ * Return: true if the packet for the given data type is a short packet, false
+ * otherwise.
+ */
+static bool mipi_dsi_packet_format_is_short(u8 type)
+{
+	switch (type) {
+	case MIPI_DSI_V_SYNC_START:
+	case MIPI_DSI_V_SYNC_END:
+	case MIPI_DSI_H_SYNC_START:
+	case MIPI_DSI_H_SYNC_END:
+	case MIPI_DSI_END_OF_TRANSMISSION:
+	case MIPI_DSI_COLOR_MODE_OFF:
+	case MIPI_DSI_COLOR_MODE_ON:
+	case MIPI_DSI_SHUTDOWN_PERIPHERAL:
+	case MIPI_DSI_TURN_ON_PERIPHERAL:
+	case MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM:
+	case MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM:
+	case MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM:
+	case MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM:
+	case MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM:
+	case MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM:
+	case MIPI_DSI_DCS_SHORT_WRITE:
+	case MIPI_DSI_DCS_SHORT_WRITE_PARAM:
+	case MIPI_DSI_DCS_READ:
+	case MIPI_DSI_DCS_COMPRESSION_MODE:
+	case MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE:
+		return true;
+	}
+
+	return false;
+}
+
+/**
+ * mipi_dsi_packet_format_is_long - check if a packet is of the long format
+ * @type: MIPI DSI data type of the packet
+ *
+ * Return: true if the packet for the given data type is a long packet, false
+ * otherwise.
+ */
+static bool mipi_dsi_packet_format_is_long(u8 type)
+{
+	switch (type) {
+	case MIPI_DSI_PPS_LONG_WRITE:
+	case MIPI_DSI_NULL_PACKET:
+	case MIPI_DSI_BLANKING_PACKET:
+	case MIPI_DSI_GENERIC_LONG_WRITE:
+	case MIPI_DSI_DCS_LONG_WRITE:
+	case MIPI_DSI_LOOSELY_PACKED_PIXEL_STREAM_YCBCR20:
+	case MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR24:
+	case MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR16:
+	case MIPI_DSI_PACKED_PIXEL_STREAM_30:
+	case MIPI_DSI_PACKED_PIXEL_STREAM_36:
+	case MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR12:
+	case MIPI_DSI_PACKED_PIXEL_STREAM_16:
+	case MIPI_DSI_PACKED_PIXEL_STREAM_18:
+	case MIPI_DSI_PIXEL_STREAM_3BYTE_18:
+	case MIPI_DSI_PACKED_PIXEL_STREAM_24:
+		return true;
+	}
+
+	return false;
+}
+
+/**
+ * mipi_dsi_create_packet - create a packet from a message according to the
+ *     DSI protocol
+ * @packet: pointer to a DSI packet structure
+ * @msg: message to translate into a packet
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+static int mipi_dsi_create_packet(struct mipi_dsi_packet *packet,
+			   const struct mipi_dsi_msg *msg)
+{
+	if (!packet || !msg)
+		return -EINVAL;
+
+	/* do some minimum sanity checking */
+	if (!mipi_dsi_packet_format_is_short(msg->type) &&
+	    !mipi_dsi_packet_format_is_long(msg->type))
+		return -EINVAL;
+
+	if (msg->channel > 3)
+		return -EINVAL;
+
+	memset(packet, 0, sizeof(*packet));
+	packet->header[0] = ((msg->channel & 0x3) << 6) | (msg->type & 0x3f);
+
+	/* TODO: compute ECC if hardware support is not available */
+
+	/*
+	 * Long write packets contain the word count in header bytes 1 and 2.
+	 * The payload follows the header and is word count bytes long.
+	 *
+	 * Short write packets encode up to two parameters in header bytes 1
+	 * and 2.
+	 */
+	if (mipi_dsi_packet_format_is_long(msg->type)) {
+		packet->header[1] = (msg->tx_len >> 0) & 0xff;
+		packet->header[2] = (msg->tx_len >> 8) & 0xff;
+
+		packet->payload_length = msg->tx_len;
+		packet->payload = msg->tx_buf;
+	} else {
+		const u8 *tx = msg->tx_buf;
+
+		packet->header[1] = (msg->tx_len > 0) ? tx[0] : 0;
+		packet->header[2] = (msg->tx_len > 1) ? tx[1] : 0;
+	}
+
+	packet->size = sizeof(packet->header) + packet->payload_length;
+
+	return 0;
+}
+
+static int rkx120_dsi_tx_transfer(struct rk_serdes *des, u8 remote_id,
+				  const struct rkx120_dsi_tx *dsi,
+				  const struct mipi_dsi_msg *msg)
+{
+	struct mipi_dsi_packet packet;
+	int ret;
+	u32 val;
+
+	if (msg->flags & MIPI_DSI_MSG_REQ_ACK)
+		dsi_update_bits(des, remote_id, DSI_CMD_MODE_CFG,
+				ACK_RQST_EN, ACK_RQST_EN);
+
+	if (msg->flags & MIPI_DSI_MSG_USE_LPM) {
+		dsi_update_bits(des, remote_id, DSI_VID_MODE_CFG,
+				LP_CMD_EN, LP_CMD_EN);
+	} else {
+		dsi_update_bits(des, remote_id, DSI_VID_MODE_CFG, LP_CMD_EN, 0);
+		dsi_update_bits(des, remote_id, DSI_LPCLK_CTRL,
+				PHY_TXREQUESTCLKHS, PHY_TXREQUESTCLKHS);
+	}
+
+	switch (msg->type) {
+	case MIPI_DSI_SHUTDOWN_PERIPHERAL:
+		//return rkx120_dsi_tx_shutdown_peripheral(dsi);
+	case MIPI_DSI_TURN_ON_PERIPHERAL:
+		//return rkx120_dsi_tx_turn_on_peripheral(dsi);
+	case MIPI_DSI_DCS_SHORT_WRITE:
+		dsi_update_bits(des, remote_id, DSI_CMD_MODE_CFG, DCS_SW_0P_TX,
+				msg->flags & MIPI_DSI_MSG_USE_LPM ?
+				DCS_SW_0P_TX : 0);
+		break;
+	case MIPI_DSI_DCS_SHORT_WRITE_PARAM:
+		dsi_update_bits(des, remote_id, DSI_CMD_MODE_CFG, DCS_SW_1P_TX,
+				msg->flags & MIPI_DSI_MSG_USE_LPM ?
+				DCS_SW_1P_TX : 0);
+		break;
+	case MIPI_DSI_DCS_LONG_WRITE:
+		dsi_update_bits(des, remote_id, DSI_CMD_MODE_CFG, DCS_LW_TX,
+				msg->flags & MIPI_DSI_MSG_USE_LPM ?
+				DCS_LW_TX : 0);
+		break;
+	case MIPI_DSI_DCS_READ:
+		dsi_update_bits(des, remote_id, DSI_CMD_MODE_CFG, DCS_SR_0P_TX,
+				msg->flags & MIPI_DSI_MSG_USE_LPM ?
+				DCS_SR_0P_TX : 0);
+		break;
+	case MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE:
+		dsi_update_bits(des, remote_id, DSI_CMD_MODE_CFG, MAX_RD_PKT_SIZE,
+				msg->flags & MIPI_DSI_MSG_USE_LPM ?
+				MAX_RD_PKT_SIZE : 0);
+		break;
+	case MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM:
+		dsi_update_bits(des, remote_id, DSI_CMD_MODE_CFG, GEN_SW_0P_TX,
+				msg->flags & MIPI_DSI_MSG_USE_LPM ?
+				GEN_SW_0P_TX : 0);
+		break;
+	case MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM:
+		dsi_update_bits(des, remote_id, DSI_CMD_MODE_CFG, GEN_SW_1P_TX,
+				msg->flags & MIPI_DSI_MSG_USE_LPM ?
+				GEN_SW_1P_TX : 0);
+		break;
+	case MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM:
+		dsi_update_bits(des, remote_id, DSI_CMD_MODE_CFG, GEN_SW_2P_TX,
+				msg->flags & MIPI_DSI_MSG_USE_LPM ?
+				GEN_SW_2P_TX : 0);
+		break;
+	case MIPI_DSI_GENERIC_LONG_WRITE:
+		dsi_update_bits(des, remote_id, DSI_CMD_MODE_CFG, GEN_LW_TX,
+				msg->flags & MIPI_DSI_MSG_USE_LPM ?
+				GEN_LW_TX : 0);
+		break;
+	case MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM:
+		dsi_update_bits(des, remote_id, DSI_CMD_MODE_CFG, GEN_SR_0P_TX,
+		msg->flags & MIPI_DSI_MSG_USE_LPM ? GEN_SR_0P_TX : 0);
+		break;
+	case MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM:
+		dsi_update_bits(des, remote_id, DSI_CMD_MODE_CFG, GEN_SR_1P_TX,
+		msg->flags & MIPI_DSI_MSG_USE_LPM ? GEN_SR_1P_TX : 0);
+		break;
+	case MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM:
+		dsi_update_bits(des, remote_id, DSI_CMD_MODE_CFG, GEN_SR_2P_TX,
+				msg->flags & MIPI_DSI_MSG_USE_LPM ? GEN_SR_2P_TX : 0);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* create a packet to the DSI protocol */
+	ret = mipi_dsi_create_packet(&packet, msg);
+	if (ret) {
+		dev_err(des->dev, "failed to create packet\n");
+		return ret;
+	}
+
+	/* Send payload */
+	while (packet.payload_length >= 4) {
+		/*
+		 * Alternatively, you can always keep the FIFO
+		 * nearly full by monitoring the FIFO state until
+		 * it is not full, and then writea single word of data.
+		 * This solution is more resource consuming
+		 * but it simultaneously avoids FIFO starvation,
+		 * making it possible to use FIFO sizes smaller than
+		 * the amount of data of the longest packet to be written.
+		 */
+		ret = genif_wait_w_pld_fifo_not_full(des, remote_id, dsi);
+		if (ret)
+			return ret;
+
+		val = get_unaligned_le32(packet.payload);
+
+		dsi_write(des, remote_id, DSI_GEN_PLD_DATA, val);
+
+
+		packet.payload += 4;
+		packet.payload_length -= 4;
+	}
+
+	val = 0;
+	switch (packet.payload_length) {
+	case 3:
+		val |= packet.payload[2] << 16;
+		fallthrough;
+	case 2:
+		val |= packet.payload[1] << 8;
+		fallthrough;
+	case 1:
+		val |= packet.payload[0];
+
+		dsi_write(des, remote_id, DSI_GEN_PLD_DATA, val);
+		break;
+	}
+
+	ret = genif_wait_cmd_fifo_not_full(des, remote_id, dsi);
+	if (ret)
+		return ret;
+
+	/* Send packet header */
+	val = get_unaligned_le32(packet.header);
+
+	dsi_write(des, remote_id, DSI_GEN_HDR, val);
+
+	ret = genif_wait_write_fifo_empty(des, remote_id, dsi);
+	if (ret)
+		return ret;
+
+	if (msg->rx_len) {
+		ret = rkx120_dsi_tx_read_from_fifo(des, remote_id, dsi, msg);
+		if (ret < 0)
+			return ret;
+	}
+
+	return msg->tx_len;
+}
+
+static int rkx120_mipi_dsi_generic_write(struct rk_serdes *des, u8 remote_id,
+				  const void *payload, size_t size)
+{
+	const struct rkx120_dsi_tx *dsi = &des->dsi_tx;
+	struct mipi_dsi_msg msg;
+
+	memset(&msg, 0, sizeof(msg));
+	msg.channel = dsi->channel;
+	msg.tx_buf = payload;
+	msg.tx_len = size;
+	msg.rx_len = 0;
+
+	switch (size) {
+	case 0:
+		msg.type = MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM;
+		break;
+
+	case 1:
+		msg.type = MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM;
+		break;
+	case 2:
+		msg.type = MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM;
+		break;
+	default:
+		msg.type = MIPI_DSI_GENERIC_LONG_WRITE;
+		break;
+	}
+
+	if (dsi->mode_flags & SERDES_MIPI_DSI_MODE_LPM)
+		msg.flags |= MIPI_DSI_MSG_USE_LPM;
+
+	return rkx120_dsi_tx_transfer(des, remote_id, dsi, &msg);
+}
+
+static int rkx120_mipi_dsi_dcs_write_buffer(struct rk_serdes *des, u8 remote_id,
+				     const void *data, size_t len)
+{
+	const struct rkx120_dsi_tx *dsi = &des->dsi_tx;
+	struct mipi_dsi_msg msg;
+
+	memset(&msg, 0, sizeof(msg));
+	msg.channel = dsi->channel;
+	msg.tx_buf = data;
+	msg.tx_len = len;
+	msg.rx_len = 0;
+
+	switch (len) {
+	case 0:
+		return -EINVAL;
+	case 1:
+		msg.type = MIPI_DSI_DCS_SHORT_WRITE;
+		break;
+	case 2:
+		msg.type = MIPI_DSI_DCS_SHORT_WRITE_PARAM;
+		break;
+	default:
+		msg.type = MIPI_DSI_DCS_LONG_WRITE;
+		break;
+	}
+
+	if (dsi->mode_flags & SERDES_MIPI_DSI_MODE_LPM)
+		msg.flags |= MIPI_DSI_MSG_USE_LPM;
+
+	return rkx120_dsi_tx_transfer(des, remote_id, dsi, &msg);
+}
+
+static __maybe_unused int
+rkx120_mipi_dsi_dcs_read(struct rk_serdes *des, u8 remote_id,
+			 u8 cmd, void *data, size_t len)
+{
+	const struct rkx120_dsi_tx *dsi = &des->dsi_tx;
+	struct mipi_dsi_msg msg;
+
+	memset(&msg, 0, sizeof(msg));
+	msg.channel = dsi->channel;
+	msg.type = MIPI_DSI_DCS_READ;
+	msg.tx_buf = &cmd;
+	msg.tx_len = 1;
+	msg.rx_buf = data;
+	msg.rx_len = len;
+
+	return rkx120_dsi_tx_transfer(des, remote_id, dsi, &msg);
+}
+
+int rkx120_dsi_tx_cmd_seq_xfer(struct rk_serdes *des, u8 remote_id,
+			       struct panel_cmds *cmds)
+{
+	u16 i;
+	int err;
+
+	if (!cmds)
+		return 0;
+
+	for (i = 0; i < cmds->cmd_cnt; i++) {
+		struct cmd_desc *cmd = &cmds->cmds[i];
+
+		switch (cmd->dchdr.dtype) {
+		case MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM:
+		case MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM:
+		case MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM:
+		case MIPI_DSI_GENERIC_LONG_WRITE:
+			err = rkx120_mipi_dsi_generic_write(des, remote_id, cmd->payload,
+							    cmd->dchdr.dlen);
+			break;
+		case MIPI_DSI_DCS_SHORT_WRITE:
+		case MIPI_DSI_DCS_SHORT_WRITE_PARAM:
+		case MIPI_DSI_DCS_LONG_WRITE:
+			err = rkx120_mipi_dsi_dcs_write_buffer(des, remote_id, cmd->payload,
+							       cmd->dchdr.dlen);
+			break;
+		default:
+			dev_err(des->dev, "panel cmd desc invalid data type\n");
+			return -EINVAL;
+		}
+
+		if (err < 0)
+			dev_err(des->dev, "failed to write cmd\n");
+
+		if (cmd->dchdr.wait)
+			mdelay(cmd->dchdr.wait);
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(rkx120_dsi_tx_cmd_seq_xfer);
+
+static u64 rkx120_dsi_tx_get_lane_rate(const struct rkx120_dsi_tx *dsi)
+{
+	struct videomode *vm = dsi->vm;
+	u64 lane_rate;
+	u32 max_lane_rate = 1500000000ULL;
+	u8 bpp, lanes;
+
+	bpp = dsi->bpp;
+	lanes = dsi->lanes;
+	lane_rate = (u64)vm->pixelclock * bpp;
+	do_div(lane_rate, lanes);
+
+	if (dsi->mode_flags & SERDES_MIPI_DSI_MODE_VIDEO_BURST) {
+		lane_rate *= 10;
+		do_div(lane_rate, 9);
+	}
+
+	if (lane_rate > max_lane_rate)
+		lane_rate = max_lane_rate;
+
+	return lane_rate;
+}
+
+static void
+mipi_dphy_power_on(struct rk_serdes *des, const struct rkx120_dsi_tx *dsi, u8 remote_id)
+{
+	struct i2c_client *client = des->chip[remote_id].client;
+	u32 val, mask;
+	int ret = 0;
+
+	dsi_update_bits(des, remote_id, DSI_PHY_RSTZ, PHY_ENABLECLK, 0);
+	dsi_update_bits(des, remote_id, DSI_PHY_RSTZ, PHY_SHUTDOWNZ, 0);
+	dsi_update_bits(des, remote_id, DSI_PHY_RSTZ, PHY_RSTZ, 0);
+
+	udelay(1);
+
+	dsi_update_bits(des, remote_id, DSI_PHY_RSTZ,
+			PHY_ENABLECLK, PHY_ENABLECLK);
+	dsi_update_bits(des, remote_id, DSI_PHY_RSTZ,
+			PHY_SHUTDOWNZ, PHY_SHUTDOWNZ);
+	dsi_update_bits(des, remote_id, DSI_PHY_RSTZ, PHY_RSTZ, PHY_RSTZ);
+	usleep_range(1500, 2000);
+
+	rkx120_combtxphy_power_on(des, remote_id, 0);
+
+	ret = read_poll_timeout(des->i2c_read_reg, ret,
+				!(ret < 0) && (val & PHY_LOCK),
+				0, MSEC_PER_SEC, false, client,
+				DSI_PHY_STATUS, &val);
+	if (ret < 0)
+		dev_err(des->dev, "PHY is not locked\n");
+
+	usleep_range(100, 200);
+
+	mask = PHY_STOPSTATELANE;
+	ret = read_poll_timeout(des->i2c_read_reg, ret,
+				!(ret < 0) && ((val & mask) == mask),
+				0, MSEC_PER_SEC, false, client,
+				DSI_PHY_STATUS, &val);
+	if (ret < 0)
+		dev_err(des->dev, "lane module is not in stop state\n");
+
+	udelay(10);
+}
+
+static void rkx120_dsi_tx_reset_control_assert(struct rk_serdes *des, u8 remote_id)
+{
+	//TXCRU_SOFTRST_CON03 bit[8]: presetn_dsitx
+	//dsi_write(des, remote_id, CRU_SOFTRST_CON02, 0x400040, remote_id);
+}
+
+static void rkx120_dsi_tx_reset_control_deassert(struct rk_serdes *des, u8 remote_id)
+{
+	//TXCRU_SOFTRST_CON03 bit[8]: presetn_dsitx
+	//dsi_i2c_write(des, remote_id, CRU_SOFTRST_CON02, 0x400000);
+}
+
+static void rkx120_dsi_tx_bridge_pre_enable(struct rk_serdes *des, u8 remote_id)
+{
+	struct rkx120_dsi_tx *dsi = &des->dsi_tx;
+	u32 val;
+
+	dsi_write(des, remote_id, DSI_PWR_UP, RESET);
+	dsi_write(des, remote_id, DSI_MODE_CFG, CMD_VIDEO_MODE(COMMAND_MODE));
+
+	val = DIV_ROUND_UP(lane_kbps >> 3, 20 * MSEC_PER_SEC);
+	dsi_write(des, remote_id, DSI_CLKMGR_CFG,
+		  TO_CLK_DIVISION(10) | TX_ESC_CLK_DIVISION(val));
+
+	val = CRC_RX_EN | ECC_RX_EN | BTA_EN | EOTP_TX_EN;
+	if (dsi->mode_flags & SERDES_MIPI_DSI_MODE_EOT_PACKET)
+		val &= ~EOTP_TX_EN;
+
+	dsi_write(des, remote_id, DSI_PCKHDL_CFG, val);
+
+	dsi_write(des, remote_id, DSI_TO_CNT_CFG,
+		  HSTX_TO_CNT(1000) | LPRX_TO_CNT(1000));
+	dsi_write(des, remote_id, DSI_BTA_TO_CNT, 0xd00);
+	dsi_write(des, remote_id, DSI_PHY_TMR_CFG,
+		  PHY_HS2LP_TIME(0x14) | PHY_LP2HS_TIME(0x10) |
+		  MAX_RD_TIME(10000));
+	dsi_write(des, remote_id, DSI_PHY_TMR_LPCLK_CFG,
+		  PHY_CLKHS2LP_TIME(0x40) | PHY_CLKLP2HS_TIME(0x40));
+	dsi_write(des, remote_id, DSI_PHY_IF_CFG,
+		  PHY_STOP_WAIT_TIME(0x20) | N_LANES(dsi->lanes - 1));
+
+	mipi_dphy_power_on(des, dsi, remote_id);
+
+	dsi_write(des, remote_id, DSI_PWR_UP, POWER_UP);
+}
+
+static void rkx120_dsi_tx_set_vid_mode(struct rk_serdes *des, u8 remote_id,
+				       const struct rkx120_dsi_tx *dsi,
+				       const struct videomode *vm)
+{
+	u64 lanebyteclk = (lane_kbps * MSEC_PER_SEC) >> 3;
+	unsigned int dpipclk = vm->pixelclock;
+	u32 hline, hsa, hbp, hline_time, hsa_time, hbp_time;
+	u32 vactive, vsa, vfp, vbp;
+	u32 val;
+	int pkt_size;
+
+	val = LP_HFP_EN | LP_HBP_EN | LP_VACT_EN | LP_VFP_EN | LP_VBP_EN |
+	      LP_VSA_EN;
+
+	if (dsi->mode_flags & SERDES_MIPI_DSI_MODE_VIDEO_HFP)
+		val &= ~LP_HFP_EN;
+
+	if (dsi->mode_flags & SERDES_MIPI_DSI_MODE_VIDEO_HBP)
+		val &= ~LP_HBP_EN;
+
+	if (dsi->mode_flags & SERDES_MIPI_DSI_MODE_VIDEO_BURST)
+		val |= VID_MODE_TYPE_BURST;
+	else if (dsi->mode_flags & SERDES_MIPI_DSI_MODE_VIDEO_SYNC_PULSE)
+		val |= VID_MODE_TYPE_NON_BURST_SYNC_PULSES;
+	else
+		val |= VID_MODE_TYPE_NON_BURST_SYNC_EVENTS;
+
+	dsi_write(des, remote_id, DSI_VID_MODE_CFG, val);
+
+	if (dsi->mode_flags & SERDES_MIPI_DSI_CLOCK_NON_CONTINUOUS)
+		dsi_update_bits(des, remote_id, DSI_LPCLK_CTRL,
+				AUTO_CLKLANE_CTRL, AUTO_CLKLANE_CTRL);
+
+	pkt_size = VID_PKT_SIZE(vm->hactive);
+
+	dsi_write(des, remote_id, DSI_VID_PKT_SIZE, pkt_size);
+
+	vactive = vm->vactive;
+	vsa = vm->vsync_len;
+	vfp = vm->vfront_porch;
+	vbp = vm->vback_porch;
+	hsa = vm->hsync_len;
+	hbp = vm->hback_porch;
+	hline = vm->hactive + vm->hfront_porch +
+		vm->hback_porch + vm->hsync_len;
+
+	//hline_time = hline * lanebyteclk / dpipclk;
+	hline_time = DIV_ROUND_CLOSEST_ULL(hline * lanebyteclk, dpipclk);
+	dsi_write(des, remote_id, DSI_VID_HLINE_TIME,
+		  VID_HLINE_TIME(hline_time));
+	//hsa_time = hsa * lanebyteclk / dpipclk;
+	hsa_time = DIV_ROUND_CLOSEST_ULL(hsa * lanebyteclk, dpipclk);
+	dsi_write(des, remote_id, DSI_VID_HSA_TIME, VID_HSA_TIME(hsa_time));
+	//hbp_time = hbp * lanebyteclk / dpipclk;
+	hbp_time = DIV_ROUND_CLOSEST_ULL(hbp * lanebyteclk, dpipclk);
+	dsi_write(des, remote_id, DSI_VID_HBP_TIME, VID_HBP_TIME(hbp_time));
+
+	dsi_write(des, remote_id, DSI_VID_VACTIVE_LINES, vactive);
+	dsi_write(des, remote_id, DSI_VID_VSA_LINES, vsa);
+	dsi_write(des, remote_id, DSI_VID_VFP_LINES, vfp);
+	dsi_write(des, remote_id, DSI_VID_VBP_LINES, vbp);
+
+	dsi_write(des, remote_id, DSI_MODE_CFG, CMD_VIDEO_MODE(VIDEO_MODE));
+}
+
+static void rkx120_dsi_tx_set_cmd_mode(struct rk_serdes *des, u8 remote_id,
+				       const struct rkx120_dsi_tx *dsi,
+				       const struct videomode *vm)
+{
+	dsi_update_bits(des, remote_id, DSI_CMD_MODE_CFG, DCS_LW_TX, 0);
+	dsi_write(des, remote_id, DSI_EDPI_CMD_SIZE,
+		  EDPI_ALLOWED_CMD_SIZE(vm->hactive));
+	dsi_write(des, remote_id, DSI_MODE_CFG, CMD_VIDEO_MODE(COMMAND_MODE));
+}
+
+static void
+rkx120_dsi_tx_bridge_enable(struct rk_serdes *des, u8 remote_id)
+{
+	struct rkx120_dsi_tx *dsi = &des->dsi_tx;
+	const struct videomode *vm = dsi->vm;
+	u32 val;
+
+	dsi_write(des, remote_id, DSI_PWR_UP, RESET);
+
+	switch (dsi->bus_format) {
+	case SERDES_MIPI_DSI_FMT_RGB666:
+		val = DPI_COLOR_CODING(DPI_COLOR_CODING_18BIT_2) |
+		      LOOSELY18_EN;
+			break;
+	case SERDES_MIPI_DSI_FMT_RGB666_PACKED:
+		val = DPI_COLOR_CODING(DPI_COLOR_CODING_18BIT_1);
+		break;
+	case SERDES_MIPI_DSI_FMT_RGB565:
+		val = DPI_COLOR_CODING(DPI_COLOR_CODING_16BIT_1);
+		break;
+	case SERDES_MIPI_DSI_FMT_RGB888:
+	default:
+		val = DPI_COLOR_CODING(DPI_COLOR_CODING_24BIT);
+		break;
+	}
+
+	dsi_write(des, remote_id, DSI_DPI_COLOR_CODING, val);
+
+	val = 0;
+	if (vm->flags & DISPLAY_FLAGS_VSYNC_LOW)
+		val |= VSYNC_ACTIVE_LOW;
+	if (vm->flags & DISPLAY_FLAGS_HSYNC_LOW)
+		val |= HSYNC_ACTIVE_LOW;
+
+	dsi_write(des, remote_id, DSI_DPI_CFG_POL, val);
+
+	dsi_write(des, remote_id, DSI_DPI_VCID, DPI_VID(0));
+	dsi_write(des, remote_id, DSI_DPI_LP_CMD_TIM,
+		  OUTVACT_LPCMD_TIME(4) | INVACT_LPCMD_TIME(4));
+	dsi_update_bits(des, remote_id, DSI_LPCLK_CTRL,
+			PHY_TXREQUESTCLKHS, PHY_TXREQUESTCLKHS);
+
+	if (dsi->mode_flags & SERDES_MIPI_DSI_MODE_VIDEO)
+		rkx120_dsi_tx_set_vid_mode(des, remote_id, dsi, vm);
+	else
+		rkx120_dsi_tx_set_cmd_mode(des, remote_id, dsi, vm);
+
+	dsi_write(des, remote_id, DSI_PWR_UP, POWER_UP);
+}
+
+void rkx120_dsi_tx_pre_enable(struct rk_serdes *des,
+			      struct rk_serdes_route *route,
+			      u8 remote_id)
+{
+	struct rkx120_dsi_tx *dsi = &des->dsi_tx;
+	u64 rate;
+
+	dsi->vm = &route->vm;
+	rate = rkx120_dsi_tx_get_lane_rate(dsi);
+
+	rkx120_combtxphy_set_mode(des, COMBTX_PHY_MODE_VIDEO_MIPI);
+	rkx120_combtxphy_set_rate(des, rate);
+	lane_kbps = rkx120_combtxphy_get_rate(des) / MSEC_PER_SEC;
+
+	/* rst for dsi */
+	rkx120_dsi_tx_reset_control_assert(des, remote_id);
+	usleep_range(20, 40);
+	rkx120_dsi_tx_reset_control_deassert(des, remote_id);
+	usleep_range(20, 40);
+
+	rkx120_dsi_tx_bridge_pre_enable(des, remote_id);
+
+#ifdef DSI_READ_POWER_MODE
+	u8 mode;
+
+	rkx120_mipi_dsi_dcs_read(des, remote_id, MIPI_DCS_GET_POWER_MODE,
+				 &mode, sizeof(mode));
+
+	dev_info(rkx120->dev, "dsi: mode: 0x%x\n", mode);
+#endif
+}
+
+void rkx120_dsi_tx_enable(struct rk_serdes *des,
+			  struct rk_serdes_route *route,
+			  u8 remote_id)
+{
+	struct rkx120_dsi_tx *dsi = &des->dsi_tx;
+
+#ifdef DSI_READ_POWER_MODE
+	u8 mode;
+
+	rkx120_mipi_dsi_dcs_read(des, remote_id, MIPI_DCS_GET_POWER_MODE,
+				 &mode, sizeof(mode));
+
+	dev_info(rkx120->dev, "dsi: mode: 0x%x\n", mode);
+#endif
+	rkx120_dsi_tx_bridge_enable(des, remote_id);
+
+	dev_info(des->dev, "rkx120_dsi_tx final DSI-Link bandwidth: %llu Kbps x %d lanes\n",
+		 lane_kbps, dsi->lanes);
+}
+
+void rkx120_dsi_tx_post_disable(struct rk_serdes *des,
+				struct rk_serdes_route *route,
+				u8 remote_id)
+{
+	rkx120_combtxphy_power_off(des, remote_id);
+}
+
+void rkx120_dsi_tx_disable(struct rk_serdes *des, struct rk_serdes_route *route, u8 remote_id)
+{
+	dsi_write(des, remote_id, DSI_PWR_UP, RESET);
+	dsi_write(des, remote_id, DSI_LPCLK_CTRL, 0);
+	dsi_write(des, remote_id, DSI_EDPI_CMD_SIZE, 0);
+	dsi_write(des, remote_id, DSI_MODE_CFG, CMD_VIDEO_MODE(COMMAND_MODE));
+	dsi_write(des, remote_id, DSI_PWR_UP, POWER_UP);
+}
diff --git a/kernel/drivers/mfd/rkx110_x120/rkx120_dsi_tx.h b/kernel/drivers/mfd/rkx110_x120/rkx120_dsi_tx.h
new file mode 100644
index 0000000..a7e9528
--- /dev/null
+++ b/kernel/drivers/mfd/rkx110_x120/rkx120_dsi_tx.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2022 Rockchip Electronics Co. Ltd.
+ *
+ * Author: Guochun Huang <hero.huang@rock-chips.com>
+ */
+
+#ifndef RKX120_DSI_TX_H
+#define RKX120_DSI_TX_H
+
+#include "rkx110_x120.h"
+
+int rkx120_dsi_tx_cmd_seq_xfer(struct rk_serdes *des, u8 remote_id,
+			       struct panel_cmds *cmds);
+void rkx120_dsi_tx_pre_enable(struct rk_serdes *serdes,
+			      struct rk_serdes_route *route, u8 remote_id);
+void rkx120_dsi_tx_enable(struct rk_serdes *serdes,
+			  struct rk_serdes_route *route, u8 remote_id);
+void rkx120_dsi_tx_post_disable(struct rk_serdes *serdes,
+				struct rk_serdes_route *route, u8 remote_id);
+void rkx120_dsi_tx_disable(struct rk_serdes *serdes,
+			   struct rk_serdes_route *route, u8 remote_id);
+#endif
diff --git a/kernel/drivers/mfd/rkx110_x120/rkx120_linkrx.c b/kernel/drivers/mfd/rkx110_x120/rkx120_linkrx.c
new file mode 100644
index 0000000..45d965b
--- /dev/null
+++ b/kernel/drivers/mfd/rkx110_x120/rkx120_linkrx.c
@@ -0,0 +1,797 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2022 Rockchip Electronics Co. Ltd.
+ *
+ * Author: Zhang Yubing <yubing.zhang@rock-chips.com>
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/iopoll.h>
+#include <dt-bindings/mfd/rockchip-serdes.h>
+
+#include "hal/cru_api.h"
+#include "hal/pinctrl_api.h"
+#include "rkx110_x120.h"
+#include "rkx120_reg.h"
+
+#define LINK_REG(x)			 ((x) + RKX120_DES_RKLINK_BASE)
+
+#define RKLINK_DES_LANE_ENGINE_CFG	LINK_REG(0x0000)
+ #define TRAIN_CLK_SEL_MASK		GENMASK(31, 30)
+ #define TRAIN_CLK_SEL_E0		UPDATE(0, 31, 30)
+ #define TRAIN_CLK_SEL_E1		UPDATE(1, 31, 30)
+ #define TRAIN_CLK_SEL_I2S		UPDATE(2, 31, 30)
+ #define DUAL_LVDS_CHANNEL_SWAP		BIT(29)
+ #define VIDEO_FREQ_AUTO_EN		BIT(28)
+ #define ENGINE1_2_LANE			BIT(23)
+ #define ENGINE1_EN			BIT(22)
+ #define ENGINE0_2_LANE			BIT(21)
+ #define ENGINE0_EN			BIT(20)
+ #define LANE1_DATA_WIDTH_8BIT		UPDATE(0, 15, 14)
+ #define LANE1_DATA_WIDTH_16BIT		UPDATE(1, 15, 14)
+ #define LANE1_DATA_WIDTH_24BIT		UPDATE(2, 15, 14)
+ #define LANE1_DATA_WIDTH_32BIT		UPDATE(3, 15, 14)
+ #define LANE0_DATA_WIDTH_8BIT		UPDATE(0, 13, 12)
+ #define LANE0_DATA_WIDTH_16BIT		UPDATE(1, 13, 12)
+ #define LANE0_DATA_WIDTH_24BIT		UPDATE(2, 13, 12)
+ #define LANE0_DATA_WIDTH_32BIT		UPDATE(3, 13, 12)
+ #define LANE0_EN			BIT(4)
+ #define LANE1_EN			BIT(5)
+ #define DES_EN				BIT(0)
+
+#define RKLINK_DES_LANE_ENGINE_DST	LINK_REG(0x0004)
+#define LANE0_ENGINE_CFG_MASK		GENMASK(3, 0)
+#define LANE0_ENGINE0			BIT(0)
+#define LANE0_ENGINE1			BIT(1)
+#define LANE1_ENGINE_CFG_MASK		GENMASK(7, 4)
+#define LANE1_ENGINE0			BIT(4)
+#define LANE1_ENGINE1			BIT(5)
+
+#define RKLINK_DES_DATA_ID_CFG		LINK_REG(0x0008)
+#define DATA_FIFO3_RD_ID_MASK		GENMASK(30, 28)
+#define DATA_FIFO3_RD_ID(x)		UPDATE(x, 30, 28)
+#define DATA_FIFO2_RD_ID_MASK		GENMASK(26, 24)
+#define DATA_FIFO2_RD_ID(x)		UPDATE(x, 26, 24)
+#define DATA_FIFO1_RD_ID_MASK		GENMASK(22, 20)
+ #define DATA_FIFO1_RD_ID(x)		UPDATE(x, 22, 20)
+#define DATA_FIFO0_RD_ID_MASK		GENMASK(18, 16)
+ #define DATA_FIFO0_RD_ID(x)		UPDATE(x, 18, 16)
+#define DATA_FIFO3_WR_ID_MASK		GENMASK(14, 12)
+#define DATA_FIFO3_WR_ID(x)		UPDATE(x, 14, 12)
+#define DATA_FIFO2_WR_ID_MASK		GENMASK(10, 8)
+#define DATA_FIFO2_WR_ID(x)		UPDATE(x, 10, 8)
+#define DATA_FIFO1_WR_ID_MASK		GENMASK(6, 4)
+ #define DATA_FIFO1_WR_ID(x)		UPDATE(x, 6, 4)
+#define DATA_FIFO0_WR_ID_MASK		GENMASK(2, 0)
+ #define DATA_FIFO0_WR_ID(x)		UPDATE(x, 2, 0)
+
+#define RKLINK_DES_ORDER_ID_CFG		LINK_REG(0x000C)
+#define ORDER_FIFO1_RD_ID_MASK		GENMASK(22, 20)
+ #define ORDER_FIFO1_RD_ID(x)		UPDATE(x, 22, 20)
+#define ORDER_FIFO0_RD_ID_MASK		GENMASK(18, 16)
+ #define ORDER_FIFO0_RD_ID(x)		UPDATE(x, 18, 16)
+#define ORDER_FIFO1_WR_ID_MASK		GENMASK(6, 4)
+ #define ORDER_FIFO1_WR_ID(x)		UPDATE(x, 6, 4)
+#define ORDER_FIFO0_WR_ID_MASK		GENMASK(2, 0)
+ #define ORDER_FIFO0_WR_ID(x)		UPDATE(x, 2, 0)
+
+#define RKLINK_DES_SOURCE_CFG		LINK_REG(0x0024)
+ #define E1_CAMERA_SRC_CSI		UPDATE(0, 23, 21)
+ #define E1_CAMERA_SRC_LVDS		UPDATE(1, 23, 21)
+ #define E1_CAMERA_SRC_DVP		UPDATE(2, 23, 21)
+ #define E1_DISPLAY_SRC_DSI		UPDATE(0, 23, 21)
+ #define E1_DISPLAY_SRC_DUAL_LDVS	UPDATE(1, 23, 21)
+ #define E1_DISPLAY_SRC_LVDS0		UPDATE(2, 23, 21)
+ #define E1_DISPLAY_SRC_LVDS1		UPDATE(3, 23, 21)
+ #define E1_DISPLAY_SRC_RGB		UPDATE(5, 23, 21)
+ #define E1_STREAM_CAMERA		UPDATE(0, 20, 20)
+ #define E1_STREAM_DISPLAY		UPDATE(1, 20, 20)
+ #define E0_CAMERA_SRC_CSI		UPDATE(0, 19, 17)
+ #define E0_CAMERA_SRC_LVDS		UPDATE(1, 19, 17)
+ #define E0_CAMERA_SRC_DVP		UPDATE(2, 19, 17)
+ #define E0_DISPLAY_SRC_DSI		UPDATE(0, 19, 17)
+ #define E0_DISPLAY_SRC_DUAL_LDVS	UPDATE(1, 19, 17)
+ #define E0_DISPLAY_SRC_LVDS0		UPDATE(2, 19, 17)
+ #define E0_DISPLAY_SRC_LVDS1		UPDATE(3, 19, 17)
+ #define E0_DISPLAY_SRC_RGB		UPDATE(5, 19, 17)
+ #define E0_STREAM_CAMERA		UPDATE(0, 16, 16)
+ #define E0_STREAM_DISPLAY		UPDATE(1, 16, 16)
+ #define LANE1_ENGINE_ID(x)		UPDATE(x, 7, 6)
+ #define LANE1_LANE_ID(x)		UPDATE(x, 5, 5)
+ #define LNAE1_ID_SEL(x)		UPDATE(x, 4, 4)
+ #define LANE0_ENGINE_ID(x)		UPDATE(x, 3, 2)
+ #define LANE0_LANE_ID(x)		UPDATE(x, 1, 1)
+ #define LNAE0_ID_SEL(x)		UPDATE(x, 0, 0)
+
+#define RKLINK_DES_REG01_ENGIN_DEL	0x0030
+#define E1_ENGINE_DELAY(x)		UPDATE(x, 31, 16)
+#define E0_ENGINE_DELAY(x)		UPDATE(x, 15, 0)
+#define RKLINK_DES_REG_PATCH		0X0050
+#define E3_FIRST_FRAME_DEL		BIT(7)
+#define E2_FIRST_FRAME_DEL		BIT(6)
+#define E1_FIRST_FRAME_DEL		BIT(5)
+#define E0_FIRST_FRAME_DEL		BIT(4)
+
+#define DES_RKLINK_STOP_CFG		LINK_REG(0x009C)
+ #define STOP_AUDIO			BIT(4)
+ #define STOP_E1			BIT(1)
+ #define STOP_E0			BIT(0)
+
+#define RKLINK_DES_SPI_CFG		LINK_REG(0x00C4)
+#define RKLINK_DES_UART_CFG		LINK_REG(0x00C8)
+#define RKLINK_DES_GPIO_CFG		LINK_REG(0x00CC)
+ #define GPIO_GROUP1_EN			BIT(17)
+ #define GPIO_GROUP0_EN			BIT(16)
+
+#define PCS_REG(id, x)			((x) + RKX120_DES_PCS0_BASE + (id) * RKX120_DES_PMA_OFFSET)
+
+#define PCS_REG00(id)			PCS_REG(id, 0x00)
+ #define DES_PCS_DUAL_LANE_MODE_EN	HIWORD_UPDATE(1, GENMASK(8, 8), 8)
+ #define DES_PCS_AUTO_START_EN		HIWORD_UPDATE(1, GENMASK(4, 4), 4)
+ #define DES_PCS_ECU_MODE		HIWORD_UPDATE(0, GENMASK(1, 1), 1)
+ #define DES_PCS_EN_MASK		HIWORD_MASK(0, 0)
+ #define DES_PCS_EN			HIWORD_UPDATE(1, GENMASK(0, 0), 0)
+ #define DES_PCS_DISABLE		HIWORD_UPDATE(0, GENMASK(0, 0), 0)
+
+#define PCS_REG04(id)			PCS_REG(id, 0x04)
+#define PCS_REG08(id)			PCS_REG(id, 0x08)
+#define PCS_REG10(id)			PCS_REG(id, 0x10)
+#define PCS_REG14(id)			PCS_REG(id, 0x14)
+#define PCS_REG18(id)			PCS_REG(id, 0x18)
+#define PCS_REG1C(id)			PCS_REG(id, 0x1C)
+#define PCS_REG20(id)			PCS_REG(id, 0x20)
+#define PCS_REG24(id)			PCS_REG(id, 0x24)
+#define PCS_REG28(id)			PCS_REG(id, 0x28)
+#define PCS_REG30(id)			PCS_REG(id, 0x30)
+#define PCS_REG34(id)			PCS_REG(id, 0x34)
+#define PCS_REG40(id)			PCS_REG(id, 0x40)
+
+#define PMA_REG(id, x)			((x) + RKX120_DES_PMA0_BASE + (id) * RKX120_DES_PMA_OFFSET)
+
+#define DES_PMA_STATUS(id)		PMA_REG(id, 0x00)
+ #define DES_PMA_FORCE_INIT_STA		BIT(23)
+ #define DES_PMA_RX_LOST		BIT(2)
+ #define DES_PMA_RX_PLL_LOCK		BIT(1)
+ #define DES_PMA_RX_RDY			BIT(0)
+
+#define DES_PMA_CTRL(id)		PMA_REG(id, 0x04)
+ #define DES_PMA_FORCE_INIT_MASK	HIWORD_MASK(8, 8)
+ #define DES_PMA_FORCE_INIT_EN		HIWORD_UPDATE(1, BIT(8), 8)
+ #define DES_PMA_FORCE_INIT_DISABLE	HIWORD_UPDATE(0, BIT(8), 8)
+ #define DES_PMA_DUAL_CHANNEL		HIWORD_UPDATE(1, BIT(3), 3)
+ #define DES_PMA_INIT_CNT_CLR_MASK	HIWORD_MASK(2, 2)
+ #define DES_PMA_INIT_CNT_CLR		HIWORD_UPDATE(1, BIT(2), 2)
+
+#define DES_PMA_LOAD00(id)		PMA_REG(id, 0x10)
+ #define PMA_RX_POL			BIT(0)
+ #define PMA_RX_WIDTH			BIT(1)
+ #define PMA_RX_MSBF_EN			BIT(2)
+ #define PMA_PLL_PWRDN			BIT(3)
+
+#define DES_PMA_LOAD01(id)		PMA_REG(id, 0x14)
+ #define DES_PMA_PLL_FORCE_LK(x)	HIWORD_UPDATE(x, GENMASK(13, 13), 13)
+ #define DES_PMA_LOS_VTH(x)		HIWORD_UPDATE(x, GENMASK(12, 11), 11)
+ #define DES_PMA_PD_CP_PD(x)		HIWORD_UPDATE(x, GENMASK(10, 10), 10)
+ #define DES_PMA_PD_CP_FP(x)		HIWORD_UPDATE(x, GENMASK(9, 9), 9)
+ #define DES_PMA_PD_LOOP_DIV(x)		HIWORD_UPDATE(x, GENMASK(8, 8), 8)
+ #define DES_PMA_PD_PFD(x)		HIWORD_UPDATE(x, GENMASK(7, 7), 7)
+ #define DES_PMA_PD_VBIAS(x)		HIWORD_UPDATE(x, GENMASK(6, 6), 6)
+ #define DES_PMA_AFE_VOS_EN(x)		HIWORD_UPDATE(x, GENMASK(5, 5), 5)
+ #define DES_PMA_PD_AFE(x)		HIWORD_UPDATE(x, GENMASK(4, 4), 4)
+ #define DES_PMA_RX_RTERM(x)		HIWORD_UPDATE(x, GENMASK(3, 0), 0)
+
+#define DES_PMA_LOAD02(id)		PMA_REG(id, 0x18)
+#define DES_PMA_LOAD03(id)		PMA_REG(id, 0x1C)
+#define DES_PMA_LOAD04(id)		PMA_REG(id, 0x20)
+
+#define DES_PMA_LOAD05(id)		PMA_REG(id, 0x24)
+ #define DES_PMA_PLL_REFCLK_DIV_MASK	HIWORD_MASK(15, 12)
+ #define DES_PMA_PLL_REFCLK_DIV(x)	HIWORD_UPDATE(x, GENMASK(15, 12), 12)
+
+#define DES_PMA_LOAD06(id)		PMA_REG(id, 0x28)
+ #define DES_PMA_MDATA_AMP_SEL(x)	HIWORD_UPDATE(x, GENMASK(15, 14), 14)
+ #define DES_PMA_RX_TSEQ(x)		HIWORD_UPDATE(x, GENMASK(13, 13), 13)
+ #define DES_PMA_FREZ_ADPT_EQ(x)	HIWORD_UPDATE(x, GENMASK(12, 12), 12)
+ #define DES_PMA__ADPT_EQ_TRIM(x)	HIWORD_UPDATE(x, GENMASK(11, 0), 0)
+
+#define DES_PMA_LOAD07(id)		PMA_REG(id, 0x2C)
+#define DES_PMA_LOAD08(id)		PMA_REG(id, 0x30)
+ #define DES_PMA_RX(x)			HIWORD_UPDATE(x, GENMASK(15, 0), 0)
+
+#define DES_PMA_LOAD09(id)		PMA_REG(id, 0x34)
+ #define DES_PMA_PLL_DIV_MASK		HIWORD_MASK(14, 0)
+ #define DES_PLL_I_POST_DIV(x)		HIWORD_UPDATE(x, GENMASK(14, 10), 10)
+ #define DES_PLL_F_POST_DIV(x)		HIWORD_UPDATE(x, GENMASK(9, 0), 0)
+ #define DES_PMA_PLL_DIV(x)		HIWORD_UPDATE(x, GENMASK(14, 0), 0)
+
+#define DES_PMA_LOAD0A(id)		PMA_REG(id, 0x38)
+ #define DES_PMA_CLK_2X_DIV_MASK	HIWORD_MASK(7, 0)
+ #define DES_PMA_CLK_2X_DIV(x)		HIWORD_UPDATE(x, GENMASK(7, 0), 0)
+
+#define DES_PMA_LOAD0B(id)		PMA_REG(id, 0x3C)
+#define DES_PMA_LOAD0C(id)		PMA_REG(id, 0x40)
+ #define DES_PMA_FCK_VCO_MASK		HIWORD_MASK(15, 15)
+ #define DES_PMA_FCK_VCO		HIWORD_UPDATE(1, BIT(15), 15)
+ #define DES_PMA_FCK_VCO_DIV2		HIWORD_UPDATE(0, BIT(15), 15)
+
+#define DES_PMA_LOAD0D(id)		PMA_REG(id, 0x44)
+ #define DES_PMA_PLL_DIV4_MASK		HIWORD_MASK(12, 12)
+ #define DES_PMA_PLL_DIV4		HIWORD_UPDATE(1, GENMASK(12, 12), 12)
+ #define DES_PMA_PLL_DIV8		HIWORD_UPDATE(0, GENMASK(12, 12), 12)
+
+#define DES_PMA_LOAD0E(id)		PMA_REG(id, 0x48)
+#define DES_PMA_REG100(id)		PMA_REG(id, 0x100)
+
+static const struct rk_serdes_pt des_pt[] = {
+	{
+		/* gpi_gpo_0 */
+		.en_reg = RKLINK_DES_GPIO_CFG,
+		.en_mask = 0x10001,
+		.en_val = 0x10001,
+		.dir_reg = RKLINK_DES_GPIO_CFG,
+		.dir_mask = 0x10,
+		.dir_val = 0x10,
+		.configs = 1,
+		{
+			{
+				.bank = RK_SERDES_DES_GPIO_BANK0,
+				.pin = RK_SERDES_GPIO_PIN_A5,
+				.incfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC6,
+				.outcfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC5,
+			},
+		},
+	}, {
+		/* gpi_gpo_1 */
+		.en_reg = RKLINK_DES_GPIO_CFG,
+		.en_mask = 0x10002,
+		.en_val = 0x10002,
+		.dir_reg = RKLINK_DES_GPIO_CFG,
+		.dir_mask = 0x20,
+		.dir_val = 0x20,
+		.configs = 1,
+		{
+			{
+				.bank = RK_SERDES_DES_GPIO_BANK0,
+				.pin = RK_SERDES_GPIO_PIN_A6,
+				.incfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC6,
+				.outcfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC5,
+			},
+		},
+	}, {
+		/* gpi_gpo_2 */
+		.en_reg = RKLINK_DES_GPIO_CFG,
+		.en_mask = 0x10004,
+		.en_val = 0x10004,
+		.dir_reg = RKLINK_DES_GPIO_CFG,
+		.dir_mask = 0x40,
+		.dir_val = 0x40,
+		.configs = 1,
+		{
+			{
+				.bank = RK_SERDES_DES_GPIO_BANK0,
+				.pin = RK_SERDES_GPIO_PIN_A7,
+				.incfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC6,
+				.outcfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC5,
+			},
+		},
+	}, {
+		/* gpi_gpo_3 */
+		.en_reg = RKLINK_DES_GPIO_CFG,
+		.en_mask = 0x10008,
+		.en_val = 0x10008,
+		.dir_reg = RKLINK_DES_GPIO_CFG,
+		.dir_mask = 0x80,
+		.dir_val = 0x80,
+		.configs = 1,
+		{
+			{
+				.bank = RK_SERDES_DES_GPIO_BANK0,
+				.pin = RK_SERDES_GPIO_PIN_B0,
+				.incfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC6,
+				.outcfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC5,
+			},
+		},
+	}, {
+		/* gpi_gpo_4 */
+		.en_reg = RKLINK_DES_GPIO_CFG,
+		.en_mask = 0x20100,
+		.en_val = 0x20100,
+		.dir_reg = RKLINK_DES_GPIO_CFG,
+		.dir_mask = 0x1000,
+		.dir_val = 0x1000,
+		.configs = 1,
+		{
+			{
+				.bank = RK_SERDES_DES_GPIO_BANK0,
+				.pin = RK_SERDES_GPIO_PIN_B3,
+				.incfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC2,
+				.outcfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC3,
+			},
+		},
+	}, {
+		/* gpi_gpo_5 */
+		.en_reg = RKLINK_DES_GPIO_CFG,
+		.en_mask = 0x20200,
+		.en_val = 0x20200,
+		.dir_reg = RKLINK_DES_GPIO_CFG,
+		.dir_mask = 0x2000,
+		.dir_val = 0x2000,
+		.configs = 1,
+		{
+			{
+				.bank = RK_SERDES_DES_GPIO_BANK0,
+				.pin = RK_SERDES_GPIO_PIN_B4,
+				.incfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC2,
+				.outcfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC3,
+			},
+		},
+	}, {
+		/* gpi_gpo_6 */
+		.en_reg = RKLINK_DES_GPIO_CFG,
+		.en_mask = 0x20400,
+		.en_val = 0x20400,
+		.dir_reg = RKLINK_DES_GPIO_CFG,
+		.dir_mask = 0x4000,
+		.dir_val = 0x4000,
+		.configs = 1,
+		{
+			{
+				.bank = RK_SERDES_DES_GPIO_BANK0,
+				.pin = RK_SERDES_GPIO_PIN_B5,
+				.incfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC2,
+				.outcfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC3,
+			},
+		},
+	}, {
+		/* passthrough irq */
+		.en_reg = RKLINK_DES_GPIO_CFG,
+		.en_mask = 0x20800,
+		.en_val = 0x20800,
+		.dir_reg = RKLINK_DES_GPIO_CFG,
+		.dir_mask = 0x8000,
+		.dir_val = 0x8000,
+		.configs = 1,
+		{
+			{
+				.bank = RK_SERDES_DES_GPIO_BANK0,
+				.pin = RK_SERDES_GPIO_PIN_A4,
+				.incfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC1,
+				.outcfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC2,
+			},
+		},
+	}, {
+		/* passthrough uart0 */
+		.en_reg = RKLINK_DES_UART_CFG,
+		.en_mask = 0x1,
+		.en_val = 0x1,
+		.configs = 1,
+		{
+			{
+				.bank = RK_SERDES_DES_GPIO_BANK0,
+				.pin = RK_SERDES_GPIO_PIN_A5 | RK_SERDES_GPIO_PIN_A6,
+				.incfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC4,
+				.outcfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC3,
+			},
+		},
+	}, {
+		/* passthrough uart1 */
+		.en_reg = RKLINK_DES_UART_CFG,
+		.en_mask = 0x2,
+		.en_val = 0x2,
+		.configs = 1,
+		{
+			{
+				.bank = RK_SERDES_DES_GPIO_BANK0,
+				.pin = RK_SERDES_GPIO_PIN_A7 | RK_SERDES_GPIO_PIN_B0,
+				.incfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC4,
+				.outcfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC3,
+			},
+		},
+	}, {
+		/* passthrough spi */
+		.en_reg = RKLINK_DES_SPI_CFG,
+		.en_mask = 0x4,
+		.en_val = 0x4,
+		.dir_reg = RKLINK_DES_SPI_CFG,
+		.dir_mask = 0x1,
+		.dir_val = 0,
+		.configs = 1,
+		{
+			{
+				.bank = RK_SERDES_DES_GPIO_BANK0,
+				.pin = RK_SERDES_GPIO_PIN_A5 | RK_SERDES_GPIO_PIN_A6 |
+				       RK_SERDES_GPIO_PIN_A7 | RK_SERDES_GPIO_PIN_B0,
+				.incfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC2,
+				.outcfgs = RK_SERDES_PIN_CONFIG_MUX_FUNC1,
+			},
+		},
+	},
+};
+
+static int rk_des_get_stream_source(struct rk_serdes_route *route, u32 port, u8 engine_id)
+{
+	if (route->stream_type == STREAM_DISPLAY) {
+		if (port & RK_SERDES_RGB_TX)
+			return engine_id ? E1_DISPLAY_SRC_RGB : E0_DISPLAY_SRC_RGB;
+		else if (port & RK_SERDES_LVDS_TX0)
+			return engine_id ? E1_DISPLAY_SRC_LVDS0 : E0_DISPLAY_SRC_LVDS0;
+		else if (port & RK_SERDES_LVDS_TX1)
+			return engine_id ? E1_DISPLAY_SRC_LVDS1 : E0_DISPLAY_SRC_LVDS1;
+		else if (port & RK_SERDES_DUAL_LVDS_TX)
+			return engine_id ? E1_DISPLAY_SRC_DUAL_LDVS : E0_DISPLAY_SRC_DUAL_LDVS;
+		else if (port & RK_SERDES_DSI_TX0)
+			return engine_id ? E1_DISPLAY_SRC_DSI : E0_DISPLAY_SRC_DSI;
+		else if (port & RK_SERDES_DSI_TX1)
+			return engine_id ? E1_DISPLAY_SRC_DSI : E0_DISPLAY_SRC_DSI;
+	} else {
+		return engine_id ? E1_CAMERA_SRC_CSI : E0_CAMERA_SRC_CSI;
+	}
+	return 0;
+}
+
+static void rk_serdes_link_rx_rgb_enable(struct rk_serdes *serdes,
+					 struct rk_serdes_route *route,
+					 u8 remote_id)
+{
+	struct i2c_client *client = serdes->chip[remote_id].client;
+
+	serdes->i2c_write_reg(client, RKLINK_DES_REG01_ENGIN_DEL,
+			      E1_ENGINE_DELAY(2800) | E0_ENGINE_DELAY(2800));
+
+	serdes->i2c_write_reg(client, RKLINK_DES_REG_PATCH,
+			      E3_FIRST_FRAME_DEL | E2_FIRST_FRAME_DEL |
+			      E1_FIRST_FRAME_DEL | E0_FIRST_FRAME_DEL);
+}
+
+static void rk_serdes_link_rx_lvds_enable(struct rk_serdes *serdes,
+					  struct rk_serdes_route *route,
+					  u8 remote_id)
+{
+	struct i2c_client *client = serdes->chip[remote_id].client;
+
+	serdes->i2c_write_reg(client, RKLINK_DES_REG01_ENGIN_DEL,
+			      E1_ENGINE_DELAY(4096) | E0_ENGINE_DELAY(4096));
+
+	serdes->i2c_write_reg(client, RKLINK_DES_REG_PATCH,
+			      E3_FIRST_FRAME_DEL | E2_FIRST_FRAME_DEL |
+			      E1_FIRST_FRAME_DEL | E0_FIRST_FRAME_DEL);
+}
+
+static void rk_serdes_link_rx_dsi_enable(struct rk_serdes *serdes,
+					struct rk_serdes_route *route,
+					u8 remote_id)
+{
+	struct i2c_client *client = serdes->chip[remote_id].client;
+
+	serdes->i2c_write_reg(client, RKLINK_DES_REG01_ENGIN_DEL,
+			      E1_ENGINE_DELAY(4096) | E0_ENGINE_DELAY(4096));
+
+	serdes->i2c_write_reg(client, RKLINK_DES_REG_PATCH,
+			      E3_FIRST_FRAME_DEL | E2_FIRST_FRAME_DEL|
+			      E1_FIRST_FRAME_DEL | E0_FIRST_FRAME_DEL);
+}
+
+static int rk120_link_rx_cfg(struct rk_serdes *serdes, struct rk_serdes_route *route, u8 remote_id)
+{
+	struct hwclk *hwclk = serdes->chip[remote_id].hwclk;
+	struct i2c_client *client;
+	u32 stream_type;
+	u32 rx_src;
+	u32 ctrl_val, mask, val;
+	u32 lane0_dsource_id, lane1_dsource_id;
+	bool is_rx_dual_lanes;
+	bool is_rx_dual_channels;
+
+	if (route->stream_type == STREAM_DISPLAY) {
+		client = serdes->chip[remote_id].client;
+		stream_type = E0_STREAM_DISPLAY;
+	} else {
+		client = serdes->chip[DEVICE_LOCAL].client;
+		stream_type = E0_STREAM_CAMERA;
+	}
+
+	is_rx_dual_lanes = (serdes->route_flag & ROUTE_MULTI_LANE) &&
+			   !(serdes->route_flag & ROUTE_MULTI_REMOTE);
+	is_rx_dual_channels = (serdes->route_flag & ROUTE_MULTI_CHANNEL) &&
+			       !(serdes->route_flag & ROUTE_MULTI_REMOTE);
+
+	serdes->i2c_read_reg(client, RKLINK_DES_LANE_ENGINE_CFG, &ctrl_val);
+
+	ctrl_val &= ~LANE1_EN;
+	ctrl_val |= LANE0_EN;
+	ctrl_val |= ENGINE0_EN;
+	if (is_rx_dual_lanes) {
+		ctrl_val |= LANE1_EN;
+		if (is_rx_dual_channels)
+			ctrl_val |= ENGINE1_EN;
+		else
+			ctrl_val |= ENGINE0_2_LANE;
+	} else {
+		if (is_rx_dual_channels)
+			ctrl_val |= ENGINE1_EN;
+	}
+	serdes->i2c_write_reg(client, RKLINK_DES_LANE_ENGINE_CFG, ctrl_val);
+
+	mask = LANE0_ENGINE_CFG_MASK;
+	val = LANE0_ENGINE0;
+	if (is_rx_dual_lanes) {
+		if (is_rx_dual_channels) {
+			mask |= LANE1_ENGINE_CFG_MASK;
+			val |= LANE1_ENGINE1;
+		} else {
+			mask |= LANE1_ENGINE_CFG_MASK;
+			val |= LANE1_ENGINE0;
+		}
+	} else {
+		if (is_rx_dual_channels)
+			val |= LANE0_ENGINE1;
+	}
+
+	serdes->i2c_update_bits(client, RKLINK_DES_LANE_ENGINE_DST, mask, val);
+
+	serdes->i2c_read_reg(client, RKLINK_DES_SOURCE_CFG, &val);
+
+	val &= ~(LANE0_ENGINE_ID(1) | LANE0_LANE_ID(1) | LANE1_ENGINE_ID(1) |
+		 LANE1_LANE_ID(1) | LNAE0_ID_SEL(1) | LNAE1_ID_SEL(1));
+
+	if (is_rx_dual_lanes) {
+		if (is_rx_dual_channels) {
+			val |= LANE0_ENGINE_ID(0);
+			val |= LANE0_LANE_ID(0);
+			val |= LNAE0_ID_SEL(1);
+			val |= LANE1_ENGINE_ID(1);
+			val |= LANE1_LANE_ID(0);
+			val |= LNAE1_ID_SEL(1);
+			stream_type |= E1_STREAM_DISPLAY;
+		} else {
+			val |= LANE0_ENGINE_ID(0);
+			val |= LANE0_LANE_ID(0);
+			val |= LNAE0_ID_SEL(1);
+			val |= LANE1_ENGINE_ID(0);
+			val |= LANE1_LANE_ID(1);
+			val |= LNAE0_ID_SEL(1);
+		}
+	} else {
+		if (is_rx_dual_channels) {
+			val |= LANE0_ENGINE_ID(0);
+			val |= LANE0_LANE_ID(0);
+			val |= LANE1_ENGINE_ID(1);
+			val |= LANE1_LANE_ID(0);
+			stream_type |= E1_STREAM_DISPLAY;
+		} else {
+			val |= LNAE0_ID_SEL(1);
+		}
+	}
+	val |= stream_type;
+	if (remote_id == DEVICE_REMOTE0)
+		rx_src = rk_des_get_stream_source(route, route->remote0_port0, 0);
+	else
+		rx_src = rk_des_get_stream_source(route, route->remote1_port0, 0);
+	val |= rx_src;
+	if (is_rx_dual_channels) {
+		rx_src = rk_des_get_stream_source(route, route->remote0_port1, 1);
+		val |= rx_src;
+	}
+	serdes->i2c_write_reg(client, RKLINK_DES_SOURCE_CFG, val);
+
+	if (is_rx_dual_lanes || is_rx_dual_channels) {
+		mask = DATA_FIFO0_WR_ID_MASK | DATA_FIFO1_WR_ID_MASK | DATA_FIFO2_WR_ID_MASK |
+			DATA_FIFO3_WR_ID_MASK;
+		mask |= DATA_FIFO0_RD_ID_MASK | DATA_FIFO1_RD_ID_MASK | DATA_FIFO2_RD_ID_MASK |
+			DATA_FIFO3_RD_ID_MASK;
+		if (is_rx_dual_channels) {
+			lane0_dsource_id = (0 << 1) | 0;
+			lane1_dsource_id = (1 << 1) | 0;
+		} else {
+			lane0_dsource_id = (0 << 1) | 0;
+			lane1_dsource_id = (0 << 1) | 1;
+		}
+		val = DATA_FIFO0_WR_ID(lane0_dsource_id) | DATA_FIFO1_WR_ID(lane0_dsource_id);
+		val |= DATA_FIFO0_RD_ID(lane0_dsource_id) | DATA_FIFO1_RD_ID(lane0_dsource_id);
+
+		val |= DATA_FIFO2_WR_ID(lane1_dsource_id) | DATA_FIFO3_WR_ID(lane1_dsource_id);
+		val |= DATA_FIFO2_RD_ID(lane1_dsource_id) | DATA_FIFO3_RD_ID(lane1_dsource_id);
+
+		serdes->i2c_update_bits(client, RKLINK_DES_DATA_ID_CFG, mask, val);
+
+		mask = ORDER_FIFO0_WR_ID_MASK | ORDER_FIFO1_WR_ID_MASK |
+			ORDER_FIFO0_RD_ID_MASK | ORDER_FIFO1_RD_ID_MASK;
+		val = ORDER_FIFO0_WR_ID(lane0_dsource_id) | ORDER_FIFO1_WR_ID(lane1_dsource_id) |
+			ORDER_FIFO0_RD_ID(lane0_dsource_id) | ORDER_FIFO1_RD_ID(lane1_dsource_id);
+
+		serdes->i2c_update_bits(client, RKLINK_DES_ORDER_ID_CFG, mask, val);
+	}
+
+	ctrl_val |= DES_EN;
+	serdes->i2c_write_reg(client, RKLINK_DES_LANE_ENGINE_CFG, ctrl_val);
+
+	hwclk_set_rate(hwclk, RKX120_CPS_E0_CLK_RKLINK_RX_PRE, route->vm.pixelclock);
+	dev_info(serdes->dev, "RKX120_CPS_E0_CLK_RKLINK_RX_PRE:%d\n",
+		 hwclk_get_rate(hwclk, RKX120_CPS_E0_CLK_RKLINK_RX_PRE));
+	if (is_rx_dual_channels) {
+		hwclk_set_rate(hwclk, RKX120_CPS_E1_CLK_RKLINK_RX_PRE, route->vm.pixelclock);
+		dev_info(serdes->dev, "RKX120_CPS_E1_CLK_RKLINK_RX_PRE:%d\n",
+			 hwclk_get_rate(hwclk, RKX120_CPS_E1_CLK_RKLINK_RX_PRE));
+	}
+
+	if (route->remote0_port0 == RK_SERDES_RGB_TX || route->remote1_port0 == RK_SERDES_RGB_TX)
+		rk_serdes_link_rx_rgb_enable(serdes, route, remote_id);
+
+	if (route->remote0_port0 == RK_SERDES_LVDS_TX0 ||
+	    route->remote1_port0 == RK_SERDES_LVDS_TX0 ||
+	    route->remote0_port0 == RK_SERDES_LVDS_TX1 ||
+	    route->remote1_port0 == RK_SERDES_LVDS_TX1 ||
+	    route->remote0_port0 == RK_SERDES_DUAL_LVDS_TX)
+		rk_serdes_link_rx_lvds_enable(serdes, route, remote_id);
+
+	if (route->remote0_port0 == RK_SERDES_DSI_TX0 || route->remote1_port0 == RK_SERDES_DSI_TX0)
+		rk_serdes_link_rx_dsi_enable(serdes, route, remote_id);
+
+	return 0;
+}
+
+static int rk120_des_pcs_cfg(struct rk_serdes *serdes, struct rk_serdes_route *route,
+			     u8 remote_id, u8 pcs_id)
+{
+	return 0;
+}
+
+static int rk120_des_pma_cfg(struct rk_serdes *serdes, struct rk_serdes_route *route, u8 remote_id,
+			     u8 pcs_id)
+{
+	return 0;
+}
+
+int rkx120_linkrx_enable(struct rk_serdes *serdes, struct rk_serdes_route *route, u8 remote_id)
+{
+	rk120_link_rx_cfg(serdes, route, remote_id);
+
+	rk120_des_pcs_cfg(serdes, route, remote_id, 0);
+	rk120_des_pma_cfg(serdes, route, remote_id, 0);
+	if ((serdes->route_flag & ROUTE_MULTI_LANE) &&
+	    !(serdes->route_flag & ROUTE_MULTI_REMOTE)) {
+		rk120_des_pcs_cfg(serdes, route, remote_id, 1);
+		rk120_des_pma_cfg(serdes, route, remote_id, 1);
+	}
+
+	return 0;
+}
+
+void rkx120_linkrx_engine_enable(struct rk_serdes *serdes, u8 en_id, u8 dev_id, bool enable)
+{
+	struct i2c_client *client = serdes->chip[dev_id].client;
+
+	if (en_id)
+		serdes->i2c_update_bits(client, DES_RKLINK_STOP_CFG, STOP_E1,
+					enable ? 0 : STOP_E1);
+	else
+		serdes->i2c_update_bits(client, DES_RKLINK_STOP_CFG, STOP_E0,
+					enable ? 0 : STOP_E0);
+}
+
+void rkx120_linkrx_passthrough_cfg(struct rk_serdes *serdes, u32 client_id, u32 func_id,
+				   bool is_rx)
+{
+	struct i2c_client *client = serdes->chip[client_id].client;
+	const struct rk_serdes_pt_pin *pt_pin = des_pt[func_id].pt_pins;
+	int i;
+
+	/* config link passthrough */
+	serdes->i2c_update_bits(client, des_pt[func_id].en_reg, des_pt[func_id].en_mask,
+				des_pt[func_id].en_val);
+	if (des_pt[func_id].en_reg)
+		serdes->i2c_update_bits(client, des_pt[func_id].dir_reg, des_pt[func_id].dir_mask,
+					is_rx ? des_pt[func_id].dir_val : ~des_pt[func_id].dir_val);
+
+	/* config passthrough pinctrl */
+	for (i = 0; i < des_pt[func_id].configs; i++) {
+		serdes->set_hwpin(serdes, client, PIN_RKX120, pt_pin[i].bank, pt_pin[i].pin,
+				  is_rx ? pt_pin[i].incfgs : pt_pin[i].outcfgs);
+	}
+}
+
+void rkx120_linkrx_wait_link_ready(struct rk_serdes *serdes, u8 id)
+{
+	struct i2c_client *client = serdes->chip[DEVICE_LOCAL].client;
+	u32 val;
+	int ret;
+	int sta;
+
+	if (id)
+		sta = DES_PCS1_READY;
+	else
+		sta = DES_PCS0_READY;
+
+	ret = read_poll_timeout(serdes->i2c_read_reg, ret,
+				!(ret < 0) && (val & sta),
+				1000, USEC_PER_SEC, false, client,
+				DES_GRF_SOC_STATUS0, &val);
+	if (ret < 0)
+		dev_err(&client->dev, "wait link ready timeout: 0x%08x\n", val);
+	else
+		dev_info(&client->dev, "link success: 0x%08x\n", val);
+}
+
+static void rkx120_pma_link_config(struct rk_serdes *serdes, u8 pcs_id, u8 dev_id)
+{
+	struct i2c_client *client = serdes->chip[dev_id].client;
+
+	serdes->i2c_write_reg(client, DES_PMA_LOAD08(pcs_id), DES_PMA_RX(0x23b1));
+	serdes->i2c_write_reg(client, DES_PMA_LOAD01(pcs_id), DES_PMA_LOS_VTH(0) |
+							      DES_PMA_RX_RTERM(0x8));
+	serdes->i2c_write_reg(client, DES_PMA_LOAD06(pcs_id), DES_PMA_MDATA_AMP_SEL(0x3));
+	serdes->i2c_write_reg(client, DES_PMA_REG100(pcs_id), 0xffff0000);
+}
+
+void rkx120_pma_set_rate(struct rk_serdes *serdes, struct rk_serdes_pma_pll *pll,
+			 u8 pcs_id, u8 dev_id)
+{
+	struct i2c_client *client = serdes->chip[dev_id].client;
+	u32 val;
+
+	serdes->i2c_read_reg(client, DES_PMA_STATUS(pcs_id), &val);
+	if (val & DES_PMA_FORCE_INIT_STA)
+		serdes->i2c_update_bits(client, DES_PMA_CTRL(pcs_id), DES_PMA_INIT_CNT_CLR_MASK,
+					DES_PMA_INIT_CNT_CLR);
+
+	if (pll->force_init_en)
+		serdes->i2c_update_bits(client, DES_PMA_CTRL(pcs_id), DES_PMA_FORCE_INIT_MASK,
+					DES_PMA_FORCE_INIT_EN);
+	else
+		serdes->i2c_update_bits(client, DES_PMA_CTRL(pcs_id), DES_PMA_FORCE_INIT_MASK,
+					DES_PMA_FORCE_INIT_DISABLE);
+
+	serdes->i2c_update_bits(client, DES_PMA_LOAD09(pcs_id), DES_PMA_PLL_DIV_MASK,
+				DES_PMA_PLL_DIV(pll->pll_div));
+	serdes->i2c_update_bits(client, DES_PMA_LOAD05(pcs_id), DES_PMA_PLL_REFCLK_DIV_MASK,
+				DES_PMA_PLL_REFCLK_DIV(pll->pll_refclk_div));
+
+	if (pll->pll_fck_vco_div2)
+		serdes->i2c_update_bits(client, DES_PMA_LOAD0C(pcs_id), DES_PMA_FCK_VCO_MASK,
+					DES_PMA_FCK_VCO_DIV2);
+	else
+		serdes->i2c_update_bits(client, DES_PMA_LOAD0C(pcs_id), DES_PMA_FCK_VCO_MASK,
+					DES_PMA_FCK_VCO);
+
+	if (pll->pll_div4)
+		serdes->i2c_update_bits(client, DES_PMA_LOAD0D(pcs_id), DES_PMA_PLL_DIV4_MASK,
+					DES_PMA_PLL_DIV4);
+	else
+		serdes->i2c_update_bits(client, DES_PMA_LOAD0D(pcs_id), DES_PMA_PLL_DIV4_MASK,
+					DES_PMA_PLL_DIV8);
+
+	serdes->i2c_update_bits(client, DES_PMA_LOAD0A(pcs_id), DES_PMA_CLK_2X_DIV_MASK,
+				DES_PMA_CLK_2X_DIV(pll->clk_div));
+
+	rkx120_pma_link_config(serdes, pcs_id, dev_id);
+}
+
+void rkx120_pcs_enable(struct rk_serdes *serdes, bool enable, u8 pcs_id, u8 dev_id)
+{
+	struct i2c_client *client = serdes->chip[dev_id].client;
+
+	dev_info(serdes->dev, "%s: %d\n", __func__, enable);
+
+	if (enable)
+		serdes->i2c_update_bits(client, PCS_REG00(pcs_id), DES_PCS_EN_MASK, DES_PCS_EN);
+	else
+		serdes->i2c_update_bits(client, PCS_REG00(pcs_id), DES_PCS_EN_MASK,
+					DES_PCS_DISABLE);
+}
+
+void rkx120_des_pma_enable(struct rk_serdes *serdes, bool enable, u8 pma_id, u8 dev_id)
+{
+	struct i2c_client *client = serdes->chip[dev_id].client;
+	u32 mask, val;
+
+	if (pma_id) {
+		mask = PMA1_EN_MASK;
+		val = enable ? PMA1_EN : PMA1_DISABLE;
+	} else {
+		mask = PMA0_EN_MASK;
+		val = enable ? PMA0_EN : PMA0_DISABLE;
+	}
+
+	serdes->i2c_update_bits(client, DES_GRF_SOC_CON4, mask, val);
+}
diff --git a/kernel/drivers/mfd/rkx110_x120/rkx120_reg.h b/kernel/drivers/mfd/rkx110_x120/rkx120_reg.h
new file mode 100644
index 0000000..91e7c91
--- /dev/null
+++ b/kernel/drivers/mfd/rkx110_x120/rkx120_reg.h
@@ -0,0 +1,468 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2022 Rockchip Electronics Co. Ltd.
+ *
+ * Author: Zhang Yubing <yubing.zhang@rock-chips.com>
+ */
+
+#ifndef __X120_REG_H
+#define __X120_REG_H
+
+#include <linux/bits.h>
+
+#define HIWORD_MASK(h, l)	(GENMASK(h, l) | GENMASK(h, l) << 16)
+#define UPDATE(x, h, l)		(((x) << (l)) & GENMASK((h), (l)))
+#define HIWORD_UPDATE(v, m, l)	(((v) << (l)) | (m << 16))
+
+/************** RKX120 DES RX ***************/
+#define RKX120_DES_CRU_BASE		0x01000000
+#define RKX120_DES_GRF_BASE		0x01010000
+#define GRF_REG(x)			 ((x) + RKX120_DES_GRF_BASE)
+#define DES_GRF_GPIO0A_IOMUX_L		GRF_REG(0x0)
+#define DES_GRF_GPIO0A_IOMUX_H		GRF_REG(0x4)
+#define DES_GRF_GPIO0B_IOMUX_L		GRF_REG(0x8)
+#define DES_GRF_GPIO0B_IOMUX_H		GRF_REG(0xC)
+#define DES_GRF_GPIO0C_IOMUX_L		GRF_REG(0x10)
+#define DES_GRF_GPIO0C_IOMUX_H		GRF_REG(0x14)
+#define DES_GRF_GPIO0A_PULL_EN		GRF_REG(0x20)
+#define DES_GRF_GPIO0B_PULL_EN		GRF_REG(0x24)
+#define DES_GRF_GPIO0C_PULL_EN		GRF_REG(0x28)
+#define DES_GRF_GPIO1A_IOMUX		GRF_REG(0x80)
+#define DES_GRF_GPIO1B_IOMUX		GRF_REG(0x84)
+#define DES_GRF_GPIO1C_IOMUX		GRF_REG(0x88)
+#define DES_GRF_GPIO1A_PULL_CFG		GRF_REG(0x90)
+#define DES_GRF_GPIO1B_PULL_CFG		GRF_REG(0x94)
+#define DES_GRF_GPIO1C_PULL_CFG		GRF_REG(0x98)
+#define DES_GRF_SOC_CON2		GRF_REG(0x108)
+#define DES_GRF_SOC_STATUS0		GRF_REG(0x160)
+
+enum {
+	/* GPIO0A_IOMUX_H */
+	GPIO0A7_SHIFT	= 12,
+	GPIO0A7_MASK	= GENMASK(14, 12),
+	GPIO0A7_GPIO	= 0,
+	GPIO0A7_SPI_MISO_M,
+	GPIO0A7_SPI_MISO_S,
+	GPIO0A7_UART1_TX_M,
+	GPIO0A7_UART1_TX_S,
+	GPIO0A7_GPO_2,
+	GPIO0A7_GPI_2,
+	GPIO0A7_PWM_0,
+
+	GPIO0A6_SHIFT	= 8,
+	GPIO0A6_MASK	= GENMASK(10, 8),
+	GPIO0A6_GPIO	= 0,
+	GPIO0A6_SPI_MOSI_M,
+	GPIO0A6_SPI_MOSI_S,
+	GPIO0A6_UART0_RX_M,
+	GPIO0A6_UART0_RX_S,
+	GPIO0A6_GPO_1,
+	GPIO0A6_GPI_1,
+	GPIO0A6_I2C_DEBUG_SDA,
+
+	GPIO0A5_SHIFT	= 4,
+	GPIO0A5_MASK	= GENMASK(6, 4),
+	GPIO0A5_GPIO	= 0,
+	GPIO0A5_SPI_CLK_M,
+	GPIO0A5_SPI_CLK_S,
+	GPIO0A5_UART0_TX_M,
+	GPIO0A5_UART0_TX_S,
+	GPIO0A5_GPO_0,
+	GPIO0A5_GPI_0,
+	GPIO0A5_I2C_DEBUG_SCL,
+
+	GPIO0A4_SHIFT	= 0,
+	GPIO0A4_MASK	= GENMASK(2, 0),
+	GPIO0A4_GPIO	= 0,
+	GPIO0A4_INT_RX,
+	GPIO0A4_INT_TX,
+
+	/* GPIO0B_IOMUX_L */
+	GPIO0B3_SHIFT	= 12,
+	GPIO0B3_MASK	= GENMASK(14, 12),
+	GPIO0B3_GPIO	= 0,
+	GPIO0B3_I2S_SDO0,
+	GPIO0B3_GPI_4,
+	GPIO0B3_GPO_4,
+	GPIO0B3_TP2,
+
+	GPIO0B2_SHIFT	= 8,
+	GPIO0B2_MASK	= GENMASK(10, 8),
+	GPIO0B2_GPIO	= 0,
+	GPIO0B2_I2S_LRCK_M,
+	GPIO0B2_I2S_LRCK_S,
+	GPIO0B2_TP1,
+
+	GPIO0B1_SHIFT	= 4,
+	GPIO0B1_MASK	= GENMASK(6, 4),
+	GPIO0B1_GPIO	= 0,
+	GPIO0B1_I2S_SCLK_M,
+	GPIO0B1_I2S_SCLK_S,
+	GPIO0B1_TP0,
+
+	GPIO0B0_SHIFT	= 0,
+	GPIO0B0_MASK	= GENMASK(2, 0),
+	GPIO0B0_GPIO	= 0,
+	GPIO0B0_SPI_CSN_M,
+	GPIO0B0_SPI_CSN_S,
+	GPIO0B0_UART1_RX_M,
+	GPIO0B0_UART1_RX_S,
+	GPIO0B0_GPO_3,
+	GPIO0B0_GPI_3,
+	GPIO0B0_PWM_1,
+
+	/* GPIO0B_IOMUX_H */
+	GPIO0B7_SHIFT	= 12,
+	GPIO0B7_MASK	= GENMASK(14, 12),
+	GPIO0B7_GPIO	= 0,
+	GPIO0B7_I2S_MCLK,
+	GPIO0B7_TEST_CLK_OUT,
+	GPIO0B7_TP6,
+
+	GPIO0B6_SHIFT	= 8,
+	GPIO0B6_MASK	= GENMASK(10, 8),
+	GPIO0B6_GPIO	= 0,
+	GPIO0B6_I2S_SDO3,
+	GPIO0B6_I2S_SDI0,
+	GPIO0B6_TP5,
+
+	GPIO0B5_SHIFT	= 4,
+	GPIO0B5_MASK	= GENMASK(6, 4),
+	GPIO0B5_GPIO	= 0,
+	GPIO0B5_I2S_SDO2,
+	GPIO0B5_GPI_6,
+	GPIO0B5_GPO_6,
+	GPIO0B5_I2C1_SDA_M,
+	GPIO0B5_I2C1_SDA_S,
+	GPIO0B5_TP4,
+
+	GPIO0B4_SHIFT	= 0,
+	GPIO0B4_MASK	= GENMASK(2, 0),
+	GPIO0B4_GPIO	= 0,
+	GPIO0B5_I2S_SDO1,
+	GPIO0B5_GPI_5,
+	GPIO0B5_GPO_5,
+	GPIO0B5_I2C1_SCL_M,
+	GPIO0B5_I2C1_SCL_S,
+	GPIO0B5_PWM2,
+	GPIO0B5_TP3,
+
+	/* GPIO0C_IOMUX_L */
+	GPIO0C4_SHIFT	= 12,
+	GPIO0C4_MASK	= GENMASK(14, 12),
+	GPIO0C4_GPIO	= 0,
+	GPIO0C4_LCDC_D0,
+	GPIO0C4_CIF_D0,
+	GPIO0C4_TP11,
+
+	GPIO0C3_SHIFT	= 9,
+	GPIO0C3_MASK	= GENMASK(11, 9),
+	GPIO0C3_GPIO	= 0,
+	GPIO0C3_LCDC_DEN,
+	GPIO0C3_TP10,
+
+	GPIO0C2_SHIFT	= 6,
+	GPIO0C2_MASK	= GENMASK(8, 6),
+	GPIO0C2_GPIO	= 0,
+	GPIO0C2_LCDC_HSYNC,
+	GPIO0C2_CIF_HSYNC,
+	GPIO0C2_TP9,
+
+	GPIO0C1_SHIFT	= 3,
+	GPIO0C1_MASK	= GENMASK(5, 3),
+	GPIO0C1_GPIO	= 0,
+	GPIO0C1_LCDC_VSYNC,
+	GPIO0C1_CIF_VSYNC,
+	GPIO0C1_TP8,
+
+	GPIO0C0_SHIFT	= 0,
+	GPIO0C0_MASK	= GENMASK(2, 0),
+	GPIO0C0_GPIO	= 0,
+	GPIO0C0_LCDC_CLK,
+	GPIO0C0_CIF_CLK,
+	GPIO0C0_TP7,
+
+	/* GPIO0C_IOMUX_H */
+	GPIO0C7_SHIFT	= 6,
+	GPIO0C7_MASK	= GENMASK(8, 6),
+	GPIO0C7_GPIO	= 0,
+	GPIO0C7_LCDC_D3,
+	GPIO0C7_CIF_D3,
+	GPIO0C7_TP14,
+
+	GPIO0C6_SHIFT	= 3,
+	GPIO0C6_MASK	= GENMASK(5, 3),
+	GPIO0C6_GPIO	= 0,
+	GPIO0C6_LCDC_D2,
+	GPIO0C6_CIF_D2,
+	GPIO0C6_TP13,
+
+	GPIO0C5_SHIFT	= 0,
+	GPIO0C5_MASK	= GENMASK(2, 0),
+	GPIO0C5_GPIO	= 0,
+	GPIO0C5_LCDC_D1,
+	GPIO0C5_CIF_D1,
+	GPIO0C5_TP12,
+
+	/* GPIO1A_IOMUX */
+	GPIO1A7_SHIFT	= 14,
+	GPIO1A7_MASK	= GENMASK(15, 14),
+	GPIO1A7_GPIO	= 0,
+	GPIO1A7_LCDC_D11,
+	GPIO1A7_CIF_D11,
+
+	GPIO1A6_SHIFT	= 12,
+	GPIO1A6_MASK	= GENMASK(13, 12),
+	GPIO1A6_GPIO	= 0,
+	GPIO1A6_LCDC_D10,
+	GPIO1A6_CIF_D10,
+
+	GPIO1A5_SHIFT	= 10,
+	GPIO1A5_MASK	= GENMASK(11, 10),
+	GPIO1A5_GPIO	= 0,
+	GPIO1A5_LCDC_D9,
+	GPIO1A5_CIF_D9,
+
+	GPIO1A4_SHIFT	= 8,
+	GPIO1A4_MASK	= GENMASK(9, 8),
+	GPIO1A4_GPIO	= 0,
+	GPIO1A4_LCDC_D8,
+	GPIO1A4_CIF_D8,
+
+	GPIO1A3_SHIFT	= 6,
+	GPIO1A3_MASK	= GENMASK(7, 6),
+	GPIO1A3_GPIO	= 0,
+	GPIO1A3_LCDC_D7,
+	GPIO1A3_CIF_D7,
+
+	GPIO1A2_SHIFT	= 4,
+	GPIO1A2_MASK	= GENMASK(5, 4),
+	GPIO1A2_GPIO	= 0,
+	GPIO1A2_LCDC_D6,
+	GPIO1A2_CIF_D6,
+
+	GPIO1A1_SHIFT	= 2,
+	GPIO1A1_MASK	= GENMASK(3, 2),
+	GPIO1A1_GPIO	= 0,
+	GPIO1A1_LCDC_D5,
+	GPIO1A1_CIF_D5,
+
+	GPIO1A0_SHIFT	= 0,
+	GPIO1A0_MASK	= GENMASK(1, 0),
+	GPIO1A0_GPIO	= 0,
+	GPIO1A0_LCDC_D4,
+	GPIO1A0_CIF_D4,
+
+	/* GPIO1B_IOMUX */
+	GPIO1B7_SHIFT	= 14,
+	GPIO1B7_MASK	= GENMASK(15, 14),
+	GPIO1B7_GPIO	= 0,
+	GPIO1B7_LCDC_D19,
+
+	GPIO1B6_SHIFT	= 12,
+	GPIO1B6_MASK	= GENMASK(13, 12),
+	GPIO1B6_GPIO	= 0,
+	GPIO1B6_LCDC_D18,
+
+	GPIO1B5_SHIFT	= 10,
+	GPIO1B5_MASK	= GENMASK(11, 10),
+	GPIO1B5_GPIO	= 0,
+	GPIO1B5_LCDC_D17,
+
+	GPIO1B4_SHIFT	= 8,
+	GPIO1B4_MASK	= GENMASK(9, 8),
+	GPIO1B4_GPIO	= 0,
+	GPIO1B4_LCDC_D16,
+
+	GPIO1B3_SHIFT	= 6,
+	GPIO1B3_MASK	= GENMASK(7, 6),
+	GPIO1B3_GPIO	= 0,
+	GPIO1B3_LCDC_D15,
+	GPIO1B3_CIF_D15,
+
+	GPIO1B2_SHIFT	= 4,
+	GPIO1B2_MASK	= GENMASK(5, 4),
+	GPIO1B2_GPIO	= 0,
+	GPIO1B2_LCDC_D14,
+	GPIO1B2_CIF_D14,
+
+	GPIO1B1_SHIFT	= 2,
+	GPIO1B1_MASK	= GENMASK(3, 2),
+	GPIO1B1_GPIO	= 0,
+	GPIO1B1_LCDC_D13,
+	GPIO1B1_CIF_D13,
+
+	GPIO1B0_SHIFT	= 0,
+	GPIO1B0_MASK	= GENMASK(1, 0),
+	GPIO1B0_GPIO	= 0,
+	GPIO1B0_LCDC_D12,
+	GPIO1B0_CIF_D12,
+
+	/* GPIO1C_IOMUX */
+	GPIO1C3_SHIFT	= 6,
+	GPIO1C3_MASK	= GENMASK(7, 6),
+	GPIO1C3_GPIO	= 0,
+	GPIO1C3_LCDC_D23,
+
+	GPIO1C2_SHIFT	= 4,
+	GPIO1C2_MASK	= GENMASK(5, 4),
+	GPIO1C2_GPIO	= 0,
+	GPIO1C2_LCDC_D22,
+
+	GPIO1C1_SHIFT	= 2,
+	GPIO1C1_MASK	= GENMASK(3, 2),
+	GPIO1C1_GPIO	= 0,
+	GPIO1C1_LCDC_D21,
+
+	GPIO1C0_SHIFT	= 0,
+	GPIO1C0_MASK	= GENMASK(1, 0),
+	GPIO1C0_GPIO	= 0,
+	GPIO1C0_LCDC_D20,
+};
+
+#define DES_GRF_SOC_CON0		GRF_REG(0x100)
+#define DES_GRF_SOC_CON1		GRF_REG(0x104)
+#define DES_GRF_SOC_CON2		GRF_REG(0x108)
+#define DES_GRF_SOC_CON3		GRF_REG(0x10C)
+#define DES_GRF_SOC_CON4		GRF_REG(0x110)
+#define DES_GRF_SOC_CON5		GRF_REG(0x114)
+#define DES_GRF_SOC_CON6		GRF_REG(0x118)
+#define DES_GRF_SOC_CON7		GRF_REG(0x11C)
+
+enum {
+	/* SOC_CON0 */
+	LVDS_ALIGN_EN_SHIFT	= 14,
+	LVDS_ALIGN_EN_MASK	= GENMASK(15, 14),
+	LVDS_ALIGN_DISABLE	= 0,
+	LVDS_ALIGN_EN,
+
+	/* SOC_CON2 */
+	LVDS1_LINK_SEL_SHIFT	= 14,
+	LVDS1_LINK_SEL_MASK	= GENMASK(14, 14),
+	/* enable lvds source from pattern generation */
+	LINK_SEL_PG_DISABLE = 0,
+	LINK_SEL_PG_EN	= 1,
+
+	LVDS0_LINK_SEL_SHIFT	= 13,
+	LVDS0_LINK_SEL_MASK	= GENMASK(13, 13),
+
+	DSI0_LINK_SEL_SHIFT	= 12,
+	DSI0_LINK_SEL_MASK	= GENMASK(12, 12),
+
+	LVDS1_MSB_SHIFT		= 5,
+	LVDS1_MSB_MASK		= GENMASK(5, 5),
+	LVDS_LSB		= 0,
+	LVDS_MSB,
+
+	LVDS1_FORMAT_SHIFT	= 4,
+	LVDS1_FORMAT_MASK	= GENMASK(4, 3),
+	LVDS_FORMAT_VESA_24BIT	= 0,
+	LVDS_FORMAT_JEIDA_24BIT,
+	LVDS_FORMAT_JEIDA_18BIT,
+	LVDS_FORMAT_VESA_18BIT,
+
+	LVDS0_MSB_SHIFT		= 2,
+	LVDS0_MSB_MASK		= GENMASK(2, 2),
+
+	LVDS0_FORMAT_SHIFT	= 0,
+	LVDS0_FORMAT_MASK	= GENMASK(1, 0),
+
+	/* SOC_CON3 */
+
+	/* SOC_CON4 */
+	PMA1_EN_SHIFT		= 11,
+	PMA1_EN_MASK		= HIWORD_MASK(11, 11),
+	PMA1_EN			= HIWORD_UPDATE(1, BIT(11), 11),
+	PMA1_DISABLE		= HIWORD_UPDATE(0, BIT(11), 11),
+
+	PMA0_EN_SHIFT		= 10,
+	PMA0_EN_MASK		= HIWORD_MASK(10, 10),
+	PMA0_EN			= HIWORD_UPDATE(1, BIT(10), 10),
+	PMA0_DISABLE		= HIWORD_UPDATE(0, BIT(10), 10),
+
+	RGB_DCLK_BYPASS_SHIFT	= 9,
+	RGB_DCLK_BYPASS_MASK	= GENMASK(9, 9),
+
+	RGB_DCLK_DCLK_DLY_SHIFT	= 1,
+	RGB_DCLK_DCLK_DLY_MASK	= GENMASK(8, 1),
+
+	RGB_DCLK_INV_SHIFT	= 0,
+	RGB_DCLK_INV_MASK	= GENMASK(0, 0),
+
+	/* SOC_CON5 */
+	MIPI_PHY0_MODE_SEL_SHIFT	= 4,
+	MIPI_PHY0_MODE_SEL_MASK		= GENMASK(7, 4),
+	MIPI_PHY0_MODE_DSI		= 0,
+	MIPI_PHY0_MODE_CSI,
+
+	/* SOC_CON6 */
+	LVDS0_CLK_SOURCE_SHIFT	= 0,
+	LVDS0_CLK_SOURCE_MASK	= GENMASK(3, 0),
+
+	/* SOC_CON7 */
+	LVDS1_CLK_SOURCE_SHIFT	= 4,
+	LVDS1_CLK_SOURCE_MASK	= GENMASK(15, 4),
+
+	DSI0_DPI_UPDATE_CFG_SHIFT	= 2,
+	DSI0_DPI_UPDATE_CFG_MASK	= GENMASK(2, 2),
+
+	DSI0_DPI_REDUCED_COLOR_SHIFT	= 1,
+	DSI0_DPI_REDUCED_COLOR_MASK	= GENMASK(1, 1),
+
+	DSI0_DPI_DISABLE_SHIFT	= 0,
+	DSI0_DPI_DISABLE_MASK	= GENMASK(0, 0),
+
+	/* SOC_CON8 */
+
+	LDO_PLC_SEL_SHIFT	= 8,
+	LDO_PLC_SEL_MASK	= GENMASK(8, 8),
+	LDO_PLC_170		= 0,
+	LDO_PLC_270,
+
+	LDO_VOL_SEL_SHIFT	= 4,
+	LDO_VOL_SEL_MASK	= GENMASK(7, 4),
+	LDO_VOL_110		= 0,
+	LDO_PLC_115,
+
+	LDO_BG_TRIM_SHIFT	= 4,
+	LDO_BG_TRIM_MASK	= GENMASK(7, 4),
+	LDO_BG_TRIM_OUT		= 0,
+	LDO_BG_TRIM_OUT_55_N	= 0,
+	LDO_BG_TRIM_OUT_110_N,
+
+	/* SOC_CON9 */
+
+	/* DES_GRF_IRQ_EN */
+
+	/* DES_GRF_SOC_STATUS0 */
+	DES_PCS1_READY		= BIT(1),
+	DES_PCS0_READY		= BIT(0),
+};
+
+#define RKX120_DVP_TX_BASE		0x01020000
+#define RKX120_DSI_TX_BASE		0x01030000
+#define RKX120_CSI_TX0_BASE		0x01040000
+#define RKX120_CSI_TX1_BASE		0x01050000
+#define RKX120_GPIO0_TX_BASE		0x01060000
+#define RKX120_GPIO1_TX_BASE		0x01068000
+
+#define RKX120_DES_RKLINK_BASE		0x01070000
+#define RKX120_DES_PCS0_BASE		0x01074000
+#define RKX120_DES_PCS1_BASE		0x01075000
+#define RKX120_DES_PCS_OFFSET		0x00001000
+
+#define RKX120_PWM_BASE			0x01080000
+#define RKX120_EFUSE_BASE		0x01090000
+#define RKX120_MIPI_LVDS_TX_PHY0_BASE	0x010A0000
+#define RKX120_MIPI_LVDS_TX_PHY1_BASE	0x010B0000
+#define RKX120_GRF_MIPI0_BASE		0x010C0000
+#define RKX120_GRF_MIPI1_BASE		0x010D0000
+#define RKX120_DES_PMA0_BASE		0x010E0000
+#define RKX120_DES_PMA1_BASE		0x010F0000
+#define RKX120_DES_PMA_OFFSET		0x00010000
+
+#define RKX120_PATTERN_GEN_DSI_BASE	0x01100000
+#define RKX120_PATTERN_GEN_LVDS0_BASE	0x01110000
+#define RKX120_PATTERN_GEN_LVDS1_BASE	0x01120000
+
+#endif
diff --git a/kernel/drivers/mfd/rkx110_x120/serdes_combphy.c b/kernel/drivers/mfd/rkx110_x120/serdes_combphy.c
new file mode 100644
index 0000000..2ce7bdc
--- /dev/null
+++ b/kernel/drivers/mfd/rkx110_x120/serdes_combphy.c
@@ -0,0 +1,87 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2022 Rockchip Electronics Co. Ltd.
+ *
+ * Author: Guochun Huang <hero.huang@rock-chips.com>
+ */
+
+#include <linux/kernel.h>
+#include "rkx110_x120.h"
+#include "serdes_combphy.h"
+
+int serdes_combphy_write(struct rk_serdes *serdes, u8 remote_id, u32 reg, u32 val)
+{
+	struct i2c_client *client = serdes->chip[remote_id].client;
+
+	return serdes->i2c_write_reg(client, reg, val);
+}
+
+int serdes_combphy_read(struct rk_serdes *serdes, u8 remote_id, u32 reg, u32 *val)
+{
+	struct i2c_client *client = serdes->chip[remote_id].client;
+
+	return serdes->i2c_read_reg(client, reg, val);
+}
+
+int serdes_combphy_update_bits(struct rk_serdes *serdes, u8 remote_id,
+			       u32 reg, u32 mask, u32 val)
+{
+	struct i2c_client *client = serdes->chip[remote_id].client;
+
+	return serdes->i2c_update_bits(client, reg, mask, val);
+}
+
+void serdes_combphy_get_default_config(u64 hs_clk_rate,
+				       struct configure_opts_combphy *cfg)
+{
+	unsigned long long ui;
+
+	ui = ALIGN(NSEC_PER_SEC, hs_clk_rate);
+	do_div(ui, hs_clk_rate);
+
+	cfg->clk_miss = 0;
+	cfg->clk_post = 60 + 52 * ui;
+	cfg->clk_pre = 8;
+	cfg->clk_prepare = 38;
+	cfg->clk_settle = 95;
+	cfg->clk_term_en = 0;
+	cfg->clk_trail = 60;
+	cfg->clk_zero = 262;
+	cfg->d_term_en = 0;
+	cfg->eot = 0;
+	cfg->hs_exit = 100;
+	cfg->hs_prepare = 40 + 4 * ui;
+	cfg->hs_zero = 105 + 6 * ui;
+	cfg->hs_settle = 85 + 6 * ui;
+	cfg->hs_skip = 40;
+
+	/*
+	 * The MIPI D-PHY specification (Section 6.9, v1.2, Table 14, Page 40)
+	 * contains this formula as:
+	 *
+	 *     T_HS-TRAIL = max(n * 8 * ui, 60 + n * 4 * ui)
+	 *
+	 * where n = 1 for forward-direction HS mode and n = 4 for reverse-
+	 * direction HS mode. There's only one setting and this function does
+	 * not parameterize on anything other that ui, so this code will
+	 * assumes that reverse-direction HS mode is supported and uses n = 4.
+	 */
+	cfg->hs_trail = max(4 * 8 * ui, 60 + 4 * 4 * ui);
+
+	/*
+	 * Note that TINIT is considered a protocol-dependent parameter, and
+	 * thus the exact requirements for TINIT,MASTER and TINIT,SLAVE (transmitter
+	 * and receiver initialization Stop state lengths, respectively,) are defined
+	 * by the protocol layer specification and are outside the scope of this document.
+	 * However, the D-PHY specification does place a minimum bound on the lengths of
+	 * TINIT,MASTER and TINIT,SLAVE, which each shall be no less than 100 µs. A protocol
+	 * layer specification using the D-PHY specification may specify any values greater
+	 * than this limit, for example, TINIT,MASTER ≥ 1 ms and TINIT,SLAVE = 500 to 800 µs
+	 */
+	cfg->init = NSEC_PER_SEC / MSEC_PER_SEC;
+	cfg->lpx = 50;
+	cfg->ta_get = 5 * cfg->lpx;
+	cfg->ta_go = 4 * cfg->lpx;
+	cfg->ta_sure = cfg->lpx;
+	cfg->wakeup = NSEC_PER_SEC / MSEC_PER_SEC;
+}
diff --git a/kernel/drivers/mfd/rkx110_x120/serdes_combphy.h b/kernel/drivers/mfd/rkx110_x120/serdes_combphy.h
new file mode 100644
index 0000000..b5447fc
--- /dev/null
+++ b/kernel/drivers/mfd/rkx110_x120/serdes_combphy.h
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2022 Rockchip Electronics Co. Ltd.
+ *
+ */
+
+#ifndef SERDES_COMBPHY_H
+#define SERDES_COMBPHY_H
+
+int serdes_combphy_write(struct rk_serdes *serdes, u8 remote_id, u32 reg, u32 val);
+int serdes_combphy_read(struct rk_serdes *serdes, u8 remote_id, u32 reg, u32 *val);
+int serdes_combphy_update_bits(struct rk_serdes *serdes, u8 remote_id,
+			       u32 reg, u32 mask, u32 val);
+void serdes_combphy_get_default_config(u64 hs_clk_rate,
+				       struct configure_opts_combphy *cfg);
+
+void rkx110_combrxphy_set_mode(struct rk_serdes *ser, enum combrx_phy_mode mode);
+void rkx110_combrxphy_set_rate(struct rk_serdes *ser, u64 rate);
+void rkx110_combrxphy_power_on(struct rk_serdes *ser, enum comb_phy_id id);
+void rkx110_combrxphy_power_off(struct rk_serdes *ser, enum comb_phy_id id);
+
+void rkx120_combtxphy_set_mode(struct rk_serdes *des, enum combtx_phy_mode mode);
+void rkx120_combtxphy_set_rate(struct rk_serdes *des, u64 rate);
+u64 rkx120_combtxphy_get_rate(struct rk_serdes *des);
+void rkx120_combtxphy_power_on(struct rk_serdes *des, u8 remote_id, u8 phy_id);
+void rkx120_combtxphy_power_off(struct rk_serdes *des, u8 remote_id);
+#endif
+
diff --git a/kernel/drivers/misc/Kconfig b/kernel/drivers/misc/Kconfig
index 276c7c4..8481b96 100644
--- a/kernel/drivers/misc/Kconfig
+++ b/kernel/drivers/misc/Kconfig
@@ -5,6 +5,8 @@
 
 menu "Misc devices"
 
+source "drivers/misc/rk628/Kconfig"
+
 config RK803
 	tristate "RK803"
 	default n
diff --git a/kernel/drivers/misc/lt7911d-fb-notifier.c b/kernel/drivers/misc/lt7911d-fb-notifier.c
index 11da6bb..1c46ecc 100644
--- a/kernel/drivers/misc/lt7911d-fb-notifier.c
+++ b/kernel/drivers/misc/lt7911d-fb-notifier.c
@@ -5,19 +5,457 @@
 
 #include <linux/kernel.h>
 #include <linux/delay.h>
+#include <linux/i2c.h>
 #include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
 #include <linux/gpio/consumer.h>
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/of_gpio.h>
 #include <linux/notifier.h>
 #include <linux/fb.h>
+#include <linux/regmap.h>
+#include <linux/clk.h>
+#include "lt7911d-fw.h"
 
 struct lt7911d {
 	struct device *dev;
-	struct gpio_descs *gpios;
+	struct regmap *regmap;
+	struct serdes_init_seq *serdes_init_seq;
+	struct gpio_desc *reset_gpio;
+	struct gpio_desc *enable_gpio;
 	struct notifier_block fb_notif;
 	int fb_blank;
+};
+
+static int Datalen = 17594;
+/*to save hdcp key */
+static unsigned char HdcpKey[286];
+/*the buffer to read flash, its size should be equal the size of bin, max size is 24KB*/
+static unsigned char ReadFirmware[17594];
+/*The buffer to read flash, hex->bin->txt*/
+//static unsigned char FirmwareData[17594];
+
+static int I2C_Write_Byte(struct lt7911d *lt7911d, unsigned char reg, unsigned char val)
+{
+	int ret;
+
+	ret = regmap_write(lt7911d->regmap, reg, val);
+	if (ret < 0) {
+		pr_info("failed to write lt7911d register 0x%x: %d\n", reg, ret);
+		return ret;
+	}
+	return 0;
+}
+
+static unsigned char I2C_Read_Byte(struct lt7911d *lt7911d, unsigned char reg)
+{
+	int ret;
+	unsigned int val;
+
+	ret = regmap_read(lt7911d->regmap, reg, &val);
+	if (ret < 0) {
+		pr_info("failed to read lt7911d register 0x%x: %d\n", reg, ret);
+		return ret;
+	}
+
+	return (unsigned char)val;
+}
+
+static bool lt7911d_check_chip_id(struct lt7911d *lt7911d)
+{
+	unsigned char id_h, id_l;
+
+	/*0x80ee=0x01 to enable i2c interface*/
+	I2C_Write_Byte(lt7911d, 0xFF, 0x80);
+	I2C_Write_Byte(lt7911d, 0xEE, 0x01);
+	/*write bank 0xa0, read 0xa000 and 0xa001*/
+	I2C_Write_Byte(lt7911d, 0xFF, 0xA0);
+	id_h = I2C_Read_Byte(lt7911d, 0x00);
+	id_l = I2C_Read_Byte(lt7911d, 0x01);
+
+	/*chip id=0x1605*/
+	if ((id_h == 0x16) && (id_l == 0x05)) {
+		pr_info("%s chip id =0x1605\n", __func__);
+		/*0x80ee=0x00 to disable i2c*/
+		I2C_Write_Byte(lt7911d, 0xFF, 0x80);
+		I2C_Write_Byte(lt7911d, 0xEE, 0x00);
+		return true;
+	} else {
+		pr_info("%s chip id 0x%x is not 0x1605\n", __func__, (id_h << 8) | id_l);
+		/*0x80ee=0x00 to disable i2c*/
+		I2C_Write_Byte(lt7911d, 0xFF, 0x80);
+		I2C_Write_Byte(lt7911d, 0xEE, 0x00);
+		return false;
+	}
+}
+
+static int lt7911d_check_fw_version(struct lt7911d *lt7911d)
+{
+	unsigned char fw;
+
+	I2C_Write_Byte(lt7911d, 0xFF, 0x80);
+	I2C_Write_Byte(lt7911d, 0xEE, 0x01);
+
+	/*read 0xD211*/
+	I2C_Write_Byte(lt7911d, 0xFF, 0xD2);
+	fw = I2C_Read_Byte(lt7911d, 0x11);
+
+	/*fw version address is 0x1dfb*/
+	if (fw < FirmwareData[0x1dfb]) {
+		pr_info("%s fw %d<%d, need to upgrade\n", __func__, fw, FirmwareData[0x1dfb]);
+		I2C_Write_Byte(lt7911d, 0xFF, 0x80);
+		I2C_Write_Byte(lt7911d, 0xEE, 0x00);
+		return 0;
+	} else {
+		pr_info("%s fw %d>=%d, no need upgrade\n", __func__, fw, FirmwareData[0x1dfb]);
+		I2C_Write_Byte(lt7911d, 0xFF, 0x80);
+		I2C_Write_Byte(lt7911d, 0xEE, 0x00);
+		return -1;
+	}
+}
+
+static void lt7911d_config_para(struct lt7911d *lt7911d)
+{
+	I2C_Write_Byte(lt7911d, 0xFF, 0x80);
+	I2C_Write_Byte(lt7911d, 0xEE, 0x01);
+	I2C_Write_Byte(lt7911d, 0x5A, 0x82);
+	I2C_Write_Byte(lt7911d, 0x5E, 0xC0);
+	I2C_Write_Byte(lt7911d, 0x58, 0x00);
+	I2C_Write_Byte(lt7911d, 0x59, 0x51);
+	I2C_Write_Byte(lt7911d, 0x5A, 0x92);
+	I2C_Write_Byte(lt7911d, 0x5A, 0x82);
+}
+
+static void lt7911d_block_erase(struct lt7911d *lt7911d)
+{
+	I2C_Write_Byte(lt7911d, 0xFF, 0x80);
+	I2C_Write_Byte(lt7911d, 0xEE, 0x01);
+	I2C_Write_Byte(lt7911d, 0x5A, 0x86);
+	I2C_Write_Byte(lt7911d, 0x5A, 0x82);
+	I2C_Write_Byte(lt7911d, 0x5B, 0x00);
+	I2C_Write_Byte(lt7911d, 0x5C, 0x00);
+	I2C_Write_Byte(lt7911d, 0x5D, 0x00);
+	I2C_Write_Byte(lt7911d, 0x5A, 0x83);
+	I2C_Write_Byte(lt7911d, 0x5A, 0x82);
+
+	/*The time to waiting for earse flash*/
+	msleep(500);
+}
+
+/*If earse flash will erase the hdcp key, so need to backup firstly*/
+static void SaveHdcpKeyFromFlash(struct lt7911d *lt7911d)
+{
+	unsigned int StartAddr;
+	unsigned int npage, i, j;
+	unsigned char npagelen = 0;
+	unsigned char addr[3] = {0};
+
+	I2C_Write_Byte(lt7911d, 0xFF, 0x80);
+	I2C_Write_Byte(lt7911d, 0xEE, 0x01);
+	I2C_Write_Byte(lt7911d, 0xFF, 0x90);
+	I2C_Write_Byte(lt7911d, 0x02, 0xdf);
+	I2C_Write_Byte(lt7911d, 0x02, 0xff);
+	I2C_Write_Byte(lt7911d, 0xFF, 0x80);
+	I2C_Write_Byte(lt7911d, 0x5a, 0x86);
+	I2C_Write_Byte(lt7911d, 0x5a, 0x82);
+
+	/*The first address of HDCP KEY*/
+	StartAddr = 0x006000;
+	addr[0] = (StartAddr & 0xFF0000) >> 16;
+	addr[1] = (StartAddr & 0xFF00) >> 8;
+	addr[2] = StartAddr & 0xFF;
+
+	/*hdcp key size is 286 byte*/
+	npage = 18;
+	npagelen = 16;
+
+	for (i = 0; i < npage; i++) {
+		I2C_Write_Byte(lt7911d, 0x5E, 0x6f);
+		I2C_Write_Byte(lt7911d, 0x5A, 0xA2);
+		I2C_Write_Byte(lt7911d, 0x5A, 0x82);
+		I2C_Write_Byte(lt7911d, 0x5B, addr[0]);
+		I2C_Write_Byte(lt7911d, 0x5C, addr[1]);
+		I2C_Write_Byte(lt7911d, 0x5D, addr[2]);
+		I2C_Write_Byte(lt7911d, 0x5A, 0x92);
+		I2C_Write_Byte(lt7911d, 0x5A, 0x82);
+		I2C_Write_Byte(lt7911d, 0x58, 0x01);
+
+		if (i == 17)
+			npagelen = 14;
+
+		for (j = 0; j < npagelen; j++)
+			HdcpKey[i * 16 + j] = I2C_Read_Byte(lt7911d, 0x5F);
+
+		StartAddr += 16;
+		addr[0] = (StartAddr & 0xFF0000) >> 16;
+		addr[1] = (StartAddr & 0xFF00) >> 8;
+		addr[2] = StartAddr & 0xFF;
+	}
+
+	I2C_Write_Byte(lt7911d, 0x5a, 0x8a);
+	I2C_Write_Byte(lt7911d, 0x5a, 0x82);
+}
+
+static void lt7911d_write_firmware_to_flash(struct lt7911d *lt7911d)
+{
+	unsigned int StartAddr;
+	unsigned int npage, i, j;
+	unsigned char npagelen = 0;
+	unsigned char addr[3] = {0};
+
+	I2C_Write_Byte(lt7911d, 0xFF, 0x80);
+	I2C_Write_Byte(lt7911d, 0xEE, 0x01);
+	I2C_Write_Byte(lt7911d, 0xFF, 0x90);
+	I2C_Write_Byte(lt7911d, 0x02, 0xdf);
+	I2C_Write_Byte(lt7911d, 0x02, 0xff);
+	I2C_Write_Byte(lt7911d, 0xFF, 0x80);
+	I2C_Write_Byte(lt7911d, 0x5a, 0x86);
+	I2C_Write_Byte(lt7911d, 0x5a, 0x82);
+
+	/*The first address of flash��Max Size 24K*/
+	StartAddr = 0x000000;
+	addr[0] = (StartAddr & 0xFF0000) >> 16;
+	addr[1] = (StartAddr & 0xFF00) >> 8;
+	addr[2] = StartAddr & 0xFF;
+
+	if (Datalen % 16) {
+		/*Datalen is the length of the firmware.*/
+		npage = Datalen / 16 + 1;
+	} else {
+		npage = Datalen / 16;
+	}
+	npagelen = 16;
+
+	for (i = 0; i < npage; i++) {
+		I2C_Write_Byte(lt7911d, 0x5A, 0x86);
+		I2C_Write_Byte(lt7911d, 0x5A, 0x82);
+
+		I2C_Write_Byte(lt7911d, 0x5E, 0xef);
+		I2C_Write_Byte(lt7911d, 0x5A, 0xA2);
+		I2C_Write_Byte(lt7911d, 0x5A, 0x82);
+		I2C_Write_Byte(lt7911d, 0x58, 0x01);
+
+		if ((Datalen - i * 16) < 16)
+			npagelen = Datalen - i*16;
+
+		for (j = 0; j < npagelen; j++) {
+			/*please just continue to write data to 0x59,*/
+			/*and lt7911d will increase the address auto use 0xff*/
+			/*as insufficient data if datelen%16 is not zero*/
+			I2C_Write_Byte(lt7911d, 0x59, FirmwareData[i*16 + j]);
+		}
+
+		/*change the first address*/
+		I2C_Write_Byte(lt7911d, 0x5B, addr[0]);
+		I2C_Write_Byte(lt7911d, 0x5C, addr[1]);
+		I2C_Write_Byte(lt7911d, 0x5D, addr[2]);
+		I2C_Write_Byte(lt7911d, 0x5E, 0xE0);
+		I2C_Write_Byte(lt7911d, 0x5A, 0x92);
+		I2C_Write_Byte(lt7911d, 0x5A, 0x82);
+
+		StartAddr += 16;
+		addr[0] = (StartAddr & 0xFF0000) >> 16;
+		addr[1] = (StartAddr & 0xFF00) >> 8;
+		addr[2] = StartAddr & 0xFF;
+	}
+
+	I2C_Write_Byte(lt7911d, 0x5a, 0x8a);
+	I2C_Write_Byte(lt7911d, 0x5a, 0x82);
+
+	/*reset fifo*/
+	I2C_Write_Byte(lt7911d, 0xFF, 0x90);
+	I2C_Write_Byte(lt7911d, 0x02, 0xDF);
+	I2C_Write_Byte(lt7911d, 0x02, 0xFF);
+	msleep(20);
+}
+
+static void lt7911d_write_hdcpkey_to_flash(struct lt7911d *lt7911d)
+{
+	unsigned int StartAddr;
+	unsigned int npage, i, j;
+	unsigned char npagelen = 0;
+	unsigned char addr[3] = {0};
+
+	I2C_Write_Byte(lt7911d, 0xFF, 0x80);
+	I2C_Write_Byte(lt7911d, 0xEE, 0x01);
+	I2C_Write_Byte(lt7911d, 0xFF, 0x90);
+	I2C_Write_Byte(lt7911d, 0x02, 0xdf);
+	I2C_Write_Byte(lt7911d, 0x02, 0xff);
+	I2C_Write_Byte(lt7911d, 0xFF, 0x80);
+	I2C_Write_Byte(lt7911d, 0x5a, 0x86);
+	I2C_Write_Byte(lt7911d, 0x5a, 0x82);
+
+	/*hdcp key first address*/
+	StartAddr = 0x006000;
+	addr[0] = (StartAddr & 0xFF0000) >> 16;
+	addr[1] = (StartAddr & 0xFF00) >> 8;
+	addr[2] = StartAddr & 0xFF;
+
+	npage = 18;
+	npagelen = 16;
+
+	for (i = 0; i < npage; i++) {
+		I2C_Write_Byte(lt7911d, 0x5A, 0x86);
+		I2C_Write_Byte(lt7911d, 0x5A, 0x82);
+
+		I2C_Write_Byte(lt7911d, 0x5E, 0xef);
+		I2C_Write_Byte(lt7911d, 0x5A, 0xA2);
+		I2C_Write_Byte(lt7911d, 0x5A, 0x82);
+		I2C_Write_Byte(lt7911d, 0x58, 0x01);
+
+		if (i == 17)
+			npagelen = 14;
+
+		for (j = 0; j < npagelen; j++) {
+			/*please just continue to write data to 0x59,*/
+			/*and lt7911d will increase the address auto use 0xff*/
+			/*as insufficient data if datelen%16 is not zero .*/
+			I2C_Write_Byte(lt7911d, 0x59, HdcpKey[i*16 + j]);
+		}
+
+		if (npagelen == 14) {
+			I2C_Write_Byte(lt7911d, 0x59, 0xFF);
+			I2C_Write_Byte(lt7911d, 0x59, 0xFF);
+		}
+
+		/*change the first address*/
+		I2C_Write_Byte(lt7911d, 0x5B, addr[0]);
+		I2C_Write_Byte(lt7911d, 0x5C, addr[1]);
+		I2C_Write_Byte(lt7911d, 0x5D, addr[2]);
+		I2C_Write_Byte(lt7911d, 0x5E, 0xE0);
+		I2C_Write_Byte(lt7911d, 0x5A, 0x92);
+		I2C_Write_Byte(lt7911d, 0x5A, 0x82);
+
+		StartAddr += 16;
+		addr[0] = (StartAddr & 0xFF0000) >> 16;
+		addr[1] = (StartAddr & 0xFF00) >> 8;
+		addr[2] = StartAddr & 0xFF;
+	}
+
+	I2C_Write_Byte(lt7911d, 0x5a, 0x8a);
+	I2C_Write_Byte(lt7911d, 0x5a, 0x82);
+
+	/*reset fifo*/
+	I2C_Write_Byte(lt7911d, 0xFF, 0x90);
+	I2C_Write_Byte(lt7911d, 0x02, 0xDF);
+	I2C_Write_Byte(lt7911d, 0x02, 0xFF);
+	msleep(20);
+}
+
+static void lt7911d_read_firmware_from_flash(struct lt7911d *lt7911d)
+{
+	unsigned int StartAddr;
+	unsigned int npage, i, j;
+	unsigned char npagelen = 0;
+	unsigned char addr[3] = {0};
+
+	memset(ReadFirmware, 0, sizeof(ReadFirmware));
+
+	I2C_Write_Byte(lt7911d, 0xFF, 0x80);
+	I2C_Write_Byte(lt7911d, 0xEE, 0x01);
+	I2C_Write_Byte(lt7911d, 0xFF, 0x90);
+	I2C_Write_Byte(lt7911d, 0x02, 0xdf);
+	I2C_Write_Byte(lt7911d, 0x02, 0xff);
+	I2C_Write_Byte(lt7911d, 0xFF, 0x80);
+	I2C_Write_Byte(lt7911d, 0x5a, 0x86);
+	I2C_Write_Byte(lt7911d, 0x5a, 0x82);
+
+	/*the first address of firmware*/
+	StartAddr = 0x000000;
+	addr[0] = (StartAddr & 0xFF0000) >> 16;
+	addr[1] = (StartAddr & 0xFF00) >> 8;
+	addr[2] = StartAddr & 0xFF;
+
+	if (Datalen % 16)
+		npage = Datalen / 16 + 1;
+	else
+		npage = Datalen / 16;
+
+	npagelen = 16;
+
+	for (i = 0; i < npage; i++) {
+		I2C_Write_Byte(lt7911d, 0x5E, 0x6f);
+		I2C_Write_Byte(lt7911d, 0x5A, 0xA2);
+		I2C_Write_Byte(lt7911d, 0x5A, 0x82);
+		I2C_Write_Byte(lt7911d, 0x5B, addr[0]);
+		I2C_Write_Byte(lt7911d, 0x5C, addr[1]);
+		I2C_Write_Byte(lt7911d, 0x5D, addr[2]);
+		I2C_Write_Byte(lt7911d, 0x5A, 0x92);
+		I2C_Write_Byte(lt7911d, 0x5A, 0x82);
+		I2C_Write_Byte(lt7911d, 0x58, 0x01);
+
+		if ((Datalen - i * 16) < 16)
+			npagelen = Datalen - i*16;
+
+		for (j = 0; j < npagelen; j++) {
+			/*please just continue to read data from 0x5f*/
+			/*lt7911d will increase the address auto*/
+			ReadFirmware[i*16 + j] = I2C_Read_Byte(lt7911d, 0x5F);
+		}
+
+		StartAddr += 16;
+		/*change the first address*/
+		addr[0] = (StartAddr & 0xFF0000) >> 16;
+		addr[1] = (StartAddr & 0xFF00) >> 8;
+		addr[2] = StartAddr & 0xFF;
+	}
+
+	I2C_Write_Byte(lt7911d, 0x5a, 0x8a);
+	I2C_Write_Byte(lt7911d, 0x5a, 0x82);
+}
+
+static int lt7911_compare_firmware(struct lt7911d *lt7911d)
+{
+	unsigned int len;
+
+	for (len = 0; len < Datalen; len++) {
+		if (ReadFirmware[len] != FirmwareData[len]) {
+			pr_info("%s: ReadFirmware[%d] 0x%x !=  0x%x FirmwareData[%d]\n",
+					__func__, len, ReadFirmware[len], FirmwareData[len], len);
+			return -1;
+		}
+	}
+	return 0;
+}
+
+static int lt7911d_firmware_upgrade(struct lt7911d *lt7911d)
+{
+	int ret = 0;
+
+	if (lt7911d_check_chip_id(lt7911d)) {
+		if (lt7911d_check_fw_version(lt7911d) == 0) {
+			lt7911d_config_para(lt7911d);
+			SaveHdcpKeyFromFlash(lt7911d);
+			lt7911d_block_erase(lt7911d);
+			lt7911d_write_firmware_to_flash(lt7911d);
+			lt7911d_write_hdcpkey_to_flash(lt7911d);
+			lt7911d_read_firmware_from_flash(lt7911d);
+
+			if (!lt7911_compare_firmware(lt7911d)) {
+				pr_info("%s: upgrade success\n", __func__);
+				ret = 0;
+			} else {
+				pr_info("%s: upgrade Fail\n", __func__);
+				ret = -1;
+			}
+		}
+	} else {
+		pr_info("the chip lt7911d is offline\n");
+		ret = 0;
+	}
+
+	I2C_Write_Byte(lt7911d, 0xFF, 0x80);
+	I2C_Write_Byte(lt7911d, 0xEE, 0x00);
+
+	return ret;
+}
+
+static const struct regmap_config lt7911d_regmap_config = {
+	.name = "lt7911d",
+	.reg_bits = 8,
+	.val_bits = 8,
+	.max_register = 0x100,
 };
 
 static int lt7911d_fb_notifier_callback(struct notifier_block *self,
@@ -26,7 +464,6 @@
 	struct lt7911d *lt7911d = container_of(self, struct lt7911d, fb_notif);
 	struct fb_event *evdata = data;
 	int fb_blank = *(int *)evdata->data;
-	int i;
 
 	if (event != FB_EVENT_BLANK)
 		return 0;
@@ -35,12 +472,12 @@
 		return 0;
 
 	if (fb_blank == FB_BLANK_UNBLANK) {
-		for (i = 0; i < lt7911d->gpios->ndescs; i++)
-			gpiod_direction_output(lt7911d->gpios->desc[i], 1);
-		msleep(20);
-		for (i = 0; i < lt7911d->gpios->ndescs; i++)
-			gpiod_direction_output(lt7911d->gpios->desc[i], 0);
-		msleep(500);
+		if (lt7911d->reset_gpio) {
+			gpiod_direction_output(lt7911d->reset_gpio, 1);
+			msleep(20);
+			gpiod_direction_output(lt7911d->reset_gpio, 0);
+			msleep(400);
+		}
 	}
 
 	lt7911d->fb_blank = fb_blank;
@@ -48,26 +485,36 @@
 	return 0;
 }
 
-static int lt7911d_fb_notifier_probe(struct platform_device *pdev)
+static int lt7911d_i2c_probe(struct i2c_client *client,
+			      const struct i2c_device_id *id)
 {
-	struct device *dev = &pdev->dev;
+	struct device *dev = &client->dev;
 	struct lt7911d *lt7911d;
-	int i, ret;
+	int ret = 0, i = 0;
 
 	lt7911d = devm_kzalloc(dev, sizeof(*lt7911d), GFP_KERNEL);
 	if (!lt7911d)
 		return -ENOMEM;
 
 	lt7911d->dev = dev;
-	platform_set_drvdata(pdev, lt7911d);
+	i2c_set_clientdata(client, lt7911d);
 
-	lt7911d->gpios = devm_gpiod_get_array(dev, "reset", GPIOD_OUT_LOW);
-	if (IS_ERR(lt7911d->gpios))
-		return dev_err_probe(dev, PTR_ERR(lt7911d->gpios),
+	lt7911d->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
+	if (IS_ERR(lt7911d->reset_gpio))
+		return dev_err_probe(dev, PTR_ERR(lt7911d->reset_gpio),
 				     "failed to acquire reset gpio\n");
 
-	for (i = 0; i < lt7911d->gpios->ndescs; i++)
-		gpiod_set_consumer_name(lt7911d->gpios->desc[i], "lt7911d-reset");
+	gpiod_set_consumer_name(lt7911d->reset_gpio, "lt7911d-reset");
+
+	lt7911d->enable_gpio = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_LOW);
+	if (IS_ERR(lt7911d->enable_gpio))
+		return dev_err_probe(dev, PTR_ERR(lt7911d->enable_gpio),
+				     "failed to acquire enable gpio\n");
+
+	lt7911d->regmap = devm_regmap_init_i2c(client, &lt7911d_regmap_config);
+	if (IS_ERR(lt7911d->regmap))
+		return dev_err_probe(dev, PTR_ERR(lt7911d->regmap),
+				     "failed to initialize regmap\n");
 
 	lt7911d->fb_blank = FB_BLANK_UNBLANK;
 	lt7911d->fb_notif.notifier_call = lt7911d_fb_notifier_callback;
@@ -75,45 +522,69 @@
 	if (ret)
 		return dev_err_probe(dev, ret, "failed to register fb client\n");
 
+	for (i = 0; i < 3; i++) {
+		if (!lt7911d_firmware_upgrade(lt7911d))
+			break;
+	}
+
+	dev_info(dev, "%s end\n", __func__);
+
 	return 0;
 }
 
-static int lt7911d_fb_notifier_remove(struct platform_device *pdev)
+static void lt7911d_i2c_shutdown(struct i2c_client *client)
 {
-	struct lt7911d *lt7911d = platform_get_drvdata(pdev);
+	struct lt7911d *lt7911d = i2c_get_clientdata(client);
+
+	gpiod_direction_output(lt7911d->reset_gpio, 1);
+	msleep(20);
+}
+
+static int lt7911d_i2c_remove(struct i2c_client *client)
+{
+	struct lt7911d *lt7911d = i2c_get_clientdata(client);
 
 	fb_unregister_client(&lt7911d->fb_notif);
 
 	return 0;
 }
 
-static void lt7911d_fb_notifier_shutdown(struct platform_device *pdev)
-{
-	struct lt7911d *lt7911d = platform_get_drvdata(pdev);
-	int i;
+static const struct i2c_device_id lt7911d_i2c_table[] = {
+	{ "lt7911d", 0 },
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, lt7911d_i2c_table);
 
-	for (i = 0; i < lt7911d->gpios->ndescs; i++)
-		gpiod_direction_output(lt7911d->gpios->desc[i], 1);
-	msleep(20);
-}
-
-static const struct of_device_id lt7911d_fb_notifier_of_match[] = {
+static const struct of_device_id lt7911d_of_match[] = {
 	{ .compatible = "lontium,lt7911d-fb-notifier" },
 	{}
 };
-MODULE_DEVICE_TABLE(of, lt7911d_fb_notifier_of_match);
+MODULE_DEVICE_TABLE(of, lt7911d_of_match);
 
-static struct platform_driver lt7911d_fb_notifier_driver = {
+static struct i2c_driver lt7911d_i2c_driver = {
 	.driver = {
-		.name = "lt7911d-fb-notifier",
-		.of_match_table = lt7911d_fb_notifier_of_match,
+		.name = "lt7911d",
+		.of_match_table = lt7911d_of_match,
 	},
-	.probe = lt7911d_fb_notifier_probe,
-	.remove = lt7911d_fb_notifier_remove,
-	.shutdown = lt7911d_fb_notifier_shutdown,
+	.probe = lt7911d_i2c_probe,
+	.remove = lt7911d_i2c_remove,
+	.shutdown = lt7911d_i2c_shutdown,
+	.id_table = lt7911d_i2c_table,
 };
 
-module_platform_driver(lt7911d_fb_notifier_driver);
+static int __init lt7911d_i2c_driver_init(void)
+{
+	i2c_add_driver(&lt7911d_i2c_driver);
 
-MODULE_DESCRIPTION("Lontium LT7911D FB Notifier");
+	return 0;
+}
+subsys_initcall_sync(lt7911d_i2c_driver_init);
+
+static void __exit lt7911d_i2c_driver_exit(void)
+{
+	i2c_del_driver(&lt7911d_i2c_driver);
+}
+module_exit(lt7911d_i2c_driver_exit);
+
+MODULE_DESCRIPTION("Lontium lt7911dD driver");
 MODULE_LICENSE("GPL");
diff --git a/kernel/drivers/misc/lt7911d-fw.h b/kernel/drivers/misc/lt7911d-fw.h
new file mode 100644
index 0000000..f4cd5dc
--- /dev/null
+++ b/kernel/drivers/misc/lt7911d-fw.h
@@ -0,0 +1,1109 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Lontium lt7911 dp2lvds firmware v0.2
+ *
+ * Copyright (C) 2023 Rockchip Electronic Co,. Ltd.
+ */
+
+static unsigned char FirmwareData[17594] = {
+0x02, 0x36, 0xFE, 0x90, 0x90, 0x62, 0x74, 0x99, 0xF0, 0x22, 0xFF, 0x02, 0x3F, 0x1B, 0x01, 0x02,
+0x04, 0x08, 0x10, 0x02, 0x35, 0x16, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x02, 0x3E, 0x86, 0x74, 0xF8,
+0xCC, 0x64, 0x80, 0xCC, 0xC8, 0x64, 0x80, 0xC8, 0xF5, 0x82, 0x04, 0x60, 0x4F, 0xC3, 0xEB, 0x9F,
+0xF5, 0xF0, 0xEA, 0x9E, 0x42, 0xF0, 0xE9, 0x9D, 0x42, 0xF0, 0xE8, 0x9C, 0x45, 0xF0, 0x60, 0x3C,
+0x50, 0x1D, 0xE5, 0x82, 0x5F, 0xFF, 0xE5, 0x82, 0xD3, 0x13, 0xCB, 0x5B, 0xCB, 0xF4, 0x2B, 0x55,
+0x82, 0xFB, 0x50, 0x28, 0x0A, 0xBA, 0x00, 0x24, 0x09, 0xB9, 0x00, 0x20, 0x08, 0x80, 0x1D, 0xE5,
+0x82, 0x5B, 0xFB, 0xE5, 0x82, 0xD3, 0x13, 0xCF, 0x5F, 0xCF, 0xF4, 0xC3, 0x13, 0x2F, 0x55, 0x82,
+0xFF, 0x50, 0x09, 0x0E, 0xBE, 0x00, 0x05, 0x0D, 0xBD, 0x00, 0x01, 0x0C, 0xC3, 0xEB, 0x9F, 0xF5,
+0xF0, 0xEA, 0x9E, 0x42, 0xF0, 0xE9, 0x9D, 0x42, 0xF0, 0xE8, 0x9C, 0x45, 0xF0, 0x60, 0x07, 0xCC,
+0x48, 0x20, 0xE7, 0x01, 0xB3, 0xEC, 0x22, 0x75, 0xF0, 0x20, 0x80, 0x0E, 0x75, 0xF0, 0x10, 0x80,
+0x05, 0x75, 0xF0, 0x08, 0x7D, 0x00, 0x7E, 0x00, 0x7F, 0x00, 0x33, 0x92, 0xD5, 0x30, 0xD5, 0x03,
+0x12, 0x08, 0x67, 0xEC, 0x33, 0x40, 0x10, 0xEF, 0x33, 0xFF, 0xEE, 0x33, 0xFE, 0xED, 0x33, 0xFD,
+0xEC, 0x33, 0xFC, 0xD5, 0xF0, 0xED, 0x22, 0xE5, 0xF0, 0x24, 0x7E, 0xA2, 0xD5, 0x13, 0xCC, 0x92,
+0xE7, 0xCD, 0xCE, 0xFF, 0x22, 0xF9, 0x20, 0xE7, 0x36, 0x24, 0xF9, 0x50, 0x03, 0x79, 0x07, 0xE4,
+0xF4, 0x24, 0x02, 0xFB, 0xE5, 0x82, 0x15, 0x82, 0x70, 0x02, 0x15, 0x83, 0xDB, 0xF6, 0xE0, 0x24,
+0xFB, 0x50, 0x1B, 0xE9, 0x60, 0x14, 0xE4, 0xF0, 0xE5, 0x82, 0x15, 0x82, 0x70, 0x02, 0x15, 0x83,
+0xE0, 0x04, 0xF0, 0xB4, 0x0A, 0x08, 0xE4, 0xF0, 0xD9, 0xEE, 0x74, 0x01, 0xF0, 0x0A, 0x22, 0xC2,
+0xD5, 0x22, 0xED, 0x33, 0xEC, 0x33, 0x92, 0xD5, 0xFA, 0x60, 0x2A, 0xB4, 0xFF, 0x33, 0xED, 0xC2,
+0xE7, 0x7B, 0xFF, 0x60, 0x06, 0x7A, 0x01, 0x79, 0x37, 0xE4, 0x22, 0x7A, 0x01, 0x79, 0x40, 0x20,
+0xD5, 0x04, 0x7A, 0x01, 0x79, 0x3B, 0x22, 0x4E, 0x61, 0x4E, 0x00, 0x2B, 0x49, 0x4E, 0x46, 0x00,
+0x2D, 0x49, 0x4E, 0x46, 0x00, 0xE4, 0x7A, 0x08, 0xF0, 0xA3, 0xDA, 0xFC, 0x7B, 0x00, 0x74, 0x07,
+0x22, 0xC0, 0x83, 0xC0, 0x82, 0xEC, 0xC0, 0xE0, 0xED, 0xC0, 0xE0, 0xEE, 0xC0, 0xE0, 0xEF, 0xC0,
+0xE0, 0xEA, 0x75, 0xF0, 0x10, 0xA4, 0xCA, 0xAB, 0xF0, 0x75, 0xF0, 0x4D, 0xA4, 0x2B, 0xFB, 0xE4,
+0x35, 0xF0, 0xFC, 0xEA, 0x24, 0x10, 0xFA, 0xEB, 0x34, 0xC5, 0xFB, 0xEC, 0x34, 0xD9, 0xFC, 0xED,
+0xC2, 0xE7, 0xFF, 0x75, 0xF0, 0x56, 0xA4, 0xF4, 0x24, 0x11, 0xC5, 0xF0, 0xF4, 0x34, 0xC5, 0x8F,
+0xF0, 0xA4, 0x2A, 0xE5, 0xF0, 0x3B, 0xE4, 0x3C, 0xF0, 0x20, 0xE7, 0x14, 0x12, 0x04, 0xE5, 0xD0,
+0xE0, 0xFF, 0xD0, 0xE0, 0xFE, 0xD0, 0xE0, 0xFD, 0xD0, 0xE0, 0xFC, 0x12, 0x04, 0x18, 0x80, 0x14,
+0xF4, 0x04, 0x12, 0x04, 0xE5, 0xD0, 0xE0, 0xFF, 0xD0, 0xE0, 0xFE, 0xD0, 0xE0, 0xFD, 0xD0, 0xE0,
+0xFC, 0x12, 0x03, 0x0F, 0xD0, 0x82, 0xD0, 0x83, 0xE0, 0xFA, 0xED, 0xD2, 0xE7, 0xCD, 0x33, 0xEC,
+0x33, 0x24, 0x82, 0xFB, 0xE4, 0xC3, 0xCF, 0x33, 0xCF, 0xCE, 0x33, 0xCE, 0xCD, 0x33, 0xCD, 0x33,
+0xDB, 0xF3, 0x7B, 0x07, 0x75, 0xF0, 0x0A, 0x84, 0x60, 0x04, 0xF0, 0xA3, 0x1B, 0x0A, 0xE5, 0xF0,
+0xF0, 0xA3, 0x8F, 0xF0, 0x74, 0x0A, 0xA4, 0xFF, 0xAC, 0xF0, 0x8E, 0xF0, 0x74, 0x0A, 0xA4, 0x2C,
+0xFE, 0xAC, 0xF0, 0x50, 0x01, 0x0C, 0x8D, 0xF0, 0x74, 0x0A, 0xA4, 0x2C, 0xFD, 0xE4, 0x35, 0xF0,
+0xF0, 0xA3, 0xDB, 0xDE, 0x74, 0x07, 0x22, 0x02, 0x05, 0x5A, 0xE8, 0x64, 0x80, 0xF8, 0xE9, 0x33,
+0xE8, 0x33, 0x60, 0x11, 0x04, 0x60, 0xF0, 0xED, 0x33, 0xEC, 0x33, 0x70, 0x09, 0xE8, 0xFC, 0xE9,
+0xFD, 0xEA, 0xFE, 0xEB, 0xFF, 0x22, 0x04, 0x60, 0xDE, 0xD3, 0xEB, 0x9F, 0xEA, 0x9E, 0xE9, 0x9D,
+0xE8, 0xC2, 0xE7, 0x8C, 0xF0, 0xC2, 0xF7, 0x95, 0xF0, 0x40, 0x0C, 0xE8, 0xCC, 0xF8, 0xE9, 0xCD,
+0xF9, 0xEA, 0xCE, 0xFA, 0xEB, 0xCF, 0xFB, 0x12, 0x05, 0x25, 0x85, 0xD0, 0xF0, 0x58, 0x04, 0x70,
+0x03, 0x20, 0xD5, 0xB3, 0xE8, 0x04, 0x70, 0x07, 0x50, 0x02, 0xB2, 0xD5, 0x02, 0x05, 0x64, 0x92,
+0xD5, 0xEC, 0x04, 0x60, 0xF7, 0xE4, 0xCC, 0xC0, 0xE0, 0xC3, 0x98, 0xF8, 0x60, 0x3B, 0x94, 0x18,
+0x60, 0x08, 0x40, 0x0D, 0xD0, 0xE0, 0xFB, 0x02, 0x05, 0x3C, 0xE4, 0xFB, 0xFA, 0xC9, 0xFC, 0x80,
+0x28, 0xE8, 0x30, 0xE4, 0x06, 0xE4, 0xC9, 0xFB, 0xE4, 0xCA, 0xFC, 0xE8, 0x30, 0xE3, 0x05, 0xE4,
+0xC9, 0xCA, 0xCB, 0xFC, 0xE8, 0x54, 0x07, 0x60, 0x10, 0xF8, 0xC3, 0xE9, 0x13, 0xF9, 0xEA, 0x13,
+0xFA, 0xEB, 0x13, 0xFB, 0xEC, 0x13, 0xFC, 0xD8, 0xF1, 0x30, 0xF5, 0x2F, 0xC3, 0xE4, 0x9C, 0xFC,
+0xEF, 0x9B, 0xFF, 0xEE, 0x9A, 0xFE, 0xED, 0x99, 0xFD, 0xD0, 0xE0, 0xFB, 0xEF, 0x4E, 0x4D, 0x4C,
+0x70, 0x12, 0x22, 0xDB, 0x03, 0x02, 0x05, 0x61, 0xEC, 0x2C, 0xFC, 0xEF, 0x33, 0xFF, 0xEE, 0x33,
+0xFE, 0xED, 0x33, 0xFD, 0xED, 0x30, 0xE7, 0xEB, 0x02, 0x05, 0x3C, 0xEF, 0x2B, 0xFF, 0xEE, 0x3A,
+0xFE, 0xED, 0x39, 0xFD, 0xD0, 0xE0, 0xFB, 0x50, 0x13, 0x0B, 0xBB, 0x00, 0x03, 0x02, 0x05, 0x64,
+0xED, 0x13, 0xFD, 0xEE, 0x13, 0xFE, 0xEF, 0x13, 0xFF, 0xEC, 0x13, 0xFC, 0x02, 0x05, 0x3C, 0xEC,
+0x4D, 0x60, 0x11, 0xE8, 0x49, 0x70, 0x17, 0xED, 0x33, 0xEC, 0x33, 0x04, 0x60, 0x0D, 0xE4, 0xFC,
+0xFF, 0xFE, 0xFD, 0x22, 0xE9, 0x33, 0xE8, 0x33, 0x04, 0x70, 0xF8, 0x02, 0x05, 0x5A, 0x12, 0x05,
+0x25, 0x58, 0x04, 0x60, 0x09, 0xE4, 0xCC, 0x24, 0x81, 0x50, 0x06, 0x28, 0x50, 0x09, 0x02, 0x05,
+0x64, 0x28, 0x40, 0x03, 0x02, 0x05, 0x61, 0xC0, 0xE0, 0xEB, 0x4A, 0x70, 0x44, 0xB9, 0x80, 0x06,
+0xD0, 0xE0, 0xFB, 0x02, 0x05, 0x50, 0xEF, 0x4E, 0x70, 0x1C, 0xBD, 0x80, 0x08, 0xEB, 0xFF, 0xEA,
+0xFE, 0xE9, 0xFD, 0x80, 0xEB, 0xE9, 0x8D, 0xF0, 0xA4, 0xFE, 0xE5, 0xF0, 0x02, 0x03, 0xF8, 0xE9,
+0xCD, 0xF9, 0xEA, 0xFE, 0xEB, 0xFF, 0xEF, 0x89, 0xF0, 0xA4, 0xFC, 0xE5, 0xF0, 0xCE, 0x89, 0xF0,
+0xA4, 0x2E, 0xFF, 0xE4, 0x35, 0xF0, 0xCD, 0x89, 0xF0, 0xA4, 0x2D, 0xFE, 0xE4, 0x35, 0xF0, 0x80,
+0x67, 0xEF, 0x4E, 0x70, 0x05, 0xBD, 0x80, 0xD7, 0x80, 0xC3, 0xEF, 0x8B, 0xF0, 0xA4, 0xAC, 0xF0,
+0xEE, 0x8B, 0xF0, 0xA4, 0x2C, 0xFC, 0xE4, 0x35, 0xF0, 0xF8, 0xEF, 0x8A, 0xF0, 0xA4, 0x2C, 0xE5,
+0xF0, 0x38, 0xFC, 0xE4, 0x33, 0xCB, 0x8D, 0xF0, 0xA4, 0x2C, 0xFC, 0xE5, 0xF0, 0x3B, 0xF8, 0xEE,
+0x8A, 0xF0, 0xA4, 0x2C, 0xFC, 0xE5, 0xF0, 0x38, 0xF8, 0xE4, 0x33, 0xCF, 0x89, 0xF0, 0xA4, 0x2C,
+0xFC, 0xE5, 0xF0, 0x38, 0xCF, 0x34, 0x00, 0xCE, 0x89, 0xF0, 0xA4, 0x2F, 0xFF, 0xE5, 0xF0, 0x3E,
+0xFE, 0xE4, 0x33, 0xC9, 0x8D, 0xF0, 0xA4, 0x2E, 0xFE, 0xE5, 0xF0, 0x39, 0xCD, 0x8A, 0xF0, 0xA4,
+0x2F, 0xFF, 0xE5, 0xF0, 0x3E, 0xFE, 0xE4, 0x3D, 0xFD, 0x33, 0xD0, 0xE0, 0xFB, 0x50, 0x07, 0x0B,
+0xBB, 0x00, 0x0F, 0x02, 0x05, 0x64, 0xEC, 0x2C, 0xFC, 0xEF, 0x33, 0xFF, 0xEE, 0x33, 0xFE, 0xED,
+0x33, 0xFD, 0x02, 0x05, 0x3C, 0x02, 0x05, 0x64, 0xEC, 0x5D, 0x04, 0x60, 0x05, 0xE8, 0x59, 0x04,
+0x70, 0x03, 0x02, 0x05, 0x5A, 0x12, 0x05, 0x25, 0x58, 0x04, 0x60, 0xF6, 0xEC, 0x48, 0x60, 0xF2,
+0xEC, 0x70, 0x04, 0xFD, 0xFE, 0xFF, 0x22, 0xC8, 0x60, 0xDB, 0x24, 0x81, 0xC8, 0x50, 0x09, 0xC3,
+0x98, 0x60, 0x02, 0x50, 0x06, 0x02, 0x05, 0x61, 0x98, 0x50, 0xCA, 0xF5, 0x82, 0xE9, 0x29, 0x4B,
+0x4A, 0x70, 0x05, 0xAB, 0x82, 0x02, 0x05, 0x50, 0x75, 0xF0, 0x00, 0x7C, 0x1A, 0x78, 0x80, 0xC3,
+0xEF, 0x9B, 0xEE, 0x9A, 0xED, 0x99, 0x40, 0x0D, 0xC3, 0xEF, 0x9B, 0xFF, 0xEE, 0x9A, 0xFE, 0xED,
+0x99, 0xFD, 0xE8, 0x42, 0xF0, 0xDC, 0x23, 0xAC, 0xF0, 0xD0, 0xE0, 0xFF, 0xD0, 0xE0, 0xFE, 0xD0,
+0xE0, 0xFD, 0xAB, 0x82, 0x20, 0xE7, 0x10, 0x1B, 0xEB, 0x60, 0xBA, 0xEC, 0x2C, 0xFC, 0xEF, 0x33,
+0xFF, 0xEE, 0x33, 0xFE, 0xED, 0x33, 0xFD, 0x02, 0x05, 0x3C, 0xE8, 0x03, 0xF8, 0x30, 0xE7, 0x05,
+0xC0, 0xF0, 0x75, 0xF0, 0x00, 0xEF, 0x2F, 0xFF, 0xEE, 0x33, 0xFE, 0xED, 0x33, 0xFD, 0x40, 0xB8,
+0x30, 0xE7, 0xC2, 0x80, 0xAA, 0x3F, 0x80, 0x00, 0x00, 0x41, 0x20, 0x00, 0x00, 0x42, 0xC8, 0x00,
+0x00, 0x44, 0x7A, 0x00, 0x00, 0x46, 0x1C, 0x40, 0x00, 0x47, 0xC3, 0x50, 0x00, 0x49, 0x74, 0x24,
+0x00, 0x4B, 0x18, 0x96, 0x80, 0x4C, 0xBE, 0xBC, 0x20, 0x5A, 0x0E, 0x1B, 0xCA, 0x67, 0x53, 0xC2,
+0x1C, 0x74, 0x9D, 0xC5, 0xAE, 0xFB, 0x60, 0x06, 0x54, 0x07, 0x60, 0x1C, 0x23, 0x23, 0x12, 0x05,
+0x13, 0xEB, 0x54, 0x38, 0x60, 0x09, 0x12, 0x04, 0xFF, 0x12, 0x05, 0x0E, 0x12, 0x03, 0x0F, 0xEC,
+0xF8, 0xED, 0xF9, 0xEE, 0xFA, 0xEF, 0xCB, 0x22, 0xEB, 0x12, 0x05, 0x0E, 0x80, 0xF1, 0x54, 0x38,
+0x03, 0x24, 0x1C, 0x90, 0x04, 0xB5, 0xFE, 0x93, 0xFC, 0x0E, 0xEE, 0x93, 0xFD, 0x0E, 0xEE, 0x93,
+0x0E, 0xCE, 0x93, 0xFF, 0x22, 0xE9, 0xD2, 0xE7, 0xC9, 0x33, 0xE8, 0x33, 0xF8, 0x92, 0xD5, 0xED,
+0xD2, 0xE7, 0xCD, 0x33, 0xEC, 0x33, 0xFC, 0x50, 0x02, 0xB2, 0xD5, 0x22, 0xEC, 0x30, 0xE7, 0x10,
+0x0F, 0xBF, 0x00, 0x0C, 0x0E, 0xBE, 0x00, 0x08, 0x0D, 0xBD, 0x00, 0x04, 0x0B, 0xEB, 0x60, 0x14,
+0xA2, 0xD5, 0xEB, 0x13, 0xFC, 0xED, 0x92, 0xE7, 0xFD, 0x22, 0x74, 0xFF, 0xFC, 0xFD, 0xFE, 0xFF,
+0x22, 0xE4, 0x80, 0xF8, 0xA2, 0xD5, 0x74, 0xFF, 0x13, 0xFC, 0x7D, 0x80, 0xE4, 0x80, 0xEF, 0xE7,
+0x09, 0xF6, 0x08, 0xDF, 0xFA, 0x80, 0x46, 0xE7, 0x09, 0xF2, 0x08, 0xDF, 0xFA, 0x80, 0x3E, 0x88,
+0x82, 0x8C, 0x83, 0xE7, 0x09, 0xF0, 0xA3, 0xDF, 0xFA, 0x80, 0x32, 0xE3, 0x09, 0xF6, 0x08, 0xDF,
+0xFA, 0x80, 0x78, 0xE3, 0x09, 0xF2, 0x08, 0xDF, 0xFA, 0x80, 0x70, 0x88, 0x82, 0x8C, 0x83, 0xE3,
+0x09, 0xF0, 0xA3, 0xDF, 0xFA, 0x80, 0x64, 0x89, 0x82, 0x8A, 0x83, 0xE0, 0xA3, 0xF6, 0x08, 0xDF,
+0xFA, 0x80, 0x58, 0x89, 0x82, 0x8A, 0x83, 0xE0, 0xA3, 0xF2, 0x08, 0xDF, 0xFA, 0x80, 0x4C, 0x80,
+0xD2, 0x80, 0xFA, 0x80, 0xC6, 0x80, 0xD4, 0x80, 0x69, 0x80, 0xF2, 0x80, 0x33, 0x80, 0x10, 0x80,
+0xA6, 0x80, 0xEA, 0x80, 0x9A, 0x80, 0xA8, 0x80, 0xDA, 0x80, 0xE2, 0x80, 0xCA, 0x80, 0x33, 0x89,
+0x82, 0x8A, 0x83, 0xEC, 0xFA, 0xE4, 0x93, 0xA3, 0xC8, 0xC5, 0x82, 0xC8, 0xCC, 0xC5, 0x83, 0xCC,
+0xF0, 0xA3, 0xC8, 0xC5, 0x82, 0xC8, 0xCC, 0xC5, 0x83, 0xCC, 0xDF, 0xE9, 0xDE, 0xE7, 0x80, 0x0D,
+0x89, 0x82, 0x8A, 0x83, 0xE4, 0x93, 0xA3, 0xF6, 0x08, 0xDF, 0xF9, 0xEC, 0xFA, 0xA9, 0xF0, 0xED,
+0xFB, 0x22, 0x89, 0x82, 0x8A, 0x83, 0xEC, 0xFA, 0xE0, 0xA3, 0xC8, 0xC5, 0x82, 0xC8, 0xCC, 0xC5,
+0x83, 0xCC, 0xF0, 0xA3, 0xC8, 0xC5, 0x82, 0xC8, 0xCC, 0xC5, 0x83, 0xCC, 0xDF, 0xEA, 0xDE, 0xE8,
+0x80, 0xDB, 0x89, 0x82, 0x8A, 0x83, 0xE4, 0x93, 0xA3, 0xF2, 0x08, 0xDF, 0xF9, 0x80, 0xCC, 0x88,
+0xF0, 0xEF, 0x60, 0x01, 0x0E, 0x4E, 0x60, 0xC3, 0x88, 0xF0, 0xED, 0x24, 0x02, 0xB4, 0x04, 0x00,
+0x50, 0xB9, 0xF5, 0x82, 0xEB, 0x24, 0x02, 0xB4, 0x04, 0x00, 0x50, 0xAF, 0x23, 0x23, 0x45, 0x82,
+0x23, 0x90, 0x05, 0xBF, 0x73, 0xBB, 0x01, 0x06, 0x89, 0x82, 0x8A, 0x83, 0xE0, 0x22, 0x50, 0x02,
+0xE7, 0x22, 0xBB, 0xFE, 0x02, 0xE3, 0x22, 0x89, 0x82, 0x8A, 0x83, 0xE4, 0x93, 0x22, 0xBB, 0x01,
+0x0C, 0xE5, 0x82, 0x29, 0xF5, 0x82, 0xE5, 0x83, 0x3A, 0xF5, 0x83, 0xE0, 0x22, 0x50, 0x06, 0xE9,
+0x25, 0x82, 0xF8, 0xE6, 0x22, 0xBB, 0xFE, 0x06, 0xE9, 0x25, 0x82, 0xF8, 0xE2, 0x22, 0xE5, 0x82,
+0x29, 0xF5, 0x82, 0xE5, 0x83, 0x3A, 0xF5, 0x83, 0xE4, 0x93, 0x22, 0xBB, 0x01, 0x06, 0x89, 0x82,
+0x8A, 0x83, 0xF0, 0x22, 0x50, 0x02, 0xF7, 0x22, 0xBB, 0xFE, 0x01, 0xF3, 0x22, 0xF8, 0xBB, 0x01,
+0x0D, 0xE5, 0x82, 0x29, 0xF5, 0x82, 0xE5, 0x83, 0x3A, 0xF5, 0x83, 0xE8, 0xF0, 0x22, 0x50, 0x06,
+0xE9, 0x25, 0x82, 0xC8, 0xF6, 0x22, 0xBB, 0xFE, 0x05, 0xE9, 0x25, 0x82, 0xC8, 0xF2, 0x22, 0xBC,
+0x00, 0x0B, 0xBE, 0x00, 0x29, 0xEF, 0x8D, 0xF0, 0x84, 0xFF, 0xAD, 0xF0, 0x22, 0xE4, 0xCC, 0xF8,
+0x75, 0xF0, 0x08, 0xEF, 0x2F, 0xFF, 0xEE, 0x33, 0xFE, 0xEC, 0x33, 0xFC, 0xEE, 0x9D, 0xEC, 0x98,
+0x40, 0x05, 0xFC, 0xEE, 0x9D, 0xFE, 0x0F, 0xD5, 0xF0, 0xE9, 0xE4, 0xCE, 0xFD, 0x22, 0xED, 0xF8,
+0xF5, 0xF0, 0xEE, 0x84, 0x20, 0xD2, 0x1C, 0xFE, 0xAD, 0xF0, 0x75, 0xF0, 0x08, 0xEF, 0x2F, 0xFF,
+0xED, 0x33, 0xFD, 0x40, 0x07, 0x98, 0x50, 0x06, 0xD5, 0xF0, 0xF2, 0x22, 0xC3, 0x98, 0xFD, 0x0F,
+0xD5, 0xF0, 0xEA, 0x22, 0xC5, 0xF0, 0xF8, 0xA3, 0xE0, 0x28, 0xF0, 0xC5, 0xF0, 0xF8, 0xE5, 0x82,
+0x15, 0x82, 0x70, 0x02, 0x15, 0x83, 0xE0, 0x38, 0xF0, 0x22, 0xE8, 0x8F, 0xF0, 0xA4, 0xCC, 0x8B,
+0xF0, 0xA4, 0x2C, 0xFC, 0xE9, 0x8E, 0xF0, 0xA4, 0x2C, 0xFC, 0x8A, 0xF0, 0xED, 0xA4, 0x2C, 0xFC,
+0xEA, 0x8E, 0xF0, 0xA4, 0xCD, 0xA8, 0xF0, 0x8B, 0xF0, 0xA4, 0x2D, 0xCC, 0x38, 0x25, 0xF0, 0xFD,
+0xE9, 0x8F, 0xF0, 0xA4, 0x2C, 0xCD, 0x35, 0xF0, 0xFC, 0xEB, 0x8E, 0xF0, 0xA4, 0xFE, 0xA9, 0xF0,
+0xEB, 0x8F, 0xF0, 0xA4, 0xCF, 0xC5, 0xF0, 0x2E, 0xCD, 0x39, 0xFE, 0xE4, 0x3C, 0xFC, 0xEA, 0xA4,
+0x2D, 0xCE, 0x35, 0xF0, 0xFD, 0xE4, 0x3C, 0xFC, 0x22, 0x75, 0xF0, 0x08, 0x75, 0x82, 0x00, 0xEF,
+0x2F, 0xFF, 0xEE, 0x33, 0xFE, 0xCD, 0x33, 0xCD, 0xCC, 0x33, 0xCC, 0xC5, 0x82, 0x33, 0xC5, 0x82,
+0x9B, 0xED, 0x9A, 0xEC, 0x99, 0xE5, 0x82, 0x98, 0x40, 0x0C, 0xF5, 0x82, 0xEE, 0x9B, 0xFE, 0xED,
+0x9A, 0xFD, 0xEC, 0x99, 0xFC, 0x0F, 0xD5, 0xF0, 0xD6, 0xE4, 0xCE, 0xFB, 0xE4, 0xCD, 0xFA, 0xE4,
+0xCC, 0xF9, 0xA8, 0x82, 0x22, 0xB8, 0x00, 0xC1, 0xB9, 0x00, 0x59, 0xBA, 0x00, 0x2D, 0xEC, 0x8B,
+0xF0, 0x84, 0xCF, 0xCE, 0xCD, 0xFC, 0xE5, 0xF0, 0xCB, 0xF9, 0x78, 0x18, 0xEF, 0x2F, 0xFF, 0xEE,
+0x33, 0xFE, 0xED, 0x33, 0xFD, 0xEC, 0x33, 0xFC, 0xEB, 0x33, 0xFB, 0x10, 0xD7, 0x03, 0x99, 0x40,
+0x04, 0xEB, 0x99, 0xFB, 0x0F, 0xD8, 0xE5, 0xE4, 0xF9, 0xFA, 0x22, 0x78, 0x18, 0xEF, 0x2F, 0xFF,
+0xEE, 0x33, 0xFE, 0xED, 0x33, 0xFD, 0xEC, 0x33, 0xFC, 0xC9, 0x33, 0xC9, 0x10, 0xD7, 0x05, 0x9B,
+0xE9, 0x9A, 0x40, 0x07, 0xEC, 0x9B, 0xFC, 0xE9, 0x9A, 0xF9, 0x0F, 0xD8, 0xE0, 0xE4, 0xC9, 0xFA,
+0xE4, 0xCC, 0xFB, 0x22, 0x75, 0xF0, 0x10, 0xEF, 0x2F, 0xFF, 0xEE, 0x33, 0xFE, 0xED, 0x33, 0xFD,
+0xCC, 0x33, 0xCC, 0xC8, 0x33, 0xC8, 0x10, 0xD7, 0x07, 0x9B, 0xEC, 0x9A, 0xE8, 0x99, 0x40, 0x0A,
+0xED, 0x9B, 0xFD, 0xEC, 0x9A, 0xFC, 0xE8, 0x99, 0xF8, 0x0F, 0xD5, 0xF0, 0xDA, 0xE4, 0xCD, 0xFB,
+0xE4, 0xCC, 0xFA, 0xE4, 0xC8, 0xF9, 0x22, 0xC3, 0xE4, 0x9F, 0xFF, 0xE4, 0x9E, 0xFE, 0xE4, 0x9D,
+0xFD, 0xE4, 0x9C, 0xFC, 0x22, 0xEB, 0x9F, 0xF5, 0xF0, 0xEA, 0x9E, 0x42, 0xF0, 0xE9, 0x9D, 0x42,
+0xF0, 0xE8, 0x9C, 0x45, 0xF0, 0x22, 0xE8, 0x60, 0x0F, 0xEC, 0xC3, 0x13, 0xFC, 0xED, 0x13, 0xFD,
+0xEE, 0x13, 0xFE, 0xEF, 0x13, 0xFF, 0xD8, 0xF1, 0x22, 0xE8, 0x60, 0x0F, 0xEF, 0xC3, 0x33, 0xFF,
+0xEE, 0x33, 0xFE, 0xED, 0x33, 0xFD, 0xEC, 0x33, 0xFC, 0xD8, 0xF1, 0x22, 0xEC, 0xF0, 0xA3, 0xED,
+0xF0, 0xA3, 0xEE, 0xF0, 0xA3, 0xEF, 0xF0, 0x22, 0xF8, 0xE0, 0xFB, 0xA3, 0xA3, 0xE0, 0xF9, 0x25,
+0xF0, 0xF0, 0xE5, 0x82, 0x15, 0x82, 0x70, 0x02, 0x15, 0x83, 0xE0, 0xFA, 0x38, 0xF0, 0x22, 0xEB,
+0xF0, 0xA3, 0xEA, 0xF0, 0xA3, 0xE9, 0xF0, 0x22, 0xD0, 0x83, 0xD0, 0x82, 0xF8, 0xE4, 0x93, 0x70,
+0x12, 0x74, 0x01, 0x93, 0x70, 0x0D, 0xA3, 0xA3, 0x93, 0xF8, 0x74, 0x01, 0x93, 0xF5, 0x82, 0x88,
+0x83, 0xE4, 0x73, 0x74, 0x02, 0x93, 0x68, 0x60, 0xEF, 0xA3, 0xA3, 0xA3, 0x80, 0xDF, 0xEF, 0x4E,
+0x60, 0x12, 0xEF, 0x60, 0x01, 0x0E, 0xED, 0xBB, 0x01, 0x0B, 0x89, 0x82, 0x8A, 0x83, 0xF0, 0xA3,
+0xDF, 0xFC, 0xDE, 0xFA, 0x22, 0x89, 0xF0, 0x50, 0x07, 0xF7, 0x09, 0xDF, 0xFC, 0xA9, 0xF0, 0x22,
+0xBB, 0xFE, 0xFC, 0xF3, 0x09, 0xDF, 0xFC, 0xA9, 0xF0, 0x22, 0xE5, 0x08, 0x24, 0x91, 0xF5, 0x82,
+0xE4, 0x34, 0x02, 0xF5, 0x83, 0xE0, 0x05, 0x08, 0x22, 0x90, 0x02, 0x8E, 0x30, 0x07, 0x03, 0x90,
+0x02, 0x91, 0xE4, 0x75, 0xF0, 0x01, 0x12, 0x08, 0xB8, 0x02, 0x06, 0x65, 0x20, 0x00, 0xE9, 0x7F,
+0x2E, 0xD2, 0x00, 0x80, 0x18, 0xEF, 0x54, 0x0F, 0x24, 0x90, 0xD4, 0x34, 0x40, 0xD4, 0xFF, 0x30,
+0x04, 0x0B, 0xEF, 0x24, 0xBF, 0xB4, 0x1A, 0x00, 0x50, 0x03, 0x24, 0x61, 0xFF, 0xE5, 0x09, 0x60,
+0x02, 0x15, 0x09, 0x05, 0x0C, 0xE5, 0x0C, 0x70, 0x02, 0x05, 0x0B, 0x30, 0x07, 0x0E, 0x90, 0x02,
+0x8E, 0xE4, 0x75, 0xF0, 0x01, 0x12, 0x08, 0xB8, 0xEF, 0x02, 0x06, 0xAB, 0x02, 0x41, 0xB7, 0x74,
+0x03, 0xD2, 0x07, 0x80, 0x03, 0xE4, 0xC2, 0x07, 0xF5, 0x08, 0x90, 0x02, 0x8E, 0x12, 0x08, 0xCF,
+0xE4, 0xF5, 0x09, 0xF5, 0x0B, 0xF5, 0x0C, 0xE5, 0x09, 0x60, 0x07, 0x7F, 0x20, 0x12, 0x09, 0x6D,
+0x80, 0xF5, 0x75, 0x0A, 0xFF, 0xC2, 0x01, 0xC2, 0x00, 0xC2, 0x02, 0xC2, 0x03, 0xC2, 0x05, 0xC2,
+0x06, 0xC2, 0x08, 0x12, 0x09, 0x39, 0xFF, 0x70, 0x0D, 0x30, 0x07, 0x05, 0x7F, 0x00, 0x12, 0x09,
+0x7E, 0xAF, 0x0C, 0xAE, 0x0B, 0x22, 0xB4, 0x25, 0x5F, 0xC2, 0xD5, 0xC2, 0x04, 0x12, 0x09, 0x39,
+0xFF, 0x24, 0xD0, 0xB4, 0x0A, 0x00, 0x50, 0x1A, 0x75, 0xF0, 0x0A, 0x78, 0x09, 0x30, 0xD5, 0x05,
+0x08, 0xB6, 0xFF, 0x01, 0x06, 0xC6, 0xA4, 0x26, 0xF6, 0x20, 0xD5, 0x04, 0x70, 0x02, 0xD2, 0x03,
+0x80, 0xD9, 0x24, 0xCF, 0xB4, 0x1A, 0x00, 0xEF, 0x50, 0x04, 0xC2, 0xE5, 0xD2, 0x04, 0x02, 0x0C,
+0x70, 0xD2, 0x01, 0x80, 0xC6, 0xD2, 0x00, 0x80, 0xC0, 0xD2, 0x02, 0x80, 0xBC, 0xD2, 0xD5, 0x80,
+0xBA, 0xD2, 0x05, 0x80, 0xB4, 0x7F, 0x20, 0x12, 0x09, 0x6D, 0x20, 0x02, 0x07, 0x74, 0x01, 0xB5,
+0x09, 0x00, 0x40, 0xF1, 0x12, 0x09, 0x2A, 0xFF, 0x12, 0x09, 0x6D, 0x02, 0x09, 0xA7, 0xD2, 0x08,
+0xD2, 0x06, 0x80, 0x95, 0x12, 0x09, 0x2A, 0xFB, 0x12, 0x09, 0x2A, 0xFA, 0x12, 0x09, 0x2A, 0xF9,
+0x4A, 0x4B, 0x70, 0x06, 0x79, 0x6E, 0x7A, 0x0D, 0x7B, 0xFF, 0x20, 0x02, 0x2E, 0xE5, 0x09, 0x60,
+0x2A, 0x7E, 0x00, 0x8E, 0x82, 0x75, 0x83, 0x00, 0x12, 0x06, 0x7E, 0x60, 0x06, 0x0E, 0xEE, 0x65,
+0x0A, 0x70, 0xF0, 0xC2, 0xD5, 0xEB, 0xC0, 0xE0, 0xEA, 0xC0, 0xE0, 0xE9, 0xC0, 0xE0, 0xEE, 0x12,
+0x0C, 0xD1, 0xD0, 0xE0, 0xF9, 0xD0, 0xE0, 0xFA, 0xD0, 0xE0, 0xFB, 0x12, 0x06, 0x65, 0xFF, 0x60,
+0xAA, 0xEB, 0xC0, 0xE0, 0xEA, 0xC0, 0xE0, 0xE9, 0xC0, 0xE0, 0x12, 0x09, 0x6D, 0xD0, 0xE0, 0x24,
+0x01, 0xF9, 0xD0, 0xE0, 0x34, 0x00, 0xFA, 0xD0, 0xE0, 0xFB, 0xE5, 0x0A, 0x04, 0x60, 0xDC, 0xD5,
+0x0A, 0xD9, 0x80, 0x87, 0xD2, 0x02, 0x80, 0xA2, 0x12, 0x0C, 0xB3, 0x60, 0xF7, 0xE5, 0x0A, 0x70,
+0x01, 0x04, 0x12, 0x00, 0xD5, 0xA9, 0x0A, 0x60, 0x0D, 0xE5, 0x0A, 0x60, 0x09, 0x14, 0xF5, 0x0A,
+0x12, 0x0D, 0x5B, 0xEF, 0x60, 0xF3, 0xEA, 0x30, 0xE7, 0x02, 0xF4, 0x04, 0xC3, 0x99, 0x50, 0x79,
+0xEA, 0xF4, 0x25, 0x0A, 0x04, 0x30, 0xE7, 0x01, 0xE4, 0x04, 0xF5, 0x0A, 0x80, 0x0D, 0x12, 0x0C,
+0xB3, 0x60, 0xC1, 0xEA, 0x05, 0x0A, 0x25, 0x0A, 0x12, 0x00, 0xD5, 0xEA, 0x90, 0x02, 0xBD, 0xF0,
+0x30, 0xE7, 0x01, 0xE4, 0x25, 0x0A, 0x04, 0xD5, 0x0A, 0x04, 0x20, 0x05, 0x01, 0x14, 0x12, 0x0C,
+0xD1, 0xE4, 0xC0, 0xE0, 0x12, 0x0D, 0x5B, 0x90, 0x02, 0xBD, 0xE0, 0x60, 0x13, 0x20, 0xE7, 0x07,
+0x14, 0xF0, 0x12, 0x09, 0x55, 0x80, 0x22, 0x04, 0xF0, 0xD0, 0xE0, 0x14, 0xC0, 0xE0, 0x7F, 0x00,
+0x12, 0x09, 0x55, 0xE5, 0x0A, 0x60, 0x07, 0x12, 0x09, 0x4C, 0x15, 0x0A, 0x80, 0x0B, 0x30, 0x05,
+0x03, 0x12, 0x09, 0x4C, 0xD0, 0xE0, 0x02, 0x09, 0xA7, 0xD0, 0xE0, 0x04, 0x80, 0xC4, 0x12, 0x0C,
+0xB3, 0x60, 0x9E, 0xE5, 0x0A, 0x04, 0x12, 0x00, 0xD5, 0x90, 0x02, 0xBD, 0xEA, 0xF0, 0xE5, 0x0A,
+0x20, 0x05, 0x02, 0x60, 0x01, 0x04, 0x24, 0x04, 0x12, 0x0C, 0xD1, 0x74, 0xFF, 0x04, 0xC0, 0xE0,
+0x12, 0x0D, 0x5B, 0x12, 0x09, 0x55, 0xE5, 0x0A, 0x20, 0x05, 0x02, 0x60, 0x03, 0x12, 0x09, 0x4C,
+0xD0, 0xE0, 0xB5, 0x0A, 0xE8, 0x7F, 0x45, 0x12, 0x09, 0x5F, 0x90, 0x02, 0xBD, 0xE0, 0x7F, 0x2B,
+0x30, 0xE7, 0x04, 0x7F, 0x2D, 0xF4, 0x04, 0xC0, 0xE0, 0x12, 0x09, 0x6D, 0xD0, 0xE0, 0x75, 0xF0,
+0x0A, 0x84, 0xC0, 0xF0, 0x12, 0x09, 0x56, 0xD0, 0xE0, 0x12, 0x09, 0x56, 0x02, 0x09, 0xA7, 0x79,
+0x10, 0x80, 0x02, 0x79, 0x08, 0xC2, 0x06, 0xC2, 0x08, 0x80, 0x08, 0xD2, 0xD5, 0x79, 0x0A, 0x80,
+0x04, 0x79, 0x0A, 0xC2, 0xD5, 0xE5, 0x0A, 0x04, 0x70, 0x02, 0xF5, 0x0A, 0xE4, 0xFA, 0xFD, 0xFE,
+0xFF, 0x12, 0x09, 0x2A, 0xFC, 0x7B, 0x08, 0x20, 0x01, 0x13, 0x12, 0x09, 0x2A, 0xFD, 0x7B, 0x10,
+0x30, 0x00, 0x0A, 0x12, 0x09, 0x2A, 0xFE, 0x12, 0x09, 0x2A, 0xFF, 0x7B, 0x20, 0xEC, 0x33, 0x82,
+0xD5, 0x92, 0xD5, 0x50, 0x13, 0xC3, 0xE4, 0x30, 0x00, 0x06, 0x9F, 0xFF, 0xE4, 0x9E, 0xFE, 0xE4,
+0x20, 0x01, 0x03, 0x9D, 0xFD, 0xE4, 0x9C, 0xFC, 0xE4, 0xCB, 0xF8, 0xC2, 0x01, 0xEC, 0x70, 0x0C,
+0xCF, 0xCE, 0xCD, 0xCC, 0xE8, 0x24, 0xF8, 0xF8, 0x70, 0xF3, 0x80, 0x17, 0xC3, 0xEF, 0x33, 0xFF,
+0xEE, 0x33, 0xFE, 0xED, 0x33, 0xFD, 0xEC, 0x33, 0xFC, 0xEB, 0x33, 0xFB, 0x99, 0x40, 0x02, 0xFB,
+0x0F, 0xD8, 0xE9, 0xEB, 0x30, 0x01, 0x05, 0xF8, 0xD0, 0xE0, 0xC4, 0x48, 0xB2, 0x01, 0xC0, 0xE0,
+0x0A, 0xEC, 0x4D, 0x4E, 0x4F, 0x78, 0x20, 0x7B, 0x00, 0x70, 0xC2, 0xEA, 0xB5, 0x0A, 0x00, 0x40,
+0xBC, 0xC0, 0xE0, 0x12, 0x0C, 0xD3, 0xD0, 0xF0, 0xD0, 0xE0, 0x20, 0x01, 0x04, 0xC4, 0xC0, 0xE0,
+0xC4, 0xB2, 0x01, 0xC0, 0xF0, 0x12, 0x09, 0x56, 0xD0, 0xF0, 0xD5, 0xF0, 0xEB, 0x02, 0x09, 0xA7,
+0x12, 0x08, 0xD8, 0x0A, 0x44, 0x53, 0x0B, 0xAF, 0x58, 0x0A, 0x15, 0x4C, 0x0A, 0x11, 0x42, 0x0B,
+0xB3, 0x4F, 0x0B, 0xBB, 0x44, 0x0B, 0xBB, 0x49, 0x0A, 0x2A, 0x43, 0x0B, 0xC1, 0x55, 0x0A, 0xEE,
+0x46, 0x0B, 0x4E, 0x45, 0x0A, 0xB8, 0x47, 0x0D, 0x8E, 0x50, 0x0A, 0x19, 0x2D, 0x0A, 0x1D, 0x2E,
+0x0A, 0x40, 0x2B, 0x0A, 0x21, 0x23, 0x0A, 0x3E, 0x20, 0x0D, 0x77, 0x2A, 0x09, 0xD9, 0x48, 0x00,
+0x00, 0x0A, 0x38, 0xE5, 0x0A, 0xB4, 0xFF, 0x03, 0x75, 0x0A, 0x06, 0x12, 0x09, 0x2A, 0xFC, 0x12,
+0x09, 0x2A, 0xFD, 0x12, 0x09, 0x2A, 0xFE, 0x12, 0x09, 0x2A, 0xFF, 0x90, 0x02, 0xB6, 0x02, 0x01,
+0x12, 0x79, 0x0A, 0xA2, 0xD5, 0x20, 0x03, 0x14, 0x30, 0x05, 0x09, 0xB9, 0x10, 0x02, 0x04, 0x04,
+0xB9, 0x08, 0x01, 0x04, 0xA2, 0xD5, 0x20, 0x06, 0x02, 0x50, 0x01, 0x04, 0x20, 0x02, 0x68, 0x92,
+0x02, 0xB5, 0x09, 0x00, 0x50, 0x34, 0xC0, 0xE0, 0x7F, 0x20, 0x30, 0x03, 0x19, 0x7F, 0x30, 0xA2,
+0x02, 0x72, 0x06, 0x72, 0x05, 0x50, 0x0F, 0x12, 0x0D, 0x2A, 0xC2, 0x02, 0xC2, 0x06, 0xC2, 0x05,
+0xC2, 0x08, 0x7F, 0x30, 0x80, 0x0F, 0x30, 0x05, 0x03, 0xE9, 0xC0, 0xE0, 0x12, 0x09, 0x6D, 0x30,
+0x05, 0x03, 0xD0, 0xE0, 0xF9, 0xD0, 0xE0, 0xB5, 0x09, 0xCC, 0x30, 0x05, 0x17, 0x7F, 0x30, 0xB9,
+0x10, 0x0C, 0x12, 0x09, 0x6D, 0x7F, 0x58, 0x30, 0x04, 0x07, 0x7F, 0x78, 0x80, 0x03, 0xB9, 0x08,
+0x03, 0x12, 0x09, 0x6D, 0x30, 0x02, 0x05, 0x7F, 0x2D, 0x02, 0x09, 0x6D, 0x7F, 0x20, 0x20, 0x08,
+0xF8, 0x7F, 0x2B, 0x20, 0x06, 0xF3, 0x22, 0x92, 0x02, 0x80, 0xCF, 0x7F, 0x00, 0xB4, 0x07, 0x00,
+0x50, 0x0B, 0x24, 0xB6, 0xF5, 0x82, 0xE4, 0x34, 0x02, 0xF5, 0x83, 0xE0, 0xFF, 0x22, 0x28, 0x6E,
+0x75, 0x6C, 0x6C, 0x29, 0x00, 0xD2, 0x01, 0x12, 0x09, 0x2A, 0x30, 0x01, 0xF8, 0xC2, 0x01, 0x78,
+0x09, 0x30, 0xD5, 0x01, 0x08, 0xF6, 0x02, 0x09, 0xD9, 0x2D, 0x50, 0x43, 0x49, 0x58, 0x12, 0x09,
+0x2A, 0x24, 0x03, 0xB4, 0x05, 0x00, 0x40, 0x01, 0xE4, 0x90, 0x0D, 0x89, 0x93, 0x12, 0x09, 0x5E,
+0x74, 0x3A, 0x12, 0x09, 0x5E, 0xD2, 0x03, 0x75, 0x09, 0x04, 0x02, 0x0B, 0xAF, 0x74, 0xFF, 0x90,
+0x01, 0x1E, 0xF0, 0xA3, 0xF0, 0xA3, 0xF0, 0xA3, 0xF0, 0xA3, 0xF0, 0xA3, 0xF0, 0xA3, 0xF0, 0xA3,
+0xF0, 0x78, 0x26, 0x7C, 0x01, 0x7D, 0x01, 0xFB, 0x7A, 0x33, 0x79, 0x13, 0x7E, 0x00, 0x7F, 0x20,
+0x12, 0x06, 0x3F, 0x78, 0x46, 0x7C, 0x01, 0x7D, 0x01, 0x7B, 0xFF, 0x7A, 0x33, 0x79, 0x33, 0x7E,
+0x00, 0x7F, 0x20, 0x12, 0x06, 0x3F, 0x78, 0x66, 0x7C, 0x01, 0x7D, 0x01, 0x7B, 0xFF, 0x7A, 0x33,
+0x79, 0x53, 0x7E, 0x00, 0x7F, 0x20, 0x12, 0x06, 0x3F, 0x78, 0x86, 0x7C, 0x01, 0x7D, 0x01, 0x7B,
+0xFF, 0x7A, 0x33, 0x79, 0x73, 0x7E, 0x00, 0x7F, 0x20, 0x12, 0x06, 0x3F, 0xE4, 0x90, 0x01, 0xA6,
+0xF0, 0x7F, 0x01, 0x12, 0x43, 0x8F, 0xEF, 0x54, 0x0F, 0x90, 0x01, 0xAF, 0xF0, 0x90, 0xD2, 0x14,
+0xF0, 0x90, 0x02, 0xBF, 0xE0, 0x44, 0x01, 0x90, 0xD8, 0x00, 0xF0, 0x7B, 0xFF, 0x7A, 0x32, 0x79,
+0xC0, 0x90, 0xD2, 0x14, 0xE0, 0x90, 0x02, 0x91, 0xF0, 0x12, 0x09, 0x95, 0xE4, 0x90, 0x01, 0xAB,
+0xF0, 0xA3, 0xF0, 0xA3, 0xF0, 0xA3, 0xF0, 0x90, 0x01, 0xA6, 0xF0, 0x90, 0x01, 0xA6, 0xE0, 0xFF,
+0xC3, 0x94, 0x10, 0x74, 0x80, 0x94, 0x80, 0x40, 0x03, 0x02, 0x10, 0xC4, 0xEF, 0x25, 0xE0, 0x25,
+0xE0, 0x24, 0xD3, 0xF5, 0x82, 0xE4, 0x34, 0x32, 0xF5, 0x83, 0xE4, 0x74, 0x01, 0x93, 0x75, 0xF0,
+0x11, 0xA4, 0xFE, 0x90, 0xD8, 0x5A, 0xF0, 0xEF, 0x25, 0xE0, 0x25, 0xE0, 0x24, 0xD5, 0xF5, 0x82,
+0xE4, 0x34, 0x32, 0xF5, 0x83, 0x74, 0x01, 0x93, 0xFF, 0x90, 0xD8, 0x5B, 0xF0, 0x90, 0xD8, 0x5C,
+0xF0, 0x90, 0xD8, 0x5D, 0xEE, 0xF0, 0x90, 0xD8, 0x5E, 0xEF, 0xF0, 0x90, 0xD8, 0x5F, 0x12, 0x41,
+0x13, 0x90, 0x01, 0xAF, 0xE0, 0xC3, 0x94, 0x01, 0x74, 0x80, 0x94, 0x80, 0x40, 0x7C, 0x7F, 0x11,
+0x7E, 0xDB, 0x12, 0x44, 0x8D, 0xEF, 0x54, 0x7F, 0xFE, 0x90, 0x01, 0xA6, 0xE0, 0x25, 0xE0, 0x24,
+0x26, 0xF5, 0x82, 0xE4, 0x34, 0x01, 0xF5, 0x83, 0xEE, 0xF0, 0xA3, 0xE4, 0xF0, 0x7F, 0x10, 0x7E,
+0xDB, 0x12, 0x44, 0x8D, 0x90, 0x01, 0xA6, 0xE0, 0xFD, 0x25, 0xE0, 0x24, 0x26, 0xF5, 0x82, 0xE4,
+0x34, 0x01, 0xF5, 0x83, 0xE4, 0x8F, 0xF0, 0x12, 0x07, 0x34, 0xED, 0x25, 0xE0, 0x24, 0x26, 0xF5,
+0x82, 0xE4, 0x34, 0x01, 0xF5, 0x83, 0xE0, 0xFE, 0xA3, 0xE0, 0xFF, 0x90, 0x01, 0x1E, 0xE0, 0xFC,
+0xA3, 0xE0, 0xFD, 0xD3, 0xEF, 0x9D, 0xEE, 0x9C, 0x50, 0x20, 0x90, 0x01, 0xA6, 0xE0, 0xFD, 0x90,
+0x01, 0x1E, 0xEE, 0xF0, 0xA3, 0xEF, 0xF0, 0x90, 0x01, 0xA7, 0xED, 0xF0, 0x90, 0x01, 0xAB, 0xE0,
+0x70, 0x08, 0x90, 0x01, 0xA6, 0xE0, 0x90, 0x01, 0xAB, 0xF0, 0x90, 0x01, 0xAF, 0xE0, 0xC3, 0x94,
+0x02, 0x74, 0x80, 0x94, 0x80, 0x40, 0x7C, 0x7F, 0x13, 0x7E, 0xDB, 0x12, 0x44, 0x8D, 0xEF, 0x54,
+0x7F, 0xFE, 0x90, 0x01, 0xA6, 0xE0, 0x25, 0xE0, 0x24, 0x46, 0xF5, 0x82, 0xE4, 0x34, 0x01, 0xF5,
+0x83, 0xEE, 0xF0, 0xA3, 0xE4, 0xF0, 0x7F, 0x12, 0x7E, 0xDB, 0x12, 0x44, 0x8D, 0x90, 0x01, 0xA6,
+0xE0, 0xFD, 0x25, 0xE0, 0x24, 0x46, 0xF5, 0x82, 0xE4, 0x34, 0x01, 0xF5, 0x83, 0xE4, 0x8F, 0xF0,
+0x12, 0x07, 0x34, 0xED, 0x25, 0xE0, 0x24, 0x46, 0xF5, 0x82, 0xE4, 0x34, 0x01, 0xF5, 0x83, 0xE0,
+0xFE, 0xA3, 0xE0, 0xFF, 0x90, 0x01, 0x20, 0xE0, 0xFC, 0xA3, 0xE0, 0xFD, 0xD3, 0xEF, 0x9D, 0xEE,
+0x9C, 0x50, 0x20, 0x90, 0x01, 0xA6, 0xE0, 0xFD, 0x90, 0x01, 0x20, 0xEE, 0xF0, 0xA3, 0xEF, 0xF0,
+0x90, 0x01, 0xA8, 0xED, 0xF0, 0x90, 0x01, 0xAC, 0xE0, 0x70, 0x08, 0x90, 0x01, 0xA6, 0xE0, 0x90,
+0x01, 0xAC, 0xF0, 0x90, 0x01, 0xAF, 0xE0, 0xC3, 0x94, 0x04, 0x74, 0x80, 0x94, 0x80, 0x50, 0x03,
+0x02, 0x10, 0xBB, 0x7F, 0x15, 0x7E, 0xDB, 0x12, 0x44, 0x8D, 0xEF, 0x54, 0x7F, 0xFE, 0x90, 0x01,
+0xA6, 0xE0, 0x25, 0xE0, 0x24, 0x66, 0xF5, 0x82, 0xE4, 0x34, 0x01, 0xF5, 0x83, 0xEE, 0xF0, 0xA3,
+0xE4, 0xF0, 0x7F, 0x14, 0x7E, 0xDB, 0x12, 0x44, 0x8D, 0x90, 0x01, 0xA6, 0xE0, 0xFD, 0x25, 0xE0,
+0x24, 0x66, 0xF5, 0x82, 0xE4, 0x34, 0x01, 0xF5, 0x83, 0xE4, 0x8F, 0xF0, 0x12, 0x07, 0x34, 0xED,
+0x25, 0xE0, 0x24, 0x66, 0xF5, 0x82, 0xE4, 0x34, 0x01, 0xF5, 0x83, 0xE0, 0xFE, 0xA3, 0xE0, 0xFF,
+0x90, 0x01, 0x22, 0xE0, 0xFC, 0xA3, 0xE0, 0xFD, 0xD3, 0xEF, 0x9D, 0xEE, 0x9C, 0x50, 0x20, 0x90,
+0x01, 0xA6, 0xE0, 0xFD, 0x90, 0x01, 0x22, 0xEE, 0xF0, 0xA3, 0xEF, 0xF0, 0x90, 0x01, 0xA9, 0xED,
+0xF0, 0x90, 0x01, 0xAD, 0xE0, 0x70, 0x08, 0x90, 0x01, 0xA6, 0xE0, 0x90, 0x01, 0xAD, 0xF0, 0x7F,
+0x17, 0x7E, 0xDB, 0x12, 0x44, 0x8D, 0xEF, 0x54, 0x7F, 0xFE, 0x90, 0x01, 0xA6, 0xE0, 0x25, 0xE0,
+0x24, 0x86, 0xF5, 0x82, 0xE4, 0x34, 0x01, 0xF5, 0x83, 0xEE, 0xF0, 0xA3, 0xE4, 0xF0, 0x7F, 0x16,
+0x7E, 0xDB, 0x12, 0x44, 0x8D, 0x90, 0x01, 0xA6, 0xE0, 0xFD, 0x25, 0xE0, 0x24, 0x86, 0xF5, 0x82,
+0xE4, 0x34, 0x01, 0xF5, 0x83, 0xE4, 0x8F, 0xF0, 0x12, 0x07, 0x34, 0xED, 0x25, 0xE0, 0x24, 0x86,
+0xF5, 0x82, 0xE4, 0x34, 0x01, 0xF5, 0x83, 0xE0, 0xFE, 0xA3, 0xE0, 0xFF, 0x90, 0x01, 0x24, 0xE0,
+0xFC, 0xA3, 0xE0, 0xFD, 0xD3, 0xEF, 0x9D, 0xEE, 0x9C, 0x50, 0x20, 0x90, 0x01, 0xA6, 0xE0, 0xFD,
+0x90, 0x01, 0x24, 0xEE, 0xF0, 0xA3, 0xEF, 0xF0, 0x90, 0x01, 0xAA, 0xED, 0xF0, 0x90, 0x01, 0xAE,
+0xE0, 0x70, 0x08, 0x90, 0x01, 0xA6, 0xE0, 0x90, 0x01, 0xAE, 0xF0, 0x90, 0x01, 0xA6, 0xE0, 0x04,
+0xF0, 0x02, 0x0E, 0x4B, 0x90, 0x01, 0xAB, 0xE0, 0xFD, 0xFF, 0x90, 0x01, 0xA7, 0xE0, 0xC3, 0x9F,
+0xFF, 0xE4, 0x94, 0x00, 0xC3, 0x13, 0xEF, 0x13, 0xFF, 0xED, 0x2F, 0xF0, 0x90, 0x01, 0xAC, 0xE0,
+0xFD, 0xFF, 0x90, 0x01, 0xA8, 0xE0, 0xC3, 0x9F, 0xFF, 0xE4, 0x94, 0x00, 0xC3, 0x13, 0xEF, 0x13,
+0xFF, 0xED, 0x2F, 0xF0, 0x90, 0x01, 0xAD, 0xE0, 0xFD, 0xFF, 0x90, 0x01, 0xA9, 0xE0, 0xC3, 0x9F,
+0xFF, 0xE4, 0x94, 0x00, 0xC3, 0x13, 0xEF, 0x13, 0xFF, 0xED, 0x2F, 0xF0, 0x90, 0x01, 0xAE, 0xE0,
+0xFD, 0xFF, 0x90, 0x01, 0xAA, 0xE0, 0xC3, 0x9F, 0xFF, 0xE4, 0x94, 0x00, 0xC3, 0x13, 0xEF, 0x13,
+0xFF, 0xED, 0x2F, 0xF0, 0x90, 0x01, 0xA8, 0xE0, 0xF9, 0x25, 0xE0, 0x25, 0xE0, 0x24, 0xD3, 0xF5,
+0x82, 0xE4, 0x34, 0x32, 0xF5, 0x83, 0x74, 0x01, 0x93, 0xC4, 0x54, 0xF0, 0xFE, 0x90, 0x01, 0xA7,
+0xE0, 0xFD, 0x25, 0xE0, 0x25, 0xE0, 0x24, 0xD3, 0xF5, 0x82, 0xE4, 0x34, 0x32, 0xF5, 0x83, 0x74,
+0x01, 0x93, 0x2E, 0x90, 0xD8, 0x5A, 0xF0, 0xED, 0x25, 0xE0, 0x25, 0xE0, 0x24, 0xD5, 0xF5, 0x82,
+0xE4, 0x34, 0x32, 0xF5, 0x83, 0x74, 0x01, 0x93, 0x90, 0xD8, 0x5B, 0xF0, 0xE9, 0x25, 0xE0, 0x25,
+0xE0, 0x24, 0xD5, 0xF5, 0x82, 0xE4, 0x34, 0x32, 0xF5, 0x83, 0x74, 0x01, 0x93, 0x90, 0xD8, 0x5C,
+0xF0, 0x90, 0x01, 0xAA, 0xE0, 0xF9, 0x25, 0xE0, 0x25, 0xE0, 0x24, 0xD3, 0xF5, 0x82, 0xE4, 0x34,
+0x32, 0xF5, 0x83, 0x74, 0x01, 0x93, 0xC4, 0x54, 0xF0, 0xFE, 0x90, 0x01, 0xA9, 0xE0, 0xFD, 0x25,
+0xE0, 0x25, 0xE0, 0x24, 0xD3, 0xF5, 0x82, 0xE4, 0x34, 0x32, 0xF5, 0x83, 0x74, 0x01, 0x93, 0x2E,
+0x90, 0xD8, 0x5D, 0xF0, 0xED, 0x25, 0xE0, 0x25, 0xE0, 0x24, 0xD5, 0xF5, 0x82, 0xE4, 0x34, 0x32,
+0xF5, 0x83, 0x74, 0x01, 0x93, 0x90, 0xD8, 0x5E, 0xF0, 0xE9, 0x25, 0xE0, 0x25, 0xE0, 0x24, 0xD5,
+0xF5, 0x82, 0xE4, 0x34, 0x32, 0xF5, 0x83, 0x74, 0x01, 0x93, 0x90, 0xD8, 0x5F, 0xF0, 0x22, 0xE4,
+0x90, 0x02, 0xE1, 0xF0, 0xA3, 0xF0, 0x78, 0xE6, 0x7C, 0x02, 0x7D, 0x01, 0x7B, 0xFF, 0x7A, 0x00,
+0x79, 0x0E, 0xFE, 0x7F, 0x05, 0x12, 0x06, 0x3F, 0xE4, 0x7B, 0xE8, 0x7A, 0x03, 0xF9, 0xF8, 0x90,
+0x00, 0x07, 0xE0, 0xFC, 0xA3, 0xE0, 0xFD, 0xA3, 0xE0, 0xFE, 0xA3, 0xE0, 0xFF, 0x12, 0x07, 0xD5,
+0xC0, 0x07, 0x90, 0x00, 0x03, 0xE0, 0xFB, 0xE4, 0xFA, 0xF9, 0xF8, 0xD0, 0x07, 0x12, 0x07, 0xD5,
+0xE4, 0x7B, 0x07, 0xFA, 0xF9, 0xF8, 0x12, 0x07, 0x4A, 0x90, 0x02, 0xE1, 0xEE, 0xF0, 0xA3, 0xEF,
+0xF0, 0x7D, 0x38, 0x7F, 0x3A, 0x7E, 0xB0, 0x12, 0x44, 0x86, 0x7D, 0x81, 0x7F, 0x3B, 0x7E, 0xB0,
+0x12, 0x44, 0x86, 0xC3, 0x90, 0x02, 0xE2, 0xE0, 0x94, 0x8A, 0x90, 0x02, 0xE1, 0xE0, 0x94, 0x02,
+0x40, 0x12, 0x7D, 0x10, 0x7F, 0x3E, 0x7E, 0xB0, 0x12, 0x44, 0x86, 0x90, 0x02, 0xE3, 0x74, 0x01,
+0xF0, 0x02, 0x13, 0x07, 0x90, 0x02, 0xE1, 0xE0, 0xFE, 0xA3, 0xE0, 0xFF, 0xC3, 0x94, 0x8A, 0xEE,
+0x94, 0x02, 0x50, 0x1A, 0xC3, 0xEF, 0x94, 0x45, 0xEE, 0x94, 0x01, 0x40, 0x11, 0xE4, 0xFD, 0x7F,
+0x3E, 0x7E, 0xB0, 0x12, 0x44, 0x86, 0x90, 0x02, 0xE3, 0x74, 0x02, 0xF0, 0x80, 0x79, 0x90, 0x02,
+0xE1, 0xE0, 0xFC, 0xA3, 0xE0, 0xFD, 0xC3, 0x94, 0x45, 0xEC, 0x94, 0x01, 0x50, 0x24, 0xE4, 0x12,
+0x00, 0x9C, 0x7B, 0x00, 0x7A, 0x80, 0x79, 0x22, 0x78, 0x43, 0x12, 0x00, 0x1E, 0x60, 0x02, 0x50,
+0x11, 0x7D, 0x20, 0x7F, 0x3E, 0x7E, 0xB0, 0x12, 0x44, 0x86, 0x90, 0x02, 0xE3, 0x74, 0x04, 0xF0,
+0x80, 0x45, 0x90, 0x02, 0xE1, 0xE0, 0xFC, 0xA3, 0xE0, 0xFD, 0xE4, 0x12, 0x00, 0x9C, 0x7B, 0x00,
+0x7A, 0x80, 0x79, 0x22, 0x78, 0x43, 0x12, 0x00, 0x1E, 0x60, 0x2C, 0x40, 0x2A, 0x90, 0x02, 0xE1,
+0xE0, 0xFC, 0xA3, 0xE0, 0xFD, 0xE4, 0x12, 0x00, 0x9C, 0x7B, 0x00, 0x7A, 0x80, 0x79, 0xA2, 0x78,
+0x42, 0x12, 0x00, 0x1E, 0x60, 0x02, 0x50, 0x0F, 0x7D, 0x30, 0x7F, 0x3E, 0x7E, 0xB0, 0x12, 0x44,
+0x86, 0x90, 0x02, 0xE3, 0x74, 0x08, 0xF0, 0xE4, 0x90, 0x02, 0xE4, 0xF0, 0x90, 0x02, 0xE4, 0xE0,
+0xC3, 0x94, 0x05, 0x74, 0x80, 0x94, 0x80, 0x40, 0x03, 0x02, 0x13, 0xC3, 0xE4, 0x7B, 0xE8, 0x7A,
+0x03, 0xF9, 0xF8, 0x90, 0x00, 0x07, 0xE0, 0xFC, 0xA3, 0xE0, 0xFD, 0xA3, 0xE0, 0xFE, 0xA3, 0xE0,
+0xFF, 0x12, 0x07, 0xD5, 0xE4, 0x7B, 0x50, 0xFA, 0xF9, 0xF8, 0x12, 0x07, 0xD5, 0xA8, 0x04, 0xA9,
+0x05, 0xAA, 0x06, 0xAB, 0x07, 0x90, 0x02, 0xE4, 0xE0, 0x24, 0xE6, 0xF5, 0x82, 0xE4, 0x34, 0x02,
+0xF5, 0x83, 0xE0, 0xFF, 0xE4, 0xFC, 0xFD, 0xFE, 0xC3, 0x12, 0x08, 0x75, 0x50, 0x5C, 0xE4, 0x7B,
+0xE8, 0x7A, 0x03, 0xF9, 0xF8, 0x90, 0x00, 0x07, 0xE0, 0xFC, 0xA3, 0xE0, 0xFD, 0xA3, 0xE0, 0xFE,
+0xA3, 0xE0, 0xFF, 0x12, 0x07, 0xD5, 0xE4, 0x7B, 0x28, 0xFA, 0xF9, 0xF8, 0x12, 0x07, 0xD5, 0xA8,
+0x04, 0xA9, 0x05, 0xAA, 0x06, 0xAB, 0x07, 0x90, 0x02, 0xE4, 0xE0, 0x24, 0xE6, 0xF5, 0x82, 0xE4,
+0x34, 0x02, 0xF5, 0x83, 0xE0, 0xFF, 0xE4, 0xFC, 0xFD, 0xFE, 0xC3, 0x12, 0x08, 0x75, 0x40, 0x1A,
+0x90, 0x02, 0xE4, 0xE0, 0x24, 0xE6, 0xF5, 0x82, 0xE4, 0x34, 0x02, 0xF5, 0x83, 0xE0, 0x24, 0x0F,
+0xFD, 0x7F, 0x40, 0x7E, 0xB0, 0x12, 0x44, 0x86, 0x80, 0x09, 0x90, 0x02, 0xE4, 0xE0, 0x04, 0xF0,
+0x02, 0x13, 0x0C, 0xE4, 0x7B, 0xE8, 0x7A, 0x03, 0xF9, 0xF8, 0x90, 0x00, 0x07, 0xE0, 0xFC, 0xA3,
+0xE0, 0xFD, 0xA3, 0xE0, 0xFE, 0xA3, 0xE0, 0xFF, 0x12, 0x07, 0xD5, 0xE4, 0x7B, 0x28, 0xFA, 0xF9,
+0xF8, 0xC3, 0x12, 0x08, 0x75, 0x40, 0x1A, 0xE4, 0x90, 0x02, 0xE4, 0xF0, 0xE0, 0x24, 0xE6, 0xF5,
+0x82, 0xE4, 0x34, 0x02, 0xF5, 0x83, 0xE0, 0x24, 0x0F, 0xFD, 0x7F, 0x40, 0x7E, 0xB0, 0x12, 0x44,
+0x86, 0x90, 0x02, 0xE4, 0xE0, 0x24, 0xE6, 0xF5, 0x82, 0xE4, 0x34, 0x02, 0xF5, 0x83, 0xE0, 0x75,
+0xF0, 0x0E, 0xA4, 0xFF, 0xAE, 0xF0, 0x90, 0x00, 0x03, 0xE0, 0xFD, 0x7C, 0x00, 0x12, 0x06, 0xDF,
+0x90, 0x02, 0xE3, 0xE0, 0xFE, 0xEF, 0x8E, 0xF0, 0xA4, 0xFD, 0x7F, 0x41, 0x7E, 0xB0, 0x12, 0x44,
+0x86, 0xE4, 0x90, 0x02, 0xE5, 0xF0, 0x90, 0x02, 0xE5, 0xE0, 0x04, 0xF0, 0x7D, 0xDF, 0x7F, 0x0F,
+0x7E, 0x90, 0x12, 0x44, 0x86, 0x7F, 0x44, 0x7E, 0xB0, 0x12, 0x44, 0x8D, 0xEF, 0x54, 0xBF, 0xFD,
+0x7F, 0x44, 0x7E, 0xB0, 0x12, 0x44, 0x86, 0x7F, 0x01, 0x7E, 0x00, 0x12, 0x43, 0x54, 0x7D, 0xFF,
+0x7F, 0x0F, 0x7E, 0x90, 0x12, 0x44, 0x86, 0x7F, 0x44, 0x7E, 0xB0, 0x12, 0x44, 0x8D, 0xEF, 0x44,
+0x40, 0xFD, 0x7F, 0x44, 0x7E, 0xB0, 0x12, 0x44, 0x86, 0x7F, 0x64, 0x7E, 0x00, 0x12, 0x43, 0x54,
+0xE4, 0xFD, 0x7F, 0x1E, 0x7E, 0xB8, 0x12, 0x44, 0x86, 0x7F, 0x0A, 0x7E, 0x00, 0x12, 0x43, 0x54,
+0x7D, 0x01, 0x7F, 0x1E, 0x7E, 0xB8, 0x12, 0x44, 0x86, 0x7F, 0x0A, 0x7E, 0x00, 0x12, 0x43, 0x54,
+0x7D, 0x03, 0x7F, 0x1E, 0x7E, 0xB8, 0x12, 0x44, 0x86, 0x7F, 0x64, 0x7E, 0x00, 0x12, 0x43, 0x54,
+0x90, 0x02, 0xE5, 0xE0, 0xC3, 0x94, 0x03, 0x74, 0x80, 0x94, 0x80, 0x50, 0x0E, 0x7F, 0xB0, 0x7E,
+0xB8, 0x12, 0x44, 0x8D, 0xEF, 0x20, 0xE4, 0x03, 0x02, 0x14, 0x36, 0x7D, 0x1A, 0x7F, 0x34, 0x7E,
+0xA0, 0x12, 0x44, 0x86, 0x7F, 0x0A, 0x7E, 0x00, 0x12, 0x43, 0x54, 0x7F, 0xB1, 0x7E, 0xB8, 0x12,
+0x44, 0x8D, 0xE4, 0xFC, 0xFD, 0xFE, 0x90, 0x02, 0xCE, 0x12, 0x08, 0xAC, 0x7F, 0xB2, 0x7E, 0xB8,
+0x12, 0x44, 0x8D, 0xE4, 0xFC, 0xFD, 0xFE, 0xC0, 0x04, 0xC0, 0x05, 0xC0, 0x06, 0xC0, 0x07, 0x90,
+0x02, 0xCE, 0xE0, 0xFC, 0xA3, 0xE0, 0xFD, 0xA3, 0xE0, 0xFE, 0xA3, 0xE0, 0xFF, 0x78, 0x08, 0x12,
+0x08, 0x99, 0xD0, 0x03, 0xD0, 0x02, 0xD0, 0x01, 0xD0, 0x00, 0xEF, 0x2B, 0xFF, 0xEE, 0x3A, 0xFE,
+0xED, 0x39, 0xFD, 0xEC, 0x38, 0xFC, 0x90, 0x02, 0xCE, 0x12, 0x08, 0xAC, 0x7F, 0xB3, 0x7E, 0xB8,
+0x12, 0x44, 0x8D, 0xE4, 0xFC, 0xFD, 0xFE, 0xC0, 0x04, 0xC0, 0x05, 0xC0, 0x06, 0xC0, 0x07, 0x90,
+0x02, 0xCE, 0xE0, 0xFC, 0xA3, 0xE0, 0xFD, 0xA3, 0xE0, 0xFE, 0xA3, 0xE0, 0xFF, 0x78, 0x08, 0x12,
+0x08, 0x99, 0xD0, 0x03, 0xD0, 0x02, 0xD0, 0x01, 0xD0, 0x00, 0xEF, 0x2B, 0xFF, 0xEE, 0x3A, 0xFE,
+0xED, 0x39, 0xFD, 0xEC, 0x38, 0xFC, 0x90, 0x02, 0xCE, 0x02, 0x08, 0xAC, 0x90, 0x02, 0xF1, 0x74,
+0xF0, 0xF0, 0xE4, 0xA3, 0xF0, 0x7F, 0x01, 0x12, 0x43, 0x8F, 0xEF, 0x54, 0x0F, 0x90, 0x02, 0xF0,
+0xF0, 0x90, 0xD1, 0xCC, 0xE0, 0xFF, 0xE4, 0xFC, 0xFD, 0xFE, 0x78, 0x10, 0x12, 0x08, 0x99, 0x90,
+0x02, 0xE0, 0x12, 0x08, 0xAC, 0x90, 0xD1, 0xCD, 0xE0, 0xFF, 0xE4, 0xFC, 0xFD, 0xFE, 0x90, 0x02,
+0xE0, 0xE0, 0xF8, 0xA3, 0xE0, 0xF9, 0xA3, 0xE0, 0xFA, 0xA3, 0xE0, 0x2F, 0xFF, 0xEE, 0x3A, 0xFE,
+0xED, 0x39, 0xFD, 0xEC, 0x38, 0xFC, 0x78, 0x08, 0x12, 0x08, 0x99, 0x90, 0x02, 0xE0, 0x12, 0x08,
+0xAC, 0x90, 0xD1, 0xCE, 0xE0, 0xFB, 0x90, 0x02, 0xF1, 0xE0, 0xFF, 0xE4, 0xFC, 0xFD, 0xFE, 0xEB,
+0x5F, 0xFF, 0x90, 0x02, 0xE0, 0xE0, 0xF8, 0xA3, 0xE0, 0xF9, 0xA3, 0xE0, 0xFA, 0xA3, 0xE0, 0x2F,
+0xFF, 0xEE, 0x3A, 0xFE, 0xED, 0x39, 0xFD, 0xEC, 0x38, 0xFC, 0x90, 0x02, 0xE0, 0x12, 0x08, 0xAC,
+0x90, 0xD1, 0xCF, 0xE0, 0xFF, 0xE4, 0xFC, 0xFD, 0xFE, 0x78, 0x10, 0x12, 0x08, 0x99, 0x90, 0x02,
+0xE4, 0x12, 0x08, 0xAC, 0x90, 0xD1, 0xD0, 0xE0, 0xFF, 0xE4, 0xFC, 0xFD, 0xFE, 0x90, 0x02, 0xE4,
+0xE0, 0xF8, 0xA3, 0xE0, 0xF9, 0xA3, 0xE0, 0xFA, 0xA3, 0xE0, 0x2F, 0xFF, 0xEE, 0x3A, 0xFE, 0xED,
+0x39, 0xFD, 0xEC, 0x38, 0xFC, 0x78, 0x08, 0x12, 0x08, 0x99, 0x90, 0x02, 0xE4, 0x12, 0x08, 0xAC,
+0x90, 0xD1, 0xD1, 0xE0, 0xFB, 0x90, 0x02, 0xF1, 0xE0, 0xFF, 0xE4, 0xFC, 0xFD, 0xFE, 0xEB, 0x5F,
+0xFF, 0x90, 0x02, 0xE4, 0xE0, 0xF8, 0xA3, 0xE0, 0xF9, 0xA3, 0xE0, 0xFA, 0xA3, 0xE0, 0x2F, 0xFF,
+0xEE, 0x3A, 0xFE, 0xED, 0x39, 0xFD, 0xEC, 0x38, 0xFC, 0x90, 0x02, 0xE4, 0x12, 0x08, 0xAC, 0x90,
+0xD1, 0xD2, 0xE0, 0xFF, 0xE4, 0xFC, 0xFD, 0xFE, 0x78, 0x10, 0x12, 0x08, 0x99, 0x90, 0x02, 0xE8,
+0x12, 0x08, 0xAC, 0x90, 0xD1, 0xD3, 0xE0, 0xFF, 0xE4, 0xFC, 0xFD, 0xFE, 0x90, 0x02, 0xE8, 0xE0,
+0xF8, 0xA3, 0xE0, 0xF9, 0xA3, 0xE0, 0xFA, 0xA3, 0xE0, 0x2F, 0xFF, 0xEE, 0x3A, 0xFE, 0xED, 0x39,
+0xFD, 0xEC, 0x38, 0xFC, 0x78, 0x08, 0x12, 0x08, 0x99, 0x90, 0x02, 0xE8, 0x12, 0x08, 0xAC, 0x90,
+0xD1, 0xD4, 0xE0, 0xFB, 0x90, 0x02, 0xF1, 0xE0, 0xFF, 0xE4, 0xFC, 0xFD, 0xFE, 0xEB, 0x5F, 0xFF,
+0x90, 0x02, 0xE8, 0xE0, 0xF8, 0xA3, 0xE0, 0xF9, 0xA3, 0xE0, 0xFA, 0xA3, 0xE0, 0x2F, 0xFF, 0xEE,
+0x3A, 0xFE, 0xED, 0x39, 0xFD, 0xEC, 0x38, 0xFC, 0x90, 0x02, 0xE8, 0x12, 0x08, 0xAC, 0x90, 0xD1,
+0xD5, 0xE0, 0xFF, 0xE4, 0xFC, 0xFD, 0xFE, 0x78, 0x10, 0x12, 0x08, 0x99, 0x90, 0x02, 0xEC, 0x12,
+0x08, 0xAC, 0x90, 0xD1, 0xD6, 0xE0, 0xFF, 0xE4, 0xFC, 0xFD, 0xFE, 0x90, 0x02, 0xEC, 0xE0, 0xF8,
+0xA3, 0xE0, 0xF9, 0xA3, 0xE0, 0xFA, 0xA3, 0xE0, 0x2F, 0xFF, 0xEE, 0x3A, 0xFE, 0xED, 0x39, 0xFD,
+0xEC, 0x38, 0xFC, 0x78, 0x08, 0x12, 0x08, 0x99, 0x90, 0x02, 0xEC, 0x12, 0x08, 0xAC, 0x90, 0xD1,
+0xD7, 0xE0, 0xFB, 0x90, 0x02, 0xF1, 0xE0, 0xFF, 0xE4, 0xFC, 0xFD, 0xFE, 0xEB, 0x5F, 0xFF, 0x90,
+0x02, 0xEC, 0xE0, 0xF8, 0xA3, 0xE0, 0xF9, 0xA3, 0xE0, 0xFA, 0xA3, 0xE0, 0x2F, 0xFF, 0xEE, 0x3A,
+0xFE, 0xED, 0x39, 0xFD, 0xEC, 0x38, 0xFC, 0x90, 0x02, 0xEC, 0x12, 0x08, 0xAC, 0x90, 0x02, 0xF0,
+0xE0, 0x64, 0x02, 0x70, 0x28, 0x90, 0x02, 0xE4, 0xE0, 0xFC, 0xA3, 0xE0, 0xFD, 0xA3, 0xE0, 0xFE,
+0xA3, 0xE0, 0xFF, 0x90, 0x02, 0xE0, 0xE0, 0xF8, 0xA3, 0xE0, 0xF9, 0xA3, 0xE0, 0xFA, 0xA3, 0xE0,
+0xFB, 0xC3, 0x12, 0x08, 0x75, 0x70, 0x03, 0x02, 0x18, 0x41, 0x02, 0x18, 0x3B, 0x90, 0x02, 0xF0,
+0xE0, 0x64, 0x04, 0x60, 0x03, 0x02, 0x18, 0x41, 0x90, 0x02, 0xE4, 0xE0, 0xFC, 0xA3, 0xE0, 0xFD,
+0xA3, 0xE0, 0xFE, 0xA3, 0xE0, 0xFF, 0x90, 0x02, 0xE0, 0xE0, 0xF8, 0xA3, 0xE0, 0xF9, 0xA3, 0xE0,
+0xFA, 0xA3, 0xE0, 0xFB, 0xC3, 0x12, 0x08, 0x75, 0x60, 0x03, 0x02, 0x18, 0x3B, 0x90, 0x02, 0xE8,
+0xE0, 0xFC, 0xA3, 0xE0, 0xFD, 0xA3, 0xE0, 0xFE, 0xA3, 0xE0, 0xFF, 0x90, 0x02, 0xE0, 0xE0, 0xF8,
+0xA3, 0xA3, 0xA3, 0xE0, 0xC3, 0x12, 0x08, 0x75, 0x60, 0x03, 0x02, 0x18, 0x3B, 0x90, 0x02, 0xEC,
+0xE0, 0xFC, 0xA3, 0xE0, 0xFD, 0xA3, 0xE0, 0xFE, 0xA3, 0xE0, 0xFF, 0x90, 0x02, 0xE0, 0xE0, 0xF8,
+0xA3, 0xA3, 0xA3, 0xE0, 0xC3, 0x12, 0x08, 0x75, 0x70, 0x61, 0x90, 0x02, 0xE8, 0xE0, 0xFC, 0xA3,
+0xE0, 0xFD, 0xA3, 0xE0, 0xFE, 0xA3, 0xE0, 0xFF, 0x90, 0x02, 0xE4, 0xE0, 0xF8, 0xA3, 0xE0, 0xF9,
+0xA3, 0xE0, 0xFA, 0xA3, 0xE0, 0xFB, 0xC3, 0x12, 0x08, 0x75, 0x70, 0x3F, 0x90, 0x02, 0xEC, 0xE0,
+0xFC, 0xA3, 0xE0, 0xFD, 0xA3, 0xE0, 0xFE, 0xA3, 0xE0, 0xFF, 0x90, 0x02, 0xE4, 0xE0, 0xF8, 0xA3,
+0xA3, 0xA3, 0xE0, 0xC3, 0x12, 0x08, 0x75, 0x70, 0x22, 0x90, 0x02, 0xEC, 0xE0, 0xFC, 0xA3, 0xE0,
+0xFD, 0xA3, 0xE0, 0xFE, 0xA3, 0xE0, 0xFF, 0x90, 0x02, 0xE8, 0xE0, 0xF8, 0xA3, 0xE0, 0xF9, 0xA3,
+0xE0, 0xFA, 0xA3, 0xE0, 0xFB, 0xC3, 0x12, 0x08, 0x75, 0x60, 0x06, 0x90, 0x02, 0xF2, 0x74, 0x01,
+0xF0, 0x90, 0x02, 0xF2, 0xE0, 0x60, 0x3B, 0x90, 0x02, 0xBF, 0xE0, 0x44, 0x01, 0x90, 0xD8, 0x00,
+0xF0, 0x90, 0x90, 0x51, 0x74, 0xF0, 0xF0, 0x7F, 0x01, 0x7E, 0x00, 0x12, 0x41, 0x1F, 0x74, 0xFF,
+0xF0, 0x90, 0x90, 0x07, 0x74, 0x7F, 0xF0, 0x7F, 0x01, 0x7E, 0x00, 0x12, 0x41, 0x1F, 0x74, 0xFF,
+0xF0, 0x90, 0x90, 0x03, 0x74, 0x0F, 0xF0, 0x7F, 0x01, 0x7E, 0x00, 0x12, 0x41, 0x1F, 0x74, 0xFF,
+0x80, 0x17, 0xE4, 0x90, 0xD2, 0x19, 0xF0, 0x90, 0x90, 0x07, 0x74, 0xF7, 0xF0, 0x90, 0x90, 0x0D,
+0x74, 0xDF, 0xF0, 0x74, 0xFF, 0xF0, 0x90, 0x90, 0x07, 0xF0, 0x7F, 0xF4, 0x7E, 0x01, 0x12, 0x41,
+0x1F, 0x22, 0x7D, 0x10, 0x7F, 0x83, 0x7E, 0xD2, 0x12, 0x44, 0x86, 0x7D, 0x21, 0x7F, 0x34, 0x7E,
+0xA0, 0x12, 0x44, 0x86, 0x7F, 0x32, 0x7E, 0x00, 0x12, 0x43, 0x54, 0x7F, 0xB1, 0x7E, 0xB8, 0x12,
+0x44, 0x8D, 0xE4, 0xFC, 0xFD, 0xFE, 0x90, 0x02, 0xE1, 0x12, 0x08, 0xAC, 0x7F, 0xB2, 0x7E, 0xB8,
+0x12, 0x44, 0x8D, 0xE4, 0xFC, 0xFD, 0xFE, 0xC0, 0x04, 0xC0, 0x05, 0xC0, 0x06, 0xC0, 0x07, 0x90,
+0x02, 0xE1, 0xE0, 0xFC, 0xA3, 0xE0, 0xFD, 0xA3, 0xE0, 0xFE, 0xA3, 0xE0, 0xFF, 0x78, 0x08, 0x12,
+0x08, 0x99, 0xD0, 0x03, 0xD0, 0x02, 0xD0, 0x01, 0xD0, 0x00, 0xEF, 0x2B, 0xFF, 0xEE, 0x3A, 0xFE,
+0xED, 0x39, 0xFD, 0xEC, 0x38, 0xFC, 0x90, 0x02, 0xE1, 0x12, 0x08, 0xAC, 0x7F, 0xB3, 0x7E, 0xB8,
+0x12, 0x44, 0x8D, 0xE4, 0xFC, 0xFD, 0xFE, 0xC0, 0x04, 0xC0, 0x05, 0xC0, 0x06, 0xC0, 0x07, 0x90,
+0x02, 0xE1, 0xE0, 0xFC, 0xA3, 0xE0, 0xFD, 0xA3, 0xE0, 0xFE, 0xA3, 0xE0, 0xFF, 0x78, 0x08, 0x12,
+0x08, 0x99, 0xD0, 0x03, 0xD0, 0x02, 0xD0, 0x01, 0xD0, 0x00, 0xEF, 0x2B, 0xFF, 0xEE, 0x3A, 0xFE,
+0xED, 0x39, 0xFD, 0xEC, 0x38, 0xFC, 0x90, 0x02, 0xE1, 0x12, 0x08, 0xAC, 0x90, 0x02, 0xE1, 0xE0,
+0xFC, 0xA3, 0xE0, 0xFD, 0xA3, 0xE0, 0xFE, 0xA3, 0xE0, 0xFF, 0xE4, 0x7B, 0x02, 0xFA, 0xF9, 0xF8,
+0x12, 0x07, 0x4A, 0x90, 0x02, 0xE1, 0x12, 0x08, 0xAC, 0x7D, 0x01, 0x7F, 0x34, 0x7E, 0xA0, 0x12,
+0x44, 0x86, 0x7F, 0xDB, 0x7E, 0xD1, 0x12, 0x44, 0x8D, 0xEF, 0x90, 0x02, 0xE5, 0xF0, 0xA3, 0xE4,
+0xF0, 0x7F, 0xDC, 0x7E, 0xD1, 0x12, 0x44, 0x8D, 0x90, 0x02, 0xE6, 0xE0, 0x2F, 0xF0, 0x90, 0x02,
+0xE5, 0xE0, 0x34, 0x00, 0xF0, 0x7F, 0xE7, 0x7E, 0xD1, 0x12, 0x44, 0x8D, 0xEF, 0x90, 0x02, 0xE7,
+0xF0, 0xA3, 0xE4, 0xF0, 0x7F, 0xE8, 0x7E, 0xD1, 0x12, 0x44, 0x8D, 0x90, 0x02, 0xE8, 0xE0, 0x2F,
+0xF0, 0x90, 0x02, 0xE7, 0xE0, 0x34, 0x00, 0xF0, 0x7F, 0xDF, 0x7E, 0xD1, 0x12, 0x44, 0x8D, 0xEF,
+0x54, 0x7F, 0x90, 0x02, 0xED, 0xF0, 0xA3, 0xE4, 0xF0, 0x7F, 0xE0, 0x7E, 0xD1, 0x12, 0x44, 0x8D,
+0x90, 0x02, 0xEE, 0xE0, 0x2F, 0xF0, 0x90, 0x02, 0xED, 0xE0, 0x34, 0x00, 0xF0, 0x7F, 0xE1, 0x7E,
+0xD1, 0x12, 0x44, 0x8D, 0xEF, 0x90, 0x02, 0xE9, 0xF0, 0xA3, 0xE4, 0xF0, 0x7F, 0xE2, 0x7E, 0xD1,
+0x12, 0x44, 0x8D, 0x90, 0x02, 0xEA, 0xE0, 0x2F, 0xF0, 0x90, 0x02, 0xE9, 0xE0, 0x34, 0x00, 0xF0,
+0x90, 0x02, 0xED, 0xE0, 0xFE, 0xA3, 0xE0, 0xFF, 0x90, 0x02, 0xE9, 0xE0, 0xFC, 0xA3, 0xE0, 0xFD,
+0xC3, 0x9F, 0xFF, 0xEC, 0x9E, 0x90, 0x02, 0xEF, 0xF0, 0xA3, 0xEF, 0xF0, 0x90, 0x02, 0xE7, 0xE0,
+0xFE, 0xA3, 0xE0, 0xFF, 0xC3, 0x90, 0x02, 0xE6, 0xE0, 0x9F, 0xFF, 0x90, 0x02, 0xE5, 0xE0, 0x9E,
+0xCF, 0xC3, 0x9D, 0xCF, 0x9C, 0x90, 0x02, 0xEB, 0xF0, 0xA3, 0xEF, 0xF0, 0x7F, 0xDD, 0x7E, 0xD1,
+0x12, 0x44, 0x8D, 0xEF, 0x90, 0x02, 0xF1, 0xF0, 0xA3, 0xE4, 0xF0, 0x7F, 0xDE, 0x7E, 0xD1, 0x12,
+0x44, 0x8D, 0x90, 0x02, 0xF2, 0xE0, 0x2F, 0xF0, 0x90, 0x02, 0xF1, 0xE0, 0x34, 0x00, 0xF0, 0x7F,
+0xE9, 0x7E, 0xD1, 0x12, 0x44, 0x8D, 0xEF, 0x90, 0x02, 0xF3, 0xF0, 0xA3, 0xE4, 0xF0, 0x7F, 0xEA,
+0x7E, 0xD1, 0x12, 0x44, 0x8D, 0x90, 0x02, 0xF4, 0xE0, 0x2F, 0xF0, 0x90, 0x02, 0xF3, 0xE0, 0x34,
+0x00, 0xF0, 0x7F, 0xE6, 0x7E, 0xD1, 0x12, 0x44, 0x8D, 0x90, 0x02, 0xF8, 0xEF, 0xF0, 0x7F, 0xE3,
+0x7E, 0xD1, 0x12, 0x44, 0x8D, 0xEF, 0x90, 0x02, 0xF5, 0xF0, 0xA3, 0xE4, 0xF0, 0x7F, 0xE4, 0x7E,
+0xD1, 0x12, 0x44, 0x8D, 0x90, 0x02, 0xF6, 0xE0, 0x2F, 0xF0, 0x90, 0x02, 0xF5, 0xE0, 0x34, 0x00,
+0xF0, 0xA3, 0xE0, 0xFD, 0x90, 0x02, 0xF8, 0xE0, 0xFE, 0xC3, 0xED, 0x9E, 0xA3, 0xF0, 0x90, 0x02,
+0xF3, 0xA3, 0xE0, 0xFF, 0x90, 0x02, 0xF1, 0xA3, 0xE0, 0xC3, 0x9F, 0xC3, 0x9D, 0x90, 0x02, 0xF7,
+0xF0, 0x7F, 0xDF, 0x7E, 0xD1, 0x12, 0x44, 0x8D, 0xEF, 0x54, 0x80, 0xC4, 0x13, 0x13, 0x13, 0x54,
+0x01, 0x70, 0x04, 0x7F, 0x01, 0x80, 0x02, 0x7F, 0x00, 0x90, 0x02, 0xFA, 0xEF, 0xF0, 0x7F, 0xE5,
+0x7E, 0xD1, 0x12, 0x44, 0x8D, 0xEF, 0x54, 0x80, 0xC4, 0x13, 0x13, 0x13, 0x54, 0x01, 0x70, 0x04,
+0x7F, 0x01, 0x80, 0x02, 0x7F, 0x00, 0xAB, 0x07, 0x90, 0x02, 0xE1, 0xE0, 0xFC, 0xA3, 0xE0, 0xFD,
+0xA3, 0xE0, 0xFE, 0xA3, 0xE0, 0xFF, 0x90, 0x00, 0x07, 0x12, 0x08, 0xAC, 0x90, 0x02, 0xE5, 0xE0,
+0xFF, 0xA3, 0xE0, 0x90, 0x00, 0x0B, 0xCF, 0xF0, 0xA3, 0xEF, 0xF0, 0x90, 0x02, 0xE7, 0xE0, 0xFF,
+0xA3, 0xE0, 0x90, 0x00, 0x0D, 0xCF, 0xF0, 0xA3, 0xEF, 0xF0, 0x90, 0x02, 0xEB, 0xE0, 0xFF, 0xA3,
+0xE0, 0x90, 0x00, 0x0F, 0xCF, 0xF0, 0xA3, 0xEF, 0xF0, 0x90, 0x02, 0xED, 0xE0, 0xFF, 0xA3, 0xE0,
+0x90, 0x00, 0x11, 0xCF, 0xF0, 0xA3, 0xEF, 0xF0, 0x90, 0x02, 0xEF, 0xE0, 0xFF, 0xA3, 0xE0, 0x90,
+0x00, 0x13, 0xCF, 0xF0, 0xA3, 0xEF, 0xF0, 0x90, 0x02, 0xF1, 0xE0, 0xFF, 0xA3, 0xE0, 0x90, 0x00,
+0x15, 0xCF, 0xF0, 0xA3, 0xEF, 0xF0, 0x90, 0x02, 0xF3, 0xE0, 0xFF, 0xA3, 0xE0, 0x90, 0x00, 0x17,
+0xCF, 0xF0, 0xA3, 0xEF, 0xF0, 0x90, 0x02, 0xF7, 0xE0, 0x90, 0x00, 0x19, 0xF0, 0x90, 0x02, 0xF8,
+0xE0, 0x90, 0x00, 0x1A, 0xF0, 0x90, 0x02, 0xF9, 0xE0, 0x90, 0x00, 0x1B, 0xF0, 0x90, 0x02, 0xFA,
+0xE0, 0x90, 0x00, 0x1C, 0xF0, 0xA3, 0xEB, 0xF0, 0x22, 0xE4, 0x90, 0x02, 0xDE, 0xF0, 0xA3, 0xF0,
+0x90, 0x02, 0xDC, 0xF0, 0x90, 0x02, 0xBF, 0xF0, 0x7F, 0xC8, 0xFE, 0x12, 0x41, 0x1F, 0x12, 0x00,
+0x03, 0x12, 0x40, 0xB1, 0x12, 0x43, 0xFD, 0x90, 0x02, 0xDB, 0xE0, 0xFF, 0x12, 0x40, 0x7C, 0xE4,
+0xFF, 0x12, 0x43, 0x18, 0x7F, 0x02, 0x12, 0x38, 0x8D, 0xE4, 0xFF, 0x12, 0x43, 0x36, 0x7F, 0x58,
+0x7E, 0xA0, 0x12, 0x44, 0x8D, 0xEF, 0x54, 0x7F, 0xFD, 0x7F, 0x58, 0x7E, 0xA0, 0x12, 0x44, 0x86,
+0x90, 0x02, 0xC2, 0xE0, 0x60, 0x03, 0x12, 0x42, 0x74, 0x12, 0x3E, 0xD3, 0x90, 0xC1, 0x17, 0x74,
+0x01, 0xF0, 0x12, 0x44, 0x7D, 0x12, 0x3E, 0x39, 0x12, 0x31, 0xDA, 0xE4, 0x90, 0xD2, 0x12, 0xF0,
+0x78, 0x38, 0xF6, 0x90, 0x02, 0xD8, 0xF0, 0x90, 0x02, 0xC0, 0xF0, 0x90, 0xD2, 0x21, 0x74, 0x8F,
+0xF0, 0x90, 0xD2, 0x22, 0x74, 0xFF, 0xF0, 0x90, 0xD2, 0x67, 0x74, 0x22, 0xF0, 0x90, 0x02, 0xBE,
+0xE0, 0x70, 0x0B, 0x12, 0x41, 0x90, 0x12, 0x3F, 0x9F, 0x12, 0x40, 0x10, 0x80, 0x0A, 0xD2, 0xAF,
+0x90, 0x03, 0x11, 0x74, 0x01, 0x12, 0x43, 0x72, 0x90, 0x02, 0xBE, 0xE0, 0x70, 0x03, 0x12, 0x3D,
+0xEB, 0x90, 0xD2, 0x17, 0xE0, 0x20, 0xE6, 0x03, 0x02, 0x1D, 0x4A, 0x90, 0x02, 0xDE, 0xE0, 0x04,
+0xF0, 0xE0, 0x64, 0x0A, 0x70, 0xE2, 0xF0, 0x90, 0xD2, 0x12, 0xE0, 0x70, 0x0F, 0x90, 0x02, 0xC0,
+0xE0, 0x70, 0x09, 0x90, 0x02, 0xD8, 0xE0, 0x70, 0x03, 0x02, 0x1D, 0x1B, 0x90, 0xB8, 0xB0, 0xE0,
+0x30, 0xE5, 0xC5, 0x30, 0xE6, 0xC2, 0x7F, 0xC8, 0x7E, 0x00, 0x12, 0x43, 0x54, 0x12, 0x29, 0x79,
+0xEF, 0x70, 0xB5, 0x7F, 0x58, 0x7E, 0xA0, 0x12, 0x44, 0x8D, 0xEF, 0x54, 0x7F, 0xFD, 0x7F, 0x58,
+0x7E, 0xA0, 0x12, 0x44, 0x86, 0xE4, 0x90, 0xD2, 0x12, 0xF0, 0x78, 0x38, 0x76, 0x01, 0x90, 0x02,
+0xC0, 0xF0, 0x90, 0x02, 0xD8, 0xF0, 0x12, 0x3C, 0x29, 0x12, 0x44, 0x46, 0x12, 0x3F, 0x5E, 0x7F,
+0x58, 0x7E, 0xA0, 0x12, 0x44, 0x8D, 0xEF, 0x44, 0x80, 0xFD, 0x7F, 0x58, 0x7E, 0xA0, 0x12, 0x44,
+0x86, 0x12, 0x40, 0xE2, 0x7F, 0x0A, 0x7E, 0x00, 0x12, 0x43, 0x54, 0xE4, 0x90, 0x02, 0xC9, 0xF0,
+0x90, 0x02, 0xDE, 0xF0, 0x7F, 0x01, 0x12, 0x38, 0x8D, 0x7F, 0x54, 0x7E, 0xD1, 0x12, 0x44, 0x8D,
+0xEF, 0x90, 0x02, 0xCA, 0xF0, 0xA3, 0xE4, 0xF0, 0x7F, 0x55, 0x7E, 0xD1, 0x12, 0x44, 0x8D, 0x90,
+0x02, 0xCB, 0xE0, 0x2F, 0xF0, 0x90, 0x02, 0xCA, 0xE0, 0x34, 0x00, 0xF0, 0x7F, 0x01, 0x12, 0x43,
+0x18, 0x7F, 0xC8, 0x7E, 0x00, 0x12, 0x43, 0x54, 0x02, 0x1C, 0x48, 0x12, 0x30, 0xF1, 0x12, 0x29,
+0x79, 0xEF, 0x70, 0x03, 0x02, 0x1C, 0x48, 0x90, 0x02, 0xD8, 0xE0, 0x60, 0x03, 0x02, 0x1C, 0x48,
+0x78, 0x38, 0xF6, 0x04, 0xF0, 0x7F, 0x58, 0x7E, 0xA0, 0x12, 0x44, 0x8D, 0xEF, 0x54, 0x7F, 0xFD,
+0x7F, 0x58, 0x7E, 0xA0, 0x12, 0x44, 0x86, 0x02, 0x1C, 0x48, 0x7F, 0x58, 0x7E, 0xA0, 0x12, 0x44,
+0x8D, 0xEF, 0x54, 0x7F, 0xFD, 0x7F, 0x58, 0x7E, 0xA0, 0x12, 0x44, 0x86, 0xE4, 0x90, 0x02, 0xDE,
+0xF0, 0x90, 0xD2, 0x12, 0xE0, 0x70, 0x08, 0x78, 0x38, 0xE6, 0x70, 0x03, 0x02, 0x1C, 0x48, 0x90,
+0x02, 0xC9, 0xE0, 0x04, 0xF0, 0x7F, 0x01, 0x7E, 0x00, 0x12, 0x41, 0x1F, 0x90, 0x02, 0xC9, 0xE0,
+0x64, 0xC8, 0x70, 0x39, 0xF0, 0x90, 0xB8, 0xB0, 0xE0, 0x20, 0xE5, 0x19, 0x30, 0xE6, 0x16, 0x90,
+0xD2, 0x17, 0xE0, 0x20, 0xE6, 0x27, 0x90, 0x02, 0xBF, 0xE0, 0x44, 0x01, 0x90, 0xD8, 0x00, 0xF0,
+0x12, 0x42, 0xB9, 0x80, 0x18, 0x90, 0xB8, 0xB0, 0xE0, 0x30, 0xE5, 0x11, 0x30, 0xE6, 0x0E, 0x12,
+0x29, 0x79, 0xEF, 0x70, 0x05, 0x12, 0x15, 0x6C, 0x80, 0x03, 0x12, 0x42, 0x98, 0x78, 0x38, 0xE6,
+0x70, 0x03, 0x02, 0x1C, 0x48, 0xE4, 0xF6, 0x90, 0x02, 0xD8, 0xE0, 0x60, 0x03, 0x02, 0x1C, 0x48,
+0xF6, 0x04, 0xF0, 0x7F, 0x58, 0x7E, 0xA0, 0x12, 0x44, 0x8D, 0xEF, 0x54, 0x7F, 0xFD, 0x7F, 0x58,
+0x7E, 0xA0, 0x12, 0x44, 0x86, 0x02, 0x1C, 0x48, 0x44, 0x02, 0xD2, 0x00, 0x00, 0x00, 0x00, 0x41,
+0x02, 0xC0, 0x00, 0x46, 0x03, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x61, 0x00, 0x00, 0x1E,
+0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x0E, 0xD4, 0x32, 0x31, 0x00, 0x88, 0x88, 0x88,
+0x20, 0x1C, 0x01, 0x03, 0x80, 0x0C, 0x07, 0x78, 0x0A, 0x0D, 0xC9, 0xA0, 0x57, 0x47, 0x98, 0x27,
+0x12, 0x48, 0x4C, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x40, 0x46, 0xC0, 0xA0, 0xF0, 0xF4, 0x18, 0x20, 0x30, 0x20,
+0xC2, 0x00, 0xC0, 0xF4, 0xF2, 0x00, 0x00, 0x1E, 0x40, 0x46, 0xC0, 0xA0, 0xF0, 0xF4, 0x18, 0x20,
+0x30, 0x20, 0xC2, 0x00, 0xC0, 0xF4, 0xF2, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x4C,
+0x6F, 0x6E, 0x74, 0x69, 0x75, 0x6D, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0C,
+0x02, 0x03, 0x12, 0xF1, 0x23, 0x09, 0x07, 0x07, 0x83, 0x01, 0x00, 0x00, 0x65, 0x03, 0x0C, 0x00,
+0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB6,
+0x43, 0x00, 0x00, 0x0A, 0x04, 0x01, 0x44, 0x00, 0x03, 0x02, 0x00, 0x00, 0x00, 0x57, 0x00, 0x07,
+0x00, 0x02, 0x44, 0x14, 0x08, 0x98, 0x07, 0x80, 0x00, 0x58, 0x00, 0x2C, 0x00, 0x94, 0x04, 0x65,
+0x04, 0x38, 0x04, 0x05, 0x24, 0x01, 0x01, 0x60, 0x64, 0x01, 0xB2, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42,
+0x02, 0x23, 0x84, 0x08, 0x01, 0x10, 0x00, 0x01, 0x13, 0x00, 0x01, 0x11, 0x00, 0x01, 0x0F, 0x00,
+0x01, 0x12, 0x00, 0x41, 0x03, 0x11, 0x00, 0x41, 0x03, 0x10, 0x00, 0x41, 0x03, 0x12, 0x00, 0x41,
+0x02, 0x7E, 0x00, 0x41, 0x02, 0x3E, 0x00, 0x41, 0x02, 0x63, 0x00, 0x41, 0x02, 0x62, 0x00, 0x41,
+0x02, 0x74, 0x00, 0x41, 0x02, 0x61, 0x00, 0x41, 0x02, 0x7C, 0x01, 0x41, 0x02, 0x79, 0x00, 0x41,
+0x02, 0x8B, 0x01, 0x41, 0x02, 0x7A, 0x00, 0x41, 0x02, 0x6A, 0x00, 0x41, 0x02, 0x76, 0x00, 0x41,
+0x02, 0x6F, 0x00, 0x41, 0x02, 0x75, 0x00, 0x41, 0x02, 0x40, 0x00, 0x41, 0x02, 0x64, 0x00, 0x41,
+0x02, 0x66, 0x00, 0x41, 0x02, 0x67, 0x01, 0x41, 0x02, 0x65, 0x01, 0x41, 0x02, 0x3D, 0x00, 0x41,
+0x02, 0x89, 0x00, 0x46, 0x02, 0x83, 0x42, 0x10, 0x5A, 0x68, 0x01, 0x13, 0x00, 0x42, 0x10, 0x5A,
+0x68, 0x01, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4F, 0x54, 0x41, 0x80, 0x00,
+0xFF, 0x00, 0x00, 0x00, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x58, 0x01, 0x12, 0x10, 0x7B, 0x00, 0x00,
+0x51, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4F, 0x36, 0x42, 0x80, 0x00, 0xFF, 0x00,
+0x00, 0x01, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4F, 0x28, 0x43, 0x80, 0x01, 0xFF, 0x05, 0x04, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x4F, 0x1A, 0x44, 0x81, 0x01, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x4F, 0x2C, 0x50, 0x81, 0x01, 0xFF, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x4F, 0x1E, 0x51, 0x81, 0x01, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4F,
+0x22, 0x06, 0x81, 0x01, 0xFF, 0x8A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4F, 0x22, 0x06,
+0x81, 0x01, 0xFF, 0x8A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4F, 0x22, 0x06, 0x81, 0x01,
+0xFF, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4F, 0x22, 0x06, 0x81, 0x01, 0xFF, 0x8A,
+0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4F, 0x2C, 0x50, 0x81, 0x01, 0xFF, 0x8A, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x12, 0x5A, 0x90, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x44, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE4,
+0x90, 0x02, 0xE1, 0xF0, 0xA3, 0xF0, 0xFD, 0x7F, 0x50, 0x7E, 0xA0, 0x12, 0x44, 0x86, 0x7D, 0x02,
+0x7F, 0x51, 0x7E, 0xA0, 0x12, 0x44, 0x86, 0x7D, 0x03, 0x7F, 0x52, 0x7E, 0xA0, 0x12, 0x44, 0x86,
+0xE4, 0xFD, 0x7F, 0x53, 0x7E, 0xA0, 0x12, 0x44, 0x86, 0x7D, 0xF1, 0x7F, 0x54, 0x7E, 0xA0, 0x12,
+0x44, 0x86, 0x7F, 0x50, 0x7E, 0xA0, 0x12, 0x44, 0x8D, 0xEF, 0x44, 0x02, 0xFD, 0x7F, 0x50, 0x7E,
+0xA0, 0x12, 0x44, 0x86, 0x7F, 0x64, 0x7E, 0x00, 0x12, 0x43, 0x54, 0x7F, 0x0C, 0x7E, 0xA0, 0x12,
+0x44, 0x8D, 0xEF, 0x54, 0x02, 0x60, 0x0E, 0x7F, 0x0C, 0x7E, 0xA0, 0x12, 0x44, 0x8D, 0xEF, 0x54,
+0x01, 0x90, 0x02, 0xE2, 0xF0, 0x7F, 0x50, 0x7E, 0xA0, 0x12, 0x44, 0x8D, 0xEF, 0x54, 0xFD, 0xFD,
+0x7F, 0x50, 0x7E, 0xA0, 0x12, 0x44, 0x86, 0x7F, 0xEB, 0x7E, 0xD1, 0x12, 0x44, 0x8D, 0xEF, 0x54,
+0x06, 0x90, 0x02, 0xE1, 0xF0, 0x70, 0x77, 0x7D, 0x04, 0x7F, 0x50, 0x7E, 0xA0, 0x12, 0x44, 0x86,
+0xE4, 0xFD, 0x7F, 0x55, 0x7E, 0xA0, 0x12, 0x44, 0x86, 0xE4, 0xFD, 0x7F, 0x57, 0x7E, 0xA0, 0x12,
+0x44, 0x86, 0x90, 0x02, 0xD7, 0xE0, 0x70, 0x03, 0x02, 0x23, 0x74, 0x64, 0x02, 0x70, 0x23, 0x90,
+0x02, 0xE2, 0xE0, 0x64, 0x01, 0x70, 0x0D, 0xFD, 0x7F, 0x50, 0x7E, 0xA0, 0x12, 0x44, 0x86, 0x7D,
+0xF0, 0x02, 0x23, 0x47, 0xE4, 0xFD, 0x7F, 0x50, 0x7E, 0xA0, 0x12, 0x44, 0x86, 0x7D, 0xE0, 0x02,
+0x23, 0x47, 0x90, 0x02, 0xD7, 0xE0, 0x64, 0x01, 0x60, 0x03, 0x02, 0x23, 0x74, 0x90, 0x02, 0xE2,
+0xE0, 0x64, 0x01, 0x70, 0x0C, 0xFD, 0x7F, 0x50, 0x7E, 0xA0, 0x12, 0x44, 0x86, 0x7D, 0xFC, 0x80,
+0x0B, 0xE4, 0xFD, 0x7F, 0x50, 0x7E, 0xA0, 0x12, 0x44, 0x86, 0x7D, 0xEC, 0x80, 0x79, 0x90, 0x02,
+0xE1, 0xE0, 0x64, 0x02, 0x70, 0x3B, 0x90, 0x02, 0xD7, 0xE0, 0x70, 0x0C, 0xFD, 0x7F, 0x50, 0x7E,
+0xA0, 0x12, 0x44, 0x86, 0x7D, 0x03, 0x80, 0x41, 0x90, 0x02, 0xD7, 0xE0, 0x64, 0x02, 0x70, 0x0C,
+0xFD, 0x7F, 0x50, 0x7E, 0xA0, 0x12, 0x44, 0x86, 0x7D, 0x03, 0x80, 0x4B, 0x90, 0x02, 0xD7, 0xE0,
+0x64, 0x01, 0x70, 0x70, 0x7D, 0x04, 0x7F, 0x50, 0x7E, 0xA0, 0x12, 0x44, 0x86, 0xE4, 0xFD, 0x80,
+0x36, 0x90, 0x02, 0xE1, 0xE0, 0x64, 0x04, 0x70, 0x5B, 0x90, 0x02, 0xD7, 0xE0, 0x70, 0x15, 0xFD,
+0x7F, 0x50, 0x7E, 0xA0, 0x12, 0x44, 0x86, 0xE4, 0xFD, 0x7F, 0x55, 0x7E, 0xA0, 0x12, 0x44, 0x86,
+0x7D, 0x3C, 0x80, 0x39, 0x90, 0x02, 0xD7, 0xE0, 0x64, 0x02, 0x70, 0x16, 0x7D, 0x04, 0x7F, 0x50,
+0x7E, 0xA0, 0x12, 0x44, 0x86, 0xE4, 0xFD, 0x7F, 0x55, 0x7E, 0xA0, 0x12, 0x44, 0x86, 0xE4, 0xFD,
+0x80, 0x1B, 0x90, 0x02, 0xD7, 0xE0, 0x64, 0x01, 0x70, 0x1A, 0xFD, 0x7F, 0x50, 0x7E, 0xA0, 0x12,
+0x44, 0x86, 0x7D, 0x0C, 0x7F, 0x55, 0x7E, 0xA0, 0x12, 0x44, 0x86, 0xE4, 0xFD, 0x7F, 0x57, 0x7E,
+0xA0, 0x12, 0x44, 0x86, 0x22, 0x90, 0x02, 0xFD, 0xEB, 0xF0, 0xA3, 0xEA, 0xF0, 0xA3, 0xE9, 0xF0,
+0x90, 0x00, 0x11, 0xE0, 0xFE, 0xA3, 0xE0, 0xFF, 0x90, 0x00, 0x10, 0xE0, 0x2F, 0xFF, 0x90, 0x00,
+0x0F, 0xE0, 0x3E, 0xFE, 0x90, 0x00, 0x14, 0xE0, 0x2F, 0xFF, 0x90, 0x00, 0x13, 0xE0, 0x3E, 0x90,
+0x03, 0x00, 0xF0, 0xA3, 0xEF, 0xF0, 0x90, 0x00, 0x1A, 0xE0, 0xFF, 0x90, 0x00, 0x19, 0xE0, 0x2F,
+0xFF, 0xE4, 0x33, 0xFE, 0x90, 0x00, 0x1B, 0xE0, 0x7C, 0x00, 0x2F, 0xFF, 0xEC, 0x3E, 0x90, 0x03,
+0x02, 0xF0, 0xA3, 0xEF, 0xF0, 0xE4, 0x7B, 0x0A, 0xFA, 0xF9, 0xF8, 0x90, 0x00, 0x07, 0xE0, 0xFC,
+0xA3, 0xE0, 0xFD, 0xA3, 0xE0, 0xFE, 0xA3, 0xE0, 0xFF, 0x12, 0x07, 0xD5, 0x90, 0x02, 0xFD, 0xE0,
+0xFB, 0xA3, 0xE0, 0xFA, 0xA3, 0xE0, 0xF9, 0xEF, 0x12, 0x06, 0xAB, 0xE4, 0x7B, 0x0A, 0xFA, 0xF9,
+0xF8, 0x90, 0x00, 0x07, 0xE0, 0xFC, 0xA3, 0xE0, 0xFD, 0xA3, 0xE0, 0xFE, 0xA3, 0xE0, 0xFF, 0x12,
+0x07, 0xD5, 0xE4, 0xFB, 0x7A, 0x01, 0xF9, 0xF8, 0x12, 0x07, 0xD5, 0x90, 0x02, 0xFD, 0xE0, 0xFB,
+0xA3, 0xE0, 0xFA, 0xA3, 0xE0, 0xF9, 0x90, 0x00, 0x01, 0xEF, 0x12, 0x06, 0xBD, 0x90, 0x00, 0x0D,
+0xE0, 0xFE, 0xA3, 0xE0, 0x90, 0x00, 0x02, 0x12, 0x06, 0xBD, 0x90, 0x03, 0x01, 0xE0, 0x90, 0x00,
+0x03, 0x12, 0x06, 0xBD, 0xEE, 0xC4, 0xF8, 0x54, 0x0F, 0xC8, 0x68, 0xFF, 0xAD, 0x07, 0x90, 0x03,
+0x00, 0xE0, 0x2D, 0x90, 0x00, 0x04, 0x12, 0x06, 0xBD, 0x90, 0x00, 0x18, 0xE0, 0x90, 0x00, 0x05,
+0x12, 0x06, 0xBD, 0x90, 0x03, 0x03, 0xE0, 0x90, 0x00, 0x06, 0x12, 0x06, 0xBD, 0x90, 0x00, 0x17,
+0xE0, 0xC4, 0xF8, 0x54, 0x0F, 0xC8, 0x68, 0xFF, 0xAC, 0x07, 0x90, 0x03, 0x02, 0xE0, 0x2C, 0x90,
+0x00, 0x07, 0x12, 0x06, 0xBD, 0x90, 0x00, 0x10, 0xE0, 0x90, 0x00, 0x08, 0x12, 0x06, 0xBD, 0x90,
+0x00, 0x12, 0xE0, 0x90, 0x00, 0x09, 0x12, 0x06, 0xBD, 0x90, 0x00, 0x19, 0xE0, 0x54, 0x0F, 0xC4,
+0x54, 0xF0, 0xFF, 0xA3, 0xE0, 0x54, 0x0F, 0x2F, 0x90, 0x00, 0x0A, 0x12, 0x06, 0xBD, 0x90, 0x00,
+0x11, 0xE0, 0xC4, 0xF8, 0x54, 0x0F, 0xC8, 0x68, 0xFF, 0xAC, 0x07, 0x90, 0x00, 0x0F, 0xE0, 0x7E,
+0x00, 0x78, 0x06, 0xC3, 0x33, 0xCE, 0x33, 0xCE, 0xD8, 0xF9, 0x2C, 0xFF, 0x90, 0x00, 0x19, 0xE0,
+0xC4, 0x54, 0x0F, 0x25, 0xE0, 0x25, 0xE0, 0x2F, 0xFF, 0xA3, 0xE0, 0xC4, 0x54, 0x0F, 0x2F, 0x90,
+0x00, 0x0B, 0x12, 0x06, 0xBD, 0x90, 0x00, 0x0E, 0xE0, 0x90, 0x00, 0x0C, 0x12, 0x06, 0xBD, 0x90,
+0x00, 0x18, 0xE0, 0x90, 0x00, 0x0D, 0x12, 0x06, 0xBD, 0x90, 0x00, 0x17, 0xE0, 0x2D, 0x90, 0x00,
+0x0E, 0x12, 0x06, 0xBD, 0x90, 0x00, 0x1C, 0xE0, 0x25, 0xE0, 0x44, 0x18, 0xFF, 0xA3, 0xE0, 0x25,
+0xE0, 0x25, 0xE0, 0x4F, 0x90, 0x00, 0x11, 0x02, 0x06, 0xBD, 0xAE, 0x07, 0xE4, 0xFF, 0x90, 0x02,
+0xFC, 0xF0, 0x90, 0xD8, 0x55, 0x74, 0x80, 0xF0, 0x78, 0x44, 0xE6, 0x90, 0xD8, 0x52, 0xF0, 0x08,
+0xE6, 0x90, 0xD8, 0x51, 0xF0, 0x08, 0xE6, 0x90, 0xD8, 0x50, 0xF0, 0xEF, 0xC3, 0x9E, 0x50, 0x12,
+0x74, 0x4E, 0x2F, 0xF8, 0xE6, 0x90, 0xD8, 0x53, 0xF0, 0x90, 0xD8, 0x50, 0xE0, 0x04, 0xF0, 0x0F,
+0x80, 0xE9, 0x78, 0x44, 0xE6, 0xF9, 0x70, 0x50, 0x08, 0xE6, 0x64, 0x01, 0x70, 0x4A, 0x08, 0xE6,
+0x70, 0x46, 0x78, 0x4E, 0xE6, 0x78, 0x3B, 0xF6, 0x78, 0x35, 0x76, 0x01, 0xE4, 0x90, 0xD2, 0x12,
+0xF0, 0x78, 0x38, 0xF6, 0x78, 0x47, 0xF6, 0xEE, 0xFD, 0x7C, 0x00, 0xD3, 0x94, 0x01, 0x74, 0x80,
+0x94, 0x80, 0x40, 0x0B, 0x78, 0x4F, 0xE6, 0xFF, 0x78, 0x3C, 0xF6, 0x54, 0x0F, 0x08, 0xF6, 0xD3,
+0xED, 0x94, 0x02, 0xEC, 0x64, 0x80, 0x94, 0x80, 0x50, 0x03, 0x02, 0x26, 0x9B, 0x78, 0x50, 0xE6,
+0x54, 0x0F, 0x78, 0x3A, 0xF6, 0x02, 0x39, 0xDE, 0xE9, 0x70, 0x42, 0x78, 0x45, 0xE6, 0x64, 0x01,
+0x70, 0x3B, 0x08, 0xE6, 0x64, 0x01, 0x70, 0x35, 0x78, 0x4E, 0xE6, 0xFF, 0x78, 0x3C, 0xF6, 0x54,
+0x0F, 0x08, 0xF6, 0xEE, 0xD3, 0x94, 0x01, 0x74, 0x80, 0x94, 0x80, 0x40, 0x05, 0x78, 0x4F, 0x12,
+0x39, 0xD8, 0xEE, 0xC3, 0x94, 0x03, 0x74, 0x80, 0x94, 0x80, 0x50, 0x03, 0x02, 0x26, 0x9B, 0x78,
+0x50, 0xE6, 0x54, 0x03, 0x64, 0x02, 0x60, 0x03, 0x02, 0x26, 0x9B, 0x80, 0x49, 0xE9, 0x70, 0x2D,
+0x78, 0x45, 0xE6, 0x64, 0x01, 0x70, 0x26, 0x08, 0xE6, 0x64, 0x02, 0x70, 0x20, 0x78, 0x4E, 0x12,
+0x39, 0xD8, 0xEE, 0xC3, 0x94, 0x02, 0x74, 0x80, 0x94, 0x80, 0x50, 0x03, 0x02, 0x26, 0x9B, 0x78,
+0x4F, 0xE6, 0x54, 0x03, 0x64, 0x02, 0x60, 0x03, 0x02, 0x26, 0x9B, 0x80, 0x19, 0xE9, 0x70, 0x1F,
+0x78, 0x45, 0xE6, 0x64, 0x01, 0x70, 0x18, 0x08, 0xE6, 0x64, 0x03, 0x70, 0x12, 0x78, 0x4E, 0xE6,
+0x54, 0x03, 0x64, 0x02, 0x70, 0x75, 0x90, 0xD8, 0x19, 0xF0, 0x90, 0xD8, 0x1F, 0xF0, 0x22, 0xE9,
+0xFD, 0x7C, 0x00, 0x70, 0x44, 0x78, 0x45, 0xE6, 0x64, 0x01, 0x70, 0x3D, 0x08, 0xE6, 0x64, 0x07,
+0x70, 0x37, 0x78, 0x23, 0x76, 0x01, 0x78, 0x4E, 0xE6, 0x90, 0xB8, 0x3A, 0x30, 0xE4, 0x02, 0x80,
+0x0C, 0x74, 0x40, 0xF0, 0x90, 0xB8, 0x3B, 0x74, 0x50, 0xF0, 0x90, 0xB8, 0x3A, 0x74, 0x80, 0xF0,
+0x90, 0xB8, 0x3B, 0x74, 0x52, 0xF0, 0xAF, 0x06, 0xD3, 0xEF, 0x94, 0x01, 0x74, 0x80, 0x94, 0x80,
+0x40, 0x29, 0x78, 0x4F, 0xE6, 0x70, 0x24, 0x80, 0x16, 0xED, 0x4C, 0x70, 0x1E, 0x78, 0x45, 0xE6,
+0x64, 0x01, 0x70, 0x17, 0x08, 0xE6, 0x64, 0x08, 0x70, 0x11, 0x78, 0x4E, 0xE6, 0x70, 0x0C, 0x90,
+0xD8, 0x50, 0x74, 0x08, 0xF0, 0x90, 0xD8, 0x53, 0x74, 0x01, 0xF0, 0x22, 0xE4, 0x90, 0x02, 0xFB,
+0xF0, 0x78, 0x36, 0x76, 0xFF, 0x78, 0x49, 0xF6, 0x7F, 0xFF, 0x90, 0xD0, 0x0F, 0xE0, 0x54, 0x1F,
+0x78, 0x48, 0xF6, 0x12, 0x44, 0xA8, 0xEF, 0x54, 0xF0, 0x78, 0x36, 0xF6, 0xEF, 0x54, 0x0F, 0x78,
+0x44, 0xF6, 0x12, 0x44, 0xA8, 0x78, 0x45, 0xA6, 0x07, 0x12, 0x44, 0xA8, 0x78, 0x46, 0xA6, 0x07,
+0x78, 0x48, 0xE6, 0xC3, 0x94, 0x04, 0x74, 0x80, 0x94, 0x80, 0x40, 0x15, 0x12, 0x44, 0xA8, 0x78,
+0x49, 0xA6, 0x07, 0x06, 0xE6, 0xD3, 0x94, 0x10, 0x74, 0x80, 0x94, 0x80, 0x40, 0x03, 0x02, 0x28,
+0x0A, 0x78, 0x48, 0xE6, 0xD3, 0x94, 0x04, 0x74, 0x80, 0x94, 0x80, 0x40, 0x20, 0xE4, 0x90, 0x02,
+0xFB, 0xF0, 0x90, 0x02, 0xFB, 0xE0, 0xC3, 0x78, 0x49, 0x96, 0x50, 0x11, 0x12, 0x44, 0xA8, 0x90,
+0x02, 0xFB, 0xE0, 0x24, 0x4E, 0xF8, 0xA6, 0x07, 0xE0, 0x04, 0xF0, 0x80, 0xE5, 0x78, 0x36, 0xE6,
+0x70, 0x03, 0x02, 0x28, 0x02, 0x24, 0xF0, 0x70, 0x03, 0x02, 0x27, 0xCB, 0x24, 0xF0, 0x70, 0x03,
+0x02, 0x28, 0x02, 0x24, 0xE0, 0x60, 0x3D, 0x24, 0xF0, 0x60, 0x56, 0x24, 0xF0, 0x70, 0x03, 0x02,
+0x28, 0x02, 0x24, 0xD0, 0x60, 0x11, 0x24, 0x10, 0x60, 0x03, 0x02, 0x28, 0x0A, 0x78, 0x49, 0xE6,
+0xFF, 0x12, 0x25, 0x0A, 0x02, 0x27, 0xFD, 0x78, 0x49, 0xE6, 0xFF, 0xC3, 0x94, 0x11, 0x74, 0x80,
+0x94, 0x80, 0x50, 0x65, 0x12, 0x2E, 0xF4, 0x12, 0x44, 0xB4, 0x78, 0x49, 0xE6, 0xFF, 0x12, 0x44,
+0x16, 0x02, 0x28, 0x05, 0x78, 0x46, 0xE6, 0x64, 0x50, 0x70, 0x14, 0x78, 0x49, 0xE6, 0x64, 0x01,
+0x70, 0x0D, 0x78, 0x48, 0xE6, 0x64, 0x05, 0x70, 0x06, 0x78, 0x4E, 0xE6, 0x78, 0x39, 0xF6, 0x80,
+0x6C, 0x78, 0x46, 0xE6, 0x64, 0x50, 0x70, 0x0F, 0x78, 0x48, 0xE6, 0x64, 0x04, 0x70, 0x08, 0x12,
+0x44, 0xB4, 0x12, 0x42, 0x04, 0x80, 0x22, 0x78, 0x46, 0xE6, 0x64, 0x50, 0x70, 0x09, 0x78, 0x48,
+0xE6, 0x64, 0x03, 0x70, 0x02, 0x80, 0x07, 0x78, 0x46, 0xE6, 0x64, 0x3C, 0x70, 0x05, 0x12, 0x44,
+0xB4, 0x80, 0x06, 0x90, 0xD0, 0x11, 0x74, 0x10, 0xF0, 0x80, 0x3A, 0x78, 0x46, 0xE6, 0x64, 0x50,
+0x70, 0x0F, 0x78, 0x48, 0xE6, 0x64, 0x04, 0x70, 0x08, 0x12, 0x44, 0xB4, 0x12, 0x42, 0x4F, 0x80,
+0x11, 0x78, 0x46, 0xE6, 0x64, 0x50, 0x70, 0x10, 0x78, 0x48, 0xE6, 0x64, 0x03, 0x70, 0x09, 0x12,
+0x44, 0xB4, 0xE4, 0x78, 0x39, 0xF6, 0x80, 0x03, 0x12, 0x44, 0xB4, 0x80, 0x08, 0x12, 0x44, 0xB4,
+0x80, 0x03, 0x12, 0x44, 0xB4, 0xE4, 0x90, 0xD0, 0x13, 0xF0, 0x22, 0x90, 0x00, 0x03, 0xE0, 0x64,
+0x01, 0x70, 0x14, 0xFD, 0x7F, 0x43, 0x7E, 0xA0, 0x12, 0x44, 0x86, 0x7F, 0x58, 0x7E, 0xA0, 0x12,
+0x44, 0x8D, 0xEF, 0x54, 0xFC, 0x80, 0x62, 0x90, 0x00, 0x03, 0xE0, 0x64, 0x02, 0x70, 0x3F, 0x90,
+0x02, 0xCC, 0xE0, 0x64, 0x01, 0x70, 0x04, 0x7D, 0x50, 0x80, 0x0E, 0x90, 0x02, 0xC7, 0xE0, 0x64,
+0x01, 0x70, 0x04, 0x7D, 0x44, 0x80, 0x02, 0x7D, 0x41, 0x7F, 0x43, 0x7E, 0xA0, 0x12, 0x44, 0x86,
+0x7F, 0x58, 0x7E, 0xA0, 0x12, 0x44, 0x8D, 0xEF, 0x54, 0xFD, 0xFD, 0x7F, 0x58, 0x7E, 0xA0, 0x12,
+0x44, 0x86, 0x7F, 0x58, 0x7E, 0xA0, 0x12, 0x44, 0x8D, 0xEF, 0x44, 0x01, 0x80, 0x1B, 0x90, 0x00,
+0x03, 0xE0, 0x64, 0x04, 0x70, 0x1B, 0x7D, 0xD8, 0x7F, 0x43, 0x7E, 0xA0, 0x12, 0x44, 0x86, 0x7F,
+0x58, 0x7E, 0xA0, 0x12, 0x44, 0x8D, 0xEF, 0x44, 0x03, 0xFD, 0x7F, 0x58, 0x7E, 0xA0, 0x12, 0x44,
+0x86, 0x7D, 0x40, 0x7F, 0x44, 0x7E, 0xB0, 0x12, 0x44, 0x86, 0x7D, 0x30, 0x7F, 0x55, 0x7E, 0xB0,
+0x12, 0x44, 0x86, 0x7D, 0x88, 0x7F, 0x5C, 0x7E, 0xB0, 0x12, 0x44, 0x86, 0x7D, 0x88, 0x7F, 0x5E,
+0x7E, 0xB0, 0x12, 0x44, 0x86, 0x7D, 0x88, 0x7F, 0x60, 0x7E, 0xB0, 0x12, 0x44, 0x86, 0x7D, 0x88,
+0x7F, 0x62, 0x7E, 0xB0, 0x12, 0x44, 0x86, 0x7D, 0x88, 0x7F, 0x64, 0x7E, 0xB0, 0x12, 0x44, 0x86,
+0x7D, 0x30, 0x7F, 0x77, 0x7E, 0xB0, 0x12, 0x44, 0x86, 0x7D, 0x88, 0x7F, 0x7E, 0x7E, 0xB0, 0x12,
+0x44, 0x86, 0x7D, 0x88, 0x7F, 0x80, 0x7E, 0xB0, 0x12, 0x44, 0x86, 0x7D, 0x88, 0x7F, 0x82, 0x7E,
+0xB0, 0x12, 0x44, 0x86, 0x7D, 0x88, 0x7F, 0x84, 0x7E, 0xB0, 0x12, 0x44, 0x86, 0x7D, 0x88, 0x7F,
+0x86, 0x7E, 0xB0, 0x12, 0x44, 0x86, 0x90, 0x02, 0xCC, 0xE0, 0x70, 0x6C, 0x7D, 0x70, 0x7F, 0x44,
+0x7E, 0xB0, 0x12, 0x44, 0x86, 0x7D, 0x88, 0x7F, 0x4B, 0x7E, 0xB0, 0x12, 0x44, 0x86, 0x7D, 0x88,
+0x7F, 0x4D, 0x7E, 0xB0, 0x12, 0x44, 0x86, 0x7D, 0x88, 0x7F, 0x4F, 0x7E, 0xB0, 0x12, 0x44, 0x86,
+0x7D, 0x88, 0x7F, 0x51, 0x7E, 0xB0, 0x12, 0x44, 0x86, 0x7D, 0x88, 0x7F, 0x53, 0x7E, 0xB0, 0x12,
+0x44, 0x86, 0x7D, 0x30, 0x7F, 0x66, 0x7E, 0xB0, 0x12, 0x44, 0x86, 0x7D, 0x88, 0x7F, 0x6D, 0x7E,
+0xB0, 0x12, 0x44, 0x86, 0x7D, 0x88, 0x7F, 0x6F, 0x7E, 0xB0, 0x12, 0x44, 0x86, 0x7D, 0x88, 0x7F,
+0x71, 0x7E, 0xB0, 0x12, 0x44, 0x86, 0x7D, 0x88, 0x7F, 0x73, 0x7E, 0xB0, 0x12, 0x44, 0x86, 0x7D,
+0x88, 0x7F, 0x75, 0x7E, 0xB0, 0x12, 0x44, 0x86, 0x22, 0x7F, 0x01, 0x12, 0x43, 0x8F, 0x90, 0x02,
+0xE8, 0xEF, 0x12, 0x41, 0x13, 0x7F, 0x11, 0x7E, 0xDB, 0x12, 0x44, 0x8D, 0xEF, 0x54, 0x7F, 0x90,
+0x02, 0xE0, 0xF0, 0xA3, 0xE4, 0xF0, 0x7F, 0x10, 0x7E, 0xDB, 0x12, 0x44, 0x8D, 0x90, 0x02, 0xE1,
+0xE0, 0x2F, 0xF0, 0x90, 0x02, 0xE0, 0xE0, 0x34, 0x00, 0xF0, 0x7F, 0x13, 0x7E, 0xDB, 0x12, 0x44,
+0x8D, 0xEF, 0x54, 0x7F, 0x90, 0x02, 0xE2, 0xF0, 0xA3, 0xE4, 0xF0, 0x7F, 0x12, 0x7E, 0xDB, 0x12,
+0x44, 0x8D, 0x90, 0x02, 0xE3, 0xE0, 0x2F, 0xF0, 0x90, 0x02, 0xE2, 0xE0, 0x34, 0x00, 0xF0, 0x7F,
+0x15, 0x7E, 0xDB, 0x12, 0x44, 0x8D, 0xEF, 0x54, 0x7F, 0x90, 0x02, 0xE4, 0xF0, 0xA3, 0xE4, 0xF0,
+0x7F, 0x14, 0x7E, 0xDB, 0x12, 0x44, 0x8D, 0x90, 0x02, 0xE5, 0xE0, 0x2F, 0xF0, 0x90, 0x02, 0xE4,
+0xE0, 0x34, 0x00, 0xF0, 0x7F, 0x17, 0x7E, 0xDB, 0x12, 0x44, 0x8D, 0xEF, 0x54, 0x7F, 0x90, 0x02,
+0xE6, 0xF0, 0xA3, 0xE4, 0xF0, 0x7F, 0x16, 0x7E, 0xDB, 0x12, 0x44, 0x8D, 0x90, 0x02, 0xE7, 0xE0,
+0x2F, 0xF0, 0x90, 0x02, 0xE6, 0xE0, 0x34, 0x00, 0xF0, 0x90, 0x02, 0xE8, 0xE0, 0x24, 0xFE, 0x60,
+0x1D, 0x24, 0xFE, 0x60, 0x3D, 0x24, 0x03, 0x70, 0x7B, 0xC3, 0x90, 0x02, 0xE1, 0xE0, 0x94, 0x0A,
+0x90, 0x02, 0xE0, 0xE0, 0x94, 0x00, 0x40, 0x03, 0x7F, 0x01, 0x22, 0x7F, 0x00, 0x22, 0xC3, 0x90,
+0x02, 0xE1, 0xE0, 0x94, 0x0A, 0x90, 0x02, 0xE0, 0xE0, 0x94, 0x00, 0x50, 0x0F, 0xC3, 0x90, 0x02,
+0xE3, 0xE0, 0x94, 0x0A, 0x90, 0x02, 0xE2, 0xE0, 0x94, 0x00, 0x40, 0x03, 0x7F, 0x01, 0x22, 0x7F,
+0x00, 0x22, 0xC3, 0x90, 0x02, 0xE1, 0xE0, 0x94, 0x0A, 0x90, 0x02, 0xE0, 0xE0, 0x94, 0x00, 0x50,
+0x2D, 0xC3, 0x90, 0x02, 0xE3, 0xE0, 0x94, 0x0A, 0x90, 0x02, 0xE2, 0xE0, 0x94, 0x00, 0x50, 0x1E,
+0xC3, 0x90, 0x02, 0xE5, 0xE0, 0x94, 0x0A, 0x90, 0x02, 0xE4, 0xE0, 0x94, 0x00, 0x50, 0x0F, 0xC3,
+0x90, 0x02, 0xE7, 0xE0, 0x94, 0x0A, 0x90, 0x02, 0xE6, 0xE0, 0x94, 0x00, 0x40, 0x03, 0x7F, 0x01,
+0x22, 0x7F, 0x00, 0x22, 0x7F, 0x01, 0x22, 0x7F, 0x54, 0x7E, 0xD1, 0x12, 0x44, 0x8D, 0xEF, 0x90,
+0x03, 0x13, 0xF0, 0xA3, 0xE4, 0xF0, 0x7F, 0x55, 0x7E, 0xD1, 0x12, 0x44, 0x8D, 0x90, 0x03, 0x14,
+0xE0, 0x2F, 0xF0, 0x90, 0x03, 0x13, 0xE0, 0x34, 0x00, 0xF0, 0xE0, 0xFE, 0xA3, 0xE0, 0xFF, 0xC3,
+0x94, 0x22, 0xEE, 0x94, 0x00, 0x50, 0x08, 0xEF, 0x94, 0x1E, 0xEE, 0x94, 0x00, 0x50, 0x66, 0xC3,
+0xEF, 0x94, 0x2E, 0xEE, 0x94, 0x00, 0x50, 0x08, 0xEF, 0x94, 0x2A, 0xEE, 0x94, 0x00, 0x50, 0x55,
+0xC3, 0xEF, 0x94, 0x32, 0xEE, 0x94, 0x00, 0x50, 0x08, 0xEF, 0x94, 0x2E, 0xEE, 0x94, 0x00, 0x50,
+0x44, 0xC3, 0xEF, 0x94, 0x5A, 0xEE, 0x94, 0x00, 0x50, 0x08, 0xEF, 0x94, 0x56, 0xEE, 0x94, 0x00,
+0x50, 0x33, 0xC3, 0xEF, 0x94, 0x62, 0xEE, 0x94, 0x00, 0x50, 0x08, 0xEF, 0x94, 0x5E, 0xEE, 0x94,
+0x00, 0x50, 0x22, 0xC3, 0xEF, 0x94, 0xB2, 0xEE, 0x94, 0x00, 0x50, 0x08, 0xEF, 0x94, 0xAE, 0xEE,
+0x94, 0x00, 0x50, 0x11, 0xC3, 0xEF, 0x94, 0xC2, 0xEE, 0x94, 0x00, 0x50, 0x54, 0xEF, 0x94, 0xBE,
+0xEE, 0x94, 0x00, 0x40, 0x4C, 0x90, 0x02, 0xCA, 0xE0, 0xFE, 0xA3, 0xE0, 0xFF, 0x24, 0xFE, 0xFD,
+0xEE, 0x34, 0xFF, 0xFC, 0x90, 0x03, 0x13, 0xE0, 0xFA, 0xA3, 0xE0, 0xFB, 0xC3, 0x9D, 0xEA, 0x9C,
+0x40, 0x0E, 0xEF, 0x24, 0x02, 0xFF, 0xE4, 0x3E, 0xFE, 0xD3, 0xEB, 0x9F, 0xEA, 0x9E, 0x40, 0x11,
+0x7F, 0x01, 0x12, 0x43, 0x18, 0x7F, 0xC8, 0x7E, 0x00, 0x12, 0x43, 0x54, 0xE4, 0xFF, 0x12, 0x43,
+0x18, 0x90, 0x03, 0x13, 0xE0, 0xFF, 0xA3, 0xE0, 0x90, 0x02, 0xCA, 0xCF, 0xF0, 0xA3, 0xEF, 0xF0,
+0x22, 0x90, 0x03, 0x13, 0xE0, 0xFE, 0xA3, 0xE0, 0xFF, 0x4E, 0x70, 0x29, 0x90, 0x02, 0xCA, 0xE0,
+0xFC, 0xA3, 0xE0, 0xFD, 0xEF, 0x6D, 0x70, 0x02, 0xEE, 0x6C, 0x60, 0x19, 0x90, 0x02, 0xCA, 0xEE,
+0xF0, 0xA3, 0xEF, 0xF0, 0x7F, 0x01, 0x12, 0x43, 0x18, 0x7F, 0xC8, 0x7E, 0x00, 0x12, 0x43, 0x54,
+0xE4, 0xFF, 0x12, 0x43, 0x18, 0x22, 0x05, 0x01, 0x00, 0x05, 0x11, 0x00, 0x05, 0x10, 0x00, 0x05,
+0x29, 0x00, 0x15, 0x36, 0x00, 0x15, 0x3A, 0x00, 0x58, 0x00, 0x09, 0x01, 0xFF, 0x00, 0x0A, 0x0B,
+0x0B, 0x28, 0xFF, 0x00, 0x10, 0x20, 0x11, 0x00, 0x12, 0x62, 0x13, 0x62, 0x18, 0x6C, 0x19, 0x0F,
+0x1A, 0x00, 0x1B, 0x00, 0x20, 0x80, 0x21, 0x07, 0x24, 0xB0, 0x25, 0x04, 0x26, 0x80, 0x27, 0x07,
+0x28, 0x20, 0x29, 0x00, 0x2A, 0x20, 0x2B, 0x00, 0x2C, 0x14, 0x2D, 0x00, 0x2E, 0x14, 0x2F, 0x00,
+0x30, 0x04, 0x31, 0x00, 0x32, 0x04, 0x33, 0x00, 0x34, 0x0C, 0x35, 0x00, 0x36, 0x14, 0x37, 0x00,
+0x38, 0x34, 0x39, 0x00, 0x3A, 0x13, 0x3B, 0x00, 0xFF, 0x00, 0x0D, 0x01, 0xFF, 0x00, 0x09, 0x00,
+0xFF, 0x2B, 0xD8, 0xFF, 0x2B, 0xDA, 0xFF, 0x2B, 0xDC, 0xFF, 0x2B, 0xDE, 0xFF, 0x2B, 0xE0, 0xFF,
+0x2B, 0xE2, 0xFF, 0x2B, 0xE4, 0xFF, 0x2B, 0xE6, 0xFF, 0x2B, 0xE8, 0xFF, 0x2B, 0xEA, 0xFF, 0x2B,
+0xEC, 0xFF, 0x2B, 0xEE, 0xFF, 0x2B, 0xF0, 0xFF, 0x2B, 0xF2, 0xFF, 0x2B, 0xF4, 0xFF, 0x2B, 0xF6,
+0xFF, 0x2B, 0xF8, 0xFF, 0x2B, 0xFA, 0xFF, 0x2B, 0xFC, 0xFF, 0x2B, 0xFE, 0xFF, 0x2C, 0x00, 0xFF,
+0x2C, 0x02, 0xFF, 0x2C, 0x04, 0xFF, 0x2C, 0x06, 0xFF, 0x2C, 0x08, 0xFF, 0x2C, 0x0A, 0xFF, 0x2C,
+0x0C, 0xFF, 0x2C, 0x0E, 0xFF, 0x2C, 0x10, 0xFF, 0x2C, 0x12, 0xFF, 0x2C, 0x14, 0xFF, 0x2C, 0x16,
+0xFF, 0x2C, 0x18, 0xFF, 0x2C, 0x1A, 0xFF, 0x2C, 0x1C, 0xFF, 0x2C, 0x1E, 0xFF, 0x2C, 0x20, 0xFF,
+0x2C, 0x22, 0xFF, 0x2C, 0x24, 0xFF, 0x2C, 0x26, 0xFF, 0x2C, 0x28, 0xFF, 0x2C, 0x2A, 0xFF, 0x2C,
+0x2C, 0xFF, 0x2C, 0x2E, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+0x78, 0x0F, 0xE6, 0x30, 0xE7, 0x10, 0x90, 0xC1, 0x9E, 0xE0, 0x54, 0x0F, 0x60, 0x05, 0x12, 0x42,
+0xDA, 0x80, 0x03, 0x12, 0x3A, 0xA8, 0x78, 0x0F, 0xE6, 0xFF, 0x30, 0xE6, 0x25, 0x90, 0xC1, 0x9C,
+0xE0, 0x54, 0x07, 0x64, 0x01, 0x90, 0xB0, 0x0B, 0x60, 0x05, 0x74, 0xE5, 0xF0, 0x80, 0x13, 0x74,
+0xC5, 0xF0, 0x90, 0xC1, 0x96, 0x74, 0x0B, 0xF0, 0xE4, 0x90, 0x02, 0x72, 0xF0, 0x90, 0x02, 0x6A,
+0x04, 0xF0, 0xEF, 0x20, 0xE5, 0x04, 0x7F, 0x01, 0x80, 0x02, 0x7F, 0x00, 0x90, 0x02, 0x6A, 0xE0,
+0x5F, 0x60, 0x5B, 0x90, 0xC1, 0xA3, 0xE0, 0xFF, 0x90, 0x02, 0x72, 0xE0, 0xFE, 0x24, 0x41, 0xF5,
+0x82, 0xE4, 0x34, 0x02, 0xF5, 0x83, 0xEF, 0xF0, 0xEE, 0xFF, 0x7E, 0x00, 0x64, 0x01, 0x70, 0x0E,
+0x90, 0x02, 0x42, 0xE0, 0x54, 0x70, 0x13, 0x13, 0x54, 0x3F, 0x90, 0x02, 0x70, 0xF0, 0x90, 0x02,
+0x70, 0xE0, 0x24, 0x01, 0xFD, 0xE4, 0x33, 0xFC, 0xED, 0xB5, 0x07, 0x11, 0xEC, 0xB5, 0x06, 0x0D,
+0x90, 0xC1, 0x96, 0x74, 0x03, 0xF0, 0xE4, 0x90, 0x02, 0x6A, 0xF0, 0x80, 0x11, 0x90, 0x02, 0x72,
+0xE0, 0x04, 0xF0, 0x90, 0xC1, 0xB6, 0xE0, 0xFF, 0x78, 0x0F, 0xF6, 0x30, 0xE5, 0xA5, 0x78, 0x0F,
+0xE6, 0xFD, 0x30, 0xE4, 0x36, 0x90, 0xC1, 0xB6, 0x74, 0x10, 0xF0, 0x90, 0x02, 0x41, 0xE0, 0xFE,
+0x54, 0x0F, 0x90, 0x02, 0x77, 0xF0, 0x90, 0x02, 0x70, 0xE0, 0x70, 0x0F, 0x90, 0x02, 0x77, 0xE0,
+0x64, 0x01, 0x70, 0x07, 0x90, 0x02, 0x75, 0x04, 0xF0, 0x80, 0x10, 0xE4, 0x90, 0x02, 0x76, 0xF0,
+0xEE, 0x64, 0x4F, 0x60, 0x06, 0x90, 0x02, 0x63, 0x74, 0x01, 0xF0, 0xED, 0x30, 0xE2, 0x21, 0x90,
+0xB0, 0x0B, 0x74, 0xC5, 0xF0, 0x90, 0x02, 0x63, 0xE0, 0x60, 0x05, 0xE4, 0xF0, 0x02, 0x3B, 0x6F,
+0x90, 0x02, 0x76, 0xE0, 0x60, 0x0A, 0x90, 0x02, 0x75, 0x74, 0x01, 0xF0, 0x90, 0x02, 0x6F, 0xF0,
+0x22, 0x90, 0x00, 0x17, 0xE0, 0xFD, 0x7F, 0x5E, 0x7E, 0xA0, 0x12, 0x44, 0x86, 0x90, 0x00, 0x18,
+0xE0, 0xFD, 0x7F, 0x5F, 0x7E, 0xA0, 0x12, 0x44, 0x86, 0x90, 0x00, 0x1A, 0xE0, 0xFD, 0x7F, 0x60,
+0x7E, 0xA0, 0x12, 0x44, 0x86, 0x90, 0x00, 0x1B, 0xE0, 0xFD, 0x7F, 0x61, 0x7E, 0xA0, 0x12, 0x44,
+0x86, 0x90, 0x02, 0xC7, 0xE0, 0xFD, 0x7C, 0x00, 0x90, 0x00, 0x0D, 0xE0, 0xFE, 0xA3, 0xE0, 0xFF,
+0x12, 0x06, 0xDF, 0xEE, 0xFD, 0x7F, 0x62, 0x7E, 0xA0, 0x12, 0x44, 0x86, 0x90, 0x02, 0xC7, 0xE0,
+0xFD, 0x7C, 0x00, 0x90, 0x00, 0x0D, 0xE0, 0xFE, 0xA3, 0xE0, 0xFF, 0x12, 0x06, 0xDF, 0xAD, 0x07,
+0x7F, 0x63, 0x7E, 0xA0, 0x12, 0x44, 0x86, 0x90, 0x02, 0xC7, 0xE0, 0xFD, 0x7C, 0x00, 0x90, 0x00,
+0x11, 0xE0, 0xFE, 0xA3, 0xE0, 0xFF, 0x12, 0x06, 0xDF, 0xEE, 0xFD, 0x7F, 0x64, 0x7E, 0xA0, 0x12,
+0x44, 0x86, 0x90, 0x02, 0xC7, 0xE0, 0xFD, 0x7C, 0x00, 0x90, 0x00, 0x11, 0xE0, 0xFE, 0xA3, 0xE0,
+0xFF, 0x12, 0x06, 0xDF, 0xAD, 0x07, 0x7F, 0x65, 0x7E, 0xA0, 0x12, 0x44, 0x86, 0x90, 0x02, 0xC7,
+0xE0, 0xFD, 0x7C, 0x00, 0x90, 0x00, 0x13, 0xE0, 0xFE, 0xA3, 0xE0, 0xFF, 0x12, 0x06, 0xDF, 0xEE,
+0xFD, 0x7F, 0x66, 0x7E, 0xA0, 0x12, 0x44, 0x86, 0x90, 0x02, 0xC7, 0xE0, 0xFD, 0x7C, 0x00, 0x90,
+0x00, 0x13, 0xE0, 0xFE, 0xA3, 0xE0, 0xFF, 0x12, 0x06, 0xDF, 0xAD, 0x07, 0x7F, 0x67, 0x7E, 0xA0,
+0x12, 0x44, 0x86, 0x90, 0x00, 0x0D, 0xE0, 0xFE, 0xA3, 0xE0, 0x78, 0x02, 0xCE, 0xC3, 0x13, 0xCE,
+0x13, 0xD8, 0xF9, 0xEE, 0xFD, 0x7F, 0x68, 0x7E, 0xA0, 0x12, 0x44, 0x86, 0x90, 0x00, 0x0D, 0xE0,
+0xFE, 0xA3, 0xE0, 0x78, 0x02, 0xCE, 0xC3, 0x13, 0xCE, 0x13, 0xD8, 0xF9, 0xFD, 0x7F, 0x69, 0x7E,
+0xA0, 0x02, 0x44, 0x86, 0x90, 0x02, 0xFC, 0xEF, 0xF0, 0xE4, 0xFE, 0x90, 0xD8, 0x55, 0x74, 0x80,
+0xF0, 0xE4, 0x90, 0xD8, 0x52, 0xF0, 0x90, 0xD8, 0x51, 0xF0, 0x90, 0xD8, 0x50, 0xF0, 0x78, 0x44,
+0xE6, 0x90, 0xD8, 0x52, 0xF0, 0x08, 0xE6, 0x90, 0xD8, 0x51, 0xF0, 0x08, 0xE6, 0x90, 0xD8, 0x50,
+0xF0, 0x90, 0x02, 0xFC, 0xE0, 0xFD, 0xEE, 0xC3, 0x9D, 0x50, 0x12, 0x12, 0x44, 0xAE, 0x74, 0x24,
+0x2E, 0xF8, 0xA6, 0x07, 0x90, 0xD8, 0x50, 0xE0, 0x04, 0xF0, 0x0E, 0x80, 0xE4, 0x78, 0x44, 0xE6,
+0xFB, 0x7A, 0x00, 0x70, 0x1B, 0x08, 0xE6, 0x64, 0x02, 0x70, 0x15, 0x08, 0xE6, 0x70, 0x11, 0xED,
+0xD3, 0x94, 0x02, 0x74, 0x80, 0x94, 0x80, 0x50, 0x03, 0x02, 0x2F, 0xF3, 0x7F, 0x02, 0x80, 0x4C,
+0xEB, 0x4A, 0x70, 0x18, 0x78, 0x45, 0xE6, 0x64, 0x02, 0x70, 0x11, 0x08, 0xE6, 0x64, 0x01, 0x70,
+0x0B, 0x90, 0x02, 0xDC, 0xE0, 0x70, 0x7C, 0x78, 0x24, 0x76, 0x04, 0x22, 0x78, 0x44, 0xE6, 0x64,
+0x06, 0x70, 0x17, 0x08, 0xE6, 0x64, 0x80, 0x70, 0x11, 0x08, 0xE6, 0x64, 0x29, 0x70, 0x0B, 0x90,
+0x02, 0xDC, 0xE0, 0x70, 0x5E, 0x78, 0x24, 0x76, 0x08, 0x22, 0x78, 0x44, 0xE6, 0x70, 0x10, 0x08,
+0xE6, 0x64, 0x02, 0x70, 0x0A, 0x08, 0xE6, 0x64, 0x02, 0x70, 0x04, 0xFF, 0x02, 0x33, 0x93, 0x78,
+0x45, 0xE6, 0xFF, 0x64, 0x22, 0x60, 0x2A, 0xEF, 0x64, 0x30, 0x70, 0x02, 0x80, 0x23, 0x78, 0x44,
+0xE6, 0x64, 0x06, 0x70, 0x06, 0x08, 0xE6, 0x70, 0x02, 0x80, 0x16, 0x78, 0x44, 0xE6, 0x64, 0x06,
+0x70, 0x08, 0x08, 0xE6, 0x64, 0x92, 0x70, 0x02, 0x80, 0x07, 0x78, 0x44, 0xE6, 0x64, 0x0F, 0x70,
+0x12, 0x90, 0x02, 0xFC, 0xE0, 0xFF, 0x7E, 0x00, 0x7D, 0x00, 0x7B, 0x00, 0x7A, 0x00, 0x79, 0x24,
+0x12, 0x08, 0xFE, 0x22, 0xE4, 0x93, 0xFF, 0x7B, 0xFF, 0x7D, 0x02, 0x90, 0x03, 0x0A, 0xEF, 0xF0,
+0xA3, 0xED, 0xF0, 0xA3, 0xEB, 0xF0, 0xA3, 0xEA, 0xF0, 0xA3, 0xE9, 0xF0, 0xE4, 0xA3, 0xF0, 0x7D,
+0x40, 0x7F, 0x1C, 0x7E, 0xC2, 0x12, 0x44, 0x86, 0x90, 0x03, 0x0B, 0xE0, 0x64, 0x02, 0x70, 0x4F,
+0x7D, 0x0C, 0x7F, 0x18, 0x7E, 0xC2, 0x12, 0x44, 0x86, 0x7D, 0x04, 0x7F, 0x19, 0x7E, 0xC2, 0x12,
+0x44, 0x86, 0x90, 0x03, 0x0A, 0xE0, 0xFD, 0x7F, 0x34, 0x7E, 0xC2, 0x12, 0x44, 0x86, 0x90, 0x03,
+0x0C, 0xE0, 0xFB, 0xA3, 0xE0, 0xFA, 0xA3, 0xE0, 0xF9, 0x12, 0x06, 0x65, 0xFD, 0x7F, 0x34, 0x7E,
+0xC2, 0x12, 0x44, 0x86, 0x90, 0x03, 0x0C, 0xE0, 0xFB, 0xA3, 0xE0, 0xFA, 0xA3, 0xE0, 0xF9, 0x90,
+0x00, 0x01, 0x12, 0x06, 0x7E, 0xFD, 0x7F, 0x34, 0x7E, 0xC2, 0x12, 0x44, 0x86, 0x80, 0x72, 0x7D,
+0x0E, 0x7F, 0x18, 0x7E, 0xC2, 0x12, 0x44, 0x86, 0x90, 0x03, 0x0B, 0xE0, 0x24, 0x06, 0xFD, 0x7F,
+0x19, 0x7E, 0xC2, 0x12, 0x44, 0x86, 0x90, 0x03, 0x0A, 0xE0, 0xFD, 0x7F, 0x34, 0x7E, 0xC2, 0x12,
+0x44, 0x86, 0x90, 0x03, 0x0B, 0xE0, 0xFD, 0x7F, 0x34, 0x7E, 0xC2, 0x12, 0x44, 0x86, 0xE4, 0xFD,
+0x7F, 0x34, 0x7E, 0xC2, 0x12, 0x44, 0x86, 0xE4, 0x90, 0x03, 0x0F, 0xF0, 0x90, 0x03, 0x0B, 0xE0,
+0xFF, 0x90, 0x03, 0x0F, 0xE0, 0xC3, 0x9F, 0x50, 0x28, 0x90, 0x03, 0x0C, 0xE0, 0xFB, 0xA3, 0xE0,
+0xFA, 0xA3, 0xE0, 0xF9, 0x12, 0x06, 0x65, 0xFD, 0x7F, 0x34, 0x7E, 0xC2, 0x12, 0x44, 0x86, 0x90,
+0x03, 0x0D, 0xE4, 0x75, 0xF0, 0x01, 0x12, 0x07, 0x34, 0x90, 0x03, 0x0F, 0xE0, 0x04, 0xF0, 0x80,
+0xCB, 0x7F, 0x01, 0x7E, 0x00, 0x12, 0x43, 0x54, 0xE4, 0xFD, 0x7F, 0x18, 0x7E, 0xC2, 0x02, 0x44,
+0x86, 0x7F, 0xDB, 0x7E, 0xD1, 0x12, 0x44, 0x8D, 0xEF, 0x90, 0x02, 0xE0, 0xF0, 0xA3, 0xE4, 0xF0,
+0x7F, 0xDC, 0x7E, 0xD1, 0x12, 0x44, 0x8D, 0x90, 0x02, 0xE1, 0xE0, 0x2F, 0xF0, 0x90, 0x02, 0xE0,
+0xE0, 0x34, 0x00, 0xF0, 0x7F, 0xE7, 0x7E, 0xD1, 0x12, 0x44, 0x8D, 0xEF, 0x90, 0x02, 0xE2, 0xF0,
+0xA3, 0xE4, 0xF0, 0x7F, 0xE8, 0x7E, 0xD1, 0x12, 0x44, 0x8D, 0x90, 0x02, 0xE3, 0xE0, 0x2F, 0xF0,
+0x90, 0x02, 0xE2, 0xE0, 0x34, 0x00, 0xF0, 0x7F, 0xDD, 0x7E, 0xD1, 0x12, 0x44, 0x8D, 0xEF, 0x90,
+0x02, 0xE4, 0xF0, 0xA3, 0xE4, 0xF0, 0x7F, 0xDE, 0x7E, 0xD1, 0x12, 0x44, 0x8D, 0x90, 0x02, 0xE5,
+0xE0, 0x2F, 0xF0, 0x90, 0x02, 0xE4, 0xE0, 0x34, 0x00, 0xF0, 0x7F, 0xE9, 0x7E, 0xD1, 0x12, 0x44,
+0x8D, 0xEF, 0x90, 0x02, 0xE6, 0xF0, 0xA3, 0xE4, 0xF0, 0x7F, 0xEA, 0x7E, 0xD1, 0x12, 0x44, 0x8D,
+0x90, 0x02, 0xE7, 0xE0, 0x2F, 0xF0, 0x90, 0x02, 0xE6, 0xE0, 0x34, 0x00, 0xF0, 0x90, 0x02, 0xE0,
+0xE0, 0xFE, 0xA3, 0xE0, 0xFF, 0x90, 0x00, 0x0B, 0xE0, 0xB5, 0x06, 0x41, 0xA3, 0xE0, 0xB5, 0x07,
+0x3C, 0x90, 0x02, 0xE2, 0xE0, 0xFE, 0xA3, 0xE0, 0xFF, 0x90, 0x00, 0x0D, 0xE0, 0xB5, 0x06, 0x2D,
+0xA3, 0xE0, 0xB5, 0x07, 0x28, 0x90, 0x02, 0xE4, 0xE0, 0xFE, 0xA3, 0xE0, 0xFF, 0x90, 0x00, 0x15,
+0xE0, 0xB5, 0x06, 0x19, 0xA3, 0xE0, 0xB5, 0x07, 0x14, 0x90, 0x02, 0xE6, 0xE0, 0xFE, 0xA3, 0xE0,
+0xFF, 0x90, 0x00, 0x17, 0xE0, 0x6E, 0x70, 0x03, 0xA3, 0xE0, 0x6F, 0x60, 0x07, 0x90, 0x02, 0xC0,
+0x74, 0x01, 0xF0, 0x22, 0xE4, 0x90, 0x02, 0xC0, 0xF0, 0x22, 0x90, 0x02, 0xE4, 0x74, 0x12, 0xF0,
+0x7F, 0x00, 0x12, 0x3B, 0xCD, 0x90, 0x00, 0x00, 0xE0, 0x90, 0x02, 0xE4, 0xF0, 0x7F, 0x01, 0x12,
+0x3B, 0xCD, 0x90, 0x00, 0x01, 0xE0, 0x44, 0x80, 0x90, 0x02, 0xE4, 0xF0, 0x7F, 0x02, 0x12, 0x3B,
+0xCD, 0x90, 0x00, 0x02, 0xE0, 0x90, 0x02, 0xE4, 0xF0, 0x7F, 0x03, 0x12, 0x3B, 0xCD, 0x90, 0x02,
+0xE4, 0x74, 0x01, 0xF0, 0x7F, 0x04, 0x12, 0x3B, 0xCD, 0xE4, 0x90, 0x02, 0xE4, 0xF0, 0x7F, 0x05,
+0xFE, 0xFD, 0xFC, 0x12, 0x3B, 0xD3, 0xE4, 0x90, 0x02, 0xE4, 0xF0, 0x7F, 0x06, 0xFE, 0xFD, 0xFC,
+0x12, 0x3B, 0xD3, 0x90, 0x02, 0xE4, 0x74, 0x01, 0xF0, 0x7F, 0x07, 0x12, 0x3B, 0xCD, 0x90, 0x02,
+0xE4, 0x74, 0x02, 0xF0, 0x7F, 0x08, 0x12, 0x3B, 0xCD, 0xE4, 0x90, 0x02, 0xE4, 0xF0, 0x7F, 0x09,
+0xFE, 0xFD, 0xFC, 0x12, 0x3B, 0xD3, 0xE4, 0x90, 0x02, 0xE4, 0xF0, 0x7F, 0x0A, 0xFE, 0xFD, 0xFC,
+0x12, 0x3B, 0xD3, 0xE4, 0x90, 0x02, 0xE4, 0xF0, 0x7F, 0x0B, 0xFE, 0xFD, 0xFC, 0x12, 0x3B, 0xD3,
+0xE4, 0x90, 0x02, 0xE4, 0xF0, 0x7F, 0x0D, 0xFE, 0xFD, 0xFC, 0x12, 0x3B, 0xD3, 0x90, 0x02, 0xE4,
+0x74, 0x04, 0xF0, 0x7F, 0x0E, 0x12, 0x3B, 0xCD, 0xE4, 0x90, 0x02, 0xE4, 0xF0, 0x7F, 0x0F, 0xFE,
+0xFD, 0xFC, 0x12, 0x3B, 0xD3, 0xE4, 0x90, 0x02, 0xE4, 0xF0, 0x7F, 0x20, 0xFE, 0xFD, 0xFC, 0x12,
+0x3B, 0xD3, 0xE4, 0x90, 0x02, 0xE4, 0xF0, 0x7F, 0x21, 0xFE, 0xFD, 0xFC, 0x12, 0x3B, 0xD3, 0x90,
+0x02, 0xE4, 0x74, 0x01, 0xF0, 0x7F, 0x00, 0x7E, 0x02, 0x7D, 0x00, 0x7C, 0x00, 0x02, 0x3B, 0xD3,
+0x0A, 0x20, 0x6C, 0x61, 0x6E, 0x65, 0x63, 0x6E, 0x74, 0x58, 0x58, 0x20, 0x3D, 0x20, 0x25, 0x62,
+0x64, 0x0A, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00,
+0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00,
+0x00, 0x00, 0x03, 0x00, 0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x02, 0x00, 0x0B, 0x00,
+0x00, 0x00, 0x0B, 0x00, 0x02, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x04, 0x00, 0x1F, 0x00,
+0x00, 0x00, 0x1F, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x74, 0x24, 0x2F, 0xF8, 0xE6, 0x54, 0x0F, 0xFF, 0x78, 0x37, 0xF6, 0x7E, 0x00,
+0x64, 0x01, 0x60, 0x07, 0x78, 0x3A, 0xE6, 0x64, 0x01, 0x60, 0x13, 0xEF, 0x64, 0x07, 0x4E, 0x60,
+0x10, 0x78, 0x3A, 0xE6, 0xFF, 0x64, 0x02, 0x60, 0x05, 0xEF, 0x64, 0x03, 0x70, 0x03, 0x02, 0x42,
+0x98, 0x78, 0x37, 0xE6, 0x64, 0x07, 0x60, 0x03, 0x02, 0x34, 0x5D, 0x78, 0x3A, 0xE6, 0xFF, 0x64,
+0x02, 0x60, 0x08, 0xEF, 0x64, 0x03, 0x60, 0x03, 0x02, 0x34, 0x5D, 0x90, 0xD2, 0x12, 0x74, 0x01,
+0xF0, 0x90, 0xD2, 0x14, 0xF0, 0x78, 0x23, 0xE6, 0x70, 0x1A, 0x76, 0x01, 0x90, 0xB8, 0x3A, 0x74,
+0x40, 0xF0, 0x90, 0xB8, 0x3B, 0x74, 0x50, 0xF0, 0x90, 0xB8, 0x3A, 0x74, 0x80, 0xF0, 0x90, 0xB8,
+0x3B, 0x74, 0x52, 0xF0, 0x12, 0x42, 0x98, 0x90, 0xDB, 0xC6, 0xE0, 0x90, 0xD8, 0x5A, 0xF0, 0x90,
+0xDB, 0xC7, 0xE0, 0x90, 0xD8, 0x5B, 0xF0, 0x90, 0xDB, 0xC9, 0xE0, 0x90, 0xD8, 0x5C, 0xF0, 0x90,
+0xDB, 0xCA, 0xE0, 0x90, 0xD8, 0x5D, 0xF0, 0x90, 0xDB, 0xCB, 0xE0, 0x90, 0xD8, 0x5E, 0xF0, 0x90,
+0xDB, 0xCD, 0xE0, 0x90, 0xD8, 0x5F, 0xF0, 0x90, 0x02, 0xBF, 0xE0, 0x44, 0x01, 0x90, 0xD8, 0x00,
+0xF0, 0x90, 0x90, 0x05, 0x74, 0xEF, 0xF0, 0x74, 0xFF, 0xF0, 0x90, 0x90, 0x07, 0x74, 0xF7, 0xF0,
+0x90, 0x90, 0x0D, 0x74, 0xDF, 0xF0, 0x74, 0xFF, 0xF0, 0x90, 0x90, 0x07, 0xF0, 0x22, 0x7F, 0x01,
+0x7E, 0x00, 0x12, 0x41, 0x1F, 0x90, 0x02, 0x77, 0xE0, 0x64, 0x0F, 0x60, 0x03, 0x02, 0x35, 0x15,
+0x90, 0x02, 0x44, 0xE0, 0x20, 0xE7, 0x03, 0x02, 0x35, 0x15, 0x7F, 0x64, 0x7E, 0x00, 0x12, 0x44,
+0x72, 0x90, 0x02, 0x43, 0xE0, 0x54, 0x1F, 0x24, 0xFE, 0x60, 0x27, 0x14, 0x60, 0x31, 0x14, 0x60,
+0x3B, 0x24, 0xF4, 0x60, 0x45, 0x14, 0x60, 0x60, 0x24, 0x10, 0x70, 0x79, 0x90, 0x02, 0x8C, 0x74,
+0x01, 0xF0, 0x90, 0x02, 0x3B, 0x74, 0x16, 0xF0, 0x12, 0x38, 0x11, 0x90, 0x02, 0x68, 0x74, 0x01,
+0xF0, 0x22, 0x90, 0x02, 0x8C, 0x74, 0x02, 0xF0, 0x90, 0x02, 0x3B, 0x74, 0x0E, 0x80, 0x18, 0x90,
+0x02, 0x8C, 0x74, 0x03, 0xF0, 0x90, 0x02, 0x3B, 0x74, 0x0A, 0x80, 0x0B, 0x90, 0x02, 0x8C, 0x74,
+0x04, 0xF0, 0x90, 0x02, 0x3B, 0x74, 0x06, 0xF0, 0x80, 0x1B, 0x78, 0x0E, 0xE6, 0x90, 0x02, 0x8C,
+0x70, 0x0B, 0x74, 0x05, 0xF0, 0x90, 0x02, 0x3B, 0x74, 0x0A, 0xF0, 0x80, 0x08, 0x74, 0x0B, 0xF0,
+0x90, 0x02, 0x3B, 0x14, 0xF0, 0x02, 0x38, 0x11, 0x90, 0x02, 0x8C, 0x74, 0x06, 0xF0, 0x90, 0x02,
+0x3B, 0xF0, 0x12, 0x38, 0x11, 0x90, 0x02, 0x68, 0x74, 0x11, 0xF0, 0x78, 0x0D, 0x76, 0x01, 0x90,
+0xA0, 0x30, 0x74, 0x25, 0xF0, 0x22, 0xC0, 0xE0, 0xC0, 0xF0, 0xC0, 0x83, 0xC0, 0x82, 0xC0, 0xD0,
+0x75, 0xD0, 0x00, 0xC0, 0x00, 0xC0, 0x01, 0xC0, 0x02, 0xC0, 0x03, 0xC0, 0x04, 0xC0, 0x05, 0xC0,
+0x06, 0xC0, 0x07, 0x90, 0x02, 0x6F, 0xE4, 0xF0, 0x90, 0xA0, 0x15, 0xE0, 0x30, 0xE2, 0x1F, 0x90,
+0xA0, 0xA0, 0x74, 0x04, 0xF0, 0xE4, 0xF0, 0x90, 0xC1, 0xB6, 0xE0, 0x78, 0x0F, 0xF6, 0xFF, 0x74,
+0xEF, 0xF0, 0xEF, 0x64, 0x20, 0x60, 0x07, 0xC2, 0xAF, 0x12, 0x2C, 0xE0, 0xD2, 0xAF, 0x90, 0x03,
+0x11, 0xE0, 0x60, 0x2C, 0x90, 0xA0, 0x16, 0xE0, 0x54, 0x80, 0x64, 0x80, 0x70, 0x22, 0x90, 0xA0,
+0xA1, 0x74, 0xFF, 0xF0, 0x74, 0x7F, 0xF0, 0x90, 0x02, 0x68, 0x74, 0xFF, 0xF0, 0x75, 0x8C, 0xFD,
+0x75, 0x8A, 0xA8, 0xD2, 0x8C, 0x12, 0x26, 0x9C, 0xC2, 0x8C, 0x75, 0x8C, 0xFD, 0x75, 0x8A, 0xA8,
+0x90, 0xA0, 0x15, 0xE0, 0x30, 0xE0, 0x0D, 0x12, 0x43, 0xC8, 0x90, 0xA0, 0xA0, 0xE0, 0x44, 0x01,
+0xF0, 0x54, 0xFE, 0xF0, 0xD0, 0x07, 0xD0, 0x06, 0xD0, 0x05, 0xD0, 0x04, 0xD0, 0x03, 0xD0, 0x02,
+0xD0, 0x01, 0xD0, 0x00, 0xD0, 0xD0, 0xD0, 0x82, 0xD0, 0x83, 0xD0, 0xF0, 0xD0, 0xE0, 0x32, 0x7D,
+0x40, 0x7F, 0x44, 0x7E, 0xA0, 0x12, 0x44, 0x86, 0x90, 0x02, 0xC7, 0xE0, 0x64, 0x01, 0x70, 0x04,
+0x7D, 0x30, 0x80, 0x02, 0x7D, 0x31, 0x7F, 0x59, 0x7E, 0xA0, 0x12, 0x44, 0x86, 0x90, 0x00, 0x06,
+0xE0, 0x7F, 0x58, 0x7E, 0xA0, 0x70, 0x08, 0x12, 0x44, 0x8D, 0xEF, 0x44, 0x08, 0x80, 0x06, 0x12,
+0x44, 0x8D, 0xEF, 0x54, 0xF7, 0xFD, 0x7F, 0x58, 0x7E, 0xA0, 0x12, 0x44, 0x86, 0x90, 0x00, 0x04,
+0xE0, 0x7F, 0x58, 0x7E, 0xA0, 0x70, 0x08, 0x12, 0x44, 0x8D, 0xEF, 0x54, 0xFB, 0x80, 0x06, 0x12,
+0x44, 0x8D, 0xEF, 0x44, 0x04, 0xFD, 0x7F, 0x58, 0x7E, 0xA0, 0x12, 0x44, 0x86, 0x90, 0x00, 0x05,
+0xE0, 0x7F, 0x58, 0x7E, 0xA0, 0x70, 0x08, 0x12, 0x44, 0x8D, 0xEF, 0x54, 0xBF, 0x80, 0x06, 0x12,
+0x44, 0x8D, 0xEF, 0x44, 0x40, 0xFD, 0x7F, 0x58, 0x7E, 0xA0, 0x12, 0x44, 0x86, 0x7D, 0x0F, 0x7F,
+0x3F, 0x7E, 0xA0, 0x12, 0x44, 0x86, 0x7D, 0xFF, 0x7F, 0x40, 0x7E, 0xA0, 0x12, 0x44, 0x86, 0x7D,
+0xFF, 0x7F, 0x41, 0x7E, 0xA0, 0x12, 0x44, 0x86, 0x7D, 0xFF, 0x7F, 0x42, 0x7E, 0xA0, 0x02, 0x44,
+0x86, 0xE4, 0x90, 0x03, 0x15, 0xF0, 0xA3, 0xF0, 0xF0, 0x90, 0x03, 0x16, 0xE0, 0xFF, 0xC3, 0x94,
+0x2C, 0x74, 0x80, 0x94, 0x80, 0x50, 0x65, 0xEF, 0x90, 0x2C, 0xB4, 0x93, 0x90, 0x03, 0x15, 0xF0,
+0x64, 0x02, 0x70, 0x23, 0xA3, 0xE0, 0x75, 0xF0, 0x03, 0xA4, 0x24, 0x30, 0xF5, 0x82, 0xE4, 0x34,
+0x2C, 0xF5, 0x83, 0xE4, 0x93, 0xFB, 0x74, 0x01, 0x93, 0xFA, 0x74, 0x02, 0x93, 0xF9, 0x90, 0x03,
+0x15, 0xE0, 0xFD, 0x7F, 0x15, 0x80, 0x2A, 0x90, 0x03, 0x15, 0xE0, 0xFD, 0xD3, 0x94, 0x02, 0x74,
+0x80, 0x94, 0x80, 0x40, 0x1F, 0xA3, 0xE0, 0x75, 0xF0, 0x03, 0xA4, 0x24, 0x30, 0xF5, 0x82, 0xE4,
+0x34, 0x2C, 0xF5, 0x83, 0xE4, 0x93, 0xFB, 0x74, 0x01, 0x93, 0xFA, 0x74, 0x02, 0x93, 0xF9, 0x7F,
+0x29, 0x12, 0x2F, 0xFB, 0x90, 0x03, 0x16, 0xE0, 0x04, 0xF0, 0x80, 0x8D, 0x90, 0x2B, 0xC9, 0x7A,
+0x2B, 0x79, 0xCA, 0x12, 0x2F, 0xF4, 0x7F, 0x96, 0x7E, 0x00, 0x12, 0x43, 0x54, 0x90, 0x2B, 0xCF,
+0x7A, 0x2B, 0x79, 0xD0, 0x12, 0x2F, 0xF4, 0x7F, 0xC8, 0x7E, 0x00, 0x02, 0x43, 0x54, 0x78, 0x7F,
+0xE4, 0xF6, 0xD8, 0xFD, 0x75, 0x81, 0x5D, 0x02, 0x37, 0x45, 0x02, 0x44, 0xA2, 0xE4, 0x93, 0xA3,
+0xF8, 0xE4, 0x93, 0xA3, 0x40, 0x03, 0xF6, 0x80, 0x01, 0xF2, 0x08, 0xDF, 0xF4, 0x80, 0x29, 0xE4,
+0x93, 0xA3, 0xF8, 0x54, 0x07, 0x24, 0x0C, 0xC8, 0xC3, 0x33, 0xC4, 0x54, 0x0F, 0x44, 0x20, 0xC8,
+0x83, 0x40, 0x04, 0xF4, 0x56, 0x80, 0x01, 0x46, 0xF6, 0xDF, 0xE4, 0x80, 0x0B, 0x01, 0x02, 0x04,
+0x08, 0x10, 0x20, 0x40, 0x80, 0x90, 0x1D, 0xE8, 0xE4, 0x7E, 0x01, 0x93, 0x60, 0xBC, 0xA3, 0xFF,
+0x54, 0x3F, 0x30, 0xE5, 0x09, 0x54, 0x1F, 0xFE, 0xE4, 0x93, 0xA3, 0x60, 0x01, 0x0E, 0xCF, 0x54,
+0xC0, 0x25, 0xE0, 0x60, 0xA8, 0x40, 0xB8, 0xE4, 0x93, 0xA3, 0xFA, 0xE4, 0x93, 0xA3, 0xF8, 0xE4,
+0x93, 0xA3, 0xC8, 0xC5, 0x82, 0xC8, 0xCA, 0xC5, 0x83, 0xCA, 0xF0, 0xA3, 0xC8, 0xC5, 0x82, 0xC8,
+0xCA, 0xC5, 0x83, 0xCA, 0xDF, 0xE9, 0xDE, 0xE7, 0x80, 0xBE, 0x90, 0x02, 0xE0, 0xEB, 0xF0, 0xA3,
+0xEA, 0xF0, 0xA3, 0xE9, 0xF0, 0xE4, 0xFF, 0xFE, 0xEE, 0xC3, 0x94, 0x7F, 0x74, 0x80, 0x94, 0x80,
+0x50, 0x18, 0x90, 0x02, 0xE0, 0xE0, 0xFB, 0xA3, 0xE0, 0xFA, 0xA3, 0xE0, 0xF9, 0x8E, 0x82, 0x75,
+0x83, 0x00, 0x12, 0x06, 0x7E, 0x2F, 0xFF, 0x0E, 0x80, 0xDE, 0xC3, 0xE4, 0x9F, 0xFF, 0x90, 0x02,
+0xE0, 0xE0, 0xFB, 0xA3, 0xE0, 0xFA, 0xA3, 0xE0, 0xF9, 0x90, 0x00, 0x7F, 0xEF, 0x12, 0x06, 0xBD,
+0xE4, 0xFF, 0xFE, 0xEE, 0xC3, 0x94, 0x7F, 0x74, 0x80, 0x94, 0x80, 0x50, 0x1E, 0xEE, 0x24, 0x80,
+0xFD, 0xE4, 0x33, 0xFC, 0x90, 0x02, 0xE0, 0xE0, 0xFB, 0xA3, 0xE0, 0xFA, 0xA3, 0xE0, 0xF9, 0x8D,
+0x82, 0x8C, 0x83, 0x12, 0x06, 0x7E, 0x2F, 0xFF, 0x0E, 0x80, 0xD8, 0xC3, 0xE4, 0x9F, 0xFF, 0x90,
+0x02, 0xE0, 0xE0, 0xFB, 0xA3, 0xE0, 0xFA, 0xA3, 0xE0, 0xF9, 0x90, 0x00, 0xFF, 0xEF, 0x02, 0x06,
+0xBD, 0xE4, 0x90, 0x02, 0x6D, 0xF0, 0x90, 0x02, 0x3B, 0xE0, 0xFF, 0x90, 0x02, 0x6D, 0xE0, 0xC3,
+0x9F, 0x50, 0x55, 0xE0, 0xFF, 0x90, 0x02, 0x8C, 0xE0, 0x75, 0xF0, 0x1E, 0xA4, 0x24, 0x0D, 0xF5,
+0x82, 0xE5, 0xF0, 0x34, 0x20, 0xF5, 0x83, 0xE5, 0x82, 0x2F, 0xF5, 0x82, 0xE4, 0x35, 0x83, 0xF5,
+0x83, 0xE4, 0x93, 0x90, 0x02, 0x3C, 0xF0, 0xEF, 0x64, 0x01, 0x70, 0x1C, 0x90, 0x02, 0x8D, 0xE0,
+0x54, 0x07, 0x25, 0xE0, 0xFF, 0x90, 0x02, 0x73, 0xF0, 0x90, 0x02, 0x3C, 0xE0, 0x54, 0xF1, 0xF0,
+0x4F, 0xF0, 0x90, 0x02, 0x8D, 0xE0, 0x04, 0xF0, 0x90, 0x02, 0x3C, 0xE0, 0x90, 0xC1, 0x9A, 0xF0,
+0x90, 0x02, 0x6D, 0xE0, 0x04, 0xF0, 0x80, 0x9E, 0x90, 0xC1, 0x98, 0x74, 0x08, 0xF0, 0xE4, 0xF0,
+0x90, 0xC1, 0x99, 0x74, 0x09, 0xF0, 0x90, 0x02, 0x76, 0x74, 0x01, 0xF0, 0x22, 0x90, 0x02, 0xE0,
+0xEF, 0xF0, 0x64, 0x01, 0x70, 0x20, 0x7F, 0x8D, 0x7E, 0xB0, 0x12, 0x44, 0x8D, 0xEF, 0x44, 0x07,
+0xFD, 0x7F, 0x8D, 0x7E, 0xB0, 0x12, 0x44, 0x86, 0x7F, 0x46, 0x7E, 0xA0, 0x12, 0x44, 0x8D, 0xEF,
+0x54, 0xF7, 0x44, 0x04, 0x80, 0x26, 0x90, 0x02, 0xE0, 0xE0, 0x64, 0x02, 0x70, 0x25, 0x7F, 0x8D,
+0x7E, 0xB0, 0x12, 0x44, 0x8D, 0xEF, 0x44, 0x07, 0xFD, 0x7F, 0x8D, 0x7E, 0xB0, 0x12, 0x44, 0x86,
+0x7F, 0x46, 0x7E, 0xA0, 0x12, 0x44, 0x8D, 0xEF, 0x54, 0xFB, 0x44, 0x08, 0xFD, 0x7F, 0x46, 0x7E,
+0xA0, 0x80, 0x17, 0x90, 0x02, 0xE0, 0xE0, 0x64, 0x03, 0x70, 0x12, 0x7F, 0x8D, 0x7E, 0xB0, 0x12,
+0x44, 0x8D, 0xEF, 0x54, 0xF0, 0xFD, 0x7F, 0x8D, 0x7E, 0xB0, 0x12, 0x44, 0x86, 0x22, 0x7F, 0x1D,
+0x7E, 0xA0, 0x12, 0x44, 0x8D, 0xEF, 0x30, 0xE0, 0x1E, 0x7F, 0x0A, 0x7E, 0x00, 0x12, 0x41, 0x1F,
+0x7F, 0x1D, 0x7E, 0xA0, 0x12, 0x44, 0x8D, 0xEF, 0x30, 0xE0, 0x50, 0x90, 0x02, 0xC7, 0xE0, 0x64,
+0x02, 0x60, 0x48, 0x74, 0x02, 0x80, 0x1C, 0x7F, 0x0A, 0x7E, 0x00, 0x12, 0x41, 0x1F, 0x7F, 0x1D,
+0x7E, 0xA0, 0x12, 0x44, 0x8D, 0xEF, 0x20, 0xE0, 0x32, 0x90, 0x02, 0xC7, 0xE0, 0x64, 0x01, 0x60,
+0x2A, 0x74, 0x01, 0xF0, 0x12, 0x44, 0x7D, 0x90, 0x02, 0x8C, 0x74, 0x09, 0xF0, 0x90, 0x02, 0x3B,
+0x04, 0xF0, 0x12, 0x38, 0x11, 0x7F, 0x58, 0x7E, 0x02, 0x12, 0x41, 0x1F, 0x90, 0x02, 0x8C, 0x74,
+0x07, 0xF0, 0x90, 0x02, 0x3B, 0x74, 0x0A, 0xF0, 0x12, 0x38, 0x11, 0x22, 0x7D, 0xFF, 0x7F, 0x48,
+0x7E, 0xB0, 0x12, 0x44, 0x86, 0x7D, 0xFF, 0x7F, 0x49, 0x7E, 0xB0, 0x12, 0x44, 0x86, 0x7D, 0xF0,
+0x7F, 0x4A, 0x7E, 0xB0, 0x12, 0x44, 0x86, 0x7D, 0xFF, 0x7F, 0x59, 0x7E, 0xB0, 0x12, 0x44, 0x86,
+0x7D, 0xFF, 0x7F, 0x5A, 0x7E, 0xB0, 0x12, 0x44, 0x86, 0x7D, 0xF0, 0x7F, 0x5B, 0x7E, 0xB0, 0x12,
+0x44, 0x86, 0x7D, 0xFF, 0x7F, 0x6A, 0x7E, 0xB0, 0x12, 0x44, 0x86, 0x7D, 0xFF, 0x7F, 0x6B, 0x7E,
+0xB0, 0x12, 0x44, 0x86, 0x7D, 0xF0, 0x7F, 0x6C, 0x7E, 0xB0, 0x12, 0x44, 0x86, 0x7D, 0xFF, 0x7F,
+0x7B, 0x7E, 0xB0, 0x12, 0x44, 0x86, 0x7D, 0xFF, 0x7F, 0x7C, 0x7E, 0xB0, 0x12, 0x44, 0x86, 0x7D,
+0xF0, 0x7F, 0x7D, 0x7E, 0xB0, 0x02, 0x44, 0x86, 0xE6, 0x54, 0x0F, 0x78, 0x3A, 0xF6, 0x78, 0x3A,
+0xE6, 0xFF, 0xFB, 0x7A, 0x00, 0x64, 0x01, 0x70, 0x3B, 0x78, 0x4D, 0xE6, 0x6F, 0x60, 0x35, 0x90,
+0x02, 0xBF, 0xE0, 0x44, 0x11, 0x90, 0xD8, 0x00, 0xF0, 0xE4, 0xFD, 0x7F, 0x03, 0x12, 0x44, 0x67,
+0x0F, 0x12, 0x44, 0x67, 0x0F, 0x12, 0x44, 0x67, 0x0F, 0x12, 0x44, 0x67, 0x90, 0xD8, 0x19, 0x74,
+0x01, 0xF0, 0x90, 0xD8, 0x1F, 0xF0, 0x78, 0x3A, 0xE6, 0x78, 0x4D, 0xF6, 0x12, 0x42, 0x98, 0x90,
+0x02, 0xBE, 0xE0, 0x22, 0xEB, 0x64, 0x02, 0x4A, 0x60, 0x07, 0x78, 0x3A, 0xE6, 0x64, 0x03, 0x70,
+0x0E, 0x78, 0x3A, 0xE6, 0xFF, 0x78, 0x4D, 0x66, 0x60, 0x05, 0xA6, 0x07, 0x12, 0x42, 0x98, 0x22,
+0xE4, 0x90, 0x02, 0x6D, 0xF0, 0x90, 0x02, 0x3B, 0xE0, 0xFF, 0x90, 0x02, 0x6D, 0xE0, 0xFE, 0xC3,
+0x9F, 0x50, 0x40, 0x74, 0x83, 0x2E, 0xF5, 0x82, 0xE4, 0x34, 0x02, 0xF5, 0x83, 0xE0, 0x90, 0x02,
+0x3C, 0xF0, 0xEE, 0x64, 0x01, 0x70, 0x1C, 0x90, 0x02, 0x8D, 0xE0, 0x54, 0x07, 0x25, 0xE0, 0xFF,
+0x90, 0x02, 0x73, 0xF0, 0x90, 0x02, 0x3C, 0xE0, 0x54, 0xF1, 0xF0, 0x4F, 0xF0, 0x90, 0x02, 0x8D,
+0xE0, 0x04, 0xF0, 0x90, 0x02, 0x3C, 0xE0, 0x90, 0xC1, 0x9A, 0xF0, 0x90, 0x02, 0x6D, 0xE0, 0x04,
+0xF0, 0x80, 0xB2, 0x90, 0xC1, 0x98, 0x74, 0x08, 0xF0, 0xE4, 0xF0, 0x90, 0xC1, 0x99, 0x74, 0x09,
+0xF0, 0x90, 0x02, 0x76, 0x74, 0x01, 0xF0, 0x22, 0xE4, 0x90, 0x02, 0x63, 0xF0, 0x90, 0x02, 0x76,
+0xF0, 0x90, 0x02, 0x75, 0xF0, 0x90, 0x02, 0x6F, 0xF0, 0x90, 0x02, 0x72, 0xF0, 0x90, 0x02, 0x70,
+0xF0, 0x90, 0x03, 0x11, 0xF0, 0x90, 0x02, 0x65, 0x04, 0xF0, 0xE4, 0x90, 0x02, 0x8D, 0xF0, 0x90,
+0x02, 0x6D, 0xF0, 0x90, 0x02, 0x7A, 0xF0, 0x90, 0x02, 0x3B, 0x74, 0x06, 0xF0, 0xE4, 0x90, 0x02,
+0x8C, 0xF0, 0x78, 0x0D, 0x76, 0xFF, 0x90, 0x02, 0x7E, 0x04, 0xF0, 0xE4, 0x78, 0x0E, 0xF6, 0x90,
+0x02, 0x41, 0x74, 0xFF, 0xF0, 0xA3, 0xF0, 0xA3, 0xF0, 0xA3, 0xF0, 0xA3, 0xF0, 0xA3, 0xF0, 0xA3,
+0xF0, 0xA3, 0xF0, 0xA3, 0xF0, 0xA3, 0xF0, 0xA3, 0xF0, 0xA3, 0xF0, 0x22, 0xE4, 0x90, 0xC1, 0x8C,
+0xF0, 0x90, 0xC1, 0x8D, 0x74, 0x0A, 0xF0, 0x90, 0xC1, 0x93, 0x74, 0x08, 0xF0, 0x90, 0xC1, 0x95,
+0x74, 0xA0, 0xF0, 0x90, 0xC1, 0x96, 0x74, 0x03, 0xF0, 0x90, 0xC1, 0x97, 0x74, 0x40, 0xF0, 0x90,
+0xC1, 0x99, 0x74, 0x01, 0xF0, 0x90, 0xC1, 0xA8, 0x74, 0x80, 0xF0, 0xE4, 0x90, 0xC1, 0xA9, 0xF0,
+0x90, 0xC1, 0xAC, 0x74, 0x62, 0xF0, 0x90, 0xC1, 0x9F, 0x74, 0x68, 0xF0, 0x90, 0xC1, 0xA2, 0x74,
+0x34, 0xF0, 0x90, 0xC1, 0x31, 0x74, 0x12, 0xF0, 0x90, 0xC1, 0x32, 0x74, 0x3C, 0xF0, 0x90, 0xC1,
+0x33, 0x04, 0xF0, 0x90, 0xC1, 0x34, 0x04, 0xF0, 0x90, 0xC1, 0x35, 0x74, 0x42, 0xF0, 0x22, 0x90,
+0x02, 0x70, 0xE0, 0x70, 0x29, 0x90, 0x02, 0x77, 0xE0, 0x24, 0xF9, 0x60, 0x05, 0x14, 0x60, 0x0F,
+0x80, 0x4A, 0x90, 0x02, 0x8C, 0x74, 0x0D, 0xF0, 0x90, 0x02, 0x3B, 0x74, 0x02, 0x80, 0x0B, 0x90,
+0x02, 0x8C, 0x74, 0x0C, 0xF0, 0x90, 0x02, 0x3B, 0x74, 0x06, 0xF0, 0x02, 0x38, 0x11, 0x90, 0x02,
+0x77, 0xE0, 0x24, 0xF1, 0x60, 0x23, 0x24, 0x0E, 0x70, 0x22, 0x7F, 0x64, 0x7E, 0x00, 0x12, 0x44,
+0x72, 0xE4, 0x90, 0x02, 0x8C, 0xF0, 0x90, 0x02, 0x3B, 0x74, 0x06, 0xF0, 0x12, 0x3C, 0x85, 0x12,
+0x3A, 0x40, 0x90, 0x02, 0x68, 0x74, 0x02, 0xF0, 0x22, 0x12, 0x34, 0x5E, 0x22, 0x7E, 0x00, 0x7D,
+0x00, 0x7C, 0x00, 0x90, 0x02, 0xE0, 0x12, 0x08, 0xAC, 0x90, 0xD8, 0x55, 0x74, 0x80, 0xF0, 0x90,
+0x02, 0xE0, 0xE0, 0xFC, 0xA3, 0xE0, 0xFD, 0xA3, 0xE0, 0xFE, 0xA3, 0xE0, 0xFF, 0x78, 0x10, 0x12,
+0x08, 0x86, 0xEF, 0x54, 0x0F, 0xFF, 0x90, 0xD8, 0x52, 0xEF, 0xF0, 0x90, 0x02, 0xE0, 0xE0, 0xFC,
+0xA3, 0xE0, 0xFD, 0xA3, 0xE0, 0xFE, 0xA3, 0xE0, 0xFF, 0x78, 0x08, 0x12, 0x08, 0x86, 0x90, 0xD8,
+0x51, 0xEF, 0xF0, 0x90, 0x02, 0xE0, 0xA3, 0xA3, 0xA3, 0xE0, 0xFF, 0x90, 0xD8, 0x50, 0xEF, 0xF0,
+0x90, 0x02, 0xE4, 0xE0, 0x90, 0xD8, 0x53, 0xF0, 0x22, 0xE4, 0x90, 0xB0, 0x00, 0xF0, 0x90, 0xB0,
+0x8C, 0xE0, 0x44, 0x07, 0xF0, 0x90, 0xB0, 0x8E, 0x74, 0x07, 0xF0, 0x90, 0xB0, 0x8F, 0x74, 0x77,
+0xF0, 0x90, 0xD1, 0x10, 0x74, 0x20, 0xF0, 0x90, 0xD1, 0x14, 0x74, 0x19, 0xF0, 0x90, 0xD1, 0x15,
+0x74, 0xC2, 0xF0, 0x7D, 0x7F, 0x7F, 0x06, 0x7E, 0x90, 0x12, 0x44, 0x86, 0x7F, 0x01, 0x7E, 0x00,
+0x12, 0x43, 0x54, 0x7D, 0xFF, 0x7F, 0x06, 0x7E, 0x90, 0x12, 0x44, 0x86, 0x7D, 0xEE, 0x7F, 0x05,
+0x7E, 0x90, 0x12, 0x44, 0x86, 0x7F, 0x01, 0x7E, 0x00, 0x12, 0x43, 0x54, 0x7D, 0xFE, 0x7F, 0x05,
+0x7E, 0x90, 0x02, 0x44, 0x86, 0xE4, 0xFF, 0xEF, 0xC3, 0x94, 0x06, 0x74, 0x80, 0x94, 0x80, 0x50,
+0x0F, 0x74, 0x83, 0x2F, 0xF5, 0x82, 0xE4, 0x34, 0x02, 0xF5, 0x83, 0xE4, 0xF0, 0x0F, 0x80, 0xE7,
+0x90, 0x02, 0x83, 0x74, 0x42, 0xF0, 0xA3, 0x74, 0x10, 0xF0, 0x90, 0x02, 0x43, 0xE0, 0x90, 0x02,
+0x85, 0xF0, 0x54, 0x3F, 0x25, 0xE0, 0x25, 0xE0, 0xFF, 0x90, 0x02, 0x44, 0xE0, 0x54, 0x03, 0xFE,
+0x4F, 0x90, 0x02, 0x86, 0xF0, 0xEE, 0x25, 0xE0, 0x25, 0xE0, 0xFF, 0x90, 0x02, 0x43, 0xE0, 0x54,
+0xC0, 0xC4, 0x13, 0x13, 0x54, 0x03, 0x4F, 0x90, 0x02, 0x87, 0xF0, 0xA3, 0x74, 0x13, 0xF0, 0x22,
+0x7D, 0x01, 0x7F, 0x35, 0x7E, 0xA0, 0x12, 0x44, 0x86, 0x7D, 0x24, 0x7F, 0x36, 0x7E, 0xA0, 0x12,
+0x44, 0x86, 0x7D, 0x30, 0x7F, 0x37, 0x7E, 0xA0, 0x12, 0x44, 0x86, 0x7D, 0x12, 0x7F, 0x38, 0x7E,
+0xA0, 0x12, 0x44, 0x86, 0x7D, 0x43, 0x7F, 0x39, 0x7E, 0xA0, 0x12, 0x44, 0x86, 0x7D, 0x01, 0x7F,
+0x3A, 0x7E, 0xA0, 0x12, 0x44, 0x86, 0x7D, 0x24, 0x7F, 0x3B, 0x7E, 0xA0, 0x12, 0x44, 0x86, 0x7D,
+0x30, 0x7F, 0x3C, 0x7E, 0xA0, 0x12, 0x44, 0x86, 0x7D, 0x12, 0x7F, 0x3D, 0x7E, 0xA0, 0x12, 0x44,
+0x86, 0x7D, 0x43, 0x7F, 0x3E, 0x7E, 0xA0, 0x02, 0x44, 0x86, 0x90, 0x00, 0x1C, 0xE0, 0x64, 0x01,
+0x60, 0x24, 0x7F, 0x05, 0x7E, 0xD2, 0x12, 0x44, 0x8D, 0xEF, 0x44, 0x04, 0xFD, 0x7F, 0x05, 0x7E,
+0xD2, 0x12, 0x44, 0x86, 0x7F, 0x05, 0x7E, 0xD2, 0x12, 0x44, 0x8D, 0xEF, 0x54, 0xF7, 0xFD, 0x7F,
+0x05, 0x7E, 0xD2, 0x12, 0x44, 0x86, 0x90, 0x00, 0x1D, 0xE0, 0x64, 0x01, 0x60, 0x24, 0x7F, 0x05,
+0x7E, 0xD2, 0x12, 0x44, 0x8D, 0xEF, 0x44, 0x10, 0xFD, 0x7F, 0x05, 0x7E, 0xD2, 0x12, 0x44, 0x86,
+0x7F, 0x05, 0x7E, 0xD2, 0x12, 0x44, 0x8D, 0xEF, 0x54, 0xDF, 0xFD, 0x7F, 0x05, 0x7E, 0xD2, 0x12,
+0x44, 0x86, 0x22, 0x90, 0xB0, 0x2C, 0x74, 0x71, 0xF0, 0x90, 0xB8, 0x16, 0x74, 0x50, 0xF0, 0xE4,
+0x90, 0xB0, 0x21, 0xF0, 0x90, 0xB0, 0x22, 0x74, 0xC1, 0xF0, 0x90, 0xB0, 0x28, 0x74, 0x19, 0xF0,
+0x90, 0x90, 0x0F, 0x74, 0x7F, 0xF0, 0x74, 0xFF, 0xF0, 0x7F, 0x0F, 0x7E, 0x00, 0x12, 0x41, 0x1F,
+0x90, 0xB8, 0x14, 0x74, 0x40, 0xF0, 0x7F, 0x0F, 0x7E, 0x00, 0x12, 0x41, 0x1F, 0x74, 0xC0, 0xF0,
+0x7F, 0x0F, 0x7E, 0x00, 0x12, 0x41, 0x1F, 0x74, 0xE0, 0xF0, 0x7F, 0x0A, 0x7E, 0x00, 0x12, 0x41,
+0x1F, 0x74, 0xE4, 0xF0, 0x90, 0xB8, 0x21, 0x74, 0xE0, 0xF0, 0x22, 0x78, 0x0D, 0xE6, 0x64, 0x01,
+0x70, 0x46, 0xF6, 0x90, 0x02, 0x68, 0xE0, 0x64, 0x11, 0x70, 0x0C, 0x78, 0x0E, 0x76, 0x01, 0x74,
+0x06, 0xF0, 0xE4, 0x90, 0x02, 0x76, 0xF0, 0x90, 0x02, 0x68, 0xE0, 0x64, 0x06, 0x70, 0x29, 0x7F,
+0x58, 0x7E, 0x02, 0x12, 0x41, 0x1F, 0x90, 0x02, 0x8C, 0x74, 0x07, 0xF0, 0x90, 0x02, 0x3B, 0x74,
+0x0A, 0xF0, 0x90, 0x02, 0x68, 0x74, 0xFF, 0xF0, 0x12, 0x38, 0x11, 0xE4, 0x90, 0x02, 0x76, 0x12,
+0x43, 0x72, 0x90, 0x03, 0x11, 0x74, 0x01, 0xF0, 0x22, 0x12, 0x44, 0x94, 0x12, 0x3D, 0x93, 0x12,
+0x40, 0x46, 0x12, 0x42, 0xF9, 0x12, 0x43, 0xAC, 0x12, 0x3F, 0xD8, 0x12, 0x41, 0x3D, 0x90, 0x02,
+0xBF, 0xE0, 0x44, 0x11, 0x90, 0xD8, 0x00, 0xF0, 0x90, 0x32, 0xEF, 0x74, 0x01, 0x93, 0xFF, 0xC4,
+0x54, 0xF0, 0x2F, 0xFF, 0x90, 0xD8, 0x5A, 0xF0, 0x90, 0x32, 0xF1, 0x74, 0x01, 0x93, 0xFE, 0x90,
+0xD8, 0x5B, 0xF0, 0x90, 0xD8, 0x5C, 0xF0, 0x90, 0xD8, 0x5D, 0xEF, 0xF0, 0x90, 0xD8, 0x5E, 0xEE,
+0xF0, 0x90, 0xD8, 0x5F, 0xF0, 0x22, 0xC0, 0xE0, 0xC0, 0x83, 0xC0, 0x82, 0xC0, 0xD0, 0xC2, 0x8E,
+0x75, 0x8D, 0xB1, 0x75, 0x8B, 0xE0, 0x90, 0x02, 0xDA, 0xE0, 0x04, 0xF0, 0x70, 0x06, 0x90, 0x02,
+0xD9, 0xE0, 0x04, 0xF0, 0xD2, 0x8E, 0x90, 0x02, 0xD9, 0xE0, 0xB4, 0x01, 0x1D, 0xA3, 0xE0, 0xB4,
+0x90, 0x18, 0x90, 0xB8, 0xB0, 0xE0, 0x30, 0xE4, 0x0B, 0x90, 0x02, 0xD9, 0xE4, 0xF0, 0xA3, 0xF0,
+0xC2, 0x8E, 0x80, 0x06, 0x90, 0xB0, 0x0B, 0x74, 0xD5, 0xF0, 0xD0, 0xD0, 0xD0, 0x82, 0xD0, 0x83,
+0xD0, 0xE0, 0x32, 0x7F, 0x8A, 0x7E, 0xB0, 0x12, 0x44, 0x8D, 0xEF, 0x44, 0x70, 0xFD, 0x7F, 0x8A,
+0x7E, 0xB0, 0x12, 0x44, 0x86, 0x7F, 0x8A, 0x7E, 0xB0, 0x12, 0x44, 0x8D, 0xEF, 0x44, 0x07, 0xFD,
+0x7F, 0x8A, 0x7E, 0xB0, 0x12, 0x44, 0x86, 0x7F, 0x8B, 0x7E, 0xB0, 0x12, 0x44, 0x8D, 0xEF, 0x44,
+0x70, 0xFD, 0x7F, 0x8B, 0x7E, 0xB0, 0x12, 0x44, 0x86, 0x7F, 0x8B, 0x7E, 0xB0, 0x12, 0x44, 0x8D,
+0xEF, 0x44, 0x07, 0xFD, 0x7F, 0x8B, 0x7E, 0xB0, 0x02, 0x44, 0x86, 0xC0, 0xE0, 0xC0, 0xF0, 0xC0,
+0x83, 0xC0, 0x82, 0xC0, 0xD0, 0x75, 0xD0, 0x00, 0xC0, 0x00, 0xC0, 0x01, 0xC0, 0x02, 0xC0, 0x03,
+0xC0, 0x04, 0xC0, 0x05, 0xC0, 0x06, 0xC0, 0x07, 0x12, 0x44, 0x5B, 0xD2, 0x8C, 0x75, 0x8C, 0xFD,
+0x75, 0x8A, 0xA8, 0xD0, 0x07, 0xD0, 0x06, 0xD0, 0x05, 0xD0, 0x04, 0xD0, 0x03, 0xD0, 0x02, 0xD0,
+0x01, 0xD0, 0x00, 0xD0, 0xD0, 0xD0, 0x82, 0xD0, 0x83, 0xD0, 0xF0, 0xD0, 0xE0, 0x32, 0xE4, 0x90,
+0x02, 0xE0, 0x80, 0x1A, 0x7F, 0xB0, 0x7E, 0xB8, 0x12, 0x44, 0x8D, 0xEF, 0x20, 0xE4, 0x18, 0x90,
+0x02, 0xE0, 0xE0, 0xC3, 0x94, 0x03, 0x74, 0x80, 0x94, 0x80, 0x50, 0x0B, 0xE0, 0x04, 0xF0, 0x12,
+0x18, 0xA2, 0x12, 0x11, 0xDF, 0x80, 0xDD, 0x12, 0x3C, 0xE0, 0x12, 0x28, 0x0B, 0x12, 0x2D, 0xF1,
+0x12, 0x39, 0x6C, 0x12, 0x35, 0xBF, 0x12, 0x21, 0xCF, 0x12, 0x3D, 0x3A, 0x02, 0x41, 0x67, 0x90,
+0xB0, 0x0B, 0x74, 0xC4, 0xF0, 0x90, 0xB0, 0x10, 0xF0, 0x90, 0xB0, 0x08, 0x74, 0xB3, 0xF0, 0x12,
+0x44, 0x9B, 0x12, 0x3B, 0x0C, 0x90, 0xB0, 0x0B, 0x74, 0xE5, 0xF0, 0x90, 0xB0, 0x10, 0x74, 0xC5,
+0xF0, 0x12, 0x3A, 0xA8, 0x90, 0x90, 0x05, 0x74, 0xBF, 0xF0, 0x7F, 0x01, 0x7E, 0x00, 0x12, 0x41,
+0x1F, 0x90, 0x90, 0x05, 0x74, 0xFF, 0xF0, 0x22, 0x90, 0xA0, 0x32, 0x74, 0xE4, 0xF0, 0xE4, 0x90,
+0xD0, 0x00, 0xF0, 0x90, 0xD0, 0x02, 0x74, 0x10, 0xF0, 0x90, 0xD0, 0x08, 0x74, 0x0B, 0xF0, 0x90,
+0xD0, 0x09, 0x74, 0x66, 0xF0, 0x90, 0xD0, 0x0A, 0x74, 0x0E, 0xF0, 0x90, 0xD0, 0x0B, 0x74, 0x71,
+0xF0, 0xE4, 0x90, 0xA0, 0x30, 0xF0, 0x90, 0x90, 0x10, 0x74, 0xEF, 0xF0, 0x74, 0xFF, 0xF0, 0x22,
+0x90, 0xC1, 0xAF, 0x74, 0xC3, 0xF0, 0x90, 0xDA, 0xFC, 0x74, 0x0F, 0xF0, 0x90, 0xDA, 0xA3, 0x74,
+0xE1, 0xF0, 0x90, 0xDA, 0xA4, 0x74, 0x05, 0xF0, 0x90, 0xB0, 0x30, 0x74, 0x42, 0xF0, 0x90, 0x02,
+0xBF, 0xE0, 0x44, 0x11, 0x90, 0xD8, 0x00, 0xF0, 0x90, 0xB0, 0x12, 0x74, 0x3E, 0xF0, 0xE4, 0x78,
+0x43, 0xF6, 0x78, 0x23, 0xF6, 0x22, 0x90, 0xB8, 0x3A, 0x74, 0x80, 0xF0, 0x90, 0xB8, 0x3B, 0x74,
+0x52, 0xF0, 0xE4, 0x90, 0xB0, 0x1F, 0xF0, 0x90, 0xB0, 0x20, 0xF0, 0x90, 0x90, 0x0D, 0x74, 0xE1,
+0xF0, 0x90, 0x90, 0x03, 0x74, 0xFE, 0xF0, 0x74, 0x1F, 0xF0, 0x90, 0x90, 0x0F, 0x74, 0xBF, 0xF0,
+0x74, 0xFF, 0xF0, 0x90, 0x90, 0x0D, 0xF0, 0x90, 0x90, 0x03, 0xF0, 0x22, 0xEF, 0x7F, 0x8C, 0x7E,
+0xB0, 0x60, 0x0D, 0x12, 0x44, 0x8D, 0xEF, 0x54, 0x0F, 0xFD, 0x7F, 0x8C, 0x7E, 0xB0, 0x80, 0x1D,
+0x12, 0x44, 0x8D, 0xEF, 0x44, 0x70, 0xFD, 0x7F, 0x8C, 0x7E, 0xB0, 0x12, 0x44, 0x86, 0x7F, 0x46,
+0x7E, 0xA0, 0x12, 0x44, 0x8D, 0xEF, 0x44, 0x80, 0xFD, 0x7F, 0x46, 0x7E, 0xA0, 0x12, 0x44, 0x86,
+0x22, 0x90, 0x03, 0x04, 0xE0, 0x90, 0xD2, 0x0C, 0xF0, 0x90, 0x03, 0x05, 0xE0, 0x90, 0xD2, 0x0D,
+0xF0, 0x90, 0x03, 0x06, 0xE0, 0x90, 0xD2, 0x0E, 0xF0, 0x90, 0x03, 0x07, 0xE0, 0x90, 0xD2, 0x0F,
+0xF0, 0x90, 0x03, 0x08, 0xE0, 0x90, 0xD2, 0x10, 0xF0, 0x90, 0x03, 0x09, 0xE0, 0x90, 0xD2, 0x11,
+0xF0, 0x22, 0x90, 0xB0, 0x24, 0x74, 0x14, 0xF0, 0x90, 0x00, 0x01, 0xE0, 0xFF, 0x7E, 0x00, 0x64,
+0x02, 0x70, 0x0D, 0x90, 0xB0, 0x1F, 0x74, 0x03, 0xF0, 0x90, 0xB0, 0x2E, 0x74, 0x23, 0xF0, 0x22,
+0xEF, 0x64, 0x01, 0x4E, 0x70, 0x0C, 0x90, 0xB0, 0x1F, 0x74, 0x07, 0xF0, 0x90, 0xB0, 0x2E, 0x74,
+0x27, 0xF0, 0x22, 0xF0, 0x90, 0xD8, 0x03, 0x74, 0xF0, 0xF0, 0xE4, 0xF0, 0x7F, 0x64, 0xFE, 0xEF,
+0x1F, 0xAA, 0x06, 0x70, 0x01, 0x1E, 0x4A, 0x60, 0x13, 0xE4, 0xFC, 0xFD, 0xC3, 0xED, 0x94, 0x45,
+0xEC, 0x94, 0x01, 0x50, 0xEA, 0x0D, 0xBD, 0x00, 0x01, 0x0C, 0x80, 0xF0, 0x22, 0x90, 0xDA, 0xFC,
+0x74, 0x0F, 0xF0, 0x90, 0xDA, 0xA3, 0x74, 0xE4, 0xF0, 0x90, 0xDA, 0xA4, 0x74, 0x80, 0xF0, 0x90,
+0xB0, 0x30, 0x74, 0x40, 0xF0, 0x90, 0x02, 0xBF, 0xE0, 0x44, 0x11, 0x90, 0xD8, 0x00, 0xF0, 0xE4,
+0x78, 0x43, 0xF6, 0x78, 0x23, 0xF6, 0x22, 0x90, 0xB0, 0x44, 0xE0, 0x54, 0xBF, 0xF0, 0x7F, 0x0A,
+0x7E, 0x00, 0x12, 0x41, 0x1F, 0x90, 0xB0, 0x44, 0xE0, 0x44, 0x40, 0xF0, 0x90, 0x90, 0x0A, 0x74,
+0xA7, 0xF0, 0x7F, 0x0A, 0x7E, 0x00, 0x12, 0x41, 0x1F, 0x90, 0x90, 0x0A, 0x74, 0xBF, 0xF0, 0x22,
+0x90, 0xA0, 0x90, 0x74, 0xFB, 0xF0, 0x90, 0xB0, 0x05, 0x74, 0x6F, 0xF0, 0x7F, 0x01, 0x7E, 0x00,
+0x12, 0x41, 0x1F, 0x90, 0xB0, 0x05, 0x74, 0xE0, 0xF0, 0x7F, 0x01, 0x7E, 0x00, 0x12, 0x41, 0x1F,
+0x90, 0xB0, 0x05, 0x74, 0x2B, 0xF0, 0x22, 0xEF, 0xB4, 0x0A, 0x07, 0x74, 0x0D, 0x12, 0x41, 0xC2,
+0x74, 0x0A, 0x30, 0x98, 0x11, 0xA8, 0x99, 0xB8, 0x13, 0x0C, 0xC2, 0x98, 0x30, 0x98, 0xFD, 0xA8,
+0x99, 0xC2, 0x98, 0xB8, 0x11, 0xF6, 0x30, 0x99, 0xFD, 0xC2, 0x99, 0xF5, 0x99, 0x22, 0x90, 0x02,
+0xCC, 0x74, 0x01, 0xF0, 0x90, 0x02, 0xBE, 0xF0, 0x90, 0x02, 0xC1, 0xF0, 0xE4, 0x90, 0x02, 0xC2,
+0xF0, 0x90, 0x02, 0xDB, 0xF0, 0x90, 0x02, 0xC8, 0x04, 0xF0, 0x90, 0x02, 0xC7, 0xF0, 0xE4, 0x90,
+0x02, 0xD7, 0xF0, 0x22, 0xE4, 0xFF, 0x78, 0x49, 0xE6, 0xFE, 0xEF, 0xC3, 0x9E, 0x50, 0x15, 0x78,
+0x39, 0xE6, 0x2F, 0x24, 0x1E, 0xF5, 0x82, 0xE4, 0x34, 0x00, 0xF5, 0x83, 0xE0, 0x90, 0xD0, 0x11,
+0xF0, 0x0F, 0x80, 0xE2, 0x78, 0x39, 0xEE, 0x26, 0xF6, 0x22, 0x90, 0x02, 0xBE, 0xE0, 0x70, 0x0D,
+0x90, 0x02, 0x8C, 0x74, 0x0A, 0xF0, 0x90, 0x02, 0x3B, 0xF0, 0x02, 0x38, 0x11, 0xE4, 0x90, 0xC0,
+0x06, 0xF0, 0x7F, 0x01, 0xFE, 0x12, 0x41, 0x1F, 0x90, 0xC0, 0x06, 0x74, 0x08, 0xF0, 0x22, 0xE4,
+0xFF, 0x78, 0x49, 0xE6, 0xFE, 0xEF, 0xC3, 0x9E, 0x50, 0x15, 0x78, 0x39, 0xE6, 0x2F, 0x24, 0x1E,
+0xF5, 0x82, 0xE4, 0x34, 0x00, 0xF5, 0x83, 0xE0, 0x90, 0xD0, 0x11, 0xF0, 0x0F, 0x80, 0xE2, 0xE4,
+0x78, 0x39, 0xF6, 0x22, 0x7F, 0x8C, 0x7E, 0xB0, 0x12, 0x44, 0x8D, 0xEF, 0x44, 0x07, 0xFD, 0x7F,
+0x8C, 0x7E, 0xB0, 0x12, 0x44, 0x86, 0x7D, 0x07, 0x7F, 0x8E, 0x7E, 0xB0, 0x12, 0x44, 0x86, 0x7D,
+0x77, 0x7F, 0x8F, 0x7E, 0xB0, 0x02, 0x44, 0x86, 0x90, 0x90, 0x03, 0x74, 0xEF, 0xF0, 0x90, 0x90,
+0x0D, 0x74, 0xE0, 0xF0, 0x90, 0x90, 0x0F, 0x74, 0xBF, 0xF0, 0x90, 0x90, 0x0D, 0x74, 0xFF, 0xF0,
+0x90, 0x90, 0x0F, 0xF0, 0x90, 0x90, 0x03, 0xF0, 0x22, 0x90, 0x90, 0x07, 0x74, 0xF7, 0xF0, 0x90,
+0x90, 0x0D, 0x74, 0xDF, 0xF0, 0x7F, 0x05, 0x7E, 0x00, 0x12, 0x41, 0x1F, 0x74, 0xFF, 0xF0, 0x90,
+0x90, 0x07, 0xF0, 0x7F, 0xF4, 0x7E, 0x01, 0x02, 0x41, 0x1F, 0x90, 0xC1, 0xAF, 0x74, 0xC1, 0xF0,
+0x90, 0xB0, 0x12, 0x74, 0x3C, 0xF0, 0x90, 0xDA, 0xA3, 0x74, 0x4B, 0xF0, 0x90, 0xDA, 0xA4, 0x74,
+0x05, 0xF0, 0x90, 0xB0, 0x30, 0x74, 0x46, 0xF0, 0x22, 0x90, 0xB0, 0x3A, 0x74, 0xF8, 0xF0, 0x90,
+0xB0, 0x92, 0x74, 0x1F, 0xF0, 0x90, 0xB0, 0x14, 0x74, 0x38, 0xF0, 0x90, 0xB0, 0x18, 0x74, 0x10,
+0xF0, 0x90, 0xB0, 0x1A, 0x74, 0x0A, 0xF0, 0x22, 0xEF, 0x7F, 0x48, 0x7E, 0xA0, 0x60, 0x08, 0x12,
+0x44, 0x8D, 0xEF, 0x44, 0x04, 0x80, 0x06, 0x12, 0x44, 0x8D, 0xEF, 0x54, 0xFB, 0xFD, 0x7F, 0x48,
+0x7E, 0xA0, 0x12, 0x44, 0x86, 0x22, 0xEF, 0x7F, 0x48, 0x7E, 0xA0, 0x60, 0x08, 0x12, 0x44, 0x8D,
+0xEF, 0x44, 0x10, 0x80, 0x06, 0x12, 0x44, 0x8D, 0xEF, 0x54, 0xEF, 0xFD, 0x7F, 0x48, 0x7E, 0xA0,
+0x12, 0x44, 0x86, 0x22, 0xEF, 0x1F, 0xAA, 0x06, 0x70, 0x01, 0x1E, 0x4A, 0x60, 0x13, 0xE4, 0xFC,
+0xFD, 0xC3, 0xED, 0x94, 0x5E, 0xEC, 0x94, 0x01, 0x50, 0xEA, 0x0D, 0xBD, 0x00, 0x01, 0x0C, 0x80,
+0xF0, 0x22, 0xF0, 0x12, 0x44, 0x46, 0x12, 0x43, 0xE3, 0x90, 0xD0, 0x05, 0xE0, 0x44, 0x40, 0xF0,
+0x90, 0xA0, 0x30, 0x74, 0x80, 0xF0, 0x7D, 0x08, 0x7F, 0x06, 0x7E, 0xC0, 0x02, 0x44, 0x86, 0x90,
+0xD8, 0x55, 0x74, 0x80, 0xF0, 0xE4, 0x90, 0xD8, 0x52, 0xF0, 0x90, 0xD8, 0x51, 0x04, 0xF0, 0xEF,
+0x90, 0xD8, 0x50, 0xF0, 0x90, 0xD8, 0x54, 0xE0, 0x54, 0x0F, 0xFF, 0x22, 0x90, 0x90, 0x07, 0x74,
+0xF7, 0xF0, 0x74, 0xFF, 0xF0, 0x90, 0xD2, 0x69, 0x74, 0x05, 0xF0, 0x90, 0xD2, 0x02, 0x74, 0x54,
+0xF0, 0x90, 0xD2, 0x67, 0x74, 0x20, 0xF0, 0x22, 0x90, 0xD2, 0x20, 0xE0, 0x30, 0xE0, 0x13, 0x90,
+0xD2, 0x1C, 0xE0, 0x44, 0x01, 0xF0, 0x54, 0xFE, 0xF0, 0x78, 0x38, 0x76, 0x01, 0xE4, 0x90, 0xD2,
+0x19, 0xF0, 0x22, 0xE4, 0x90, 0xD2, 0x19, 0xF0, 0x90, 0xD2, 0x1C, 0x74, 0xFE, 0xF0, 0x90, 0xA0,
+0x90, 0xE0, 0x54, 0xFE, 0xF0, 0x90, 0xA0, 0xA0, 0xE0, 0x54, 0xFE, 0xF0, 0x22, 0x75, 0x89, 0x11,
+0xC2, 0x8C, 0xC2, 0x8A, 0x75, 0x8C, 0xF7, 0x75, 0x8A, 0x40, 0xC2, 0x8E, 0x75, 0x8D, 0xB1, 0x75,
+0x8B, 0xE0, 0x75, 0xA8, 0x8E, 0x22, 0xE4, 0xFE, 0xEE, 0xC3, 0x9F, 0x50, 0x0C, 0x74, 0x24, 0x2E,
+0xF8, 0xE6, 0x90, 0xD0, 0x11, 0xF0, 0x0E, 0x80, 0xEF, 0xE4, 0x90, 0xD0, 0x13, 0xF0, 0x22, 0x90,
+0xC0, 0xC0, 0xE0, 0x54, 0x80, 0x64, 0x80, 0x70, 0x07, 0x90, 0x02, 0xBF, 0x74, 0x02, 0xF0, 0x22,
+0xE4, 0x90, 0x02, 0xBF, 0xF0, 0x22, 0x90, 0xD0, 0x05, 0xE0, 0x44, 0x03, 0xF0, 0x90, 0xA0, 0xA1,
+0x74, 0xFF, 0xF0, 0x74, 0x7F, 0xF0, 0x90, 0xA0, 0x91, 0xF0, 0x22, 0x90, 0xD0, 0x11, 0x74, 0x20,
+0xF0, 0xE4, 0x90, 0xD0, 0x13, 0xF0, 0x22, 0x90, 0xD8, 0x50, 0xEF, 0xF0, 0x90, 0xD8, 0x53, 0xED,
+0xF0, 0x22, 0xEF, 0x1F, 0xAC, 0x06, 0x70, 0x01, 0x1E, 0x4C, 0x70, 0xF6, 0x22, 0x7B, 0x01, 0x7A,
+0x00, 0x79, 0x1E, 0x02, 0x37, 0x8A, 0x8F, 0x82, 0x8E, 0x83, 0xED, 0xF0, 0x22, 0x8F, 0x82, 0x8E,
+0x83, 0xE0, 0xFF, 0x22, 0x90, 0xB0, 0x2E, 0x74, 0x20, 0xF0, 0x22, 0x90, 0xC1, 0x16, 0x74, 0x01,
+0xF0, 0x22, 0x12, 0x41, 0xDE, 0x02, 0x1B, 0xA9, 0x90, 0xD0, 0x12, 0xE0, 0xFF, 0x22, 0x90, 0xD8,
+0x54, 0xE0, 0xFF, 0x22, 0xE4, 0x90, 0xD0, 0x11, 0xF0, 0x22,
+};
diff --git a/kernel/drivers/misc/nkio/nk_io_core.c b/kernel/drivers/misc/nkio/nk_io_core.c
index e17fb86..c2f4ae1 100755
--- a/kernel/drivers/misc/nkio/nk_io_core.c
+++ b/kernel/drivers/misc/nkio/nk_io_core.c
@@ -13,6 +13,7 @@
 #include <linux/string.h>
 #include <linux/uaccess.h>
 #include <linux/device.h>
+#include <linux/proc_fs.h>
  
 #define GPIO_FUNCTION_OUTPUT 0
 #define GPIO_FUNCTION_INPUT 1
@@ -20,7 +21,7 @@
 #define HIGH "1"
 #define LOW "0"
 
-static int flash_flag = 0;
+//static int flash_flag = 0;
  
 struct ndj_gpio {
    int gpio_num;        //gpio num
@@ -94,7 +95,7 @@
 }; 
  
 #endif
-static int event_flag = 0;
+//static int event_flag = 0;
 static int open_now = 0;
 static char* file_name = NULL;
  
@@ -152,26 +153,21 @@
 }
  
  
-static const struct file_operations gpio_ops = {
-   .owner          = THIS_MODULE,
-    .open           = gpio_open,
-    .write          = gpio_write,
-    .read           = gpio_read,
+static const struct proc_ops gpio_ops = {
+    .proc_open           = gpio_open,
+    .proc_write          = gpio_write,
+    .proc_read           = gpio_read,
 };
  
 static int ndj_gpio_probe(struct platform_device *pdev) {
    struct device_node *np = pdev->dev.of_node;
    struct device_node *child_np;
-   struct device *dev = &pdev->dev;
    static struct proc_dir_entry *root_entry_gpio;
    enum of_gpio_flags  gpio_flags;
    int ret = 0;
    int gpio_cnt = 0;    
    char gpio_name_num[20];
    int gpio_in_cnt = 0;
-   int cnt =0;
-   int i=0;
-   enum of_gpio_flags flags;
  
    gpio_data = devm_kzalloc(&pdev->dev, sizeof(struct ndj_gpio_data),GFP_KERNEL);
    if (!gpio_data) {
@@ -286,15 +282,15 @@
  
 static int ndj_gpio_suspend(struct platform_device *pdev, pm_message_t state)
 {
-   int ret;
-   struct nk_io_pdata *pdata;
+//   int ret;
+//   struct nk_io_pdata *pdata;
    printk("nk_suspend  !!!!\n");
    return 0;
 }
  
 static int ndj_gpio_resume(struct platform_device *pdev)
 {
-    int ret,reset_pin;
+//    int ret,reset_pin;
     printk("nk_io resume !!!!\n");
     return 0;
 }
@@ -325,4 +321,4 @@
  
 module_platform_driver(ndj_gpio_driver);
 MODULE_LICENSE("GPL");
-MODULE_LICENSE("GPL");
\ No newline at end of file
+MODULE_LICENSE("GPL");
diff --git a/kernel/drivers/misc/nkmcu/nk_mcu.c b/kernel/drivers/misc/nkmcu/nk_mcu.c
index b0702be..3d2436b 100755
--- a/kernel/drivers/misc/nkmcu/nk_mcu.c
+++ b/kernel/drivers/misc/nkmcu/nk_mcu.c
@@ -96,7 +96,7 @@
 	
 static int  nk_mcu_probe(struct i2c_client *client, const struct i2c_device_id *id)
 {
-	struct device_node *np = client->dev.of_node;
+	//struct device_node *np = client->dev.of_node;
 	int ret;
 	
 	printk("%s: probe\n", __FUNCTION__);
diff --git a/kernel/drivers/misc/rk628/Kconfig b/kernel/drivers/misc/rk628/Kconfig
new file mode 100644
index 0000000..7f89f18
--- /dev/null
+++ b/kernel/drivers/misc/rk628/Kconfig
@@ -0,0 +1,27 @@
+# SPDX-License-Identifier: GPL-2.0
+menu "RK628 misc driver"
+config RK628_MISC
+	tristate "rk628 misc driver"
+	default n
+	help
+	  Say y here to enable Rockchip rk628 misc driver.
+          This option is used to support rgb/hdmi/bt1120 input and dsi/lvds/gvi/hdmi output.
+
+config RK628_MISC_HDMITX
+	bool "rk628 misc hdmitx driver"
+	default n
+	depends on RK628_MISC
+	depends on DRM
+	help
+	  Say y here to enable Rockchip rk628 misc hdmitx driver.
+	  This option is used to support hdmi output.
+
+config ROCKCHIP_THUNDER_BOOT_RK628
+	bool "Rockchip RK628 Thunder Boot support"
+	default n
+	depends on RK628_MISC
+	help
+	  Say y here to enable Rockchip rk628 thunder boot support.
+	  This option make the kernel boot faster.
+
+endmenu
diff --git a/kernel/drivers/misc/rk628/Makefile b/kernel/drivers/misc/rk628/Makefile
new file mode 100644
index 0000000..cb4285a
--- /dev/null
+++ b/kernel/drivers/misc/rk628/Makefile
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0
+
+rk628_misc-$(CONFIG_RK628_MISC) +=	rk628.o rk628_cru.o rk628_config.o rk628_post_process.o \
+					rk628_combrxphy.o rk628_hdmirx.o rk628_combtxphy.o rk628_dsi.o \
+					panel.o rk628_lvds.o rk628_rgb.o rk628_gvi.o rk628_pinctrl.o \
+					rk628_csi.o
+
+rk628_misc-$(CONFIG_RK628_MISC_HDMITX) += rk628_hdmitx.o
+
+obj-$(CONFIG_RK628_MISC) += rk628_misc.o
diff --git a/kernel/drivers/misc/rk628/panel.c b/kernel/drivers/misc/rk628/panel.c
new file mode 100644
index 0000000..a0a3503
--- /dev/null
+++ b/kernel/drivers/misc/rk628/panel.c
@@ -0,0 +1,245 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Rockchip Electronics Co. Ltd.
+ *
+ * Author: Guochun Huang <hero.huang@rock-chips.com>
+ */
+
+#include "rk628.h"
+#include <linux/gpio/consumer.h>
+#include <linux/backlight.h>
+
+#include "panel.h"
+
+static int
+dsi_panel_parse_cmds(const u8 *data, int blen, struct panel_cmds *pcmds)
+{
+	unsigned int len;
+	char *buf, *bp;
+	struct cmd_ctrl_hdr *dchdr;
+	int i, cnt;
+
+	if (!pcmds)
+		return -EINVAL;
+
+	buf = kmemdup(data, blen, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	/* scan init commands */
+	bp = buf;
+	len = blen;
+	cnt = 0;
+	while (len > sizeof(*dchdr)) {
+		dchdr = (struct cmd_ctrl_hdr *)bp;
+
+		if (dchdr->dlen > len) {
+			pr_err("%s: error, len=%d", __func__, dchdr->dlen);
+			return -EINVAL;
+		}
+
+		bp += sizeof(*dchdr);
+		len -= sizeof(*dchdr);
+		bp += dchdr->dlen;
+		len -= dchdr->dlen;
+		cnt++;
+	}
+
+	if (len != 0) {
+		pr_err("%s: dcs_cmd=%x len=%d error!", __func__, buf[0], blen);
+		kfree(buf);
+		return -EINVAL;
+	}
+
+	pcmds->cmds = kcalloc(cnt, sizeof(struct cmd_desc), GFP_KERNEL);
+	if (!pcmds->cmds) {
+		kfree(buf);
+		return -ENOMEM;
+	}
+
+	pcmds->cmd_cnt = cnt;
+	pcmds->buf = buf;
+	pcmds->blen = blen;
+
+	bp = buf;
+	len = blen;
+	for (i = 0; i < cnt; i++) {
+		dchdr = (struct cmd_ctrl_hdr *)bp;
+		len -= sizeof(*dchdr);
+		bp += sizeof(*dchdr);
+		pcmds->cmds[i].dchdr = *dchdr;
+		pcmds->cmds[i].payload = bp;
+		bp += dchdr->dlen;
+		len -= dchdr->dlen;
+	}
+
+	return 0;
+}
+
+static int dsi_panel_get_cmds(struct rk628 *rk628, struct device_node *dsi_np)
+{
+	struct device_node *np;
+	const void *data;
+	int len;
+	int ret, err;
+
+	np = of_find_node_by_name(dsi_np, "rk628-panel");
+	if (!np)
+		return -EINVAL;
+
+	data = of_get_property(np, "panel-init-sequence", &len);
+	if (data) {
+		rk628->panel->on_cmds = kcalloc(1, sizeof(struct panel_cmds), GFP_KERNEL);
+		if (!rk628->panel->on_cmds)
+			return -ENOMEM;
+
+		err = dsi_panel_parse_cmds(data, len, rk628->panel->on_cmds);
+		if (err) {
+			dev_err(rk628->dev, "failed to parse dsi panel init sequence\n");
+			ret = err;
+			goto init_err;
+		}
+	}
+
+	data = of_get_property(np, "panel-exit-sequence", &len);
+	if (data) {
+		rk628->panel->off_cmds = kcalloc(1, sizeof(struct panel_cmds), GFP_KERNEL);
+		if (!rk628->panel->off_cmds) {
+			ret = -ENOMEM;
+			goto on_err;
+		}
+
+		err = dsi_panel_parse_cmds(data, len, rk628->panel->off_cmds);
+		if (err) {
+			dev_err(rk628->dev, "failed to parse dsi panel exit sequence\n");
+			ret = err;
+			goto exit_err;
+		}
+	}
+
+	return 0;
+
+exit_err:
+	kfree(rk628->panel->off_cmds);
+on_err:
+	kfree(rk628->panel->on_cmds->cmds);
+	kfree(rk628->panel->on_cmds->buf);
+init_err:
+	kfree(rk628->panel->on_cmds);
+
+	return ret;
+}
+
+int rk628_panel_info_get(struct rk628 *rk628, struct device_node *np)
+{
+	struct panel_simple *panel;
+	struct device *dev = rk628->dev;
+	struct device_node *backlight;
+	int ret;
+
+	panel = devm_kzalloc(dev, sizeof(struct panel_simple), GFP_KERNEL);
+	if (!panel)
+		return -ENOMEM;
+
+	panel->supply = devm_regulator_get(dev, "power");
+	if (IS_ERR(panel->supply)) {
+		ret = PTR_ERR(panel->supply);
+		dev_err(dev, "failed to get power regulator: %d\n", ret);
+		return ret;
+	}
+
+	panel->enable_gpio = devm_gpiod_get_optional(dev, "panel-enable", GPIOD_OUT_LOW);
+	if (IS_ERR(panel->enable_gpio)) {
+		ret = PTR_ERR(panel->enable_gpio);
+		dev_err(dev, "failed to request panel enable GPIO: %d\n", ret);
+		return ret;
+	}
+
+	panel->reset_gpio = devm_gpiod_get_optional(dev, "panel-reset", GPIOD_OUT_LOW);
+	if (IS_ERR(panel->reset_gpio)) {
+		ret = PTR_ERR(panel->reset_gpio);
+		dev_err(dev, "failed to request panel reset GPIO: %d\n", ret);
+		return ret;
+	}
+
+	backlight = of_parse_phandle(dev->of_node, "panel-backlight", 0);
+	if (backlight) {
+		panel->backlight = of_find_backlight_by_node(backlight);
+		of_node_put(backlight);
+
+		if (!panel->backlight) {
+			dev_err(dev, "failed to find backlight\n");
+			return -EPROBE_DEFER;
+		}
+
+	}
+
+	rk628->panel = panel;
+
+	if (rk628->output_mode == OUTPUT_MODE_DSI) {
+		ret = dsi_panel_get_cmds(rk628, np);
+		if (ret) {
+			dev_err(dev, "failed to get cmds\n");
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+void rk628_panel_prepare(struct rk628 *rk628)
+{
+	int ret;
+
+	if (rk628->panel->supply) {
+		ret = regulator_enable(rk628->panel->supply);
+		if (ret)
+			dev_info(rk628->dev, "failed to enable panel power supply\n");
+	}
+
+	if (rk628->panel->enable_gpio) {
+		gpiod_set_value(rk628->panel->enable_gpio, 0);
+		mdelay(120);
+		gpiod_set_value(rk628->panel->enable_gpio, 1);
+		mdelay(120);
+	}
+
+	if (rk628->panel->reset_gpio) {
+		gpiod_set_value(rk628->panel->reset_gpio, 0);
+		mdelay(120);
+		gpiod_set_value(rk628->panel->reset_gpio, 1);
+		mdelay(120);
+		gpiod_set_value(rk628->panel->reset_gpio, 0);
+		mdelay(120);
+	}
+}
+
+void rk628_panel_enable(struct rk628 *rk628)
+{
+	if (rk628->panel->backlight)
+		backlight_enable(rk628->panel->backlight);
+}
+
+void rk628_panel_unprepare(struct rk628 *rk628)
+{
+
+	if (rk628->panel->reset_gpio) {
+		gpiod_set_value(rk628->panel->reset_gpio, 1);
+		mdelay(120);
+	}
+
+	if (rk628->panel->enable_gpio) {
+		gpiod_set_value(rk628->panel->enable_gpio, 0);
+		mdelay(120);
+	}
+
+	if (rk628->panel->supply)
+		regulator_disable(rk628->panel->supply);
+}
+
+void rk628_panel_disable(struct rk628 *rk628)
+{
+	if (rk628->panel->backlight)
+		backlight_disable(rk628->panel->backlight);
+
+}
diff --git a/kernel/drivers/misc/rk628/panel.h b/kernel/drivers/misc/rk628/panel.h
new file mode 100644
index 0000000..07a34cf
--- /dev/null
+++ b/kernel/drivers/misc/rk628/panel.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: BSD-3-Clause */
+/*
+ * Copyright (c) 2021 Rockchip Electronics Co. Ltd.
+ *
+ * Author: Guochun Huang <hero.huang@rock-chips.com>
+ */
+#ifndef _PANEL_H
+#define _PANEL_H
+
+#include "rk628.h"
+
+int rk628_panel_info_get(struct rk628 *rk628, struct device_node *np);
+void rk628_panel_prepare(struct rk628 *rk628);
+void rk628_panel_enable(struct rk628 *rk628);
+void rk628_panel_unprepare(struct rk628 *rk628);
+void rk628_panel_disable(struct rk628 *rk628);
+#endif
+
diff --git a/kernel/drivers/misc/rk628/rk628.c b/kernel/drivers/misc/rk628/rk628.c
new file mode 100644
index 0000000..15df730
--- /dev/null
+++ b/kernel/drivers/misc/rk628/rk628.c
@@ -0,0 +1,1310 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Rockchip Electronics Co. Ltd.
+ *
+ * Author: Wyon Bi <bivvy.bi@rock-chips.com>
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/i2c.h>
+#include <linux/gpio/consumer.h>
+#include <linux/regmap.h>
+#include <linux/backlight.h>
+#include <linux/pm_runtime.h>
+#include <video/videomode.h>
+#include <linux/debugfs.h>
+
+#include "rk628.h"
+#include "rk628_cru.h"
+#include "rk628_combrxphy.h"
+#include "rk628_post_process.h"
+#include "rk628_hdmirx.h"
+#include "rk628_combtxphy.h"
+#include "rk628_dsi.h"
+#include "rk628_rgb.h"
+#include "rk628_lvds.h"
+#include "rk628_gvi.h"
+#include "rk628_csi.h"
+#include "rk628_hdmitx.h"
+
+static const struct regmap_range rk628_cru_readable_ranges[] = {
+	regmap_reg_range(CRU_CPLL_CON0, CRU_CPLL_CON4),
+	regmap_reg_range(CRU_GPLL_CON0, CRU_GPLL_CON4),
+	regmap_reg_range(CRU_MODE_CON00, CRU_MODE_CON00),
+	regmap_reg_range(CRU_CLKSEL_CON00, CRU_CLKSEL_CON21),
+	regmap_reg_range(CRU_GATE_CON00, CRU_GATE_CON05),
+	regmap_reg_range(CRU_SOFTRST_CON00, CRU_SOFTRST_CON04),
+};
+
+static const struct regmap_access_table rk628_cru_readable_table = {
+	.yes_ranges     = rk628_cru_readable_ranges,
+	.n_yes_ranges   = ARRAY_SIZE(rk628_cru_readable_ranges),
+};
+
+static const struct regmap_range rk628_combrxphy_readable_ranges[] = {
+	regmap_reg_range(COMBRX_REG(0x6600), COMBRX_REG(0x665b)),
+	regmap_reg_range(COMBRX_REG(0x66a0), COMBRX_REG(0x66db)),
+	regmap_reg_range(COMBRX_REG(0x66f0), COMBRX_REG(0x66ff)),
+	regmap_reg_range(COMBRX_REG(0x6700), COMBRX_REG(0x6790)),
+};
+
+static const struct regmap_access_table rk628_combrxphy_readable_table = {
+	.yes_ranges     = rk628_combrxphy_readable_ranges,
+	.n_yes_ranges   = ARRAY_SIZE(rk628_combrxphy_readable_ranges),
+};
+
+static const struct regmap_range rk628_hdmirx_readable_ranges[] = {
+	regmap_reg_range(HDMI_RX_HDMI_SETUP_CTRL, HDMI_RX_HDMI_SETUP_CTRL),
+	regmap_reg_range(HDMI_RX_HDMI_PCB_CTRL, HDMI_RX_HDMI_PCB_CTRL),
+	regmap_reg_range(HDMI_RX_HDMI_MODE_RECOVER, HDMI_RX_HDMI_ERROR_PROTECT),
+	regmap_reg_range(HDMI_RX_HDMI_SYNC_CTRL, HDMI_RX_HDMI_CKM_RESULT),
+	regmap_reg_range(HDMI_RX_HDMI_RESMPL_CTRL, HDMI_RX_HDMI_RESMPL_CTRL),
+	regmap_reg_range(HDMI_RX_HDMI_VM_CFG_CH2, HDMI_RX_HDMI_STS),
+	regmap_reg_range(HDMI_RX_HDCP_CTRL, HDMI_RX_HDCP_SETTINGS),
+	regmap_reg_range(HDMI_RX_HDCP_KIDX, HDMI_RX_HDCP_KIDX),
+	regmap_reg_range(HDMI_RX_HDCP_DBG, HDMI_RX_HDCP_AN0),
+	regmap_reg_range(HDMI_RX_HDCP_STS, HDMI_RX_HDCP_STS),
+	regmap_reg_range(HDMI_RX_MD_HCTRL1, HDMI_RX_MD_HACT_PX),
+	regmap_reg_range(HDMI_RX_MD_VCTRL, HDMI_RX_MD_VSC),
+	regmap_reg_range(HDMI_RX_MD_VOL, HDMI_RX_MD_VTL),
+	regmap_reg_range(HDMI_RX_MD_IL_POL, HDMI_RX_MD_STS),
+	regmap_reg_range(HDMI_RX_AUD_CTRL, HDMI_RX_AUD_CTRL),
+	regmap_reg_range(HDMI_RX_AUD_PLL_CTRL, HDMI_RX_AUD_PLL_CTRL),
+	regmap_reg_range(HDMI_RX_AUD_CLK_CTRL, HDMI_RX_AUD_CLK_CTRL),
+	regmap_reg_range(HDMI_RX_AUD_FIFO_CTRL, HDMI_RX_AUD_FIFO_TH),
+	regmap_reg_range(HDMI_RX_AUD_CHEXTR_CTRL, HDMI_RX_AUD_PAO_CTRL),
+	regmap_reg_range(HDMI_RX_AUD_FIFO_STS, HDMI_RX_AUD_FIFO_STS),
+	regmap_reg_range(HDMI_RX_AUDPLL_GEN_CTS, HDMI_RX_AUDPLL_GEN_N),
+	regmap_reg_range(HDMI_RX_PDEC_CTRL, HDMI_RX_PDEC_CTRL),
+	regmap_reg_range(HDMI_RX_PDEC_AUDIODET_CTRL, HDMI_RX_PDEC_AUDIODET_CTRL),
+	regmap_reg_range(HDMI_RX_PDEC_ERR_FILTER, HDMI_RX_PDEC_ASP_CTRL),
+	regmap_reg_range(HDMI_RX_PDEC_STS, HDMI_RX_PDEC_STS),
+	regmap_reg_range(HDMI_RX_PDEC_GCP_AVMUTE, HDMI_RX_PDEC_GCP_AVMUTE),
+	regmap_reg_range(HDMI_RX_PDEC_ACR_CTS, HDMI_RX_PDEC_ACR_N),
+	regmap_reg_range(HDMI_RX_PDEC_AIF_CTRL, HDMI_RX_PDEC_AIF_PB0),
+	regmap_reg_range(HDMI_RX_PDEC_AVI_PB, HDMI_RX_PDEC_AVI_PB),
+	regmap_reg_range(HDMI_RX_HDMI20_CONTROL, HDMI_RX_CHLOCK_CONFIG),
+	regmap_reg_range(HDMI_RX_SCDC_REGS1, HDMI_RX_SCDC_REGS2),
+	regmap_reg_range(HDMI_RX_SCDC_WRDATA0, HDMI_RX_SCDC_WRDATA0),
+	regmap_reg_range(HDMI_RX_PDEC_ISTS, HDMI_RX_PDEC_IEN),
+	regmap_reg_range(HDMI_RX_AUD_FIFO_ISTS, HDMI_RX_AUD_FIFO_IEN),
+	regmap_reg_range(HDMI_RX_MD_ISTS, HDMI_RX_MD_IEN),
+	regmap_reg_range(HDMI_RX_HDMI_ISTS, HDMI_RX_HDMI_IEN),
+	regmap_reg_range(HDMI_RX_DMI_DISABLE_IF, HDMI_RX_DMI_DISABLE_IF),
+};
+
+static const struct regmap_access_table rk628_hdmirx_readable_table = {
+	.yes_ranges     = rk628_hdmirx_readable_ranges,
+	.n_yes_ranges   = ARRAY_SIZE(rk628_hdmirx_readable_ranges),
+};
+
+static const struct regmap_range rk628_key_readable_ranges[] = {
+	regmap_reg_range(EDID_BASE, EDID_BASE + 0x400),
+};
+
+static const struct regmap_access_table rk628_key_readable_table = {
+	.yes_ranges     = rk628_key_readable_ranges,
+	.n_yes_ranges   = ARRAY_SIZE(rk628_key_readable_ranges),
+};
+
+static const struct regmap_range rk628_combtxphy_readable_ranges[] = {
+	regmap_reg_range(COMBTXPHY_BASE, COMBTXPHY_CON10),
+};
+
+static const struct regmap_access_table rk628_combtxphy_readable_table = {
+	.yes_ranges     = rk628_combtxphy_readable_ranges,
+	.n_yes_ranges   = ARRAY_SIZE(rk628_combtxphy_readable_ranges),
+};
+
+static const struct regmap_range rk628_dsi0_readable_ranges[] = {
+	regmap_reg_range(DSI0_BASE, DSI0_BASE + DSI_MAX_REGISTER),
+};
+
+static const struct regmap_access_table rk628_dsi0_readable_table = {
+	.yes_ranges     = rk628_dsi0_readable_ranges,
+	.n_yes_ranges   = ARRAY_SIZE(rk628_dsi0_readable_ranges),
+};
+
+static const struct regmap_range rk628_dsi1_readable_ranges[] = {
+	regmap_reg_range(DSI1_BASE, DSI1_BASE + DSI_MAX_REGISTER),
+};
+
+static const struct regmap_access_table rk628_dsi1_readable_table = {
+	.yes_ranges     = rk628_dsi1_readable_ranges,
+	.n_yes_ranges   = ARRAY_SIZE(rk628_dsi1_readable_ranges),
+};
+
+static const struct regmap_range rk628_gvi_readable_ranges[] = {
+	regmap_reg_range(GVI_BASE, GVI_BASE + GVI_COLOR_BAR_VTIMING1),
+};
+
+static const struct regmap_access_table rk628_gvi_readable_table = {
+	.yes_ranges     = rk628_gvi_readable_ranges,
+	.n_yes_ranges   = ARRAY_SIZE(rk628_gvi_readable_ranges),
+};
+
+static const struct regmap_range rk628_csi_readable_ranges[] = {
+	regmap_reg_range(CSITX_CONFIG_DONE, CSITX_CSITX_VERSION),
+	regmap_reg_range(CSITX_SYS_CTRL0_IMD, CSITX_TIMING_HPW_PADDING_NUM),
+	regmap_reg_range(CSITX_VOP_PATH_CTRL, CSITX_VOP_PATH_CTRL),
+	regmap_reg_range(CSITX_VOP_PATH_PKT_CTRL, CSITX_VOP_PATH_PKT_CTRL),
+	regmap_reg_range(CSITX_CSITX_STATUS0, CSITX_LPDT_DATA_IMD),
+	regmap_reg_range(CSITX_DPHY_CTRL, CSITX_DPHY_CTRL),
+};
+
+static const struct regmap_access_table rk628_csi_readable_table = {
+	.yes_ranges     = rk628_csi_readable_ranges,
+	.n_yes_ranges   = ARRAY_SIZE(rk628_csi_readable_ranges),
+};
+
+static const struct regmap_range rk628_hdmi_volatile_reg_ranges[] = {
+	regmap_reg_range(HDMI_SYS_CTRL, HDMI_MAX_REG),
+};
+
+static const struct regmap_access_table rk628_hdmi_volatile_regs = {
+	.yes_ranges = rk628_hdmi_volatile_reg_ranges,
+	.n_yes_ranges = ARRAY_SIZE(rk628_hdmi_volatile_reg_ranges),
+};
+
+static const struct regmap_range rk628_gpio0_readable_ranges[] = {
+	regmap_reg_range(RK628_GPIO0_BASE, RK628_GPIO0_BASE + GPIO_VER_ID),
+};
+
+static const struct regmap_access_table rk628_gpio0_readable_table = {
+	.yes_ranges     = rk628_gpio0_readable_ranges,
+	.n_yes_ranges   = ARRAY_SIZE(rk628_gpio0_readable_ranges),
+};
+
+static const struct regmap_range rk628_gpio1_readable_ranges[] = {
+	regmap_reg_range(RK628_GPIO1_BASE, RK628_GPIO1_BASE + GPIO_VER_ID),
+};
+
+static const struct regmap_access_table rk628_gpio1_readable_table = {
+	.yes_ranges     = rk628_gpio1_readable_ranges,
+	.n_yes_ranges   = ARRAY_SIZE(rk628_gpio1_readable_ranges),
+};
+
+static const struct regmap_range rk628_gpio2_readable_ranges[] = {
+	regmap_reg_range(RK628_GPIO2_BASE, RK628_GPIO2_BASE + GPIO_VER_ID),
+};
+
+static const struct regmap_access_table rk628_gpio2_readable_table = {
+	.yes_ranges     = rk628_gpio2_readable_ranges,
+	.n_yes_ranges   = ARRAY_SIZE(rk628_gpio2_readable_ranges),
+};
+
+static const struct regmap_range rk628_gpio3_readable_ranges[] = {
+	regmap_reg_range(RK628_GPIO3_BASE, RK628_GPIO3_BASE + GPIO_VER_ID),
+};
+
+static const struct regmap_access_table rk628_gpio3_readable_table = {
+	.yes_ranges     = rk628_gpio3_readable_ranges,
+	.n_yes_ranges   = ARRAY_SIZE(rk628_gpio3_readable_ranges),
+};
+
+static const struct regmap_config rk628_regmap_config[RK628_DEV_MAX] = {
+	[RK628_DEV_GRF] = {
+		.name = "grf",
+		.reg_bits = 32,
+		.val_bits = 32,
+		.reg_stride = 4,
+		.max_register = GRF_MAX_REGISTER,
+		.reg_format_endian = REGMAP_ENDIAN_NATIVE,
+		.val_format_endian = REGMAP_ENDIAN_NATIVE,
+	},
+	[RK628_DEV_CRU] = {
+		.name = "cru",
+		.reg_bits = 32,
+		.val_bits = 32,
+		.reg_stride = 4,
+		.max_register = CRU_MAX_REGISTER,
+		.reg_format_endian = REGMAP_ENDIAN_NATIVE,
+		.val_format_endian = REGMAP_ENDIAN_NATIVE,
+		.rd_table = &rk628_cru_readable_table,
+	},
+	[RK628_DEV_COMBRXPHY] = {
+		.name = "combrxphy",
+		.reg_bits = 32,
+		.val_bits = 32,
+		.reg_stride = 4,
+		.max_register = COMBRX_REG(0x6790),
+		.reg_format_endian = REGMAP_ENDIAN_NATIVE,
+		.val_format_endian = REGMAP_ENDIAN_NATIVE,
+		.rd_table = &rk628_combrxphy_readable_table,
+	},
+	[RK628_DEV_HDMIRX] = {
+		.name = "hdmirx",
+		.reg_bits = 32,
+		.val_bits = 32,
+		.reg_stride = 4,
+		.max_register = HDMI_RX_MAX_REGISTER,
+		.reg_format_endian = REGMAP_ENDIAN_NATIVE,
+		.val_format_endian = REGMAP_ENDIAN_NATIVE,
+		.rd_table = &rk628_hdmirx_readable_table,
+	},
+	[RK628_DEV_ADAPTER] = {
+		.name = "adapter",
+		.reg_bits = 32,
+		.val_bits = 32,
+		.reg_stride = 4,
+		.max_register = KEY_MAX_REGISTER,
+		.reg_format_endian = REGMAP_ENDIAN_NATIVE,
+		.val_format_endian = REGMAP_ENDIAN_NATIVE,
+		.rd_table = &rk628_key_readable_table,
+	},
+	[RK628_DEV_COMBTXPHY] = {
+		.name = "combtxphy",
+		.reg_bits = 32,
+		.val_bits = 32,
+		.reg_stride = 4,
+		.max_register = COMBTXPHY_CON10,
+		.reg_format_endian = REGMAP_ENDIAN_NATIVE,
+		.val_format_endian = REGMAP_ENDIAN_NATIVE,
+		.rd_table = &rk628_combtxphy_readable_table,
+	},
+	[RK628_DEV_DSI0] = {
+		.name = "dsi0",
+		.reg_bits = 32,
+		.val_bits = 32,
+		.reg_stride = 4,
+		.max_register = DSI0_BASE + DSI_MAX_REGISTER,
+		.reg_format_endian = REGMAP_ENDIAN_NATIVE,
+		.val_format_endian = REGMAP_ENDIAN_NATIVE,
+		.rd_table = &rk628_dsi0_readable_table,
+	},
+	[RK628_DEV_DSI1] = {
+		.name = "dsi1",
+		.reg_bits = 32,
+		.val_bits = 32,
+		.reg_stride = 4,
+		.max_register = DSI1_BASE + DSI_MAX_REGISTER,
+		.reg_format_endian = REGMAP_ENDIAN_NATIVE,
+		.val_format_endian = REGMAP_ENDIAN_NATIVE,
+		.rd_table = &rk628_dsi1_readable_table,
+	},
+	[RK628_DEV_GVI] = {
+		.name = "gvi",
+		.reg_bits = 32,
+		.val_bits = 32,
+		.reg_stride = 4,
+		.max_register = GVI_COLOR_BAR_VTIMING1,
+		.reg_format_endian = REGMAP_ENDIAN_NATIVE,
+		.val_format_endian = REGMAP_ENDIAN_NATIVE,
+		.rd_table = &rk628_gvi_readable_table,
+	},
+	[RK628_DEV_CSI] = {
+		.name = "csi",
+		.reg_bits = 32,
+		.val_bits = 32,
+		.reg_stride = 4,
+		.max_register = CSI_MAX_REGISTER,
+		.reg_format_endian = REGMAP_ENDIAN_NATIVE,
+		.val_format_endian = REGMAP_ENDIAN_NATIVE,
+		.rd_table = &rk628_csi_readable_table,
+	},
+	[RK628_DEV_HDMITX] = {
+		.name = "hdmi",
+		.reg_bits = 32,
+		.val_bits = 32,
+		.reg_stride = 4,
+		.max_register = HDMI_MAX_REG,
+		.reg_format_endian = REGMAP_ENDIAN_NATIVE,
+		.val_format_endian = REGMAP_ENDIAN_NATIVE,
+		.rd_table = &rk628_hdmi_volatile_regs,
+	},
+	[RK628_DEV_GPIO0] = {
+		.name = "gpio0",
+		.reg_bits = 32,
+		.val_bits = 32,
+		.reg_stride = 4,
+		.max_register = RK628_GPIO0_BASE + GPIO_VER_ID,
+		.reg_format_endian = REGMAP_ENDIAN_NATIVE,
+		.val_format_endian = REGMAP_ENDIAN_NATIVE,
+		.rd_table = &rk628_gpio0_readable_table,
+	},
+	[RK628_DEV_GPIO1] = {
+		.name = "gpio1",
+		.reg_bits = 32,
+		.val_bits = 32,
+		.reg_stride = 4,
+		.max_register = RK628_GPIO1_BASE + GPIO_VER_ID,
+		.reg_format_endian = REGMAP_ENDIAN_NATIVE,
+		.val_format_endian = REGMAP_ENDIAN_NATIVE,
+		.rd_table = &rk628_gpio1_readable_table,
+	},
+	[RK628_DEV_GPIO2] = {
+		.name = "gpio2",
+		.reg_bits = 32,
+		.val_bits = 32,
+		.reg_stride = 4,
+		.max_register = RK628_GPIO2_BASE + GPIO_VER_ID,
+		.reg_format_endian = REGMAP_ENDIAN_NATIVE,
+		.val_format_endian = REGMAP_ENDIAN_NATIVE,
+		.rd_table = &rk628_gpio2_readable_table,
+	},
+	[RK628_DEV_GPIO3] = {
+		.name = "gpio3",
+		.reg_bits = 32,
+		.val_bits = 32,
+		.reg_stride = 4,
+		.max_register = RK628_GPIO3_BASE + GPIO_VER_ID,
+		.reg_format_endian = REGMAP_ENDIAN_NATIVE,
+		.val_format_endian = REGMAP_ENDIAN_NATIVE,
+		.rd_table = &rk628_gpio3_readable_table,
+	},
+};
+
+static void rk628_display_disable(struct rk628 *rk628)
+{
+	if (!rk628->display_enabled)
+		return;
+
+	if (rk628->output_mode == OUTPUT_MODE_CSI)
+		rk628_csi_disable(rk628);
+
+	if (rk628->output_mode == OUTPUT_MODE_GVI)
+		rk628_gvi_disable(rk628);
+
+	if (rk628->output_mode == OUTPUT_MODE_LVDS)
+		rk628_lvds_disable(rk628);
+
+	if (rk628->output_mode == OUTPUT_MODE_DSI)
+		rk628_dsi_disable(rk628);
+
+	rk628_post_process_disable(rk628);
+
+	if (rk628->input_mode == INPUT_MODE_HDMI)
+		rk628_hdmirx_disable(rk628);
+
+	rk628->display_enabled = false;
+}
+
+static void rk628_display_resume(struct rk628 *rk628)
+{
+	u8 ret = 0;
+
+	if (rk628->display_enabled)
+		return;
+
+	if (rk628->input_mode == INPUT_MODE_HDMI) {
+		ret = rk628_hdmirx_enable(rk628);
+		if ((ret == HDMIRX_PLUGOUT) || (ret & HDMIRX_NOSIGNAL)) {
+			rk628_display_disable(rk628);
+			return;
+		}
+	}
+
+	if (rk628->input_mode == INPUT_MODE_RGB)
+		rk628_rgb_rx_enable(rk628);
+
+	if (rk628->input_mode == INPUT_MODE_BT1120)
+		rk628_bt1120_rx_enable(rk628);
+
+	rk628_post_process_init(rk628);
+	rk628_post_process_enable(rk628);
+
+	if (rk628->output_mode == OUTPUT_MODE_DSI) {
+		rk628_mipi_dsi_pre_enable(rk628);
+		rk628_mipi_dsi_enable(rk628);
+	}
+
+	if (rk628->output_mode == OUTPUT_MODE_LVDS)
+		rk628_lvds_enable(rk628);
+
+	if (rk628->output_mode == OUTPUT_MODE_GVI)
+		rk628_gvi_enable(rk628);
+
+	if (rk628->output_mode == OUTPUT_MODE_CSI)
+		rk628_csi_enable(rk628);
+
+#ifdef CONFIG_RK628_MISC_HDMITX
+	if (rk628->output_mode == OUTPUT_MODE_HDMI)
+		rk628_hdmitx_enable(rk628);
+#endif
+
+	rk628->display_enabled = true;
+}
+
+static void rk628_display_enable(struct rk628 *rk628)
+{
+	u8 ret = 0;
+
+	if (rk628->display_enabled)
+		return;
+
+	if (rk628->input_mode == INPUT_MODE_RGB)
+		rk628_rgb_rx_enable(rk628);
+
+	if (rk628->input_mode == INPUT_MODE_BT1120)
+		rk628_bt1120_rx_enable(rk628);
+
+	if (rk628->output_mode == OUTPUT_MODE_BT1120)
+		rk628_bt1120_tx_enable(rk628);
+
+	if (rk628->output_mode == OUTPUT_MODE_DSI)
+		queue_delayed_work(rk628->dsi_wq, &rk628->dsi_delay_work, msecs_to_jiffies(10));
+
+	if (rk628->input_mode == INPUT_MODE_HDMI) {
+		ret = rk628_hdmirx_enable(rk628);
+		if ((ret == HDMIRX_PLUGOUT) || (ret & HDMIRX_NOSIGNAL)) {
+			rk628_display_disable(rk628);
+			return;
+		}
+	}
+
+	if (rk628->output_mode != OUTPUT_MODE_HDMI) {
+		rk628_post_process_init(rk628);
+		rk628_post_process_enable(rk628);
+	}
+
+	if (rk628->output_mode == OUTPUT_MODE_LVDS)
+		rk628_lvds_enable(rk628);
+
+	if (rk628->output_mode == OUTPUT_MODE_GVI)
+		rk628_gvi_enable(rk628);
+
+	if (rk628->output_mode == OUTPUT_MODE_CSI)
+		rk628_csi_enable(rk628);
+
+#ifdef CONFIG_RK628_MISC_HDMITX
+	if (rk628->output_mode == OUTPUT_MODE_HDMI)
+		rk628_hdmitx_enable(rk628);
+#endif
+
+	rk628->display_enabled = true;
+}
+
+static void rk628_display_work(struct work_struct *work)
+{
+	u8 ret = 0;
+	struct rk628 *rk628 =
+		container_of(work, struct rk628, delay_work.work);
+	int delay = msecs_to_jiffies(2000);
+
+	if (rk628->input_mode == INPUT_MODE_HDMI) {
+		ret = rk628_hdmirx_detect(rk628);
+		if (!(ret & (HDMIRX_CHANGED | HDMIRX_NOLOCK))) {
+			if (!rk628->plugin_det_gpio)
+				queue_delayed_work(rk628->monitor_wq,
+						   &rk628->delay_work, delay);
+			else
+				rk628_hdmirx_enable_interrupts(rk628, true);
+			return;
+		}
+	}
+
+	if (ret & HDMIRX_PLUGIN) {
+		/* if resolution or input format change, disable first */
+		rk628_display_disable(rk628);
+		rk628_display_enable(rk628);
+	} else if (ret & HDMIRX_PLUGOUT) {
+		rk628_display_disable(rk628);
+	}
+
+	if (rk628->input_mode == INPUT_MODE_HDMI) {
+		if (!rk628->plugin_det_gpio) {
+			if (ret & HDMIRX_NOLOCK)
+				delay = msecs_to_jiffies(200);
+			queue_delayed_work(rk628->monitor_wq, &rk628->delay_work,
+					   delay);
+		} else {
+			rk628_hdmirx_enable_interrupts(rk628, true);
+		}
+	}
+}
+
+static void rk628_dsi_work(struct work_struct *work)
+{
+	struct rk628 *rk628 = container_of(work, struct rk628, dsi_delay_work.work);
+
+	rk628_mipi_dsi_pre_enable(rk628);
+	rk628_mipi_dsi_enable(rk628);
+}
+
+static irqreturn_t rk628_hdmirx_plugin_irq(int irq, void *dev_id)
+{
+	struct rk628 *rk628 = dev_id;
+
+	rk628_hdmirx_enable_interrupts(rk628, false);
+	/* clear interrupts */
+	rk628_i2c_write(rk628, HDMI_RX_MD_ICLR, 0xffffffff);
+	rk628_i2c_write(rk628, HDMI_RX_PDEC_ICLR, 0xffffffff);
+	rk628_i2c_write(rk628, GRF_INTR0_CLR_EN, 0x01000100);
+
+	/* control hpd after 50ms */
+	schedule_delayed_work(&rk628->delay_work, HZ / 20);
+
+	return IRQ_HANDLED;
+}
+
+static bool rk628_input_is_rgb(struct rk628 *rk628)
+{
+	if (rk628->input_mode == INPUT_MODE_RGB || rk628->input_mode == INPUT_MODE_BT1120)
+		return true;
+
+	return false;
+}
+
+static int rk628_display_route_info_parse(struct rk628 *rk628)
+{
+	struct device_node *np;
+	int ret = 0;
+	u32 val;
+
+	if (of_property_read_bool(rk628->dev->of_node, "rk628,hdmi-in"))
+		rk628->input_mode = INPUT_MODE_HDMI;
+	else if (of_property_read_bool(rk628->dev->of_node, "rk628,rgb-in"))
+		rk628->input_mode = INPUT_MODE_RGB;
+	else if (of_property_read_bool(rk628->dev->of_node, "rk628,bt1120-in"))
+		rk628->input_mode = INPUT_MODE_BT1120;
+	else
+		rk628->input_mode = INPUT_MODE_RGB;
+
+	if (of_find_node_by_name(rk628->dev->of_node, "rk628-dsi")) {
+		np = of_find_node_by_name(rk628->dev->of_node, "rk628-dsi");
+		ret = rk628_dsi_parse(rk628, np);
+	} else if (of_find_node_by_name(rk628->dev->of_node, "rk628-lvds")) {
+		np = of_find_node_by_name(rk628->dev->of_node, "rk628-lvds");
+		ret = rk628_lvds_parse(rk628, np);
+	} else if (of_find_node_by_name(rk628->dev->of_node, "rk628-gvi")) {
+		np = of_find_node_by_name(rk628->dev->of_node, "rk628-gvi");
+		ret = rk628_gvi_parse(rk628, np);
+	} else if (of_find_node_by_name(rk628->dev->of_node, "rk628-bt1120")) {
+		rk628->output_mode = OUTPUT_MODE_BT1120;
+	} else {
+		if (of_property_read_bool(rk628->dev->of_node, "rk628,hdmi-out"))
+			rk628->output_mode = OUTPUT_MODE_HDMI;
+		else if (of_property_read_bool(rk628->dev->of_node, "rk628,csi-out"))
+			rk628->output_mode = OUTPUT_MODE_CSI;
+	}
+
+	if (of_property_read_u32(rk628->dev->of_node, "mode-sync-pol", &val) < 0)
+		rk628->sync_pol = MODE_FLAG_PSYNC;
+	else
+		rk628->sync_pol = (!val ? MODE_FLAG_NSYNC : MODE_FLAG_PSYNC);
+
+	if (rk628_input_is_rgb(rk628) && rk628->output_mode == OUTPUT_MODE_RGB)
+		return -EINVAL;
+
+	return ret;
+}
+
+static void
+rk628_display_mode_from_videomode(const struct rk628_videomode *vm,
+				  struct rk628_display_mode *dmode)
+{
+	dmode->hdisplay = vm->hactive;
+	dmode->hsync_start = dmode->hdisplay + vm->hfront_porch;
+	dmode->hsync_end = dmode->hsync_start + vm->hsync_len;
+	dmode->htotal = dmode->hsync_end + vm->hback_porch;
+
+	dmode->vdisplay = vm->vactive;
+	dmode->vsync_start = dmode->vdisplay + vm->vfront_porch;
+	dmode->vsync_end = dmode->vsync_start + vm->vsync_len;
+	dmode->vtotal = dmode->vsync_end + vm->vback_porch;
+
+	dmode->clock = vm->pixelclock / 1000;
+	dmode->flags = vm->flags;
+}
+
+static void
+of_parse_rk628_display_timing(struct device_node *np, struct rk628_videomode *vm)
+{
+	u8 val;
+
+	of_property_read_u32(np, "clock-frequency", &vm->pixelclock);
+	of_property_read_u32(np, "hactive", &vm->hactive);
+	of_property_read_u32(np, "hfront-porch", &vm->hfront_porch);
+	of_property_read_u32(np, "hback-porch", &vm->hback_porch);
+	of_property_read_u32(np, "hsync-len", &vm->hsync_len);
+
+	of_property_read_u32(np, "vactive", &vm->vactive);
+	of_property_read_u32(np, "vfront-porch", &vm->vfront_porch);
+	of_property_read_u32(np, "vback-porch", &vm->vback_porch);
+	of_property_read_u32(np, "vsync-len", &vm->vsync_len);
+
+	vm->flags = 0;
+	of_property_read_u8(np, "hsync-active", &val);
+	vm->flags |= val ? DRM_MODE_FLAG_PHSYNC : DRM_MODE_FLAG_NHSYNC;
+
+	of_property_read_u8(np, "vsync-active", &val);
+	vm->flags |= val ? DRM_MODE_FLAG_PVSYNC : DRM_MODE_FLAG_NVSYNC;
+}
+
+static int rk628_get_video_mode(struct rk628 *rk628)
+{
+
+	struct device_node *timings_np, *src_np, *dst_np;
+	struct rk628_videomode vm;
+
+	timings_np = of_get_child_by_name(rk628->dev->of_node, "display-timings");
+	if (!timings_np) {
+		dev_info(rk628->dev, "failed to found display timings\n");
+		return -EINVAL;
+	}
+
+	src_np = of_get_child_by_name(timings_np, "src-timing");
+	if (!src_np) {
+		dev_info(rk628->dev, "failed to found src timing\n");
+		of_node_put(timings_np);
+		return -EINVAL;
+	}
+
+	of_parse_rk628_display_timing(src_np, &vm);
+	rk628_display_mode_from_videomode(&vm, &rk628->src_mode);
+	dev_info(rk628->dev, "src mode: %d %d %d %d %d %d %d %d %d 0x%x\n",
+		 rk628->src_mode.clock, rk628->src_mode.hdisplay, rk628->src_mode.hsync_start,
+		 rk628->src_mode.hsync_end, rk628->src_mode.htotal, rk628->src_mode.vdisplay,
+		 rk628->src_mode.vsync_start, rk628->src_mode.vsync_end, rk628->src_mode.vtotal,
+		 rk628->src_mode.flags);
+
+	dst_np = of_get_child_by_name(timings_np, "dst-timing");
+	if (!dst_np) {
+		dev_info(rk628->dev, "failed to found dst timing\n");
+		of_node_put(timings_np);
+		of_node_put(src_np);
+		return -EINVAL;
+	}
+
+	of_parse_rk628_display_timing(dst_np, &vm);
+	rk628_display_mode_from_videomode(&vm, &rk628->dst_mode);
+	dev_info(rk628->dev, "dst mode: %d %d %d %d %d %d %d %d %d 0x%x\n",
+		 rk628->dst_mode.clock, rk628->dst_mode.hdisplay, rk628->dst_mode.hsync_start,
+		 rk628->dst_mode.hsync_end, rk628->dst_mode.htotal, rk628->dst_mode.vdisplay,
+		 rk628->dst_mode.vsync_start, rk628->dst_mode.vsync_end, rk628->dst_mode.vtotal,
+		 rk628->dst_mode.flags);
+
+	of_node_put(timings_np);
+	of_node_put(src_np);
+	of_node_put(dst_np);
+
+	return 0;
+}
+
+static int rk628_display_timings_get(struct rk628 *rk628)
+{
+	int ret;
+
+	ret = rk628_get_video_mode(rk628);
+
+	return ret;
+
+}
+
+#define DEBUG_PRINT(args...) \
+		do { \
+			if (s) \
+				seq_printf(s, args); \
+			else \
+				pr_info(args); \
+		} while (0)
+
+static int rk628_debugfs_dump(struct seq_file *s, void *data)
+{
+	struct rk628 *rk628 = s->private;
+
+	u32 val;
+	u32 dsp_htotal, dsp_hs_end, dsp_hact_st, dsp_hact_end;
+	u32 dsp_vtotal, dsp_vs_end, dsp_vact_st, dsp_vact_end;
+	u32 src_hactive, src_hoffset, src_htotal, src_hs_end;
+	u32 src_vactive, src_voffset, src_vtotal, src_vs_end;
+
+	u32 input_mode, output_mode;
+	char input_s[10];
+	char output_s[13];
+
+	bool r2y, y2r;
+	char csc_mode_r2y_s[10];
+	char csc_mode_y2r_s[10];
+	u32 csc;
+	enum csc_mode {
+		BT601_L,
+		BT709_L,
+		BT601_F,
+		BT2020
+	};
+
+	int sw_hsync_pol, sw_vsync_pol;
+	u32 dsp_frame_v_start, dsp_frame_h_start;
+
+	int sclk_vop_sel = 0;
+	u32 sclk_vop_div;
+	u64 sclk_vop;
+	u32 reg_v;
+	u32 fps;
+
+	u32 imodet_clk;
+	u32 imodet_clk_sel;
+	u32 imodet_clk_div;
+
+	int clk_rx_read_sel = 0;
+	u32 clk_rx_read_div;
+	u64 clk_rx_read;
+
+	u32 tdms_clk_div;
+	u32 tdms_clk;
+	u32 common_tdms_clk[19] = {
+		25170, 27000, 33750, 40000, 59400,
+		65000, 68250, 74250, 83500, 85500,
+		88750, 92812, 101000, 108000, 119000,
+		135000, 148500, 162000, 297000,
+	};
+
+	//get sclk vop
+	rk628_i2c_read(rk628, 0xc0088, &reg_v);
+	sclk_vop_sel = (reg_v & 0x20) ? 1 : 0;
+	rk628_i2c_read(rk628, 0xc00b4, &reg_v);
+	if (reg_v)
+		sclk_vop_div = reg_v;
+	else
+		sclk_vop_div = 0x10002;
+	/* gpll 983.04MHz */
+	/* cpll 1188MHz */
+	if (sclk_vop_sel)
+		sclk_vop = (u64)983040 * ((sclk_vop_div & 0xffff0000) >> 16);
+	else
+		sclk_vop = (u64)1188000 * ((sclk_vop_div & 0xffff0000) >> 16);
+	do_div(sclk_vop, sclk_vop_div & 0xffff);
+
+	//get rx read clk
+	rk628_i2c_read(rk628, 0xc0088, &reg_v);
+	clk_rx_read_sel = (reg_v & 0x10) ? 1 : 0;
+	rk628_i2c_read(rk628, 0xc00b8, &reg_v);
+	if (reg_v)
+		clk_rx_read_div = reg_v;
+	else
+		clk_rx_read_div = 0x10002;
+	/* gpll 983.04MHz */
+	/* cpll 1188MHz */
+	if (clk_rx_read_sel)
+		clk_rx_read = (u64)983040 * ((clk_rx_read_div & 0xffff0000) >> 16);
+	else
+		clk_rx_read = (u64)1188000 * ((clk_rx_read_div & 0xffff0000) >> 16);
+	do_div(clk_rx_read, clk_rx_read_div & 0xffff);
+
+	//get imodet clk
+	rk628_i2c_read(rk628, 0xc0094, &reg_v);
+	imodet_clk_sel = (reg_v & 0x20) ? 1 : 0;
+
+	if (reg_v)
+		imodet_clk_div = (reg_v & 0x1f) + 1;
+	else
+		imodet_clk_div = 0x18;
+	/* gpll 983.04MHz */
+	/* cpll 1188MHz */
+	if (imodet_clk_sel)
+		imodet_clk = 983040 / imodet_clk_div;
+	else
+		imodet_clk = 1188000 / imodet_clk_div;
+
+	//get input interface type
+	rk628_i2c_read(rk628, GRF_SYSTEM_CON0, &val);
+	input_mode = val & 0x7;
+	output_mode = (val & 0xf8) >> 3;
+	sw_hsync_pol = (val & 0x4000000) ? 1 : 0;
+	sw_vsync_pol = (val & 0x2000000) ? 1 : 0;
+	switch (input_mode) {
+	case 0:
+		strcpy(input_s, "HDMI");
+		break;
+	case 1:
+		strcpy(input_s, "reserved");
+		break;
+	case 2:
+		strcpy(input_s, "BT1120");
+		break;
+	case 3:
+		strcpy(input_s, "RGB");
+		break;
+	case 4:
+		strcpy(input_s, "YUV");
+		break;
+	default:
+		strcpy(input_s, "unknown");
+	}
+	DEBUG_PRINT("input:%s\n", input_s);
+	if (input_mode == 0) {
+		//get tdms clk
+		rk628_i2c_read(rk628, 0x16654, &reg_v);
+		reg_v = (reg_v & 0x3f0000) >> 16;
+		if (reg_v >= 0 && reg_v <= 19)
+			tdms_clk = common_tdms_clk[reg_v];
+		else
+			tdms_clk = 148500;
+
+		rk628_i2c_read(rk628, 0x166a8, &reg_v);
+		reg_v = (reg_v & 0xf00) >> 8;
+		if (reg_v == 0x6)
+			tdms_clk_div = 1;
+		else if (reg_v == 0x0)
+			tdms_clk_div = 2;
+		else
+			tdms_clk_div = 1;
+
+		//get input hdmi timing
+		//get horizon timing
+		rk628_i2c_read(rk628, 0x30150, &reg_v);
+		src_hactive = reg_v & 0xffff;
+
+		rk628_i2c_read(rk628, 0x3014c, &reg_v);
+		src_hoffset = (reg_v & 0xffff);
+
+		src_hactive *= tdms_clk_div;
+		src_hoffset *=  tdms_clk_div;
+
+		src_htotal = (reg_v & 0xffff0000)>>16;
+		src_htotal *= tdms_clk_div;
+
+		rk628_i2c_read(rk628, 0x30148, &reg_v);
+		reg_v = reg_v & 0xffff;
+		src_hs_end = reg_v * tdms_clk * tdms_clk_div / imodet_clk;
+
+		//get vertical timing
+		rk628_i2c_read(rk628, 0x30168, &reg_v);
+		src_vactive = reg_v & 0xffff;
+		rk628_i2c_read(rk628, 0x30170, &reg_v);
+		src_vtotal = reg_v & 0xffff;
+		rk628_i2c_read(rk628, 0x30164, &reg_v);
+		src_voffset = (reg_v & 0xffff);
+
+		rk628_i2c_read(rk628, 0x3015c, &reg_v);
+		reg_v = reg_v & 0xffff;
+		src_vs_end = reg_v * clk_rx_read;
+		do_div(src_vs_end, imodet_clk * src_htotal);
+
+		//get fps and print
+		fps = clk_rx_read * 1000;
+		do_div(fps, src_htotal * src_vtotal);
+		DEBUG_PRINT("    Display mode: %dx%dp%d,dclk[%llu],tdms_clk[%d]\n",
+			    src_hactive, src_vactive, fps, clk_rx_read, tdms_clk);
+
+		DEBUG_PRINT("\tH: %d %d %d %d\n", src_hactive, src_htotal - src_hoffset,
+			    src_htotal - src_hoffset + src_hs_end, src_htotal);
+
+		DEBUG_PRINT("\tV: %d %d %d %d\n", src_vactive,
+			    src_vtotal - src_voffset - src_vs_end,
+			    src_vtotal - src_voffset, src_vtotal);
+	} else if (input_mode == 2 || input_mode == 3 || input_mode == 4) {
+		//get timing
+		rk628_i2c_read(rk628, 0x130, &reg_v);
+		src_hactive = reg_v & 0xffff;
+
+		rk628_i2c_read(rk628, 0x12c, &reg_v);
+		src_vactive = (reg_v & 0xffff);
+
+		rk628_i2c_read(rk628, 0x134, &reg_v);
+		src_htotal = (reg_v & 0xffff0000) >> 16;
+		src_vtotal = reg_v & 0xffff;
+
+		//get fps and print
+		fps = clk_rx_read * 1000;
+		do_div(fps, src_htotal * src_vtotal);
+		DEBUG_PRINT("    Display mode: %dx%dp%d,dclk[%llu]\n",
+			    src_hactive, src_vactive, fps, clk_rx_read);
+
+		DEBUG_PRINT("\tH-total: %d\n", src_htotal);
+
+		DEBUG_PRINT("\tV-total: %d\n", src_vtotal);
+	}
+	//get output interface type
+	switch (output_mode & 0x7) {
+	case 1:
+		strcpy(output_s, "GVI");
+		break;
+	case 2:
+		strcpy(output_s, "LVDS");
+		break;
+	case 3:
+		strcpy(output_s, "HDMI");
+		break;
+	case 4:
+		strcpy(output_s, "CSI");
+		break;
+	case 5:
+		strcpy(output_s, "DSI");
+		break;
+	default:
+		strcpy(output_s, "");
+	}
+	strcpy(output_s + 4, " ");
+	switch (output_mode >> 2) {
+	case 0:
+		strcpy(output_s + 5, "");
+		break;
+	case 1:
+		strcpy(output_s + 5, "BT1120");
+		break;
+	case 2:
+		strcpy(output_s + 5, "RGB");
+		break;
+	case 3:
+		strcpy(output_s + 5, "YUV");
+		break;
+	default:
+		strcpy(output_s + 5, "unknown");
+	}
+	DEBUG_PRINT("output:%s\n", output_s);
+
+	//get output timing
+	rk628_i2c_read(rk628, GRF_SCALER_CON3, &val);
+	dsp_htotal = val & 0xffff;
+	dsp_hs_end = (val & 0xff0000) >> 16;
+
+	rk628_i2c_read(rk628, GRF_SCALER_CON4, &val);
+	dsp_hact_end = val & 0xffff;
+	dsp_hact_st = (val & 0xfff0000) >> 16;
+
+	rk628_i2c_read(rk628, GRF_SCALER_CON5, &val);
+	dsp_vtotal = val & 0xfff;
+	dsp_vs_end = (val & 0xff0000) >> 16;
+
+	rk628_i2c_read(rk628, GRF_SCALER_CON6, &val);
+	dsp_vact_st = (val & 0xfff0000) >> 16;
+	dsp_vact_end = val & 0xfff;
+
+	fps = sclk_vop * 1000;
+	do_div(fps, dsp_vtotal * dsp_htotal);
+
+	DEBUG_PRINT("    Display mode: %dx%dp%d,dclk[%llu]\n",
+		    dsp_hact_end - dsp_hact_st, dsp_vact_end - dsp_vact_st, fps, sclk_vop);
+	DEBUG_PRINT("\tH: %d %d %d %d\n", dsp_hact_end - dsp_hact_st,
+		    dsp_htotal - dsp_hact_st, dsp_htotal - dsp_hact_st + dsp_hs_end, dsp_htotal);
+	DEBUG_PRINT("\tV: %d %d %d %d\n", dsp_vact_end - dsp_vact_st,
+		    dsp_vtotal - dsp_vact_st, dsp_vtotal - dsp_vact_st + dsp_vs_end, dsp_vtotal);
+
+	//get csc and system information
+	rk628_i2c_read(rk628, GRF_CSC_CTRL_CON, &val);
+	r2y = ((val & 0x10) == 0x10);
+	y2r = ((val & 0x1) == 0x1);
+	csc = (val & 0xc0) >> 6;
+	switch (csc) {
+	case BT601_L:
+		strcpy(csc_mode_r2y_s, "BT601_L");
+		break;
+	case BT601_F:
+		strcpy(csc_mode_r2y_s, "BT601_F");
+		break;
+	case BT709_L:
+		strcpy(csc_mode_r2y_s, "BT709_L");
+		break;
+	case BT2020:
+		strcpy(csc_mode_r2y_s, "BT2020");
+		break;
+	}
+
+	csc = (val & 0xc) >> 2;
+	switch (csc) {
+	case BT601_L:
+		strcpy(csc_mode_y2r_s, "BT601_L");
+		break;
+	case BT601_F:
+		strcpy(csc_mode_y2r_s, "BT601_F");
+		break;
+	case BT709_L:
+		strcpy(csc_mode_y2r_s, "BT709_L");
+		break;
+	case BT2020:
+		strcpy(csc_mode_y2r_s, "BT2020");
+		break;
+
+	}
+	DEBUG_PRINT("csc:\n");
+
+	if (r2y)
+		DEBUG_PRINT("\tr2y[1],csc mode:%s\n", csc_mode_r2y_s);
+	else if (y2r)
+		DEBUG_PRINT("\ty2r[1],csc mode:%s\n", csc_mode_y2r_s);
+	else
+		DEBUG_PRINT("\tnot open\n");
+
+	rk628_i2c_read(rk628, GRF_SCALER_CON2, &val);
+	dsp_frame_h_start = val & 0xffff;
+	dsp_frame_v_start = (val & 0xffff0000) >> 16;
+
+	DEBUG_PRINT("system:\n");
+	DEBUG_PRINT("\tsw_hsync_pol:%d, sw_vsync_pol:%d\n", sw_hsync_pol, sw_vsync_pol);
+	DEBUG_PRINT("\tdsp_frame_h_start:%d, dsp_frame_v_start:%d\n",
+		    dsp_frame_h_start, dsp_frame_v_start);
+
+	return 0;
+}
+
+static int rk628_debugfs_open(struct inode *inode, struct file *file)
+{
+	struct rk628 *rk628 = inode->i_private;
+
+	return single_open(file, rk628_debugfs_dump, rk628);
+}
+
+
+static const struct file_operations rk628_debugfs_summary_fops = {
+	.owner = THIS_MODULE,
+	.open = rk628_debugfs_open,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = single_release,
+
+};
+
+static int
+rk628_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+	struct device *dev = &client->dev;
+	struct rk628 *rk628;
+	int i, ret;
+	int err;
+	unsigned long irq_flags;
+	struct dentry *debug_dir;
+
+	dev_info(dev, "RK628 misc driver version: %s\n", DRIVER_VERSION);
+
+	rk628 = devm_kzalloc(dev, sizeof(*rk628), GFP_KERNEL);
+	if (!rk628)
+		return -ENOMEM;
+
+	rk628->dev = dev;
+	rk628->client = client;
+	i2c_set_clientdata(client, rk628);
+	rk628->hdmirx_irq = client->irq;
+
+	ret = rk628_display_route_info_parse(rk628);
+	if (ret) {
+		dev_err(dev, "display route err\n");
+		return ret;
+	}
+
+	if (rk628->output_mode != OUTPUT_MODE_HDMI &&
+	    rk628->output_mode != OUTPUT_MODE_CSI) {
+		ret = rk628_display_timings_get(rk628);
+		if (ret) {
+			dev_info(dev, "display timings err\n");
+			return ret;
+		}
+	}
+
+	rk628->soc_24M = devm_clk_get(dev, "soc_24M");
+	if (rk628->soc_24M == ERR_PTR(-ENOENT))
+		rk628->soc_24M = NULL;
+
+	if (IS_ERR(rk628->soc_24M)) {
+		ret = PTR_ERR(rk628->soc_24M);
+		dev_err(dev, "Unable to get soc_24M: %d\n", ret);
+		return ret;
+	}
+
+	clk_prepare_enable(rk628->soc_24M);
+
+	rk628->enable_gpio = devm_gpiod_get_optional(dev, "enable",
+						     GPIOD_OUT_LOW);
+	if (IS_ERR(rk628->enable_gpio)) {
+		ret = PTR_ERR(rk628->enable_gpio);
+		dev_err(dev, "failed to request enable GPIO: %d\n", ret);
+		return ret;
+	}
+
+	rk628->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
+	if (IS_ERR(rk628->reset_gpio)) {
+		ret = PTR_ERR(rk628->reset_gpio);
+		dev_err(dev, "failed to request reset GPIO: %d\n", ret);
+		return ret;
+	}
+
+	rk628->plugin_det_gpio = devm_gpiod_get_optional(dev, "plugin-det",
+						    GPIOD_IN);
+	if (IS_ERR(rk628->plugin_det_gpio)) {
+		dev_err(rk628->dev, "failed to get hdmirx det gpio\n");
+		ret = PTR_ERR(rk628->plugin_det_gpio);
+		return ret;
+	}
+
+	gpiod_set_value(rk628->enable_gpio, 1);
+	usleep_range(10000, 11000);
+	gpiod_set_value(rk628->reset_gpio, 0);
+	usleep_range(10000, 11000);
+	gpiod_set_value(rk628->reset_gpio, 1);
+	usleep_range(10000, 11000);
+	gpiod_set_value(rk628->reset_gpio, 0);
+	usleep_range(10000, 11000);
+
+	for (i = 0; i < RK628_DEV_MAX; i++) {
+		const struct regmap_config *config = &rk628_regmap_config[i];
+
+		if (!config->name)
+			continue;
+
+		rk628->regmap[i] = devm_regmap_init_i2c(client, config);
+		if (IS_ERR(rk628->regmap[i])) {
+			ret = PTR_ERR(rk628->regmap[i]);
+			dev_err(dev, "failed to allocate register map %d: %d\n",
+				i, ret);
+			return ret;
+		}
+	}
+
+	/* selete int io function */
+	ret = rk628_i2c_write(rk628, GRF_GPIO3AB_SEL_CON, 0x30002000);
+	if (ret) {
+		dev_err(dev, "failed to access register: %d\n", ret);
+		return ret;
+	}
+
+	rk628->monitor_wq = alloc_ordered_workqueue("%s",
+		WQ_MEM_RECLAIM | WQ_FREEZABLE, "rk628-monitor-wq");
+	INIT_DELAYED_WORK(&rk628->delay_work, rk628_display_work);
+
+	if (rk628->output_mode == OUTPUT_MODE_DSI) {
+		rk628->dsi_wq = alloc_ordered_workqueue("%s",
+			WQ_MEM_RECLAIM | WQ_FREEZABLE, "rk628-dsi-wq");
+		INIT_DELAYED_WORK(&rk628->dsi_delay_work, rk628_dsi_work);
+	}
+
+	rk628_cru_init(rk628);
+
+	if (rk628->output_mode == OUTPUT_MODE_CSI)
+		rk628_csi_init(rk628);
+
+	if (rk628->input_mode == INPUT_MODE_HDMI) {
+		if (rk628->plugin_det_gpio) {
+			rk628->plugin_irq = gpiod_to_irq(rk628->plugin_det_gpio);
+			if (rk628->plugin_irq < 0) {
+				dev_err(rk628->dev, "failed to get plugin det irq\n");
+				err = rk628->plugin_irq;
+				return err;
+			}
+
+			err = devm_request_threaded_irq(dev, rk628->plugin_irq, NULL,
+					rk628_hdmirx_plugin_irq, IRQF_TRIGGER_FALLING |
+					IRQF_TRIGGER_RISING | IRQF_ONESHOT, "rk628_hdmirx", rk628);
+			if (err) {
+				dev_err(rk628->dev, "failed to register plugin det irq (%d)\n",
+					err);
+				return err;
+			}
+
+			if (rk628->hdmirx_irq) {
+				irq_flags =
+					irqd_get_trigger_type(irq_get_irq_data(rk628->hdmirx_irq));
+				dev_dbg(rk628->dev, "cfg hdmirx irq, flags: %lu!\n", irq_flags);
+				err = devm_request_threaded_irq(dev, rk628->hdmirx_irq, NULL,
+						rk628_hdmirx_plugin_irq, irq_flags |
+						IRQF_ONESHOT, "rk628", rk628);
+				if (err) {
+					dev_err(rk628->dev, "request rk628 irq failed! err:%d\n",
+							err);
+					return err;
+				}
+				/* hdmirx int en */
+				rk628_i2c_write(rk628, GRF_INTR0_EN, 0x01000100);
+				rk628_display_enable(rk628);
+				queue_delayed_work(rk628->monitor_wq, &rk628->delay_work,
+						   msecs_to_jiffies(20));
+			}
+		} else {
+			rk628_display_enable(rk628);
+			queue_delayed_work(rk628->monitor_wq, &rk628->delay_work,
+					    msecs_to_jiffies(50));
+		}
+	} else {
+		rk628_display_enable(rk628);
+	}
+
+	pm_runtime_enable(dev);
+	debug_dir = debugfs_create_dir(rk628->dev->driver->name, NULL);
+	if (!debug_dir)
+		return 0;
+
+	debugfs_create_file("summary", 0400, debug_dir, rk628, &rk628_debugfs_summary_fops);
+
+	return 0;
+}
+
+static int rk628_i2c_remove(struct i2c_client *client)
+{
+	struct rk628 *rk628 = i2c_get_clientdata(client);
+	struct device *dev = &client->dev;
+
+	if (rk628->output_mode == OUTPUT_MODE_DSI) {
+		cancel_delayed_work_sync(&rk628->dsi_delay_work);
+		destroy_workqueue(rk628->dsi_wq);
+	}
+
+	cancel_delayed_work_sync(&rk628->delay_work);
+	destroy_workqueue(rk628->monitor_wq);
+	pm_runtime_disable(dev);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int rk628_suspend(struct device *dev)
+{
+	struct rk628 *rk628 = dev_get_drvdata(dev);
+
+	rk628_display_disable(rk628);
+
+	return 0;
+}
+
+static int rk628_resume(struct device *dev)
+{
+	struct rk628 *rk628 = dev_get_drvdata(dev);
+
+	rk628_display_resume(rk628);
+
+	return 0;
+}
+#endif
+
+static const struct dev_pm_ops rk628_pm_ops = {
+#ifdef CONFIG_PM_SLEEP
+	.suspend = rk628_suspend,
+	.resume = rk628_resume,
+#endif
+};
+static const struct of_device_id rk628_of_match[] = {
+	{ .compatible = "rockchip,rk628", },
+	{}
+};
+MODULE_DEVICE_TABLE(of, rk628_of_match);
+
+static const struct i2c_device_id rk628_i2c_id[] = {
+	{ "rk628", 0 },
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, rk628_i2c_id);
+
+static struct i2c_driver rk628_i2c_driver = {
+	.driver = {
+		.name = "rk628",
+		.of_match_table = of_match_ptr(rk628_of_match),
+		.pm = &rk628_pm_ops,
+	},
+	.probe = rk628_i2c_probe,
+	.remove = rk628_i2c_remove,
+	.id_table = rk628_i2c_id,
+};
+
+#ifdef CONFIG_ROCKCHIP_THUNDER_BOOT_RK628
+static int __init rk628_i2c_driver_init(void)
+{
+	i2c_add_driver(&rk628_i2c_driver);
+
+	return 0;
+}
+subsys_initcall_sync(rk628_i2c_driver_init);
+
+static void __exit rk628_i2c_driver_exit(void)
+{
+	i2c_del_driver(&rk628_i2c_driver);
+}
+module_exit(rk628_i2c_driver_exit);
+#else
+module_i2c_driver(rk628_i2c_driver);
+#endif
+
+MODULE_AUTHOR("Wyon Bi <bivvy.bi@rock-chips.com>");
+MODULE_DESCRIPTION("Rockchip RK628 MFD driver");
+MODULE_LICENSE("GPL");
diff --git a/kernel/drivers/misc/rk628/rk628.h b/kernel/drivers/misc/rk628/rk628.h
new file mode 100644
index 0000000..82e39d4
--- /dev/null
+++ b/kernel/drivers/misc/rk628/rk628.h
@@ -0,0 +1,484 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2020 Rockchip Electronics Co. Ltd.
+ *
+ * Author: Wyon Bi <bivvy.bi@rock-chips.com>
+ */
+
+#ifndef _RK628_H
+#define _RK628_H
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/regmap.h>
+#include <linux/version.h>
+#include <linux/of.h>
+#include <linux/wakelock.h>
+#include <linux/workqueue.h>
+#include <linux/regulator/consumer.h>
+
+#define DRIVER_VERSION				"0.0.1"
+#define UPDATE(x, h, l)		(((x) << (l)) & GENMASK((h), (l)))
+#define HIWORD_UPDATE(v, h, l)	((((v) << (l)) & GENMASK((h), (l))) | \
+				 (GENMASK((h), (l)) << 16))
+
+#define GRF_SYSTEM_CON0			0x0000
+#define SW_VSYNC_POL_MASK		BIT(26)
+#define SW_VSYNC_POL(x)			UPDATE(x, 26, 26)
+#define SW_HSYNC_POL_MASK		BIT(25)
+#define SW_HSYNC_POL(x)			UPDATE(x, 25, 25)
+#define SW_ADAPTER_I2CSLADR_MASK	GENMASK(24, 22)
+#define SW_ADAPTER_I2CSLADR(x)		UPDATE(x, 24, 22)
+#define SW_EDID_MODE_MASK		BIT(21)
+#define SW_EDID_MODE(x)			UPDATE(x, 21, 21)
+#define SW_I2S_DATA_OEN_MASK		BIT(10)
+#define SW_I2S_DATA_OEN(x)		UPDATE(x, 10, 10)
+#define SW_BT_DATA_OEN_MASK		BIT(9)
+#define SW_BT_DATA_OEN			BIT(9)
+#define SW_EFUSE_HDCP_EN_MASK		BIT(8)
+#define SW_EFUSE_HDCP_EN(x)		UPDATE(x, 8, 8)
+#define SW_OUTPUT_MODE_MASK		GENMASK(7, 3)
+#define SW_OUTPUT_MODE(x)		UPDATE(x, 7, 3)
+#define SW_INPUT_MODE_MASK		GENMASK(2, 0)
+#define SW_INPUT_MODE(x)		UPDATE(x, 2, 0)
+#define GRF_SYSTEM_CON1			0x0004
+#define GRF_SYSTEM_CON2			0x0008
+#define GRF_SYSTEM_CON3			0x000c
+#define GRF_GPIO_RX_CEC_SEL_MASK	BIT(7)
+#define GRF_GPIO_RX_CEC_SEL(x)		UPDATE(x, 7, 7)
+#define GRF_GPIO_RXDDC_SDA_SEL_MASK	BIT(6)
+#define GRF_GPIO_RXDDC_SDA_SEL(x)	UPDATE(x, 6, 6)
+#define GRF_GPIO_RXDDC_SCL_SEL_MASK	BIT(5)
+#define GRF_GPIO_RXDDC_SCL_SEL(x)	UPDATE(x, 5, 5)
+#define GRF_SCALER_CON0			0x0010
+#define SCL_VER_DOWN_MODE(x)		HIWORD_UPDATE(x, 8, 8)
+#define SCL_HOR_DOWN_MODE(x)		HIWORD_UPDATE(x, 7, 7)
+#define SCL_BIC_COE_SEL(x)		HIWORD_UPDATE(x, 6, 5)
+#define SCL_VER_MODE(x)			HIWORD_UPDATE(x, 4, 3)
+#define SCL_HOR_MODE(x)			HIWORD_UPDATE(x, 2, 1)
+#define SCL_EN(x)			HIWORD_UPDATE(x, 0, 0)
+#define GRF_SCALER_CON1			0x0014
+#define SCL_V_FACTOR(x)			UPDATE(x, 31, 16)
+#define SCL_H_FACTOR(x)			UPDATE(x, 15, 0)
+#define GRF_SCALER_CON2			0x0018
+#define DSP_FRAME_VST(x)		UPDATE(x, 28, 16)
+#define DSP_FRAME_HST(x)		UPDATE(x, 12, 0)
+#define GRF_SCALER_CON3			0x001c
+#define DSP_HS_END(x)			UPDATE(x, 23, 16)
+#define DSP_HTOTAL(x)			UPDATE(x, 12, 0)
+#define GRF_SCALER_CON4			0x0020
+#define DSP_HACT_ST(x)			UPDATE(x, 28, 16)
+#define DSP_HACT_END(x)			UPDATE(x, 12, 0)
+#define GRF_SCALER_CON5			0x0024
+#define DSP_VS_END(x)			UPDATE(x, 23, 16)
+#define DSP_VTOTAL(x)			UPDATE(x, 12, 0)
+#define GRF_SCALER_CON6			0x0028
+#define DSP_VACT_ST(x)			UPDATE(x, 28, 16)
+#define DSP_VACT_END(x)			UPDATE(x, 12, 0)
+#define GRF_SCALER_CON7			0x002c
+#define DSP_HBOR_ST(x)			UPDATE(x, 28, 16)
+#define DSP_HBOR_END(x)			UPDATE(x, 12, 0)
+#define GRF_SCALER_CON8			0x0030
+#define DSP_VBOR_ST(x)			UPDATE(x, 28, 16)
+#define DSP_VBOR_END(x)			UPDATE(x, 12, 0)
+#define GRF_POST_PROC_CON		0x0034
+#define SW_DCLK_OUT_INV_EN		BIT(9)
+#define SW_DCLK_IN_INV_EN		BIT(8)
+#define SW_TXPHY_REFCLK_SEL_MASK	GENMASK(6, 5)
+#define SW_TXPHY_REFCLK_SEL(x)		UPDATE(x, 6, 5)
+#define SW_HDMITX_VCLK_PLLREF_SEL_MASK	BIT(4)
+#define SW_HDMITX_VCLK_PLLREF_SEL(x)	UPDATE(x, 4, 4)
+#define SW_HDMITX_DCLK_INV_EN		BIT(3)
+#define SW_SPLIT_MODE(x)		UPDATE(x, 1, 1)
+#define SW_SPLIT_EN			BIT(0)
+#define GRF_CSC_CTRL_CON		0x0038
+#define SW_YUV2VYU_SWP(x)		HIWORD_UPDATE(x, 8, 8)
+#define SW_R2Y_EN(x)			HIWORD_UPDATE(x, 4, 4)
+#define SW_Y2R_EN(x)			HIWORD_UPDATE(x, 0, 0)
+#define GRF_LVDS_TX_CON			0x003c
+#define SW_LVDS_CON_DUAL_SEL(x)		HIWORD_UPDATE(x, 12, 12)
+#define SW_LVDS_CON_DEN_POLARITY(x)	HIWORD_UPDATE(x, 11, 11)
+#define SW_LVDS_CON_HS_POLARITY(x)	HIWORD_UPDATE(x, 10, 10)
+#define SW_LVDS_CON_CLKINV(x)		HIWORD_UPDATE(x, 9, 9)
+#define SW_LVDS_STARTPHASE(x)		HIWORD_UPDATE(x, 8, 8)
+#define SW_LVDS_CON_STARTSEL(x)		HIWORD_UPDATE(x, 7, 7)
+#define SW_LVDS_CON_CHASEL(x)		HIWORD_UPDATE(x, 6, 6)
+#define SW_LVDS_TIE_VSYNC_VALUE(x)	HIWORD_UPDATE(x, 5, 5)
+#define SW_LVDS_TIE_HSYNC_VALUE(x)	HIWORD_UPDATE(x, 4, 4)
+#define SW_LVDS_TIE_DEN_ONLY(x)		HIWORD_UPDATE(x, 3, 3)
+#define SW_LVDS_CON_MSBSEL(x)		HIWORD_UPDATE(x, 2, 2)
+#define SW_LVDS_CON_SELECT(x)		HIWORD_UPDATE(x, 1, 0)
+#define GRF_RGB_DEC_CON0		0x0040
+#define SW_HRES_MASK			GENMASK(28, 16)
+#define SW_HRES(x)			UPDATE(x, 28, 16)
+#define DUAL_DATA_SWAP			BIT(6)
+#define DEC_DUALEDGE_EN			BIT(5)
+#define SW_PROGRESS_EN			BIT(4)
+#define SW_YC_SWAP			BIT(3)
+#define SW_CAP_EN_ASYNC			BIT(1)
+#define SW_CAP_EN_PSYNC			BIT(0)
+#define GRF_RGB_DEC_CON1		0x0044
+#define SW_SET_X_MASK			GENMASK(28, 16)
+#define SW_SET_X(x)			HIWORD_UPDATE(x, 28, 16)
+#define SW_SET_Y_MASK			GENMASK(28, 16)
+#define SW_SET_Y(x)			HIWORD_UPDATE(x, 28, 16)
+#define GRF_RGB_DEC_CON2		0x0048
+#define GRF_RGB_ENC_CON			0x004c
+#define BT1120_UV_SWAP(x)		HIWORD_UPDATE(x, 5, 5)
+#define ENC_DUALEDGE_EN(x)		HIWORD_UPDATE(x, 3, 3)
+#define GRF_MIPI_LANE_DELAY_CON0	0x0050
+#define GRF_MIPI_LANE_DELAY_CON1	0x0054
+#define GRF_BT1120_DCLK_DELAY_CON0	0x0058
+#define GRF_BT1120_DCLK_DELAY_CON1	0x005c
+#define GRF_MIPI_TX0_CON		0x0060
+#define DPIUPDATECFG			BIT(26)
+#define DPICOLORM			BIT(25)
+#define DPISHUTDN			BIT(24)
+#define CSI_PHYRSTZ			BIT(21)
+#define CSI_PHYSHUTDOWNZ		BIT(20)
+#define FORCETXSTOPMODE_MASK		GENMASK(19, 16)
+#define FORCETXSTOPMODE(x)		UPDATE(x, 19, 16)
+#define FORCERXMODE_MASK		GENMASK(15, 12)
+#define FORCERXMODE(x)			UPDATE(x, 15, 12)
+#define PHY_TESTCLR			BIT(10)
+#define PHY_TESTCLK			BIT(9)
+#define PHY_TESTEN			BIT(8)
+#define PHY_TESTDIN_MASK		GENMASK(7, 0)
+#define PHY_TESTDIN(x)			UPDATE(x, 7, 0)
+#define GRF_DPHY0_STATUS		0x0064
+#define DPHY_PHYLOCK			BIT(24)
+#define PHY_TESTDOUT_SHIFT		8
+#define GRF_MIPI_TX1_CON		0x0068
+#define GRF_DPHY1_STATUS		0x006c
+#define GRF_GPIO0AB_SEL_CON		0x0070
+#define GRF_GPIO1AB_SEL_CON		0x0074
+#define GRF_GPIO2AB_SEL_CON		0x0078
+#define GRF_GPIO2C_SEL_CON		0x007c
+#define GRF_GPIO3AB_SEL_CON		0x0080
+#define GRF_GPIO2A_SMT			0x0090
+#define GRF_GPIO2B_SMT			0x0094
+#define GRF_GPIO2C_SMT			0x0098
+#define GRF_GPIO3AB_SMT			0x009c
+#define GRF_GPIO0A_P_CON		0x00a0
+#define GRF_GPIO1A_P_CON		0x00a4
+#define GRF_GPIO2A_P_CON		0x00a8
+#define GRF_GPIO2B_P_CON		0x00ac
+#define GRF_GPIO2C_P_CON		0x00b0
+#define GRF_GPIO3A_P_CON		0x00b4
+#define GRF_GPIO3B_P_CON		0x00b8
+#define GRF_GPIO0B_D_CON		0x00c0
+#define GRF_GPIO1B_D_CON		0x00c4
+#define GRF_GPIO2A_D0_CON		0x00c8
+#define GRF_GPIO2A_D1_CON		0x00cc
+#define GRF_GPIO2B_D0_CON		0x00d0
+#define GRF_GPIO2B_D1_CON		0x00d4
+#define GRF_GPIO2C_D0_CON		0x00d8
+#define GRF_GPIO2C_D1_CON		0x00dc
+#define GRF_GPIO3A_D0_CON		0x00e0
+#define GRF_GPIO3A_D1_CON		0x00e4
+#define GRF_GPIO3B_D_CON		0x00e8
+#define GRF_GPIO_SR_CON			0x00ec
+#define GRF_INTR0_EN			0x0100
+#define GRF_INTR0_CLR_EN		0x0104
+#define GRF_INTR0_STATUS		0x0108
+#define GRF_INTR0_RAW_STATUS		0x010c
+#define GRF_INTR1_EN			0x0110
+#define GRF_INTR1_CLR_EN		0x0114
+#define GRF_INTR1_STATUS		0x0118
+#define GRF_INTR1_RAW_STATUS		0x011c
+#define GRF_SYSTEM_STATUS0		0x0120
+/* 0: i2c mode and mcu mode; 1: i2c mode only */
+#define I2C_ONLY_FLAG			BIT(6)
+#define GRF_SYSTEM_STATUS3		0x012c
+#define GRF_SYSTEM_STATUS4		0x0130
+#define GRF_OS_REG0			0x0140
+#define GRF_OS_REG1			0x0144
+#define GRF_OS_REG2			0x0148
+#define GRF_OS_REG3			0x014c
+#define GRF_SOC_VERSION			0x0150
+#define GRF_MAX_REGISTER		GRF_SOC_VERSION
+
+#define DRM_MODE_FLAG_PHSYNC                    (1<<0)
+#define DRM_MODE_FLAG_NHSYNC                    (1<<1)
+#define DRM_MODE_FLAG_PVSYNC                    (1<<2)
+#define DRM_MODE_FLAG_NVSYNC                    (1<<3)
+
+enum {
+	COMBTXPHY_MODULEA_EN = BIT(0),
+	COMBTXPHY_MODULEB_EN = BIT(1),
+};
+
+enum {
+	RK628_DEV_GRF,
+	RK628_DEV_COMBRXPHY,
+	RK628_DEV_HDMIRX = 3,
+	RK628_DEV_CSI,
+	RK628_DEV_DSI0,
+	RK628_DEV_DSI1,
+	RK628_DEV_HDMITX,
+	RK628_DEV_GVI,
+	RK628_DEV_COMBTXPHY,
+	RK628_DEV_ADAPTER,
+	RK628_DEV_EFUSE,
+	RK628_DEV_CRU,
+	RK628_DEV_GPIO0,
+	RK628_DEV_GPIO1,
+	RK628_DEV_GPIO2,
+	RK628_DEV_GPIO3,
+	RK628_DEV_MAX,
+};
+
+enum rk628_input_mode {
+	INPUT_MODE_HDMI,
+	INPUT_MODE_BT1120 = 2,
+	INPUT_MODE_RGB,
+	INPUT_MODE_YUV,
+};
+
+
+enum rk628_output_mode {
+	OUTPUT_MODE_GVI = 1,
+	OUTPUT_MODE_LVDS,
+	OUTPUT_MODE_HDMI,
+	OUTPUT_MODE_CSI,
+	OUTPUT_MODE_DSI,
+	OUTPUT_MODE_BT1120 = 8,
+	OUTPUT_MODE_RGB = 16,
+	OUTPUT_MODE_YUV = 24,
+};
+
+enum phy_mode {
+	PHY_MODE_INVALID,
+	PHY_MODE_VIDEO_MIPI,
+	PHY_MODE_VIDEO_LVDS,
+	PHY_MODE_VIDEO_GVI,
+};
+
+enum lvds_format {
+	LVDS_FORMAT_VESA_24BIT,
+	LVDS_FORMAT_JEIDA_24BIT,
+	LVDS_FORMAT_JEIDA_18BIT,
+	LVDS_FORMAT_VESA_18BIT,
+};
+
+enum lvds_link_type {
+	LVDS_SINGLE_LINK,
+	LVDS_DUAL_LINK_ODD_EVEN_PIXELS,
+	LVDS_DUAL_LINK_EVEN_ODD_PIXELS,
+	LVDS_DUAL_LINK_LEFT_RIGHT_PIXELS,
+	LVDS_DUAL_LINK_RIGHT_LEFT_PIXELS,
+};
+
+enum gvi_color_depth {
+	COLOR_DEPTH_RGB_YUV444_18BIT,
+	COLOR_DEPTH_RGB_YUV444_24BIT,
+	COLOR_DEPTH_RGB_YUV444_30BIT,
+	COLOR_DEPTH_YUV422_16BIT = 8,
+	COLOR_DEPTH_YUV422_20BIT,
+};
+
+enum dsi_mode_flags {
+	MIPI_DSI_MODE_VIDEO = 1,
+	MIPI_DSI_MODE_VIDEO_BURST = 2,
+	MIPI_DSI_MODE_VIDEO_SYNC_PULSE = 4,
+	MIPI_DSI_MODE_VIDEO_HFP = 8,
+	MIPI_DSI_MODE_VIDEO_HBP = 16,
+	MIPI_DSI_MODE_EOT_PACKET = 32,
+	MIPI_DSI_CLOCK_NON_CONTINUOUS = 64,
+	MIPI_DSI_MODE_LPM = 128,
+};
+
+enum dsi_bus_format {
+	MIPI_DSI_FMT_RGB888,
+	MIPI_DSI_FMT_RGB666,
+	MIPI_DSI_FMT_RGB666_PACKED,
+	MIPI_DSI_FMT_RGB565,
+};
+
+enum gvi_bus_format {
+	GVI_MEDIA_BUS_FMT_RGB666_1X18 = 9,
+	GVI_MEDIA_BUS_FMT_RGB888_1X24 = 10,
+	GVI_MEDIA_BUS_FMT_YUYV10_1X20 = 13,
+	GVI_MEDIA_BUS_FMT_YUYV8_1X16 = 17,
+	GVI_MEDIA_BUS_FMT_RGB101010_1X30 = 24,
+};
+
+enum bus_format {
+	BUS_FMT_RGB = 0,
+	BUS_FMT_YUV422 = 1,
+	BUS_FMT_YUV444 = 2,
+	BUS_FMT_YUV420 = 3,
+	BUS_FMT_UNKNOWN,
+};
+
+enum rk628_mode_sync_pol {
+	MODE_FLAG_NSYNC,
+	MODE_FLAG_PSYNC,
+};
+
+#undef BT1120_DUAL_EDGE
+
+struct rk628_videomode {
+	u32 pixelclock;	/* pixelclock in Hz */
+
+	u32 hactive;
+	u32 hfront_porch;
+	u32 hback_porch;
+	u32 hsync_len;
+
+	u32 vactive;
+	u32 vfront_porch;
+	u32 vback_porch;
+	u32 vsync_len;
+
+	unsigned int flags; /* display flags */
+};
+
+struct rk628_display_mode {
+	int clock; /* in kHz */
+	int hdisplay;
+	int hsync_start;
+	int hsync_end;
+	int htotal;
+	int vdisplay;
+	int vsync_start;
+	int vsync_end;
+	int vtotal;
+	unsigned int flags;
+};
+
+struct cmd_ctrl_hdr {
+	u8 dtype;       /* data type */
+	u8 wait;        /* ms */
+	u8 dlen;        /* payload len */
+} __packed;
+
+struct cmd_desc {
+	struct cmd_ctrl_hdr dchdr;
+	u8 *payload;
+};
+
+struct panel_cmds {
+	u8 *buf;
+	int blen;
+	struct cmd_desc *cmds;
+	int cmd_cnt;
+};
+
+struct panel_simple {
+	struct backlight_device *backlight;
+
+	struct regulator *supply;
+	struct gpio_desc *enable_gpio;
+	struct gpio_desc *reset_gpio;
+	struct panel_cmds *on_cmds;
+	struct panel_cmds *off_cmds;
+};
+
+struct rk628_dsi {
+	int bpp; /* 24/18/16*/
+	enum dsi_bus_format bus_format;
+	enum dsi_mode_flags mode_flags;
+	bool slave;
+	bool master;
+	uint8_t channel;
+	uint8_t  lanes;
+	uint8_t  id; /* 0:dsi0 1:dsi1 */
+	struct rk628 *rk628;
+};
+
+struct rk628_lvds {
+	enum lvds_format format;
+	enum lvds_link_type link_type;
+};
+
+struct rk628_gvi {
+	enum gvi_bus_format bus_format;
+	enum gvi_color_depth color_depth;
+	uint8_t lanes;
+	bool division_mode;
+	bool frm_rst;
+	u8 byte_mode;
+};
+
+struct rk628_combtxphy {
+	enum phy_mode mode;
+	unsigned int flags;
+	u8 ref_div;
+	u8 fb_div;
+	u16 frac_div;
+	u8 rate_div;
+	u32 bus_width;
+	bool division_mode;
+};
+
+struct rk628 {
+	struct device *dev;
+	struct i2c_client *client;
+	struct regmap *regmap[RK628_DEV_MAX];
+	struct gpio_desc *reset_gpio;
+	struct gpio_desc *enable_gpio;
+	struct gpio_desc *plugin_det_gpio;
+	int plugin_irq;
+	int hdmirx_irq;
+	struct clk *soc_24M;
+	struct workqueue_struct *monitor_wq;
+	struct delayed_work delay_work;
+	struct workqueue_struct *dsi_wq;
+	struct delayed_work dsi_delay_work;
+	struct panel_simple *panel;
+	void *hdmirx;
+	bool display_enabled;
+	enum rk628_input_mode input_mode;
+	enum rk628_output_mode output_mode;
+	struct rk628_display_mode src_mode;
+	struct rk628_display_mode dst_mode;
+	enum bus_format input_fmt;
+	enum bus_format output_fmt;
+	struct rk628_dsi dsi0;
+	struct rk628_dsi dsi1;
+	struct rk628_lvds lvds;
+	struct rk628_gvi gvi;
+	struct rk628_combtxphy combtxphy;
+	int sync_pol;
+	void *csi;
+};
+
+static inline int rk628_i2c_write(struct rk628 *rk628, u32 reg, u32 val)
+{
+	int region = (reg >> 16) & 0xff;
+	int ret = 0;
+
+	ret = regmap_write(rk628->regmap[region], reg, val);
+	if (ret < 0)
+		pr_info("%s: i2c err reg=0x%x, val=0x%x, ret=%d\n", __func__, reg, val, ret);
+
+	return ret;
+}
+
+static inline int rk628_i2c_read(struct rk628 *rk628, u32 reg, u32 *val)
+{
+	int region = (reg >> 16) & 0xff;
+	int ret = 0;
+
+	ret = regmap_read(rk628->regmap[region], reg, val);
+	if (ret < 0)
+		pr_info("%s: i2c err reg=0x%x, val=0x%x ret=%d\n", __func__, reg, *val, ret);
+
+	return ret;
+}
+
+static inline int rk628_i2c_update_bits(struct rk628 *rk628, u32 reg, u32 mask,
+					u32 val)
+{
+	int region = (reg >> 16) & 0xff;
+
+	return regmap_update_bits(rk628->regmap[region], reg, mask, val);
+}
+
+#include "rk628_grf.h"
+#include "rk628_gpio.h"
+#include "rk628_pinctrl.h"
+
+#endif
diff --git a/kernel/drivers/misc/rk628/rk628_combrxphy.c b/kernel/drivers/misc/rk628/rk628_combrxphy.c
new file mode 100644
index 0000000..2e5013b
--- /dev/null
+++ b/kernel/drivers/misc/rk628/rk628_combrxphy.c
@@ -0,0 +1,567 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2020 Rockchip Electronics Co. Ltd.
+ *
+ * Author: Shunqing Chen <csq@rock-chisp.com>
+ */
+
+#include "rk628.h"
+#include "rk628_combrxphy.h"
+
+#define MAX_ROUND		6
+#define MAX_DATA_NUM		16
+#define MAX_CHANNEL		3
+#define CLK_DET_TRY_TIMES	10
+#define CHECK_CNT		100
+#define CLK_STABLE_LOOP_CNT	10
+#define CLK_STABLE_THRESHOLD	6
+
+static int rk628_combrxphy_try_clk_detect(struct rk628 *rk628)
+{
+	u32 val, i;
+	int ret;
+
+	ret = -1;
+
+	/* step1: set pin_rst_n to 1’b0.wait 1 period(1us).release reset */
+	/* step2: select pll clock src and enable auto check */
+	rk628_i2c_read(rk628, COMBRX_REG(0x6630), &val);
+	/* clear bit0 and bit3 */
+	val = val & 0xfffffff6;
+	rk628_i2c_write(rk628, COMBRX_REG(0x6630), val);
+	/*
+	 * step3: select hdmi mode and enable chip, read reg6654,
+	 * make sure auto setup done.
+	 */
+	/* auto fsm reset related */
+	rk628_i2c_read(rk628, COMBRX_REG(0x6630), &val);
+	val = val | BIT(24);
+	rk628_i2c_write(rk628, COMBRX_REG(0x6630), val);
+	/* pull down ana rstn */
+	rk628_i2c_read(rk628, COMBRX_REG(0x66f0), &val);
+	val = val & 0xfffffeff;
+	rk628_i2c_write(rk628, COMBRX_REG(0x66f0), val);
+	/* pull down dig rstn */
+	rk628_i2c_read(rk628, COMBRX_REG(0x66f4), &val);
+	val = val & 0xfffffffe;
+	rk628_i2c_write(rk628, COMBRX_REG(0x66f4), val);
+	/* pull up ana rstn */
+	rk628_i2c_read(rk628, COMBRX_REG(0x66f0), &val);
+	val = val | 0x100;
+	rk628_i2c_write(rk628, COMBRX_REG(0x66f0), val);
+	/* pull up dig rstn */
+	rk628_i2c_read(rk628, COMBRX_REG(0x66f4), &val);
+	val = val  | 0x1;
+	rk628_i2c_write(rk628, COMBRX_REG(0x66f4), val);
+
+	rk628_i2c_read(rk628, COMBRX_REG(0x66f0), &val);
+	/* set bit0 and bit2 to 1*/
+	val = (val & 0xfffffff8) | 0x5;
+	rk628_i2c_write(rk628, COMBRX_REG(0x66f0), val);
+
+	/* auto fsm en = 0 */
+	rk628_i2c_read(rk628, COMBRX_REG(0x66f0), &val);
+	/* set bit0 and bit2 to 1*/
+	val = (val & 0xfffffff8) | 0x4;
+	rk628_i2c_write(rk628, COMBRX_REG(0x66f0), val);
+
+	for (i = 0; i < 10; i++) {
+		mdelay(1);
+		rk628_i2c_read(rk628, COMBRX_REG(0x6654), &val);
+		if ((val & 0xf0000000) == 0x80000000) {
+			ret = 0;
+			dev_info(rk628->dev, "clock detected!\n");
+			break;
+		}
+	}
+
+	return ret;
+}
+
+static void rk628_combrxphy_get_data_of_round(struct rk628 *rk628,
+					      u32 *data)
+{
+	u32 i;
+
+	for (i = 0; i < MAX_DATA_NUM; i++)
+		rk628_i2c_read(rk628, COMBRX_REG(0x6740 + i * 4), &data[i]);
+}
+
+static void rk628_combrxphy_set_dc_gain(struct rk628 *rk628,
+					u32 x, u32 y, u32 z)
+{
+	u32 val;
+	u32 dc_gain_ch0, dc_gain_ch1, dc_gain_ch2;
+
+	dc_gain_ch0 = x & 0xf;
+	dc_gain_ch1 = y & 0xf;
+	dc_gain_ch2 = z & 0xf;
+	rk628_i2c_read(rk628, COMBRX_REG(0x661c), &val);
+
+	val = (val & 0xff0f0f0f) | (dc_gain_ch0 << 20) | (dc_gain_ch1 << 12) |
+		(dc_gain_ch2 << 4);
+	rk628_i2c_write(rk628, COMBRX_REG(0x661c), val);
+}
+
+static void rk628_combrxphy_set_data_of_round(u32 *data, u32 *data_in)
+{
+	if ((data != NULL) && (data_in != NULL)) {
+		data_in[0] = data[0];
+		data_in[1] = data[7];
+		data_in[2] = data[13];
+		data_in[3] = data[14];
+		data_in[4] = data[15];
+		data_in[5] = data[1];
+		data_in[6] = data[2];
+		data_in[7] = data[3];
+		data_in[8] = data[4];
+		data_in[9] = data[5];
+		data_in[10] = data[6];
+		data_in[11] = data[8];
+		data_in[12] = data[9];
+		data_in[13] = data[10];
+		data_in[14] = data[11];
+		data_in[15] = data[12];
+	}
+}
+
+static void
+rk628_combrxphy_max_zero_of_round(struct rk628 *rk628,
+				  u32 *data_in, u32 *max_zero,
+				  u32 *max_val, int n, int ch)
+{
+	u32 i;
+	u32 cnt = 0;
+	u32 max_cnt = 0;
+	u32 max_v = 0;
+
+	for (i = 0; i < MAX_DATA_NUM; i++) {
+		if (max_v < data_in[i])
+			max_v = data_in[i];
+	}
+
+	for (i = 0; i < MAX_DATA_NUM; i++) {
+		if (data_in[i] == 0)
+			cnt = cnt + 200;
+		else if ((data_in[i] > 0) && (data_in[i] < 100))
+			cnt = cnt + 100 - data_in[i];
+	}
+	max_cnt = (cnt >= 3200) ? 0 : cnt;
+
+	max_zero[n] = max_cnt;
+	max_val[n] = max_v;
+	dev_info(rk628->dev, "channel:%d, round:%d, max_zero_cnt:%d, max_val:%#x\n",
+		 ch, n, max_zero[n], max_val[n]);
+}
+
+static int rk628_combrxphy_chose_round_for_ch(struct rk628 *rk628,
+					      u32 *rd_max_zero,
+					      u32 *rd_max_val, int ch)
+{
+	int i, rd = 0;
+	u32 max = 0;
+	u32 max_v = 0;
+
+	for (i = 0; i < MAX_ROUND; i++) {
+		if (rd_max_zero[i] > max) {
+			max = rd_max_zero[i];
+			max_v = rd_max_val[i];
+			rd = i;
+		} else if (rd_max_zero[i] == max && rd_max_val[i] > max_v) {
+			max = rd_max_zero[i];
+			max_v = rd_max_val[i];
+			rd = i;
+		}
+	}
+	dev_info(rk628->dev, "%s channel:%d, rd:%d\n", __func__, ch, rd);
+
+	return rd;
+}
+
+static void rk628_combrxphy_set_sample_edge_round(struct rk628 *rk628,
+						  u32 x, u32 y, u32 z)
+{
+	u32 val;
+	u32 equ_gain_ch0, equ_gain_ch1, equ_gain_ch2;
+
+	equ_gain_ch0 = (x & 0xf);
+	equ_gain_ch1 = (y & 0xf);
+	equ_gain_ch2 = (z & 0xf);
+	rk628_i2c_read(rk628, COMBRX_REG(0x6618), &val);
+	val = (val & 0xff00f0ff) | (equ_gain_ch1 << 20) |
+		(equ_gain_ch0 << 16) | (equ_gain_ch2 << 8);
+	rk628_i2c_write(rk628, COMBRX_REG(0x6618), val);
+}
+
+static void rk628_combrxphy_start_sample_edge(struct rk628 *rk628)
+{
+	u32 val;
+
+	rk628_i2c_read(rk628, COMBRX_REG(0x66f0), &val);
+	val &= 0xfffff1ff;
+	rk628_i2c_write(rk628, COMBRX_REG(0x66f0), val);
+	rk628_i2c_read(rk628, COMBRX_REG(0x66f0), &val);
+	val = (val & 0xfffff1ff) | (0x7 << 9);
+	rk628_i2c_write(rk628, COMBRX_REG(0x66f0), val);
+}
+
+static void rk628_combrxphy_set_sample_edge_mode(struct rk628 *rk628, int ch)
+{
+	u32 val;
+
+	rk628_i2c_read(rk628, COMBRX_REG(0x6634), &val);
+	val = val & (~(0xf << ((ch + 1) * 4)));
+	rk628_i2c_write(rk628, COMBRX_REG(0x6634), val);
+}
+
+static void rk628_combrxphy_select_channel(struct rk628 *rk628, int ch)
+{
+	u32 val;
+
+	rk628_i2c_read(rk628, COMBRX_REG(0x6700), &val);
+	val = (val & 0xfffffffc) | (ch & 0x3);
+	rk628_i2c_write(rk628, COMBRX_REG(0x6700), val);
+}
+
+static void rk628_combrxphy_cfg_6730(struct rk628 *rk628)
+{
+	u32 val;
+
+	rk628_i2c_read(rk628, COMBRX_REG(0x6730), &val);
+	val = (val & 0xffff0000) | 0x1;
+	rk628_i2c_write(rk628, COMBRX_REG(0x6730), val);
+}
+
+static void rk628_combrxphy_sample_edge_procedure_for_cable(struct rk628 *rk628,
+							    u32 cdr_mode)
+{
+	u32 n, ch;
+	u32 data[MAX_DATA_NUM];
+	u32 data_in[MAX_DATA_NUM];
+	u32 round_max_zero[MAX_CHANNEL][MAX_ROUND];
+	u32 round_max_value[MAX_CHANNEL][MAX_ROUND];
+	u32 ch_round[MAX_CHANNEL];
+	u32 edge, dc_gain;
+	u32 rd_offset;
+
+	/* Step1: set sample edge mode for channel 0~2 */
+	for (ch = 0; ch < MAX_CHANNEL; ch++)
+		rk628_combrxphy_set_sample_edge_mode(rk628, ch);
+
+	/* step2: once per round */
+	for (ch = 0; ch < MAX_CHANNEL; ch++) {
+		rk628_combrxphy_select_channel(rk628, ch);
+		rk628_combrxphy_cfg_6730(rk628);
+	}
+
+	/*
+	 * step3: config sample edge until the end of one frame
+	 * (for example 1080p:2200*1125=32’h25c3f8)
+	 */
+	if (cdr_mode < 16) {
+		dc_gain = 0;
+		rd_offset = 0;
+	} else if (cdr_mode < 18) {
+		dc_gain = 1;
+		rd_offset = 0;
+	} else {
+		dc_gain = 3;
+		rd_offset = 2;
+	}
+
+	/*
+	 * When the pix clk is the same, the low frame rate resolution is used
+	 * to calculate the sampling window (the frame rate is not less than
+	 * 30). The sampling delay time is configured as 40ms.
+	 */
+	if (cdr_mode <= 1) { /* 27M vic17 720x576P50 */
+		edge = 864 * 625;
+	} else if (cdr_mode <= 4) { /* 59.4M vic81 1680x720P30 */
+		edge = 2640 * 750;
+	} else if (cdr_mode <= 7) { /* 74.25M vic34 1920x1080P30 */
+		edge = 2200 * 1125;
+	} else if (cdr_mode <= 14) { /* 119M vic88 2560x1180P30 */
+		edge = 3520 * 1125;
+	} else if (cdr_mode <= 16) { /* 148.5M vic31 1920x1080P50 */
+		edge = 2640 * 1125;
+	} else if (cdr_mode <= 17) { /* 162M vic89 2560x1080P50 */
+		edge = 3300 * 1125;
+	} else if (cdr_mode <= 18) { /* 297M vic95 3840x2160P30 */
+		edge = 4400 * 2250;
+	} else {         /* unknown vic16 1920x1080P60 */
+		edge = 2200 * 1125;
+	}
+
+	dev_info(rk628->dev, "cdr_mode:%d, dc_gain:%d, rd_offset:%d, edge:%#x\n",
+		 cdr_mode, dc_gain, rd_offset, edge);
+	for (ch = 0; ch < MAX_CHANNEL; ch++) {
+		rk628_combrxphy_select_channel(rk628, ch);
+		rk628_i2c_write(rk628, COMBRX_REG(0x6708), edge);
+	}
+
+	rk628_combrxphy_set_dc_gain(rk628, dc_gain, dc_gain, dc_gain);
+	for (n = rd_offset; n < (rd_offset + MAX_ROUND); n++) {
+		/* step4:set sample edge round value n,n=0(n=0~31) */
+		rk628_combrxphy_set_sample_edge_round(rk628, n, n, n);
+		/* step5:start sample edge */
+		rk628_combrxphy_start_sample_edge(rk628);
+		/* step6:waiting more than one frame time */
+		mdelay(41);
+		for (ch = 0; ch < MAX_CHANNEL; ch++) {
+			/* step7: get data of round n */
+			rk628_combrxphy_select_channel(rk628, ch);
+			rk628_combrxphy_get_data_of_round(rk628, data);
+			rk628_combrxphy_set_data_of_round(data, data_in);
+			/* step8: get the max constant value of round n */
+			rk628_combrxphy_max_zero_of_round(rk628, data_in,
+				round_max_zero[ch], round_max_value[ch],
+				n - rd_offset, ch);
+		}
+	}
+
+	/*
+	 * step9: after finish round, get the max constant value and
+	 * corresponding value n.
+	 */
+	for (ch = 0; ch < MAX_CHANNEL; ch++) {
+		ch_round[ch] = rk628_combrxphy_chose_round_for_ch(rk628, round_max_zero[ch],
+								  round_max_value[ch], ch);
+		ch_round[ch] += rd_offset;
+	}
+	dev_info(rk628->dev, "last equ gain ch0:%d, ch1:%d, ch2:%d\n",
+		 ch_round[0], ch_round[1], ch_round[2]);
+
+	/* step10: write result to sample edge round value  */
+	rk628_combrxphy_set_sample_edge_round(rk628, ch_round[0], ch_round[1], ch_round[2]);
+
+	/* do step5, step6 again */
+	/* step5:start sample edge */
+	rk628_combrxphy_start_sample_edge(rk628);
+	/* step6:waiting more than one frame time */
+	mdelay(41);
+}
+
+static int rk628_combrxphy_set_hdmi_mode_for_cable(struct rk628 *rk628, int f)
+{
+	u32 val, val_a, val_b, data_a, data_b;
+	u32 i, j, count, ret;
+	u32 cdr_mode, cdr_data, pll_man;
+	u32 tmds_bitrate_per_lane;
+	u32 cdr_data_min, cdr_data_max;
+	u32 state, channel_st;
+	bool is_yuv420;
+
+	/*
+	 * use the mode of automatic clock detection, only supports fixed TMDS
+	 * frequency.Refer to register 0x6654[21:16]:
+	 * 5'd31:Error mode
+	 * 5'd30:manual mode detected
+	 * 5'd18:rx3p clock = 297MHz
+	 * 5'd17:rx3p clock = 162MHz
+	 * 5'd16:rx3p clock = 148.5MHz
+	 * 5'd15:rx3p clock = 135MHz
+	 * 5'd14:rx3p clock = 119MHz
+	 * 5'd13:rx3p clock = 108MHz
+	 * 5'd12:rx3p clock = 101MHz
+	 * 5'd11:rx3p clock = 92.8125MHz
+	 * 5'd10:rx3p clock = 88.75MHz
+	 * 5'd9:rx3p clock  = 85.5MHz
+	 * 5'd8:rx3p clock  = 83.5MHz
+	 * 5'd7:rx3p clock  = 74.25MHz
+	 * 5'd6:rx3p clock  = 68.25MHz
+	 * 5'd5:rx3p clock  = 65MHz
+	 * 5'd4:rx3p clock  = 59.4MHz
+	 * 5'd3:rx3p clock  = 40MHz
+	 * 5'd2:rx3p clock  = 33.75MHz
+	 * 5'd1:rx3p clock  = 27MHz
+	 * 5'd0:rx3p clock  = 25.17MHz
+	 */
+
+	const u32 cdr_mode_to_khz[] = {
+		25170,   27000,  33750,  40000,  59400,  65000,  68250,
+		74250,   83500,  85500,  88750,  92812, 101000, 108000,
+		119000,  135000, 148500, 162000, 297000,
+	};
+
+	for (i = 0; i < CLK_DET_TRY_TIMES; i++) {
+		if (rk628_combrxphy_try_clk_detect(rk628) >= 0)
+			break;
+		mdelay(1);
+	}
+	rk628_i2c_read(rk628, COMBRX_REG(0x6654), &val);
+	dev_info(rk628->dev, "clk det over cnt:%d, reg_0x6654:%#x\n", i, val);
+	state = (val >> 28) & 0xf;
+	if (state == 5) {
+		dev_info(rk628->dev, "Clock detection anomaly\n");
+	} else if (state == 4) {
+		channel_st = (val >> 21) & 0x7f;
+		dev_info(rk628->dev, "%s%s%s%s%s%s%s%s level detection anomaly\n",
+			 channel_st & 0x40 ? "|clk_p|" : "",
+			 channel_st & 0x20 ? "|clk_n|" : "",
+			 channel_st & 0x10 ? "|d0_p|" : "",
+			 channel_st & 0x08 ? "|d0_n|" : "",
+			 channel_st & 0x04 ? "|d1_p|" : "",
+			 channel_st & 0x02 ? "|d1_n|" : "",
+			 channel_st & 0x01 ? "|d2_p|" : "",
+			 channel_st ? "" : "|d2_n|");
+	}
+
+	rk628_i2c_read(rk628, COMBRX_REG(0x6620), &val);
+	if ((i == CLK_DET_TRY_TIMES) ||
+	    ((val & 0x7f000000) == 0) ||
+	    ((val & 0x007f0000) == 0) ||
+	    ((val & 0x00007f00) == 0) ||
+	    ((val & 0x0000007f) == 0)) {
+		dev_info(rk628->dev, "clock detected failed, cfg resistance manual!\n");
+		rk628_i2c_write(rk628, COMBRX_REG(0x6620), 0x66666666);
+		rk628_i2c_update_bits(rk628, COMBRX_REG(0x6604), BIT(31), BIT(31));
+		mdelay(1);
+	}
+
+	/* step4: get cdr_mode and cdr_data */
+	for (j = 0; j < CLK_STABLE_LOOP_CNT ; j++) {
+		cdr_data_min = 0xffffffff;
+		cdr_data_max = 0;
+
+		for (i = 0; i < CLK_DET_TRY_TIMES; i++) {
+			rk628_i2c_read(rk628, COMBRX_REG(0x6654), &val);
+			cdr_data = val & 0xffff;
+			if (cdr_data <= cdr_data_min)
+				cdr_data_min = cdr_data;
+			if (cdr_data >= cdr_data_max)
+				cdr_data_max = cdr_data;
+			udelay(50);
+		}
+
+		if (((cdr_data_max - cdr_data_min) <= CLK_STABLE_THRESHOLD) &&
+				(cdr_data_min >= 60)) {
+			dev_info(rk628->dev, "clock stable!");
+			break;
+		}
+	}
+
+	if (j == CLK_STABLE_LOOP_CNT) {
+		rk628_i2c_read(rk628, COMBRX_REG(0x6630), &val_a);
+		rk628_i2c_read(rk628, COMBRX_REG(0x6608), &val_b);
+		dev_err(rk628->dev,
+			"clk not stable, reg_0x6630:%#x, reg_0x6608:%#x",
+			val_a, val_b);
+		/* bypass level detection anomaly */
+		if (state == 4)
+			rk628_i2c_update_bits(rk628, COMBRX_REG(0x6628), BIT(31), BIT(31));
+		else
+			return -EINVAL;
+	}
+
+	rk628_i2c_read(rk628, COMBRX_REG(0x6654), &val);
+	if ((val & 0x1f0000) == 0x1f0000) {
+		rk628_i2c_read(rk628, COMBRX_REG(0x6630), &val_a);
+		rk628_i2c_read(rk628, COMBRX_REG(0x6608), &val_b);
+		dev_err(rk628->dev,
+			"clock error: 0x1f, reg_0x6630:%#x, reg_0x6608:%#x",
+			val_a, val_b);
+
+		return -EINVAL;
+	}
+
+	cdr_mode = (val >> 16) & 0x1f;
+	cdr_data =  val & 0xffff;
+	dev_info(rk628->dev, "cdr_mode:%d, cdr_data:%d\n", cdr_mode, cdr_data);
+
+	f = f & 0x7fffffff;
+	is_yuv420 = (f & BIT(30)) ? true : false;
+	f = f & 0xffffff;
+	dev_info(rk628->dev, "f:%d\n", f);
+
+	/*
+	 * step5: manually configure PLL
+	 * cfg reg 66a8 tmds clock div2 for rgb/yuv444 as default
+	 * reg 662c[16:8] pll_pre_div
+	 */
+	if (f <= 340000) {
+		rk628_i2c_write(rk628, COMBRX_REG(0x662c), 0x01000500);
+		if (is_yuv420)
+			rk628_i2c_write(rk628, COMBRX_REG(0x66a8), 0x0000c000);
+		else
+			rk628_i2c_write(rk628, COMBRX_REG(0x66a8), 0x0000c600);
+	} else {
+		rk628_i2c_write(rk628, COMBRX_REG(0x662c), 0x01001400);
+		rk628_i2c_write(rk628, COMBRX_REG(0x66a8), 0x0000c600);
+	}
+
+	/* when tmds bitrate/lane <= 340M, bitrate/lane = pix_clk * 10 */
+	tmds_bitrate_per_lane = cdr_mode_to_khz[cdr_mode] * 10;
+	if (tmds_bitrate_per_lane < 400000)
+		pll_man = 0x7960c;
+	else if (tmds_bitrate_per_lane < 600000)
+		pll_man = 0x7750c;
+	else if (tmds_bitrate_per_lane < 800000)
+		pll_man = 0x7964c;
+	else if (tmds_bitrate_per_lane < 1000000)
+		pll_man = 0x7754c;
+	else if (tmds_bitrate_per_lane < 1600000)
+		pll_man = 0x7a108;
+	else if (tmds_bitrate_per_lane < 2400000)
+		pll_man = 0x73588;
+	else if (tmds_bitrate_per_lane < 3400000)
+		pll_man = 0x7a108;
+	else
+		pll_man = 0x7f0c8;
+
+	dev_info(rk628->dev, "cdr_mode:%d, pll_man:%#x\n", cdr_mode, pll_man);
+	rk628_i2c_write(rk628, COMBRX_REG(0x6630), pll_man);
+
+	/* step6: EQ and SAMPLE cfg */
+	rk628_combrxphy_sample_edge_procedure_for_cable(rk628, cdr_mode);
+
+	/* step7: Deassert fifo reset,enable fifo write and read */
+	/* reset rx_infifo */
+	rk628_i2c_write(rk628, COMBRX_REG(0x66a0), 0x00000003);
+	/* rx_infofo wr/rd disable */
+	rk628_i2c_write(rk628, COMBRX_REG(0x66b0), 0x00080060);
+	/* deassert rx_infifo reset */
+	rk628_i2c_write(rk628, COMBRX_REG(0x66a0), 0x00000083);
+	/* enable rx_infofo wr/rd en */
+	rk628_i2c_write(rk628, COMBRX_REG(0x66b0), 0x00380060);
+	/* cfg 0x2260 high_8b to 0x66ac high_8b, low_8b to 0x66b0 low_8b */
+	rk628_i2c_update_bits(rk628, COMBRX_REG(0x66ac),
+			      GENMASK(31, 24), UPDATE(0x22, 31, 24));
+	mdelay(6);
+
+	/* step8: check all 3 data channels alignment */
+	count = 0;
+	for (i = 0; i < CHECK_CNT; i++) {
+		mdelay(1);
+		rk628_i2c_read(rk628, COMBRX_REG(0x66b4), &data_a);
+		rk628_i2c_read(rk628, COMBRX_REG(0x66b8), &data_b);
+		/* ch0 ch1 ch2 lock */
+		if (((data_a & 0x00ff00ff) == 0x00ff00ff) &&
+			((data_b & 0xff) == 0xff)) {
+			count++;
+		}
+	}
+
+	if (count >= CHECK_CNT) {
+		dev_info(rk628->dev, "channel alignment done\n");
+		dev_info(rk628->dev, "rx initial done\n");
+		ret = 0;
+	} else if (count > 0) {
+		dev_info(rk628->dev, "link not stable, count:%d of 100\n", count);
+		ret = 0;
+	} else {
+		dev_err(rk628->dev, "channel alignment failed!\n");
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+int rk628_combrxphy_power_on(struct rk628 *rk628, int f)
+{
+	return rk628_combrxphy_set_hdmi_mode_for_cable(rk628, f);
+}
+
+int rk628_combrxphy_power_off(struct rk628 *rk628)
+{
+	return 0;
+}
diff --git a/kernel/drivers/misc/rk628/rk628_combrxphy.h b/kernel/drivers/misc/rk628/rk628_combrxphy.h
new file mode 100644
index 0000000..edac0a0
--- /dev/null
+++ b/kernel/drivers/misc/rk628/rk628_combrxphy.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2020 Rockchip Electronics Co. Ltd.
+ *
+ * Author: Chen Shunqing <csq@rock-chips.com>
+ */
+
+#ifndef COMBRXPHY_H
+#define COMBRXPHY_H
+
+#define COMBRX_REG(x)			((x) + 0x10000)
+
+int rk628_combrxphy_power_on(struct rk628 *rk628, int f);
+int rk628_combrxphy_power_off(struct rk628 *rk628);
+
+#endif
diff --git a/kernel/drivers/misc/rk628/rk628_combtxphy.c b/kernel/drivers/misc/rk628/rk628_combtxphy.c
new file mode 100644
index 0000000..ddb4691
--- /dev/null
+++ b/kernel/drivers/misc/rk628/rk628_combtxphy.c
@@ -0,0 +1,309 @@
+// SPDX-License-Identifier: BSD-3-Clause
+/*
+ * Copyright (c) 2021 Rockchip Electronics Co. Ltd.
+ *
+ * Author: Guochun Huang <hero.huang@rock-chips.com>
+ */
+
+#include "rk628.h"
+#include "rk628_combtxphy.h"
+#include "rk628_cru.h"
+
+static void rk628_combtxphy_dsi_power_on(struct rk628 *rk628)
+{
+	struct rk628_combtxphy *combtxphy = &rk628->combtxphy;
+	u32 val;
+	int ret;
+
+	rk628_i2c_update_bits(rk628, COMBTXPHY_CON0, SW_BUS_WIDTH_MASK |
+			      SW_GVI_LVDS_EN_MASK | SW_MIPI_DSI_EN_MASK,
+			       SW_BUS_WIDTH_8BIT | SW_MIPI_DSI_EN);
+
+	if (combtxphy->flags & COMBTXPHY_MODULEA_EN)
+		rk628_i2c_update_bits(rk628, COMBTXPHY_CON0,
+				      SW_MODULEA_EN_MASK, SW_MODULEA_EN);
+
+	if (combtxphy->flags & COMBTXPHY_MODULEB_EN)
+		rk628_i2c_update_bits(rk628, COMBTXPHY_CON0,
+				      SW_MODULEB_EN_MASK, SW_MODULEB_EN);
+
+	rk628_i2c_write(rk628, COMBTXPHY_CON5,
+			SW_REF_DIV(combtxphy->ref_div - 1) |
+			SW_PLL_FB_DIV(combtxphy->fb_div) |
+			SW_PLL_FRAC_DIV(combtxphy->frac_div) |
+			SW_RATE(combtxphy->rate_div / 2));
+
+	rk628_i2c_update_bits(rk628, COMBTXPHY_CON0, SW_PD_PLL, 0);
+
+	ret = regmap_read_poll_timeout(rk628->regmap[RK628_DEV_GRF],
+				       GRF_DPHY0_STATUS, val,
+				       val & DPHY_PHYLOCK, 0, 1000);
+	if (ret < 0)
+		dev_err(rk628->dev, "phy is not lock\n");
+
+
+	rk628_i2c_update_bits(rk628, COMBTXPHY_CON9,
+			      SW_DSI_FSET_EN_MASK | SW_DSI_RCAL_EN_MASK,
+			      SW_DSI_FSET_EN | SW_DSI_RCAL_EN);
+
+	usleep_range(200, 400);
+}
+
+static void rk628_combtxphy_lvds_power_on(struct rk628 *rk628)
+{
+
+	struct rk628_combtxphy *combtxphy = &rk628->combtxphy;
+	u32 val;
+	int ret;
+
+	/* Adjust terminal resistance 133 ohm, bypass 0.95v ldo for driver. */
+	rk628_i2c_update_bits(rk628, COMBTXPHY_CON7,
+			      SW_TX_RTERM_MASK | SW_TX_MODE_MASK |
+			      BYPASS_095V_LDO_MASK | TX_COM_VOLT_ADJ_MASK,
+			      SW_TX_RTERM(6) | SW_TX_MODE(3) |
+			      BYPASS_095V_LDO(1) | TX_COM_VOLT_ADJ(0));
+
+	rk628_i2c_write(rk628, COMBTXPHY_CON10, TX7_CKDRV_EN | TX2_CKDRV_EN);
+	rk628_i2c_update_bits(rk628, COMBTXPHY_CON0,
+			      SW_BUS_WIDTH_MASK | SW_GVI_LVDS_EN_MASK |
+			      SW_MIPI_DSI_EN_MASK,
+			      SW_BUS_WIDTH_7BIT | SW_GVI_LVDS_EN);
+
+	if (combtxphy->flags & COMBTXPHY_MODULEA_EN)
+		rk628_i2c_update_bits(rk628, COMBTXPHY_CON0,
+				      SW_MODULEA_EN_MASK, SW_MODULEA_EN);
+
+	if (combtxphy->flags & COMBTXPHY_MODULEB_EN)
+		rk628_i2c_update_bits(rk628, COMBTXPHY_CON0,
+				      SW_MODULEB_EN_MASK, SW_MODULEB_EN);
+
+	rk628_i2c_write(rk628, COMBTXPHY_CON5,
+			SW_REF_DIV(combtxphy->ref_div - 1) |
+			SW_PLL_FB_DIV(combtxphy->fb_div) |
+			SW_PLL_FRAC_DIV(combtxphy->frac_div) |
+			SW_RATE(combtxphy->rate_div / 2));
+
+	rk628_i2c_update_bits(rk628, COMBTXPHY_CON0,
+			      SW_PD_PLL, 0);
+
+	ret = regmap_read_poll_timeout(rk628->regmap[RK628_DEV_GRF],
+				       GRF_DPHY0_STATUS, val,
+				       val & DPHY_PHYLOCK, 0, 1000);
+	if (ret < 0)
+		dev_err(rk628->dev, "phy is not lock\n");
+
+	usleep_range(100, 200);
+	rk628_i2c_update_bits(rk628, COMBTXPHY_CON0, SW_TX_IDLE_MASK | SW_TX_PD_MASK, 0);
+}
+
+static void rk628_combtxphy_gvi_power_on(struct rk628 *rk628)
+{
+	struct rk628_combtxphy *combtxphy = &rk628->combtxphy;
+	int ref_div = 0;
+
+	if (combtxphy->ref_div % 2) {
+		ref_div = combtxphy->ref_div - 1;
+	} else {
+		ref_div = BIT(4);
+		ref_div |= combtxphy->ref_div / 2 - 1;
+	}
+
+	rk628_i2c_write(rk628, COMBTXPHY_CON5,
+			SW_REF_DIV(ref_div) |
+			SW_PLL_FB_DIV(combtxphy->fb_div) |
+			SW_PLL_FRAC_DIV(combtxphy->frac_div) |
+			SW_RATE(combtxphy->rate_div / 2));
+	rk628_i2c_update_bits(rk628, COMBTXPHY_CON0,
+			      SW_BUS_WIDTH_MASK | SW_GVI_LVDS_EN_MASK |
+			      SW_MIPI_DSI_EN_MASK |
+			      SW_MODULEB_EN_MASK | SW_MODULEA_EN_MASK,
+			      SW_BUS_WIDTH_10BIT | SW_GVI_LVDS_EN |
+			      SW_MODULEB_EN | SW_MODULEA_EN);
+
+	rk628_i2c_update_bits(rk628, COMBTXPHY_CON0,
+			      SW_PD_PLL | SW_TX_PD_MASK, 0);
+	usleep_range(100, 200);
+	rk628_i2c_update_bits(rk628, COMBTXPHY_CON0,
+			      SW_TX_IDLE_MASK, 0);
+}
+
+void rk628_combtxphy_power_on(struct rk628 *rk628)
+{
+	struct rk628_combtxphy *combtxphy = &rk628->combtxphy;
+
+	rk628_i2c_update_bits(rk628, COMBTXPHY_CON0,
+			      SW_TX_IDLE_MASK | SW_TX_PD_MASK |
+			      SW_PD_PLL_MASK, SW_TX_IDLE(0x3ff) |
+			      SW_TX_PD(0x3ff) | SW_PD_PLL);
+
+	switch (combtxphy->mode) {
+	case PHY_MODE_VIDEO_MIPI:
+
+		rk628_i2c_update_bits(rk628, GRF_POST_PROC_CON,
+				      SW_TXPHY_REFCLK_SEL_MASK,
+				      SW_TXPHY_REFCLK_SEL(0));
+		rk628_combtxphy_dsi_power_on(rk628);
+		break;
+	case PHY_MODE_VIDEO_LVDS:
+		rk628_i2c_update_bits(rk628, GRF_POST_PROC_CON,
+				      SW_TXPHY_REFCLK_SEL_MASK,
+				      SW_TXPHY_REFCLK_SEL(1));
+		rk628_combtxphy_lvds_power_on(rk628);
+		break;
+	case PHY_MODE_VIDEO_GVI:
+		rk628_i2c_update_bits(rk628, GRF_POST_PROC_CON,
+				      SW_TXPHY_REFCLK_SEL_MASK,
+				      SW_TXPHY_REFCLK_SEL(2));
+		rk628_combtxphy_gvi_power_on(rk628);
+		break;
+	default:
+		break;
+	}
+}
+
+void rk628_combtxphy_power_off(struct rk628 *rk628)
+{
+	rk628_i2c_update_bits(rk628, COMBTXPHY_CON0, SW_TX_IDLE_MASK |
+			      SW_TX_PD_MASK | SW_PD_PLL_MASK |
+			      SW_MODULEB_EN_MASK | SW_MODULEA_EN_MASK,
+			      SW_TX_IDLE(0x3ff) | SW_TX_PD(0x3ff) | SW_PD_PLL);
+}
+
+void rk628_combtxphy_set_bus_width(struct rk628 *rk628, u32 bus_width)
+{
+	rk628->combtxphy.bus_width = bus_width;
+}
+
+u32 rk628_combtxphy_get_bus_width(struct rk628 *rk628)
+{
+	return rk628->combtxphy.bus_width;
+}
+
+void rk628_combtxphy_set_gvi_division_mode(struct rk628 *rk628, bool division)
+{
+	rk628->combtxphy.division_mode = division;
+}
+
+void rk628_combtxphy_set_mode(struct rk628 *rk628, enum phy_mode mode)
+{
+	struct rk628_combtxphy *combtxphy = &rk628->combtxphy;
+	unsigned int fvco, fpfd, frac_rate, fin = 24;
+
+	switch (mode) {
+	case PHY_MODE_VIDEO_MIPI:
+	{
+		int bus_width = rk628_combtxphy_get_bus_width(rk628);
+		unsigned int fhsc = bus_width >> 8;
+		unsigned int flags = bus_width & 0xff;
+
+		fhsc = fin * (fhsc / fin);
+		if (fhsc < 80 || fhsc > 1500)
+			return;
+		else if (fhsc < 375)
+			combtxphy->rate_div = 4;
+		else if (fhsc < 750)
+			combtxphy->rate_div = 2;
+		else
+			combtxphy->rate_div = 1;
+
+		combtxphy->flags = flags;
+
+		fvco = fhsc * 2 * combtxphy->rate_div;
+		combtxphy->ref_div = 1;
+		combtxphy->fb_div = fvco / 8 / fin;
+		frac_rate = fvco - (fin * 8 * combtxphy->fb_div);
+		if (frac_rate) {
+			frac_rate <<= 10;
+			frac_rate /= fin * 8;
+			combtxphy->frac_div = frac_rate;
+		} else {
+			combtxphy->frac_div = 0;
+		}
+
+		fvco = fin * (1024 * combtxphy->fb_div + combtxphy->frac_div);
+		fvco *= 8;
+		fvco = DIV_ROUND_UP(fvco, 1024 * combtxphy->ref_div);
+		fhsc = fvco / 2 / combtxphy->rate_div;
+		combtxphy->bus_width = fhsc;
+
+		break;
+	}
+	case PHY_MODE_VIDEO_LVDS:
+	{
+		int bus_width = rk628_combtxphy_get_bus_width(rk628);
+		unsigned int flags = bus_width & 0xff;
+		unsigned int rate = (bus_width >> 8) * 7;
+
+		combtxphy->flags = flags;
+		combtxphy->ref_div = 1;
+		combtxphy->fb_div = 14;
+		combtxphy->frac_div = 0;
+
+		if (rate < 500)
+			combtxphy->rate_div = 4;
+		else if (rate < 1000)
+			combtxphy->rate_div = 2;
+		else
+			combtxphy->rate_div = 1;
+		break;
+	}
+	case PHY_MODE_VIDEO_GVI:
+	{
+		unsigned int i, delta_freq, best_delta_freq, fb_div;
+		unsigned int bus_width = rk628_combtxphy_get_bus_width(rk628);
+		unsigned long ref_clk;
+		unsigned long long pre_clk;
+
+		if (bus_width < 500000 || bus_width > 4000000)
+			return;
+		else if (bus_width < 1000000)
+			combtxphy->rate_div = 4;
+		else if (bus_width < 2000000)
+			combtxphy->rate_div = 2;
+		else
+			combtxphy->rate_div = 1;
+		fvco = bus_width * combtxphy->rate_div;
+		ref_clk = rk628_cru_clk_get_rate(rk628, CGU_SCLK_VOP) / 1000; /* khz */
+		if (combtxphy->division_mode)
+			ref_clk /= 2;
+		/*
+		 * the reference clock at PFD(FPFD = ref_clk / ref_div) about
+		 * 25MHz is recommende, FPFD must range from 16MHz to 35MHz,
+		 * here to find the best rev_div.
+		 */
+		best_delta_freq = ref_clk;
+		for (i = 1; i <= 32; i++) {
+			fpfd = ref_clk / i;
+			delta_freq = abs(fpfd - 25000);
+			if (delta_freq < best_delta_freq) {
+				best_delta_freq = delta_freq;
+				combtxphy->ref_div = i;
+			}
+		}
+
+		/*
+		 * ref_clk / ref_div * 8 * fb_div = FVCO
+		 */
+		pre_clk = (unsigned long long)fvco / 8 * combtxphy->ref_div * 1024;
+		do_div(pre_clk, ref_clk);
+		fb_div = pre_clk / 1024;
+
+		/*
+		 * get the actually frequency
+		 */
+		bus_width = ref_clk / combtxphy->ref_div * 8;
+		bus_width *= fb_div;
+		bus_width /= combtxphy->rate_div;
+
+		combtxphy->frac_div = 0;
+		combtxphy->fb_div = fb_div;
+
+		combtxphy->bus_width = bus_width;
+		break;
+	}
+	default:
+		break;
+	}
+
+	combtxphy->mode = mode;
+}
diff --git a/kernel/drivers/misc/rk628/rk628_combtxphy.h b/kernel/drivers/misc/rk628/rk628_combtxphy.h
new file mode 100644
index 0000000..b59594d
--- /dev/null
+++ b/kernel/drivers/misc/rk628/rk628_combtxphy.h
@@ -0,0 +1,82 @@
+/* SPDX-License-Identifier: BSD-3-Clause */
+/*
+ * Copyright (c) 2021 Rockchip Electronics Co. Ltd.
+ *
+ * Author: Guochun Huang <hero.huang@rock-chips.com>
+ */
+#ifndef RK628_COMBTXPHY_H
+#define RK628_COMBTXPHY_H
+#include "rk628.h"
+
+#define COMBTXPHY_BASE		0x90000
+#define REG(x)			((x) + COMBTXPHY_BASE)
+
+#define COMBTXPHY_CON0		REG(0x0000)
+#define SW_TX_IDLE_MASK		GENMASK(29, 20)
+#define SW_TX_IDLE(x)		UPDATE(x, 29, 20)
+#define SW_TX_PD_MASK		GENMASK(17, 8)
+#define SW_TX_PD(x)		UPDATE(x, 17, 8)
+#define SW_BUS_WIDTH_MASK	GENMASK(6, 5)
+#define SW_BUS_WIDTH_7BIT	UPDATE(0x3, 6, 5)
+#define SW_BUS_WIDTH_8BIT	UPDATE(0x2, 6, 5)
+#define SW_BUS_WIDTH_9BIT	UPDATE(0x1, 6, 5)
+#define SW_BUS_WIDTH_10BIT	UPDATE(0x0, 6, 5)
+#define SW_PD_PLL_MASK		BIT(4)
+#define SW_PD_PLL		BIT(4)
+#define SW_GVI_LVDS_EN_MASK	BIT(3)
+#define SW_GVI_LVDS_EN		BIT(3)
+#define SW_MIPI_DSI_EN_MASK	BIT(2)
+#define SW_MIPI_DSI_EN		BIT(2)
+#define SW_MODULEB_EN_MASK	BIT(1)
+#define SW_MODULEB_EN		BIT(1)
+#define SW_MODULEA_EN_MASK	BIT(0)
+#define SW_MODULEA_EN		BIT(0)
+#define COMBTXPHY_CON1		REG(0x0004)
+#define COMBTXPHY_CON2		REG(0x0008)
+#define COMBTXPHY_CON3		REG(0x000c)
+#define COMBTXPHY_CON4		REG(0x0010)
+#define COMBTXPHY_CON5		REG(0x0014)
+#define SW_RATE(x)		UPDATE(x, 26, 24)
+#define SW_REF_DIV(x)		UPDATE(x, 20, 16)
+#define SW_PLL_FB_DIV(x)	UPDATE(x, 14, 10)
+#define SW_PLL_FRAC_DIV(x)	UPDATE(x, 9, 0)
+#define COMBTXPHY_CON6		REG(0x0018)
+#define COMBTXPHY_CON7		REG(0x001c)
+#define SW_TX_RTERM_MASK	GENMASK(22, 20)
+#define SW_TX_RTERM(x)		UPDATE(x, 22, 20)
+#define SW_TX_MODE_MASK		GENMASK(17, 16)
+#define SW_TX_MODE(x)		UPDATE(x, 17, 16)
+#define SW_TX_CTL_CON5_MASK	BIT(10)
+#define SW_TX_CTL_CON5(x)	UPDATE(x, 10, 10)
+#define SW_TX_CTL_CON4_MASK	GENMASK(9, 8)
+#define SW_TX_CTL_CON4(x)	UPDATE(x, 9, 8)
+#define BYPASS_095V_LDO_MASK    BIT(3)
+#define BYPASS_095V_LDO(x)      UPDATE(x, 3, 3)
+#define TX_COM_VOLT_ADJ_MASK    GENMASK(2, 0)
+#define TX_COM_VOLT_ADJ(x)      UPDATE(x, 2, 0)
+
+#define COMBTXPHY_CON8		REG(0x0020)
+#define COMBTXPHY_CON9		REG(0x0024)
+#define SW_DSI_FSET_EN_MASK	BIT(29)
+#define SW_DSI_FSET_EN		BIT(29)
+#define SW_DSI_RCAL_EN_MASK	BIT(28)
+#define SW_DSI_RCAL_EN		BIT(28)
+#define COMBTXPHY_CON10		REG(0x0028)
+#define TX9_CKDRV_EN		BIT(9)
+#define TX8_CKDRV_EN		BIT(8)
+#define TX7_CKDRV_EN		BIT(7)
+#define TX6_CKDRV_EN		BIT(6)
+#define TX5_CKDRV_EN		BIT(5)
+#define TX4_CKDRV_EN		BIT(4)
+#define TX3_CKDRV_EN		BIT(3)
+#define TX2_CKDRV_EN		BIT(2)
+#define TX1_CKDRV_EN		BIT(1)
+#define TX0_CKDRV_EN		BIT(0)
+
+void rk628_combtxphy_set_gvi_division_mode(struct rk628 *rk628, bool division);
+void rk628_combtxphy_set_mode(struct rk628 *rk628, enum phy_mode mode);
+void rk628_combtxphy_set_bus_width(struct rk628 *rk628, uint32_t bus_width);
+uint32_t rk628_combtxphy_get_bus_width(struct rk628 *rk628);
+void rk628_combtxphy_power_on(struct rk628 *rk628);
+void rk628_combtxphy_power_off(struct rk628 *rk628);
+#endif
diff --git a/kernel/drivers/misc/rk628/rk628_config.c b/kernel/drivers/misc/rk628/rk628_config.c
new file mode 100644
index 0000000..a91e1fd
--- /dev/null
+++ b/kernel/drivers/misc/rk628/rk628_config.c
@@ -0,0 +1,52 @@
+// SPDX-License-Identifier: BSD-3-Clause
+/*
+ * Copyright (c) 2021 Rockchip Electronics Co. Ltd.
+ *
+ * Author: Guochun Huang <hero.huang@rock-chips.com>
+ */
+
+#include "rk628_config.h"
+
+struct rk628_display_mode *rk628_display_get_src_mode(struct rk628 *rk628)
+{
+	return &rk628->src_mode;
+}
+
+struct rk628_display_mode *rk628_display_get_dst_mode(struct rk628 *rk628)
+{
+	return &rk628->dst_mode;
+}
+
+void rk628_mode_copy(struct rk628_display_mode *to, struct rk628_display_mode *from)
+{
+	to->clock = from->clock;
+	to->hdisplay = from->hdisplay;
+	to->hsync_start = from->hsync_start;
+	to->hsync_end = from->hsync_end;
+	to->htotal = from->htotal;
+	to->vdisplay = from->vdisplay;
+	to->vsync_start = from->vsync_start;
+	to->vsync_end = from->vsync_end;
+	to->vtotal = from->vtotal;
+	to->flags = from->flags;
+}
+
+void rk628_set_input_bus_format(struct rk628 *rk628, enum bus_format format)
+{
+	rk628->input_fmt = format;
+}
+
+enum bus_format rk628_get_input_bus_format(struct rk628 *rk628)
+{
+	return rk628->input_fmt;
+}
+
+void rk628_set_output_bus_format(struct rk628 *rk628, enum bus_format format)
+{
+	rk628->output_fmt = format;
+}
+
+enum bus_format rk628_get_output_bus_format(struct rk628 *rk628)
+{
+	return rk628->output_fmt;
+}
diff --git a/kernel/drivers/misc/rk628/rk628_config.h b/kernel/drivers/misc/rk628/rk628_config.h
new file mode 100644
index 0000000..7b9eb91
--- /dev/null
+++ b/kernel/drivers/misc/rk628/rk628_config.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2021 Rockchip Electronics Co. Ltd.
+ *
+ * Author: Guochun Huang <hero.huang@rock-chips.com>
+ */
+
+#ifndef CONFIG_H
+#define CONFIG_H
+
+#include "rk628.h"
+
+struct rk628_display_mode *rk628_display_get_src_mode(struct rk628 *rk628);
+struct rk628_display_mode *rk628_display_get_dst_mode(struct rk628 *rk628);
+void rk628_mode_copy(struct rk628_display_mode *to, struct rk628_display_mode *from);
+
+
+void rk628_set_input_bus_format(struct rk628 *rk628, enum bus_format format);
+enum bus_format rk628_get_input_bus_format(struct rk628 *rk628);
+void rk628_set_output_bus_format(struct rk628 *rk628, enum bus_format format);
+enum bus_format rk628_get_output_bus_format(struct rk628 *rk628);
+
+#endif
+
diff --git a/kernel/drivers/misc/rk628/rk628_cru.c b/kernel/drivers/misc/rk628/rk628_cru.c
new file mode 100644
index 0000000..31dc2d0
--- /dev/null
+++ b/kernel/drivers/misc/rk628/rk628_cru.c
@@ -0,0 +1,472 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Rockchip Electronics Co. Ltd.
+ *
+ * Author: Wyon Bi <bivvy.bi@rock-chips.com>
+ */
+
+#include "rk628.h"
+#include "rk628_cru.h"
+
+#define REFCLK_RATE		24000000UL
+#define MIN_FREF_RATE		10000000UL
+#define MAX_FREF_RATE		800000000UL
+#define MIN_FREFDIV_RATE	1000000UL
+#define MAX_FREFDIV_RATE	100000000UL
+#define MIN_FVCO_RATE		600000000UL
+#define MAX_FVCO_RATE		1600000000UL
+#define MIN_FOUTPOSTDIV_RATE	12000000UL
+#define MAX_FOUTPOSTDIV_RATE	1600000000UL
+
+static void rational_best_approximation(unsigned long given_numerator,
+					unsigned long given_denominator,
+					unsigned long max_numerator,
+					unsigned long max_denominator,
+					unsigned long *best_numerator,
+					unsigned long *best_denominator)
+{
+	unsigned long n, d, n0, d0, n1, d1;
+
+	n = given_numerator;
+	d = given_denominator;
+	n0 = d1 = 0;
+	n1 = d0 = 1;
+	for (;;) {
+		unsigned long t, a;
+
+		if ((n1 > max_numerator) || (d1 > max_denominator)) {
+			n1 = n0;
+			d1 = d0;
+			break;
+		}
+		if (d == 0)
+			break;
+		t = d;
+		a = n / d;
+		d = n % d;
+		n = t;
+		t = n0 + a * n1;
+		n0 = n1;
+		n1 = t;
+		t = d0 + a * d1;
+		d0 = d1;
+		d1 = t;
+	}
+	*best_numerator = n1;
+	*best_denominator = d1;
+}
+
+static unsigned long rk628_cru_clk_get_rate_pll(struct rk628 *rk628,
+						unsigned int id)
+{
+	unsigned long parent_rate = REFCLK_RATE;
+	u32 postdiv1, fbdiv, dsmpd, postdiv2, refdiv, frac, bypass;
+	u32 con0, con1, con2;
+	u64 foutvco, foutpostdiv;
+	u32 offset, val;
+
+	rk628_i2c_read(rk628, CRU_MODE_CON00, &val);
+	if (id == CGU_CLK_CPLL) {
+		val &= CLK_CPLL_MODE_MASK;
+		val >>= CLK_CPLL_MODE_SHIFT;
+		if (val == CLK_CPLL_MODE_OSC)
+			return parent_rate;
+
+		offset = 0x00;
+	} else {
+		val &= CLK_GPLL_MODE_MASK;
+		val >>= CLK_GPLL_MODE_SHIFT;
+		if (val == CLK_GPLL_MODE_OSC)
+			return parent_rate;
+
+		offset = 0x20;
+	}
+
+	rk628_i2c_read(rk628, offset + CRU_CPLL_CON0, &con0);
+	rk628_i2c_read(rk628, offset + CRU_CPLL_CON1, &con1);
+	rk628_i2c_read(rk628, offset + CRU_CPLL_CON2, &con2);
+
+	bypass = (con0 & PLL_BYPASS_MASK) >> PLL_BYPASS_SHIFT;
+	postdiv1 = (con0 & PLL_POSTDIV1_MASK) >> PLL_POSTDIV1_SHIFT;
+	fbdiv = (con0 & PLL_FBDIV_MASK) >> PLL_FBDIV_SHIFT;
+	dsmpd = (con1 & PLL_DSMPD_MASK) >> PLL_DSMPD_SHIFT;
+	postdiv2 = (con1 & PLL_POSTDIV2_MASK) >> PLL_POSTDIV2_SHIFT;
+	refdiv = (con1 & PLL_REFDIV_MASK) >> PLL_REFDIV_SHIFT;
+	frac = (con2 & PLL_FRAC_MASK) >> PLL_FRAC_SHIFT;
+
+	if (bypass)
+		return parent_rate;
+
+	foutvco = parent_rate * fbdiv;
+	do_div(foutvco, refdiv);
+
+	if (!dsmpd) {
+		u64 frac_rate = (u64)parent_rate * frac;
+
+		do_div(frac_rate, refdiv);
+		foutvco += frac_rate >> 24;
+	}
+
+	foutpostdiv = foutvco;
+	do_div(foutpostdiv, postdiv1);
+	do_div(foutpostdiv, postdiv2);
+
+	return foutpostdiv;
+}
+
+static unsigned long rk628_cru_clk_set_rate_pll(struct rk628 *rk628,
+						unsigned int id,
+						unsigned long rate)
+{
+	unsigned long fin = REFCLK_RATE, fout = rate;
+	u8 min_refdiv, max_refdiv, postdiv;
+	u8 dsmpd = 1, postdiv1 = 0, postdiv2 = 0, refdiv = 0;
+	u16 fbdiv = 0;
+	u32 frac = 0;
+	u64 foutvco, foutpostdiv;
+	u32 offset, val;
+
+	/*
+	 * FREF : 10MHz ~ 800MHz
+	 * FREFDIV : 1MHz ~ 40MHz
+	 * FOUTVCO : 400MHz ~ 1.6GHz
+	 * FOUTPOSTDIV : 8MHz ~ 1.6GHz
+	 */
+	if (fin < MIN_FREF_RATE || fin > MAX_FREF_RATE)
+		return 0;
+
+	if (fout < MIN_FOUTPOSTDIV_RATE || fout > MAX_FOUTPOSTDIV_RATE)
+		return 0;
+
+	if (id == CGU_CLK_CPLL)
+		offset = 0x00;
+	else
+		offset = 0x20;
+
+	rk628_i2c_write(rk628, offset + CRU_CPLL_CON1, PLL_PD(1));
+
+	if (fin == fout) {
+		rk628_i2c_write(rk628, offset + CRU_CPLL_CON0, PLL_BYPASS(1));
+		rk628_i2c_write(rk628, offset + CRU_CPLL_CON1, PLL_PD(0));
+		while (1) {
+			rk628_i2c_read(rk628, offset + CRU_CPLL_CON1, &val);
+			if (val & PLL_LOCK)
+				break;
+		}
+		return fin;
+	}
+
+	min_refdiv = fin / MAX_FREFDIV_RATE + 1;
+	max_refdiv = fin / MIN_FREFDIV_RATE;
+	if (max_refdiv > 64)
+		max_refdiv = 64;
+
+	if (fout < MIN_FVCO_RATE) {
+		postdiv = MIN_FVCO_RATE / fout + 1;
+
+		for (postdiv2 = 1; postdiv2 < 8; postdiv2++) {
+			if (postdiv % postdiv2)
+				continue;
+
+			postdiv1 = postdiv / postdiv2;
+
+			if (postdiv1 > 0 && postdiv1 < 8)
+				break;
+		}
+
+		if (postdiv2 > 7)
+			return 0;
+
+		fout *= postdiv1 * postdiv2;
+	} else {
+		postdiv1 = 1;
+		postdiv2 = 1;
+	}
+
+	for (refdiv = min_refdiv; refdiv <= max_refdiv; refdiv++) {
+		u64 tmp, frac_rate;
+
+		if (fin % refdiv)
+			continue;
+
+		tmp = (u64)fout * refdiv;
+		do_div(tmp, fin);
+		fbdiv = tmp;
+		if (fbdiv < 10 || fbdiv > 1600)
+			continue;
+
+		tmp = (u64)fbdiv * fin;
+		do_div(tmp, refdiv);
+		if (fout < MIN_FVCO_RATE || fout > MAX_FVCO_RATE)
+			continue;
+
+		frac_rate = fout - tmp;
+
+		if (frac_rate) {
+			tmp = (u64)frac_rate * refdiv;
+			tmp <<= 24;
+			do_div(tmp, fin);
+			frac = tmp;
+			dsmpd = 0;
+		}
+
+		break;
+	}
+
+	/*
+	 * If DSMPD = 1 (DSM is disabled, "integer mode")
+	 * FOUTVCO = FREF / REFDIV * FBDIV
+	 * FOUTPOSTDIV = FOUTVCO / POSTDIV1 / POSTDIV2
+	 *
+	 * If DSMPD = 0 (DSM is enabled, "fractional mode")
+	 * FOUTVCO = FREF / REFDIV * (FBDIV + FRAC / 2^24)
+	 * FOUTPOSTDIV = FOUTVCO / POSTDIV1 / POSTDIV2
+	 */
+	foutvco = fin * fbdiv;
+	do_div(foutvco, refdiv);
+
+	if (!dsmpd) {
+		u64 frac_rate = (u64)fin * frac;
+
+		do_div(frac_rate, refdiv);
+		foutvco += frac_rate >> 24;
+	}
+
+	foutpostdiv = foutvco;
+	do_div(foutpostdiv, postdiv1);
+	do_div(foutpostdiv, postdiv2);
+
+	rk628_i2c_write(rk628, offset + CRU_CPLL_CON0,
+			PLL_BYPASS(0) | PLL_POSTDIV1(postdiv1) |
+			PLL_FBDIV(fbdiv));
+	rk628_i2c_write(rk628, offset + CRU_CPLL_CON1,
+			PLL_DSMPD(dsmpd) | PLL_POSTDIV2(postdiv2) |
+			PLL_REFDIV(refdiv));
+	rk628_i2c_write(rk628, offset + CRU_CPLL_CON2, PLL_FRAC(frac));
+
+	rk628_i2c_write(rk628, offset + CRU_CPLL_CON1, PLL_PD(0));
+	while (1) {
+		rk628_i2c_read(rk628, offset + CRU_CPLL_CON1, &val);
+		if (val & PLL_LOCK)
+			break;
+	}
+
+	return (unsigned long)foutpostdiv;
+}
+
+static unsigned long rk628_cru_clk_set_rate_sclk_vop(struct rk628 *rk628,
+						     unsigned long rate)
+{
+	unsigned long m, n, parent_rate;
+	u32 val;
+
+	rk628_i2c_read(rk628, CRU_CLKSEL_CON02, &val);
+	val &= SCLK_VOP_SEL_MASK;
+	val >>= SCLK_VOP_SEL_SHIFT;
+	if (val == SCLK_VOP_SEL_GPLL)
+		parent_rate = rk628_cru_clk_get_rate_pll(rk628, CGU_CLK_GPLL);
+	else
+		parent_rate = rk628_cru_clk_get_rate_pll(rk628, CGU_CLK_CPLL);
+
+	rational_best_approximation(rate, parent_rate,
+				    GENMASK(15, 0), GENMASK(15, 0),
+				    &m, &n);
+	rk628_i2c_write(rk628, CRU_CLKSEL_CON13, m << 16 | n);
+
+	return rate;
+}
+
+static unsigned long rk628_cru_clk_get_rate_sclk_vop(struct rk628 *rk628)
+{
+	unsigned long rate, parent_rate, m, n;
+	u32 mux, div;
+
+	rk628_i2c_read(rk628, CRU_CLKSEL_CON02, &mux);
+	mux &= CLK_UART_SRC_SEL_MASK;
+	mux >>= SCLK_VOP_SEL_SHIFT;
+	if (mux == SCLK_VOP_SEL_GPLL)
+		parent_rate = rk628_cru_clk_get_rate_pll(rk628, CGU_CLK_GPLL);
+	else
+		parent_rate = rk628_cru_clk_get_rate_pll(rk628, CGU_CLK_CPLL);
+
+	rk628_i2c_read(rk628, CRU_CLKSEL_CON13, &div);
+	m = div >> 16 & 0xffff;
+	n = div & 0xffff;
+	rate = parent_rate * m / n;
+
+	return rate;
+}
+
+static unsigned long rk628_cru_clk_set_rate_rx_read(struct rk628 *rk628,
+						    unsigned long rate)
+{
+	unsigned long m, n, parent_rate;
+	u32 val;
+
+	rk628_i2c_read(rk628, CRU_CLKSEL_CON02, &val);
+	val &= CLK_RX_READ_SEL_MASK;
+	val >>= CLK_RX_READ_SEL_SHIFT;
+	if (val == CLK_RX_READ_SEL_GPLL)
+		parent_rate = rk628_cru_clk_get_rate_pll(rk628, CGU_CLK_GPLL);
+	else
+		parent_rate = rk628_cru_clk_get_rate_pll(rk628, CGU_CLK_CPLL);
+
+	rational_best_approximation(rate, parent_rate,
+				    GENMASK(15, 0), GENMASK(15, 0),
+				    &m, &n);
+	rk628_i2c_write(rk628, CRU_CLKSEL_CON14, m << 16 | n);
+
+	return rate;
+}
+
+static unsigned long rk628_cru_clk_get_rate_uart_src(struct rk628 *rk628)
+{
+	unsigned long rate, parent_rate;
+	u32 mux, div;
+
+	rk628_i2c_read(rk628, CRU_CLKSEL_CON21, &mux);
+	mux &= SCLK_VOP_SEL_MASK;
+	if (mux == CLK_UART_SRC_SEL_GPLL)
+		parent_rate = rk628_cru_clk_get_rate_pll(rk628, CGU_CLK_GPLL);
+	else
+		parent_rate = rk628_cru_clk_get_rate_pll(rk628, CGU_CLK_CPLL);
+
+	rk628_i2c_read(rk628, CRU_CLKSEL_CON21, &div);
+	div &= CLK_UART_SRC_DIV_MASK;
+	div >>= CLK_UART_SRC_DIV_SHIFT;
+	rate = parent_rate / (div + 1);
+
+	return rate;
+}
+
+static unsigned long rk628_cru_clk_set_rate_sclk_uart(struct rk628 *rk628,
+						      unsigned long rate)
+{
+	unsigned long m, n, parent_rate;
+
+	parent_rate = rk628_cru_clk_get_rate_uart_src(rk628);
+
+	if (rate == REFCLK_RATE) {
+		rk628_i2c_write(rk628, CRU_CLKSEL_CON06,
+				SCLK_UART_SEL(SCLK_UART_SEL_OSC));
+		return rate;
+	} else if (rate == parent_rate) {
+		rk628_i2c_write(rk628, CRU_CLKSEL_CON06,
+				SCLK_UART_SEL(SCLK_UART_SEL_UART_SRC));
+		return rate;
+	}
+
+	rk628_i2c_write(rk628, CRU_CLKSEL_CON06,
+			SCLK_UART_SEL(SCLK_UART_SEL_UART_FRAC));
+
+	rational_best_approximation(rate, parent_rate,
+				    GENMASK(15, 0), GENMASK(15, 0),
+				    &m, &n);
+	rk628_i2c_write(rk628, CRU_CLKSEL_CON20, m << 16 | n);
+
+	return rate;
+}
+
+static unsigned long
+rk628_cru_clk_get_rate_bt1120_dec_parent(struct rk628 *rk628)
+{
+	unsigned long parent_rate;
+	u32 mux;
+
+	rk628_i2c_read(rk628, CRU_CLKSEL_CON02, &mux);
+	mux &= CLK_BT1120DEC_SEL_MASK;
+	if (mux == CLK_BT1120DEC_SEL_GPLL)
+		parent_rate = rk628_cru_clk_get_rate_pll(rk628, CGU_CLK_GPLL);
+	else
+		parent_rate = rk628_cru_clk_get_rate_pll(rk628, CGU_CLK_CPLL);
+
+	return parent_rate;
+}
+
+static unsigned long rk628_cru_clk_set_rate_bt1120_dec(struct rk628 *rk628,
+						       unsigned long rate)
+{
+	unsigned long parent_rate;
+	u32 div;
+
+	parent_rate = rk628_cru_clk_get_rate_bt1120_dec_parent(rk628);
+	div = DIV_ROUND_UP(parent_rate, rate);
+	rk628_i2c_write(rk628, CRU_CLKSEL_CON02, CLK_BT1120DEC_DIV(div-1));
+
+	return parent_rate / div;
+}
+
+int rk628_cru_clk_set_rate(struct rk628 *rk628, unsigned int id,
+			   unsigned long rate)
+{
+	switch (id) {
+	case CGU_CLK_CPLL:
+	case CGU_CLK_GPLL:
+		rk628_cru_clk_set_rate_pll(rk628, id, rate);
+		break;
+	case CGU_CLK_RX_READ:
+		rk628_cru_clk_set_rate_rx_read(rk628, rate);
+		break;
+	case CGU_SCLK_VOP:
+		rk628_cru_clk_set_rate_sclk_vop(rk628, rate);
+		break;
+	case CGU_SCLK_UART:
+		rk628_cru_clk_set_rate_sclk_uart(rk628, rate);
+		break;
+	case CGU_BT1120DEC:
+		rk628_cru_clk_set_rate_bt1120_dec(rk628, rate);
+		break;
+	default:
+		return -1;
+	}
+
+	return 0;
+}
+
+unsigned long rk628_cru_clk_get_rate(struct rk628 *rk628, unsigned int id)
+{
+	unsigned long rate;
+
+	switch (id) {
+	case CGU_CLK_CPLL:
+	case CGU_CLK_GPLL:
+		rate = rk628_cru_clk_get_rate_pll(rk628, id);
+		break;
+	case CGU_SCLK_VOP:
+		rate = rk628_cru_clk_get_rate_sclk_vop(rk628);
+		break;
+	default:
+		return 0;
+	}
+
+	return rate;
+}
+
+void rk628_cru_init(struct rk628 *rk628)
+{
+	u32 val;
+	u8 mcu_mode;
+
+	rk628_i2c_read(rk628, GRF_SYSTEM_STATUS0, &val);
+	mcu_mode = (val & I2C_ONLY_FLAG) ? 0 : 1;
+	if (mcu_mode)
+		return;
+
+	rk628_i2c_write(rk628, CRU_GPLL_CON0, 0xffff701d);
+	mdelay(1);
+	rk628_i2c_write(rk628, CRU_MODE_CON00, 0xffff0004);
+	mdelay(1);
+	rk628_i2c_write(rk628, CRU_CLKSEL_CON00, 0x00ff0080);
+	rk628_i2c_write(rk628, CRU_CLKSEL_CON00, 0x00ff0083);
+	rk628_i2c_write(rk628, CRU_CPLL_CON0, 0xffff3063);
+	mdelay(1);
+	rk628_i2c_write(rk628, CRU_MODE_CON00, 0xffff0005);
+	rk628_i2c_write(rk628, CRU_CLKSEL_CON00, 0x00ff0003);
+	rk628_i2c_write(rk628, CRU_CLKSEL_CON00, 0x00ff000b);
+	rk628_i2c_write(rk628, CRU_GPLL_CON0, 0xffff1028);
+	mdelay(1);
+	rk628_i2c_write(rk628, CRU_CLKSEL_CON00, 0x00ff008b);
+	rk628_i2c_write(rk628, CRU_CPLL_CON0, 0xffff1063);
+	mdelay(1);
+	rk628_i2c_write(rk628, CRU_CLKSEL_CON00, 0x00ff000b);
+}
diff --git a/kernel/drivers/misc/rk628/rk628_cru.h b/kernel/drivers/misc/rk628/rk628_cru.h
new file mode 100644
index 0000000..e13a559
--- /dev/null
+++ b/kernel/drivers/misc/rk628/rk628_cru.h
@@ -0,0 +1,159 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2020 Rockchip Electronics Co. Ltd.
+ *
+ * Author: Wyon Bi <bivvy.bi@rock-chips.com>
+ */
+
+#ifndef RK628_CRU_H
+#define RK628_CRU_H
+
+#include "rk628.h"
+
+#define CRU_REG(x)		((x) + 0xc0000)
+
+#define CRU_CPLL_CON0		CRU_REG(0x0000)
+#define PLL_BYPASS_MASK		BIT(15)
+#define PLL_BYPASS(x)		HIWORD_UPDATE(x, 15, 15)
+#define PLL_BYPASS_SHIFT	15
+#define PLL_POSTDIV1_MASK	GENMASK(14, 12)
+#define PLL_POSTDIV1(x)		HIWORD_UPDATE(x, 14, 12)
+#define PLL_POSTDIV1_SHIFT	12
+#define PLL_FBDIV_MASK		GENMASK(11, 0)
+#define PLL_FBDIV(x)		HIWORD_UPDATE(x, 11, 0)
+#define PLL_FBDIV_SHIFT		0
+#define CRU_CPLL_CON1		CRU_REG(0x0004)
+#define PLL_PD_MASK		BIT(13)
+#define PLL_PD(x)		HIWORD_UPDATE(x, 13, 13)
+#define PLL_DSMPD_MASK		BIT(12)
+#define PLL_DSMPD(x)		HIWORD_UPDATE(x, 12, 12)
+#define PLL_DSMPD_SHIFT		12
+#define PLL_LOCK		BIT(10)
+#define PLL_POSTDIV2_MASK	GENMASK(8, 6)
+#define PLL_POSTDIV2(x)		HIWORD_UPDATE(x, 8, 6)
+#define PLL_POSTDIV2_SHIFT	6
+#define PLL_REFDIV_MASK		GENMASK(5, 0)
+#define PLL_REFDIV(x)		HIWORD_UPDATE(x, 5, 0)
+#define PLL_REFDIV_SHIFT	0
+#define CRU_CPLL_CON2		CRU_REG(0x0008)
+#define PLL_FRAC_MASK		GENMASK(23, 0)
+#define PLL_FRAC(x)		((x) << 0)
+#define PLL_FRAC_SHIFT		0
+#define CRU_CPLL_CON3		CRU_REG(0x000c)
+#define CRU_CPLL_CON4		CRU_REG(0x0010)
+#define CRU_GPLL_CON0		CRU_REG(0x0020)
+#define CRU_GPLL_CON1		CRU_REG(0x0024)
+#define CRU_GPLL_CON2		CRU_REG(0x0028)
+#define CRU_GPLL_CON3		CRU_REG(0x002c)
+#define CRU_GPLL_CON4		CRU_REG(0x0030)
+#define CRU_MODE_CON00		CRU_REG(0x0060)
+#define CLK_GPLL_MODE_MASK	BIT(2)
+#define CLK_GPLL_MODE_SHIFT	2
+#define CLK_GPLL_MODE_GPLL	1
+#define CLK_GPLL_MODE_OSC	0
+#define CLK_CPLL_MODE_MASK	BIT(0)
+#define CLK_CPLL_MODE_SHIFT	0
+#define CLK_CPLL_MODE_CPLL	1
+#define CLK_CPLL_MODE_OSC	0
+#define CRU_CLKSEL_CON00	CRU_REG(0x0080)
+#define CRU_CLKSEL_CON01	CRU_REG(0x0084)
+#define CRU_CLKSEL_CON02	CRU_REG(0x0088)
+#define SCLK_VOP_SEL_MASK	BIT(9)
+#define SCLK_VOP_SEL_SHIFT	9
+#define SCLK_VOP_SEL_GPLL	1
+#define SCLK_VOP_SEL_CPLL	0
+#define CLK_RX_READ_SEL_MASK	BIT(8)
+#define CLK_RX_READ_SEL_SHIFT	8
+#define CLK_RX_READ_SEL_GPLL	1
+#define CLK_RX_READ_SEL_CPLL	0
+#define CLK_BT1120DEC_SEL_MASK	BIT(7)
+#define CLK_BT1120DEC_SEL_SHIFT	7
+#define CLK_BT1120DEC_SEL_GPLL	1
+#define CLK_BT1120DEC_SEL_CPLL	0
+#define CLK_BT1120DEC_DIV(x)	HIWORD_UPDATE(x, 4, 0)
+#define CRU_CLKSEL_CON03	CRU_REG(0x008c)
+#define CRU_CLKSEL_CON04	CRU_REG(0x0090)
+#define CRU_CLKSEL_CON05	CRU_REG(0x0094)
+#define CRU_CLKSEL_CON06	CRU_REG(0x0098)
+#define SCLK_UART_SEL(x)	HIWORD_UPDATE(x, 15, 14)
+#define SCLK_UART_SEL_MASK	GENMASK(15, 14)
+#define SCLK_UART_SEL_SHIFT	14
+#define SCLK_UART_SEL_OSC	2
+#define SCLK_UART_SEL_UART_FRAC	1
+#define SCLK_UART_SEL_UART_SRC	0
+#define CRU_CLKSEL_CON07	CRU_REG(0x009c)
+#define CRU_CLKSEL_CON08	CRU_REG(0x00a0)
+#define CRU_CLKSEL_CON09	CRU_REG(0x00a4)
+#define CRU_CLKSEL_CON10	CRU_REG(0x00a8)
+#define CRU_CLKSEL_CON11	CRU_REG(0x00ac)
+#define CRU_CLKSEL_CON12	CRU_REG(0x00b0)
+#define CRU_CLKSEL_CON13	CRU_REG(0x00b4)
+#define CRU_CLKSEL_CON14	CRU_REG(0x00b8)
+#define CRU_CLKSEL_CON15	CRU_REG(0x00bc)
+#define CRU_CLKSEL_CON16	CRU_REG(0x00c0)
+#define CRU_CLKSEL_CON17	CRU_REG(0x00c4)
+#define CRU_CLKSEL_CON18	CRU_REG(0x00c8)
+#define CRU_CLKSEL_CON20	CRU_REG(0x00d0)
+#define CRU_CLKSEL_CON21	CRU_REG(0x00d4)
+#define CLK_UART_SRC_SEL_MASK	BIT(15)
+#define CLK_UART_SRC_SEL_GPLL	(1 << 15)
+#define CLK_UART_SRC_SEL_CPLL	(0 << 15)
+#define CLK_UART_SRC_DIV_MASK	GENMASK(12, 8)
+#define CLK_UART_SRC_DIV_SHIFT	8
+#define CRU_GATE_CON00		CRU_REG(0x0180)
+#define CRU_GATE_CON01		CRU_REG(0x0184)
+#define CRU_GATE_CON02		CRU_REG(0x0188)
+#define CRU_GATE_CON03		CRU_REG(0x018c)
+#define CRU_GATE_CON04		CRU_REG(0x0190)
+#define CRU_GATE_CON05		CRU_REG(0x0194)
+#define CRU_SOFTRST_CON00	CRU_REG(0x0200)
+#define CRU_SOFTRST_CON01	CRU_REG(0x0204)
+#define CRU_SOFTRST_CON02	CRU_REG(0x0208)
+#define CRU_SOFTRST_CON04	CRU_REG(0x0210)
+#define CRU_MAX_REGISTER	CRU_SOFTRST_CON04
+
+#define CGU_CLK_CPLL		1
+#define CGU_CLK_GPLL		2
+#define CGU_CLK_CPLL_MUX	3
+#define CGU_CLK_GPLL_MUX	4
+#define CGU_PCLK_GPIO0		5
+#define CGU_PCLK_GPIO1		6
+#define CGU_PCLK_GPIO2		7
+#define CGU_PCLK_GPIO3		8
+#define CGU_PCLK_TXPHY_CON	9
+#define CGU_PCLK_EFUSE		10
+#define CGU_PCLK_DSI0		11
+#define CGU_PCLK_DSI1		12
+#define CGU_PCLK_CSI		13
+#define CGU_PCLK_HDMITX		14
+#define CGU_PCLK_RXPHY		15
+#define CGU_PCLK_HDMIRX		16
+#define CGU_PCLK_DPRX		17
+#define CGU_PCLK_GVIHOST	18
+#define CGU_CLK_CFG_DPHY0	19
+#define CGU_CLK_CFG_DPHY1	20
+#define CGU_CLK_TXESC		21
+#define CGU_CLK_DPRX_VID	22
+#define CGU_CLK_IMODET		23
+#define CGU_CLK_HDMIRX_AUD	24
+#define CGU_CLK_HDMIRX_CEC	25
+#define CGU_CLK_RX_READ		26
+#define CGU_SCLK_VOP		27
+#define CGU_PCLK_LOGIC		28
+#define CGU_CLK_GPIO_DB0	29
+#define CGU_CLK_GPIO_DB1	30
+#define CGU_CLK_GPIO_DB2	31
+#define CGU_CLK_GPIO_DB3	32
+#define CGU_CLK_I2S_8CH_SRC	33
+#define CGU_CLK_I2S_8CH_FRAC	34
+#define CGU_MCLK_I2S_8CH	35
+#define CGU_I2S_MCLKOUT		36
+#define CGU_BT1120DEC		37
+#define CGU_SCLK_UART		38
+
+unsigned long rk628_cru_clk_get_rate(struct rk628 *rk628, unsigned int id);
+int rk628_cru_clk_set_rate(struct rk628 *rk628, unsigned int id,
+			   unsigned long rate);
+void rk628_cru_init(struct rk628 *rk628);
+
+#endif
diff --git a/kernel/drivers/misc/rk628/rk628_csi.c b/kernel/drivers/misc/rk628/rk628_csi.c
new file mode 100644
index 0000000..a121c8f0
--- /dev/null
+++ b/kernel/drivers/misc/rk628/rk628_csi.c
@@ -0,0 +1,434 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2020 Rockchip Electronics Co. Ltd.
+ *
+ * Author: Shunqing Chen <csq@rock-chisp.com>
+ */
+
+#include "rk628.h"
+#include "rk628_combtxphy.h"
+#include "rk628_config.h"
+#include "rk628_csi.h"
+
+#define CSITX_ERR_RETRY_TIMES		3
+
+#define MIPI_DATARATE_MBPS_LOW		750
+#define MIPI_DATARATE_MBPS_HIGH		1250
+
+#define USE_4_LANES			4
+#define YUV422_8BIT			0x1e
+/* Test Code: 0x44 (HS RX Control of Lane 0) */
+#define HSFREQRANGE(x)			UPDATE(x, 6, 1)
+
+struct rk628_csi {
+	struct rk628_display_mode mode;
+	bool txphy_pwron;
+	u64 lane_mbps;
+};
+
+static inline void testif_testclk_assert(struct rk628 *rk628)
+{
+	rk628_i2c_update_bits(rk628, GRF_MIPI_TX0_CON,
+			   PHY_TESTCLK, PHY_TESTCLK);
+	udelay(1);
+}
+
+static inline void testif_testclk_deassert(struct rk628 *rk628)
+{
+	rk628_i2c_update_bits(rk628, GRF_MIPI_TX0_CON,
+			   PHY_TESTCLK, 0);
+	udelay(1);
+}
+
+static inline void testif_testclr_assert(struct rk628 *rk628)
+{
+	rk628_i2c_update_bits(rk628, GRF_MIPI_TX0_CON,
+			   PHY_TESTCLR, PHY_TESTCLR);
+	udelay(1);
+}
+
+static inline void testif_testclr_deassert(struct rk628 *rk628)
+{
+	rk628_i2c_update_bits(rk628, GRF_MIPI_TX0_CON,
+			   PHY_TESTCLR, 0);
+	udelay(1);
+}
+
+static inline void testif_testen_assert(struct rk628 *rk628)
+{
+	rk628_i2c_update_bits(rk628, GRF_MIPI_TX0_CON,
+			   PHY_TESTEN, PHY_TESTEN);
+	udelay(1);
+}
+
+static inline void testif_testen_deassert(struct rk628 *rk628)
+{
+	rk628_i2c_update_bits(rk628, GRF_MIPI_TX0_CON,
+			   PHY_TESTEN, 0);
+	udelay(1);
+}
+
+static inline void testif_set_data(struct rk628 *rk628, u8 data)
+{
+	rk628_i2c_update_bits(rk628, GRF_MIPI_TX0_CON,
+			   PHY_TESTDIN_MASK, PHY_TESTDIN(data));
+	udelay(1);
+}
+
+static inline u8 testif_get_data(struct rk628 *rk628)
+{
+	u32 data = 0;
+
+	rk628_i2c_read(rk628, GRF_DPHY0_STATUS, &data);
+
+	return data >> PHY_TESTDOUT_SHIFT;
+}
+
+static void testif_test_code_write(struct rk628 *rk628, u8 test_code)
+{
+	testif_testclk_assert(rk628);
+	testif_set_data(rk628, test_code);
+	testif_testen_assert(rk628);
+	testif_testclk_deassert(rk628);
+	testif_testen_deassert(rk628);
+}
+
+static void testif_test_data_write(struct rk628 *rk628, u8 test_data)
+{
+	testif_testclk_deassert(rk628);
+	testif_set_data(rk628, test_data);
+	testif_testclk_assert(rk628);
+}
+
+static u8 testif_write(struct rk628 *rk628, u8 test_code, u8 test_data)
+{
+	u8 monitor_data;
+
+	testif_test_code_write(rk628, test_code);
+	testif_test_data_write(rk628, test_data);
+	monitor_data = testif_get_data(rk628);
+
+	dev_info(rk628->dev, "test_code=0x%02x, ", test_code);
+	dev_info(rk628->dev, "test_data=0x%02x, ", test_data);
+	dev_info(rk628->dev, "monitor_data=0x%02x\n", monitor_data);
+
+	return monitor_data;
+}
+static void rk628_csi_get_detected_timings(struct rk628 *rk628)
+{
+	struct rk628_display_mode *src, *dst;
+	struct rk628_csi *csi = rk628->csi;
+
+	if (!csi)
+		return;
+
+	src = rk628_display_get_src_mode(rk628);
+	dst = rk628_display_get_dst_mode(rk628);
+
+	rk628_set_output_bus_format(rk628, BUS_FMT_YUV422);
+	rk628_mode_copy(dst, src);
+	rk628_mode_copy(&csi->mode, dst);
+}
+
+
+static inline u8 testif_read(struct rk628 *rk628, u8 test_code)
+{
+	u8 test_data;
+
+	testif_test_code_write(rk628, test_code);
+	test_data = testif_get_data(rk628);
+	testif_test_data_write(rk628, test_data);
+
+	return test_data;
+}
+
+static inline void mipi_dphy_enableclk_assert(struct rk628 *rk628)
+{
+	rk628_i2c_update_bits(rk628, CSITX_DPHY_CTRL, DPHY_ENABLECLK,
+			DPHY_ENABLECLK);
+	udelay(1);
+}
+
+static inline void mipi_dphy_enableclk_deassert(struct rk628 *rk628)
+{
+	rk628_i2c_update_bits(rk628,  CSITX_DPHY_CTRL, DPHY_ENABLECLK, 0);
+	udelay(1);
+}
+
+static inline void mipi_dphy_shutdownz_assert(struct rk628 *rk628)
+{
+	rk628_i2c_update_bits(rk628,  GRF_MIPI_TX0_CON, CSI_PHYSHUTDOWNZ, 0);
+	udelay(1);
+}
+
+static inline void mipi_dphy_shutdownz_deassert(struct rk628 *rk628)
+{
+	rk628_i2c_update_bits(rk628,  GRF_MIPI_TX0_CON, CSI_PHYSHUTDOWNZ,
+			CSI_PHYSHUTDOWNZ);
+	udelay(1);
+}
+
+static inline void mipi_dphy_rstz_assert(struct rk628 *rk628)
+{
+	rk628_i2c_update_bits(rk628,  GRF_MIPI_TX0_CON, CSI_PHYRSTZ, 0);
+	udelay(1);
+}
+
+static inline void mipi_dphy_rstz_deassert(struct rk628 *rk628)
+{
+	rk628_i2c_update_bits(rk628,  GRF_MIPI_TX0_CON, CSI_PHYRSTZ,
+			CSI_PHYRSTZ);
+	udelay(1);
+}
+
+static void mipi_dphy_init_hsfreqrange(struct rk628 *rk628)
+{
+	const struct {
+		unsigned long max_lane_mbps;
+		u8 hsfreqrange;
+	} hsfreqrange_table[] = {
+		{  90, 0x00}, { 100, 0x10}, { 110, 0x20}, { 130, 0x01},
+		{ 140, 0x11}, { 150, 0x21}, { 170, 0x02}, { 180, 0x12},
+		{ 200, 0x22}, { 220, 0x03}, { 240, 0x13}, { 250, 0x23},
+		{ 270, 0x04}, { 300, 0x14}, { 330, 0x05}, { 360, 0x15},
+		{ 400, 0x25}, { 450, 0x06}, { 500, 0x16}, { 550, 0x07},
+		{ 600, 0x17}, { 650, 0x08}, { 700, 0x18}, { 750, 0x09},
+		{ 800, 0x19}, { 850, 0x29}, { 900, 0x39}, { 950, 0x0a},
+		{1000, 0x1a}, {1050, 0x2a}, {1100, 0x3a}, {1150, 0x0b},
+		{1200, 0x1b}, {1250, 0x2b}, {1300, 0x3b}, {1350, 0x0c},
+		{1400, 0x1c}, {1450, 0x2c}, {1500, 0x3c}
+	};
+	u8 hsfreqrange;
+	unsigned int index;
+	struct rk628_csi *csi = rk628->csi;
+
+	if (!csi)
+		return;
+
+	for (index = 0; index < ARRAY_SIZE(hsfreqrange_table); index++)
+		if (csi->lane_mbps <= hsfreqrange_table[index].max_lane_mbps)
+			break;
+
+	if (index == ARRAY_SIZE(hsfreqrange_table))
+		--index;
+
+	hsfreqrange = hsfreqrange_table[index].hsfreqrange;
+	testif_write(rk628, 0x44, HSFREQRANGE(hsfreqrange));
+}
+
+static int mipi_dphy_reset(struct rk628 *rk628)
+{
+	u32 val;
+	int ret;
+
+	mipi_dphy_enableclk_deassert(rk628);
+	mipi_dphy_shutdownz_assert(rk628);
+	mipi_dphy_rstz_assert(rk628);
+	testif_testclr_assert(rk628);
+
+	/* Set all REQUEST inputs to zero */
+	rk628_i2c_update_bits(rk628, GRF_MIPI_TX0_CON,
+		     FORCETXSTOPMODE_MASK | FORCERXMODE_MASK,
+		     FORCETXSTOPMODE(0) | FORCERXMODE(0));
+	udelay(1);
+	testif_testclr_deassert(rk628);
+	mipi_dphy_enableclk_assert(rk628);
+	mipi_dphy_shutdownz_deassert(rk628);
+	mipi_dphy_rstz_deassert(rk628);
+	usleep_range(1500, 2000);
+
+	ret = rk628_i2c_read(rk628, CSITX_CSITX_STATUS1, &val);
+	if (ret < 0) {
+		dev_info(rk628->dev, "lane module is not in stop state\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int mipi_dphy_power_on(struct rk628 *rk628)
+{
+	unsigned int val;
+	u32 bus_width, mask;
+	struct rk628_csi *csi = rk628->csi;
+
+	if (!csi)
+		return -1;
+
+	if ((csi->mode.hdisplay == 3840) &&
+	    (csi->mode.vdisplay == 2160)) {
+		csi->lane_mbps = MIPI_DATARATE_MBPS_HIGH;
+	} else {
+		csi->lane_mbps = MIPI_DATARATE_MBPS_LOW;
+	}
+
+	bus_width =  csi->lane_mbps << 8;
+	bus_width |= COMBTXPHY_MODULEA_EN;
+	dev_info(rk628->dev, "%s mipi bitrate:%llu mbps\n", __func__, csi->lane_mbps);
+	rk628_combtxphy_set_bus_width(rk628, bus_width);
+	rk628_combtxphy_set_mode(rk628, PHY_MODE_VIDEO_MIPI);
+
+	mipi_dphy_init_hsfreqrange(rk628);
+	usleep_range(1500, 2000);
+	rk628_combtxphy_power_on(rk628);
+
+	usleep_range(1500, 2000);
+	mask = DPHY_PLL_LOCK;
+	rk628_i2c_read(rk628, CSITX_CSITX_STATUS1, &val);
+	if ((val & mask) != mask) {
+		dev_info(rk628->dev, "PHY is not locked\n");
+		return -1;
+	}
+
+	udelay(10);
+
+	return 0;
+}
+
+static void mipi_dphy_power_off(struct rk628 *rk628)
+{
+	rk628_combtxphy_power_off(rk628);
+}
+
+static void rk62_csi_reset(struct rk628 *rk628)
+{
+	rk628_i2c_write(rk628, CSITX_SYS_CTRL0_IMD, 0x1);
+	usleep_range(1000, 1100);
+	rk628_i2c_write(rk628, CSITX_SYS_CTRL0_IMD, 0x0);
+}
+
+static void rk628_csi_set_csi(struct rk628 *rk628)
+{
+	u8 lanes = USE_4_LANES;
+	u8 lane_num;
+	u8 dphy_lane_en;
+	u32 wc_usrdef;
+	struct rk628_csi *csi;
+
+	if (!rk628->csi) {
+		csi = devm_kzalloc(rk628->dev, sizeof(*csi), GFP_KERNEL);
+		if (!csi)
+			return;
+		rk628->csi = csi;
+	} else {
+		csi = rk628->csi;
+	}
+	lane_num = lanes - 1;
+	dphy_lane_en = (1 << (lanes + 1)) - 1;
+	wc_usrdef = csi->mode.hdisplay * 2;
+
+	rk62_csi_reset(rk628);
+
+	if (csi->txphy_pwron) {
+		dev_info(rk628->dev, "%s: txphy already power on, power off\n",
+			__func__);
+		mipi_dphy_power_off(rk628);
+		csi->txphy_pwron = false;
+	}
+
+	mipi_dphy_power_on(rk628);
+	csi->txphy_pwron = true;
+	dev_info(rk628->dev, "%s: txphy power on!\n", __func__);
+	usleep_range(1000, 1500);
+
+	rk628_i2c_update_bits(rk628, CSITX_CSITX_EN,
+			VOP_UV_SWAP_MASK |
+			VOP_YUV422_EN_MASK |
+			VOP_P2_EN_MASK |
+			LANE_NUM_MASK |
+			DPHY_EN_MASK |
+			CSITX_EN_MASK,
+			VOP_UV_SWAP(1) |
+			VOP_YUV422_EN(1) |
+			VOP_P2_EN(1) |
+			LANE_NUM(lane_num) |
+			DPHY_EN(0) |
+			CSITX_EN(0));
+	rk628_i2c_update_bits(rk628, CSITX_SYS_CTRL1,
+			BYPASS_SELECT_MASK,
+			BYPASS_SELECT(1));
+	rk628_i2c_write(rk628, CSITX_CONFIG_DONE, CONFIG_DONE_IMD);
+	rk628_i2c_write(rk628, CSITX_SYS_CTRL2, VOP_WHOLE_FRM_EN | VSYNC_ENABLE);
+	rk628_i2c_update_bits(rk628, CSITX_SYS_CTRL3_IMD,
+			CONT_MODE_CLK_CLR_MASK |
+			CONT_MODE_CLK_SET_MASK |
+			NON_CONTINOUS_MODE_MASK,
+			CONT_MODE_CLK_CLR(0) |
+			CONT_MODE_CLK_SET(0) |
+			NON_CONTINOUS_MODE(1));
+
+	rk628_i2c_write(rk628, CSITX_VOP_PATH_CTRL,
+			VOP_WC_USERDEFINE(wc_usrdef) |
+			VOP_DT_USERDEFINE(YUV422_8BIT) |
+			VOP_PIXEL_FORMAT(0) |
+			VOP_WC_USERDEFINE_EN(1) |
+			VOP_DT_USERDEFINE_EN(1) |
+			VOP_PATH_EN(1));
+	rk628_i2c_update_bits(rk628, CSITX_DPHY_CTRL,
+				CSI_DPHY_EN_MASK,
+				CSI_DPHY_EN(dphy_lane_en));
+	rk628_i2c_write(rk628, CSITX_CONFIG_DONE, CONFIG_DONE_IMD);
+	dev_info(rk628->dev, "%s csi cofig done\n", __func__);
+}
+
+static void enable_csitx(struct rk628 *rk628)
+{
+	u32 i, ret, val;
+
+	for (i = 0; i < CSITX_ERR_RETRY_TIMES; i++) {
+		rk628_csi_set_csi(rk628);
+		rk628_i2c_update_bits(rk628, CSITX_CSITX_EN,
+					DPHY_EN_MASK |
+					CSITX_EN_MASK,
+					DPHY_EN(1) |
+					CSITX_EN(1));
+		rk628_i2c_write(rk628, CSITX_CONFIG_DONE, CONFIG_DONE_IMD);
+		msleep(40);
+		rk628_i2c_write(rk628, CSITX_ERR_INTR_CLR_IMD, 0xffffffff);
+		rk628_i2c_update_bits(rk628, CSITX_SYS_CTRL1,
+				BYPASS_SELECT_MASK, BYPASS_SELECT(0));
+		rk628_i2c_write(rk628, CSITX_CONFIG_DONE, CONFIG_DONE_IMD);
+		msleep(40);
+		ret = rk628_i2c_read(rk628, CSITX_ERR_INTR_RAW_STATUS_IMD, &val);
+		if (!ret && !val)
+			break;
+
+		dev_info(rk628->dev, "%s csitx err, retry:%d, err status:%#x, ret:%d\n",
+			 __func__, i, val, ret);
+	}
+
+}
+
+static void enable_stream(struct rk628 *rk628, bool en)
+{
+	dev_info(rk628->dev, "%s: %sable\n", __func__, en ? "en" : "dis");
+	if (en) {
+		enable_csitx(rk628);
+	} else {
+		rk628_i2c_update_bits(rk628, CSITX_CSITX_EN,
+					DPHY_EN_MASK |
+					CSITX_EN_MASK,
+					DPHY_EN(0) |
+					CSITX_EN(0));
+		rk628_i2c_write(rk628, CSITX_CONFIG_DONE, CONFIG_DONE_IMD);
+	}
+}
+
+void rk628_csi_init(struct rk628 *rk628)
+{
+	rk628_i2c_update_bits(rk628, GRF_SYSTEM_CON0,
+			      SW_OUTPUT_MODE_MASK, SW_OUTPUT_MODE(OUTPUT_MODE_CSI));
+	rk628_csi_get_detected_timings(rk628);
+	mipi_dphy_reset(rk628);
+}
+
+void rk628_csi_enable(struct rk628 *rk628)
+{
+	rk628_csi_get_detected_timings(rk628);
+	return enable_stream(rk628, true);
+}
+
+void rk628_csi_disable(struct rk628 *rk628)
+{
+	return enable_stream(rk628, false);
+}
diff --git a/kernel/drivers/misc/rk628/rk628_csi.h b/kernel/drivers/misc/rk628/rk628_csi.h
new file mode 100644
index 0000000..c6ce468
--- /dev/null
+++ b/kernel/drivers/misc/rk628/rk628_csi.h
@@ -0,0 +1,86 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2020 Rockchip Electronics Co. Ltd.
+ *
+ * Author: Chen Shunqing <csq@rock-chips.com>
+ */
+
+#ifndef RK628_CSI_H
+#define RK628_CSI_H
+
+#include "rk628.h"
+
+#define CSI_REG(x)			((x) + 0x40000)
+
+#define CSITX_CONFIG_DONE		CSI_REG(0x0000)
+#define CONFIG_DONE_IMD			BIT(4)
+#define CONFIG_DONE			BIT(0)
+#define CSITX_CSITX_EN			CSI_REG(0x0004)
+#define VOP_YU_SWAP_MASK		BIT(14)
+#define VOP_YU_SWAP(x)			UPDATE(x, 14, 14)
+#define VOP_UV_SWAP_MASK		BIT(13)
+#define VOP_UV_SWAP(x)			UPDATE(x, 13, 13)
+#define VOP_YUV422_EN_MASK		BIT(12)
+#define VOP_YUV422_EN(x)		UPDATE(x, 12, 12)
+#define VOP_P2_EN_MASK			BIT(8)
+#define VOP_P2_EN(x)			UPDATE(x, 8, 8)
+#define LANE_NUM_MASK			GENMASK(5, 4)
+#define LANE_NUM(x)			UPDATE(x, 5, 4)
+#define DPHY_EN_MASK			BIT(2)
+#define DPHY_EN(x)			UPDATE(x, 2, 2)
+#define CSITX_EN_MASK			BIT(0)
+#define CSITX_EN(x)			UPDATE(x, 0, 0)
+#define CSITX_CSITX_VERSION		CSI_REG(0x0008)
+#define CSITX_SYS_CTRL0_IMD		CSI_REG(0x0010)
+#define CSITX_SYS_CTRL1			CSI_REG(0x0014)
+#define BYPASS_SELECT_MASK		BIT(0)
+#define BYPASS_SELECT(x)		UPDATE(x, 0, 0)
+#define CSITX_SYS_CTRL2			CSI_REG(0x0018)
+#define VOP_WHOLE_FRM_EN		BIT(5)
+#define VSYNC_ENABLE			BIT(0)
+#define CSITX_SYS_CTRL3_IMD		CSI_REG(0x001c)
+#define CONT_MODE_CLK_CLR_MASK		BIT(8)
+#define CONT_MODE_CLK_CLR(x)		UPDATE(x, 8, 8)
+#define CONT_MODE_CLK_SET_MASK		BIT(4)
+#define CONT_MODE_CLK_SET(x)		UPDATE(x, 4, 4)
+#define NON_CONTINOUS_MODE_MASK		BIT(0)
+#define NON_CONTINOUS_MODE(x)		UPDATE(x, 0, 0)
+#define CSITX_TIMING_HPW_PADDING_NUM	CSI_REG(0x0030)
+#define CSITX_VOP_PATH_CTRL		CSI_REG(0x0040)
+#define VOP_WC_USERDEFINE_MASK		GENMASK(31, 16)
+#define VOP_WC_USERDEFINE(x)		UPDATE(x, 31, 16)
+#define VOP_DT_USERDEFINE_MASK		GENMASK(13, 8)
+#define VOP_DT_USERDEFINE(x)		UPDATE(x, 13, 8)
+#define VOP_PIXEL_FORMAT_MASK		GENMASK(7, 4)
+#define VOP_PIXEL_FORMAT(x)		UPDATE(x, 7, 4)
+#define VOP_WC_USERDEFINE_EN_MASK	BIT(3)
+#define VOP_WC_USERDEFINE_EN(x)		UPDATE(x, 3, 3)
+#define VOP_DT_USERDEFINE_EN_MASK	BIT(1)
+#define VOP_DT_USERDEFINE_EN(x)		UPDATE(x, 1, 1)
+#define VOP_PATH_EN_MASK		BIT(0)
+#define VOP_PATH_EN(x)			UPDATE(x, 0, 0)
+#define CSITX_VOP_PATH_PKT_CTRL		CSI_REG(0x0050)
+#define CSITX_CSITX_STATUS0		CSI_REG(0x0070)
+#define CSITX_CSITX_STATUS1		CSI_REG(0x0074)
+#define STOPSTATE_LANE3			BIT(7)
+#define STOPSTATE_LANE2			BIT(6)
+#define STOPSTATE_LANE1			BIT(5)
+#define STOPSTATE_LANE0			BIT(4)
+#define STOPSTATE_CLK			BIT(1)
+#define DPHY_PLL_LOCK			BIT(0)
+#define CSITX_ERR_INTR_EN_IMD		CSI_REG(0x0090)
+#define CSITX_ERR_INTR_CLR_IMD		CSI_REG(0x0094)
+#define CSITX_ERR_INTR_STATUS_IMD	CSI_REG(0x0098)
+#define CSITX_ERR_INTR_RAW_STATUS_IMD	CSI_REG(0x009c)
+#define CSITX_LPDT_DATA_IMD		CSI_REG(0x00a8)
+#define CSITX_DPHY_CTRL			CSI_REG(0x00b0)
+#define CSI_DPHY_EN_MASK		GENMASK(7, 3)
+#define CSI_DPHY_EN(x)			UPDATE(x, 7, 3)
+#define DPHY_ENABLECLK			BIT(3)
+#define CSI_MAX_REGISTER		CSITX_DPHY_CTRL
+
+void rk628_csi_init(struct rk628 *rk628);
+void rk628_csi_enable(struct rk628 *rk628);
+void rk628_csi_disable(struct rk628 *rk628);
+
+#endif
diff --git a/kernel/drivers/misc/rk628/rk628_dsi.c b/kernel/drivers/misc/rk628/rk628_dsi.c
new file mode 100644
index 0000000..368f704
--- /dev/null
+++ b/kernel/drivers/misc/rk628/rk628_dsi.c
@@ -0,0 +1,1310 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2020 Rockchip Electronics Co. Ltd.
+ *
+ * Author: Guochun Huang <hero.huang@rock-chips.com>
+ */
+
+#include <asm/unaligned.h>
+#include "rk628.h"
+#include "rk628_cru.h"
+#include "rk628_dsi.h"
+#include "rk628_combtxphy.h"
+#include "rk628_config.h"
+#include "panel.h"
+
+/* Test Code: 0x44 (HS RX Control of Lane 0) */
+#define HSFREQRANGE(x)			UPDATE(x, 6, 1)
+
+/* request ACK from peripheral */
+#define MIPI_DSI_MSG_REQ_ACK	BIT(0)
+/* use Low Power Mode to transmit message */
+#define MIPI_DSI_MSG_USE_LPM	BIT(1)
+
+static u32 lane_mbps;
+
+enum vid_mode_type {
+	VIDEO_MODE,
+	COMMAND_MODE,
+};
+
+enum dpi_color_coding {
+	DPI_COLOR_CODING_16BIT_1,
+	DPI_COLOR_CODING_16BIT_2,
+	DPI_COLOR_CODING_16BIT_3,
+	DPI_COLOR_CODING_18BIT_1,
+	DPI_COLOR_CODING_18BIT_2,
+	DPI_COLOR_CODING_24BIT,
+};
+
+enum {
+	VID_MODE_TYPE_NON_BURST_SYNC_PULSES,
+	VID_MODE_TYPE_NON_BURST_SYNC_EVENTS,
+	VID_MODE_TYPE_BURST,
+};
+
+/* MIPI DSI Processor-to-Peripheral transaction types */
+enum {
+	MIPI_DSI_V_SYNC_START				= 0x01,
+	MIPI_DSI_V_SYNC_END				= 0x11,
+	MIPI_DSI_H_SYNC_START				= 0x21,
+	MIPI_DSI_H_SYNC_END				= 0x31,
+
+	MIPI_DSI_COLOR_MODE_OFF				= 0x02,
+	MIPI_DSI_COLOR_MODE_ON				= 0x12,
+	MIPI_DSI_SHUTDOWN_PERIPHERAL			= 0x22,
+	MIPI_DSI_TURN_ON_PERIPHERAL			= 0x32,
+
+	MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM		= 0x03,
+	MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM		= 0x13,
+	MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM		= 0x23,
+
+	MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM		= 0x04,
+	MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM		= 0x14,
+	MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM		= 0x24,
+
+	MIPI_DSI_DCS_SHORT_WRITE			= 0x05,
+	MIPI_DSI_DCS_SHORT_WRITE_PARAM			= 0x15,
+
+	MIPI_DSI_DCS_READ				= 0x06,
+
+	MIPI_DSI_DCS_COMPRESSION_MODE                   = 0x07,
+	MIPI_DSI_PPS_LONG_WRITE                         = 0x0A,
+
+	MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE		= 0x37,
+
+	MIPI_DSI_END_OF_TRANSMISSION			= 0x08,
+
+	MIPI_DSI_NULL_PACKET				= 0x09,
+	MIPI_DSI_BLANKING_PACKET			= 0x19,
+	MIPI_DSI_GENERIC_LONG_WRITE			= 0x29,
+	MIPI_DSI_DCS_LONG_WRITE				= 0x39,
+
+	MIPI_DSI_LOOSELY_PACKED_PIXEL_STREAM_YCBCR20	= 0x0c,
+	MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR24		= 0x1c,
+	MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR16		= 0x2c,
+
+	MIPI_DSI_PACKED_PIXEL_STREAM_30			= 0x0d,
+	MIPI_DSI_PACKED_PIXEL_STREAM_36			= 0x1d,
+	MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR12		= 0x3d,
+
+	MIPI_DSI_PACKED_PIXEL_STREAM_16			= 0x0e,
+	MIPI_DSI_PACKED_PIXEL_STREAM_18			= 0x1e,
+	MIPI_DSI_PIXEL_STREAM_3BYTE_18			= 0x2e,
+	MIPI_DSI_PACKED_PIXEL_STREAM_24			= 0x3e,
+};
+
+/* MIPI DSI Peripheral-to-Processor transaction types */
+enum {
+	MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT	= 0x02,
+	MIPI_DSI_RX_END_OF_TRANSMISSION			= 0x08,
+	MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_1BYTE	= 0x11,
+	MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_2BYTE	= 0x12,
+	MIPI_DSI_RX_GENERIC_LONG_READ_RESPONSE		= 0x1a,
+	MIPI_DSI_RX_DCS_LONG_READ_RESPONSE		= 0x1c,
+	MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_1BYTE	= 0x21,
+	MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_2BYTE	= 0x22,
+};
+
+/* MIPI DCS commands */
+enum {
+	MIPI_DCS_NOP			= 0x00,
+	MIPI_DCS_SOFT_RESET		= 0x01,
+	MIPI_DCS_GET_DISPLAY_ID		= 0x04,
+	MIPI_DCS_GET_RED_CHANNEL	= 0x06,
+	MIPI_DCS_GET_GREEN_CHANNEL	= 0x07,
+	MIPI_DCS_GET_BLUE_CHANNEL	= 0x08,
+	MIPI_DCS_GET_DISPLAY_STATUS	= 0x09,
+	MIPI_DCS_GET_POWER_MODE		= 0x0A,
+	MIPI_DCS_GET_ADDRESS_MODE	= 0x0B,
+	MIPI_DCS_GET_PIXEL_FORMAT	= 0x0C,
+	MIPI_DCS_GET_DISPLAY_MODE	= 0x0D,
+	MIPI_DCS_GET_SIGNAL_MODE	= 0x0E,
+	MIPI_DCS_GET_DIAGNOSTIC_RESULT	= 0x0F,
+	MIPI_DCS_ENTER_SLEEP_MODE	= 0x10,
+	MIPI_DCS_EXIT_SLEEP_MODE	= 0x11,
+	MIPI_DCS_ENTER_PARTIAL_MODE	= 0x12,
+	MIPI_DCS_ENTER_NORMAL_MODE	= 0x13,
+	MIPI_DCS_EXIT_INVERT_MODE	= 0x20,
+	MIPI_DCS_ENTER_INVERT_MODE	= 0x21,
+	MIPI_DCS_SET_GAMMA_CURVE	= 0x26,
+	MIPI_DCS_SET_DISPLAY_OFF	= 0x28,
+	MIPI_DCS_SET_DISPLAY_ON		= 0x29,
+	MIPI_DCS_SET_COLUMN_ADDRESS	= 0x2A,
+	MIPI_DCS_SET_PAGE_ADDRESS	= 0x2B,
+	MIPI_DCS_WRITE_MEMORY_START	= 0x2C,
+	MIPI_DCS_WRITE_LUT		= 0x2D,
+	MIPI_DCS_READ_MEMORY_START	= 0x2E,
+	MIPI_DCS_SET_PARTIAL_AREA	= 0x30,
+	MIPI_DCS_SET_SCROLL_AREA	= 0x33,
+	MIPI_DCS_SET_TEAR_OFF		= 0x34,
+	MIPI_DCS_SET_TEAR_ON		= 0x35,
+	MIPI_DCS_SET_ADDRESS_MODE	= 0x36,
+	MIPI_DCS_SET_SCROLL_START	= 0x37,
+	MIPI_DCS_EXIT_IDLE_MODE		= 0x38,
+	MIPI_DCS_ENTER_IDLE_MODE	= 0x39,
+	MIPI_DCS_SET_PIXEL_FORMAT	= 0x3A,
+	MIPI_DCS_WRITE_MEMORY_CONTINUE	= 0x3C,
+	MIPI_DCS_READ_MEMORY_CONTINUE	= 0x3E,
+	MIPI_DCS_SET_TEAR_SCANLINE	= 0x44,
+	MIPI_DCS_GET_SCANLINE		= 0x45,
+	MIPI_DCS_SET_DISPLAY_BRIGHTNESS = 0x51,		/* MIPI DCS 1.3 */
+	MIPI_DCS_GET_DISPLAY_BRIGHTNESS = 0x52,		/* MIPI DCS 1.3 */
+	MIPI_DCS_WRITE_CONTROL_DISPLAY  = 0x53,		/* MIPI DCS 1.3 */
+	MIPI_DCS_GET_CONTROL_DISPLAY	= 0x54,		/* MIPI DCS 1.3 */
+	MIPI_DCS_WRITE_POWER_SAVE	= 0x55,		/* MIPI DCS 1.3 */
+	MIPI_DCS_GET_POWER_SAVE		= 0x56,		/* MIPI DCS 1.3 */
+	MIPI_DCS_SET_CABC_MIN_BRIGHTNESS = 0x5E,	/* MIPI DCS 1.3 */
+	MIPI_DCS_GET_CABC_MIN_BRIGHTNESS = 0x5F,	/* MIPI DCS 1.3 */
+	MIPI_DCS_READ_DDB_START		= 0xA1,
+	MIPI_DCS_READ_DDB_CONTINUE	= 0xA8,
+};
+
+/**
+ * struct mipi_dsi_msg - read/write DSI buffer
+ * @channel: virtual channel id
+ * @type: payload data type
+ * @flags: flags controlling this message transmission
+ * @tx_len: length of @tx_buf
+ * @tx_buf: data to be written
+ * @rx_len: length of @rx_buf
+ * @rx_buf: data to be read, or NULL
+ */
+struct mipi_dsi_msg {
+	u8 channel;
+	u8 type;
+	u16 flags;
+
+	size_t tx_len;
+	const void *tx_buf;
+
+	size_t rx_len;
+	void *rx_buf;
+};
+
+/**
+ * struct mipi_dsi_packet - represents a MIPI DSI packet in protocol format
+ * @size: size (in bytes) of the packet
+ * @header: the four bytes that make up the header (Data ID, Word Count or
+ * Packet Data, and ECC)
+ * @payload_length: number of bytes in the payload
+ * @payload: a pointer to a buffer containing the payload, if any
+ */
+struct mipi_dsi_packet {
+	size_t size;
+	u8 header[4];
+	size_t payload_length;
+	const u8 *payload;
+};
+
+static inline int dsi_write(struct rk628 *rk628, const struct rk628_dsi *dsi,
+			    u32 reg, u32 val)
+{
+	unsigned int dsi_base;
+
+	dsi_base = dsi->id ? DSI1_BASE : DSI0_BASE;
+
+	return rk628_i2c_write(rk628, dsi_base + reg, val);
+}
+
+static inline int dsi_read(struct rk628 *rk628, const struct rk628_dsi *dsi,
+			   u32 reg, u32 *val)
+{
+	unsigned int dsi_base;
+
+	dsi_base = dsi->id ? DSI1_BASE : DSI0_BASE;
+
+	return rk628_i2c_read(rk628, dsi_base + reg, val);
+}
+
+static inline int dsi_update_bits(struct rk628 *rk628,
+				  const struct rk628_dsi *dsi,
+				  u32 reg, u32 mask, u32 val)
+{
+	unsigned int dsi_base;
+
+	dsi_base = dsi->id ? DSI1_BASE : DSI0_BASE;
+
+	return rk628_i2c_update_bits(rk628, dsi_base + reg, mask, val);
+}
+
+int rk628_dsi_parse(struct rk628 *rk628, struct device_node *dsi_np)
+{
+	u32 val;
+	const char *string;
+	int ret;
+
+	if (!of_device_is_available(dsi_np))
+		return -EINVAL;
+
+	rk628->output_mode = OUTPUT_MODE_DSI;
+	rk628->dsi0.id = 0;
+	rk628->dsi0.channel = 0;
+	rk628->dsi0.rk628 = rk628;
+
+	if (!of_property_read_u32(dsi_np, "dsi,lanes", &val))
+		rk628->dsi0.lanes = val;
+
+	if (of_property_read_bool(dsi_np, "dsi,video-mode"))
+		rk628->dsi0.mode_flags |= MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_VIDEO |
+					  MIPI_DSI_MODE_VIDEO_BURST;
+	else
+		rk628->dsi0.mode_flags |= MIPI_DSI_MODE_LPM;
+
+	if (of_property_read_bool(dsi_np, "dsi,eotp"))
+		rk628->dsi0.mode_flags |= MIPI_DSI_MODE_EOT_PACKET;
+
+	if (!of_property_read_string(dsi_np, "dsi,format", &string)) {
+		if (!strcmp(string, "rgb666")) {
+			rk628->dsi0.bus_format = MIPI_DSI_FMT_RGB666;
+			rk628->dsi0.bpp = 24;
+		} else if (!strcmp(string, "rgb666-packed")) {
+			rk628->dsi0.bus_format = MIPI_DSI_FMT_RGB666_PACKED;
+			rk628->dsi0.bpp = 18;
+		} else if (!strcmp(string, "rgb565")) {
+			rk628->dsi0.bus_format = MIPI_DSI_FMT_RGB565;
+			rk628->dsi0.bpp = 16;
+		} else {
+			rk628->dsi0.bus_format = MIPI_DSI_FMT_RGB888;
+			rk628->dsi0.bpp = 24;
+		}
+	}
+
+	if (of_property_read_bool(dsi_np, "rockchip,dual-channel")) {
+		rk628->dsi0.master = false;
+		rk628->dsi0.slave = true;
+
+		memcpy(&rk628->dsi1, &rk628->dsi0, sizeof(struct rk628_dsi));
+		rk628->dsi1.id = 1;
+		rk628->dsi1.master = true;
+		rk628->dsi1.slave = false;
+	}
+
+	ret = rk628_panel_info_get(rk628, dsi_np);
+	if (ret)
+		return ret;
+
+
+	return 0;
+}
+
+static int genif_wait_w_pld_fifo_not_full(struct rk628 *rk628,
+					  const struct rk628_dsi *dsi)
+{
+	u32 sts;
+	int ret;
+	int dev_id;
+	unsigned int dsi_base;
+
+	dev_id = dsi->id ? RK628_DEV_DSI1 : RK628_DEV_DSI0;
+	dsi_base = dsi->id ? DSI1_BASE : DSI0_BASE;
+
+	ret = regmap_read_poll_timeout(rk628->regmap[dev_id],
+				       dsi_base + DSI_CMD_PKT_STATUS,
+				       sts, !(sts & GEN_PLD_W_FULL),
+				       0, 1000);
+	if (ret < 0) {
+		dev_err(rk628->dev, "generic write payload fifo is full\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int genif_wait_cmd_fifo_not_full(struct rk628 *rk628,
+					const struct rk628_dsi *dsi)
+{
+	u32 sts;
+	int ret = 0;
+	int dev_id;
+	unsigned int dsi_base;
+
+	dev_id = dsi->id ? RK628_DEV_DSI1 : RK628_DEV_DSI0;
+	dsi_base = dsi->id ? DSI1_BASE : DSI0_BASE;
+
+	ret = regmap_read_poll_timeout(rk628->regmap[dev_id],
+				       dsi_base + DSI_CMD_PKT_STATUS,
+				       sts, !(sts & GEN_CMD_FULL),
+				       0, 1000);
+	if (ret < 0) {
+		dev_err(rk628->dev, "generic write cmd fifo is full\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int genif_wait_write_fifo_empty(struct rk628 *rk628, const struct rk628_dsi *dsi)
+{
+	u32 sts;
+	u32 mask;
+	int ret;
+	int dev_id;
+	unsigned int dsi_base;
+
+	dev_id = dsi->id ? RK628_DEV_DSI1 : RK628_DEV_DSI0;
+	dsi_base = dsi->id ? DSI1_BASE : DSI0_BASE;
+
+	mask = GEN_CMD_EMPTY | GEN_PLD_W_EMPTY;
+
+	ret = regmap_read_poll_timeout(rk628->regmap[dev_id],
+				       dsi_base + DSI_CMD_PKT_STATUS,
+				       sts, (sts & mask) == mask,
+				       0, 1000);
+
+	if (ret < 0) {
+		dev_err(rk628->dev, "generic write fifo is full\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int rk628_dsi_read_from_fifo(struct rk628 *rk628,
+				    const struct rk628_dsi *dsi,
+				    const struct mipi_dsi_msg *msg)
+{
+	u8 *payload = msg->rx_buf;
+	unsigned int vrefresh = 60;
+	u16 length;
+	u32 val;
+	int ret;
+	int dev_id;
+	unsigned int dsi_base;
+
+	dev_id = dsi->id ? RK628_DEV_DSI1 : RK628_DEV_DSI0;
+	dsi_base = dsi->id ? DSI1_BASE : DSI0_BASE;
+
+	ret = regmap_read_poll_timeout(rk628->regmap[dev_id],
+				       dsi_base + DSI_CMD_PKT_STATUS,
+				       val, !(val & GEN_RD_CMD_BUSY),
+				       0, DIV_ROUND_UP(1000000, vrefresh));
+	if (ret) {
+		dev_err(rk628->dev, "entire response isn't stored in the FIFO\n");
+		return ret;
+	}
+
+	/* Receive payload */
+	for (length = msg->rx_len; length; length -= 4) {
+		dsi_read(rk628, dsi, DSI_CMD_PKT_STATUS, &val);
+		if (val & GEN_PLD_R_EMPTY)
+			ret = -ETIMEDOUT;
+		if (ret) {
+			dev_err(rk628->dev, "dsi Read payload FIFO is empty\n");
+			return ret;
+		}
+
+		dsi_read(rk628, dsi, DSI_GEN_PLD_DATA, &val);
+
+		switch (length) {
+		case 3:
+			payload[2] = (val >> 16) & 0xff;
+			fallthrough;
+		case 2:
+			payload[1] = (val >> 8) & 0xff;
+			fallthrough;
+		case 1:
+			payload[0] = val & 0xff;
+			return 0;
+		}
+
+		payload[0] = (val >>  0) & 0xff;
+		payload[1] = (val >>  8) & 0xff;
+		payload[2] = (val >> 16) & 0xff;
+		payload[3] = (val >> 24) & 0xff;
+		payload += 4;
+	}
+
+	return 0;
+}
+
+/**
+ * mipi_dsi_packet_format_is_short - check if a packet is of the short format
+ * @type: MIPI DSI data type of the packet
+ *
+ * Return: true if the packet for the given data type is a short packet, false
+ * otherwise.
+ */
+static bool mipi_dsi_packet_format_is_short(u8 type)
+{
+	switch (type) {
+	case MIPI_DSI_V_SYNC_START:
+	case MIPI_DSI_V_SYNC_END:
+	case MIPI_DSI_H_SYNC_START:
+	case MIPI_DSI_H_SYNC_END:
+	case MIPI_DSI_END_OF_TRANSMISSION:
+	case MIPI_DSI_COLOR_MODE_OFF:
+	case MIPI_DSI_COLOR_MODE_ON:
+	case MIPI_DSI_SHUTDOWN_PERIPHERAL:
+	case MIPI_DSI_TURN_ON_PERIPHERAL:
+	case MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM:
+	case MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM:
+	case MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM:
+	case MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM:
+	case MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM:
+	case MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM:
+	case MIPI_DSI_DCS_SHORT_WRITE:
+	case MIPI_DSI_DCS_SHORT_WRITE_PARAM:
+	case MIPI_DSI_DCS_READ:
+	case MIPI_DSI_DCS_COMPRESSION_MODE:
+	case MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE:
+		return true;
+	}
+
+	return false;
+}
+
+/**
+ * mipi_dsi_packet_format_is_long - check if a packet is of the long format
+ * @type: MIPI DSI data type of the packet
+ *
+ * Return: true if the packet for the given data type is a long packet, false
+ * otherwise.
+ */
+static bool mipi_dsi_packet_format_is_long(u8 type)
+{
+	switch (type) {
+	case MIPI_DSI_PPS_LONG_WRITE:
+	case MIPI_DSI_NULL_PACKET:
+	case MIPI_DSI_BLANKING_PACKET:
+	case MIPI_DSI_GENERIC_LONG_WRITE:
+	case MIPI_DSI_DCS_LONG_WRITE:
+	case MIPI_DSI_LOOSELY_PACKED_PIXEL_STREAM_YCBCR20:
+	case MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR24:
+	case MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR16:
+	case MIPI_DSI_PACKED_PIXEL_STREAM_30:
+	case MIPI_DSI_PACKED_PIXEL_STREAM_36:
+	case MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR12:
+	case MIPI_DSI_PACKED_PIXEL_STREAM_16:
+	case MIPI_DSI_PACKED_PIXEL_STREAM_18:
+	case MIPI_DSI_PIXEL_STREAM_3BYTE_18:
+	case MIPI_DSI_PACKED_PIXEL_STREAM_24:
+		return true;
+	}
+
+	return false;
+}
+
+/**
+ * mipi_dsi_create_packet - create a packet from a message according to the
+ *     DSI protocol
+ * @packet: pointer to a DSI packet structure
+ * @msg: message to translate into a packet
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+static int mipi_dsi_create_packet(struct mipi_dsi_packet *packet,
+			   const struct mipi_dsi_msg *msg)
+{
+	if (!packet || !msg)
+		return -EINVAL;
+
+	/* do some minimum sanity checking */
+	if (!mipi_dsi_packet_format_is_short(msg->type) &&
+	    !mipi_dsi_packet_format_is_long(msg->type))
+		return -EINVAL;
+
+	if (msg->channel > 3)
+		return -EINVAL;
+
+	memset(packet, 0, sizeof(*packet));
+	packet->header[0] = ((msg->channel & 0x3) << 6) | (msg->type & 0x3f);
+
+	/* TODO: compute ECC if hardware support is not available */
+
+	/*
+	 * Long write packets contain the word count in header bytes 1 and 2.
+	 * The payload follows the header and is word count bytes long.
+	 *
+	 * Short write packets encode up to two parameters in header bytes 1
+	 * and 2.
+	 */
+	if (mipi_dsi_packet_format_is_long(msg->type)) {
+		packet->header[1] = (msg->tx_len >> 0) & 0xff;
+		packet->header[2] = (msg->tx_len >> 8) & 0xff;
+
+		packet->payload_length = msg->tx_len;
+		packet->payload = msg->tx_buf;
+	} else {
+		const u8 *tx = msg->tx_buf;
+
+		packet->header[1] = (msg->tx_len > 0) ? tx[0] : 0;
+		packet->header[2] = (msg->tx_len > 1) ? tx[1] : 0;
+	}
+
+	packet->size = sizeof(packet->header) + packet->payload_length;
+
+	return 0;
+}
+
+static int rk628_dsi_transfer(struct rk628 *rk628, const struct rk628_dsi *dsi,
+			      const struct mipi_dsi_msg *msg)
+{
+	struct mipi_dsi_packet packet;
+	int ret;
+	u32 val;
+
+	if (msg->flags & MIPI_DSI_MSG_REQ_ACK)
+		dsi_update_bits(rk628, dsi, DSI_CMD_MODE_CFG,
+				ACK_RQST_EN, ACK_RQST_EN);
+
+	if (msg->flags & MIPI_DSI_MSG_USE_LPM) {
+		dsi_update_bits(rk628, dsi, DSI_VID_MODE_CFG,
+				LP_CMD_EN, LP_CMD_EN);
+	} else {
+		dsi_update_bits(rk628, dsi, DSI_VID_MODE_CFG, LP_CMD_EN, 0);
+		dsi_update_bits(rk628, dsi, DSI_LPCLK_CTRL,
+				PHY_TXREQUESTCLKHS, PHY_TXREQUESTCLKHS);
+	}
+
+	switch (msg->type) {
+	case MIPI_DSI_SHUTDOWN_PERIPHERAL:
+		//return rk628_dsi_shutdown_peripheral(dsi);
+	case MIPI_DSI_TURN_ON_PERIPHERAL:
+		//return rk628_dsi_turn_on_peripheral(dsi);
+	case MIPI_DSI_DCS_SHORT_WRITE:
+		dsi_update_bits(rk628, dsi, DSI_CMD_MODE_CFG, DCS_SW_0P_TX,
+				msg->flags & MIPI_DSI_MSG_USE_LPM ?
+				DCS_SW_0P_TX : 0);
+		break;
+	case MIPI_DSI_DCS_SHORT_WRITE_PARAM:
+		dsi_update_bits(rk628, dsi, DSI_CMD_MODE_CFG, DCS_SW_1P_TX,
+				msg->flags & MIPI_DSI_MSG_USE_LPM ?
+				DCS_SW_1P_TX : 0);
+		break;
+	case MIPI_DSI_DCS_LONG_WRITE:
+		dsi_update_bits(rk628, dsi, DSI_CMD_MODE_CFG, DCS_LW_TX,
+				msg->flags & MIPI_DSI_MSG_USE_LPM ?
+				DCS_LW_TX : 0);
+		break;
+	case MIPI_DSI_DCS_READ:
+		dsi_update_bits(rk628, dsi, DSI_CMD_MODE_CFG, DCS_SR_0P_TX,
+				msg->flags & MIPI_DSI_MSG_USE_LPM ?
+				DCS_SR_0P_TX : 0);
+		break;
+	case MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE:
+		dsi_update_bits(rk628, dsi, DSI_CMD_MODE_CFG, MAX_RD_PKT_SIZE,
+				msg->flags & MIPI_DSI_MSG_USE_LPM ?
+				MAX_RD_PKT_SIZE : 0);
+		break;
+	case MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM:
+		dsi_update_bits(rk628, dsi, DSI_CMD_MODE_CFG, GEN_SW_0P_TX,
+				msg->flags & MIPI_DSI_MSG_USE_LPM ?
+				GEN_SW_0P_TX : 0);
+		break;
+	case MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM:
+		dsi_update_bits(rk628, dsi, DSI_CMD_MODE_CFG, GEN_SW_1P_TX,
+				msg->flags & MIPI_DSI_MSG_USE_LPM ?
+				GEN_SW_1P_TX : 0);
+		break;
+	case MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM:
+		dsi_update_bits(rk628, dsi, DSI_CMD_MODE_CFG, GEN_SW_2P_TX,
+				msg->flags & MIPI_DSI_MSG_USE_LPM ?
+				GEN_SW_2P_TX : 0);
+		break;
+	case MIPI_DSI_GENERIC_LONG_WRITE:
+		dsi_update_bits(rk628, dsi, DSI_CMD_MODE_CFG, GEN_LW_TX,
+				msg->flags & MIPI_DSI_MSG_USE_LPM ?
+				GEN_LW_TX : 0);
+		break;
+	case MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM:
+		dsi_update_bits(rk628, dsi, DSI_CMD_MODE_CFG, GEN_SR_0P_TX,
+		msg->flags & MIPI_DSI_MSG_USE_LPM ? GEN_SR_0P_TX : 0);
+		break;
+	case MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM:
+		dsi_update_bits(rk628, dsi, DSI_CMD_MODE_CFG, GEN_SR_1P_TX,
+		msg->flags & MIPI_DSI_MSG_USE_LPM ? GEN_SR_1P_TX : 0);
+		break;
+	case MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM:
+		dsi_update_bits(rk628, dsi, DSI_CMD_MODE_CFG, GEN_SR_2P_TX,
+				msg->flags & MIPI_DSI_MSG_USE_LPM ? GEN_SR_2P_TX : 0);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* create a packet to the DSI protocol */
+	ret = mipi_dsi_create_packet(&packet, msg);
+	if (ret) {
+		dev_err(rk628->dev, "failed to create packet\n");
+		return ret;
+	}
+
+	/* Send payload */
+	while (packet.payload_length >= 4) {
+		/*
+		 * Alternatively, you can always keep the FIFO
+		 * nearly full by monitoring the FIFO state until
+		 * it is not full, and then writea single word of data.
+		 * This solution is more resource consuming
+		 * but it simultaneously avoids FIFO starvation,
+		 * making it possible to use FIFO sizes smaller than
+		 * the amount of data of the longest packet to be written.
+		 */
+		ret = genif_wait_w_pld_fifo_not_full(rk628, dsi);
+		if (ret)
+			return ret;
+
+		val = get_unaligned_le32(packet.payload);
+
+		dsi_write(rk628, dsi, DSI_GEN_PLD_DATA, val);
+
+
+		packet.payload += 4;
+		packet.payload_length -= 4;
+	}
+
+	val = 0;
+	switch (packet.payload_length) {
+	case 3:
+		val |= packet.payload[2] << 16;
+		fallthrough;
+	case 2:
+		val |= packet.payload[1] << 8;
+		fallthrough;
+	case 1:
+		val |= packet.payload[0];
+
+		dsi_write(rk628, dsi, DSI_GEN_PLD_DATA, val);
+		break;
+	}
+
+	ret = genif_wait_cmd_fifo_not_full(rk628, dsi);
+	if (ret)
+		return ret;
+
+	/* Send packet header */
+	val = get_unaligned_le32(packet.header);
+
+	dsi_write(rk628, dsi, DSI_GEN_HDR, val);
+
+	ret = genif_wait_write_fifo_empty(rk628, dsi);
+	if (ret)
+		return ret;
+
+	if (msg->rx_len) {
+		ret = rk628_dsi_read_from_fifo(rk628, dsi, msg);
+		if (ret < 0)
+			return ret;
+	}
+
+	if (dsi->slave) {
+		const struct rk628_dsi *dsi1 = &rk628->dsi1;
+
+		rk628_dsi_transfer(rk628, dsi1, msg);
+	}
+
+	return msg->tx_len;
+}
+
+int rk628_mipi_dsi_generic_write(struct rk628 *rk628,
+				 const void *payload, size_t size)
+{
+	const struct rk628_dsi *dsi = &rk628->dsi0;
+	struct mipi_dsi_msg msg;
+
+	memset(&msg, 0, sizeof(msg));
+	msg.channel = dsi->channel;
+	msg.tx_buf = payload;
+	msg.tx_len = size;
+	msg.rx_len = 0;
+
+	switch (size) {
+	case 0:
+		msg.type = MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM;
+		break;
+
+	case 1:
+		msg.type = MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM;
+		break;
+	case 2:
+		msg.type = MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM;
+		break;
+	default:
+		msg.type = MIPI_DSI_GENERIC_LONG_WRITE;
+		break;
+	}
+
+		if (dsi->mode_flags & MIPI_DSI_MODE_LPM)
+			msg.flags |= MIPI_DSI_MSG_USE_LPM;
+
+		return rk628_dsi_transfer(rk628, dsi, &msg);
+}
+
+int rk628_mipi_dsi_dcs_write_buffer(struct rk628 *rk628,
+				    const void *data, size_t len)
+{
+	const struct rk628_dsi *dsi = &rk628->dsi0;
+	struct mipi_dsi_msg msg;
+
+	memset(&msg, 0, sizeof(msg));
+	msg.channel = dsi->channel;
+	msg.tx_buf = data;
+	msg.tx_len = len;
+	msg.rx_len = 0;
+
+	switch (len) {
+	case 0:
+		return -EINVAL;
+	case 1:
+		msg.type = MIPI_DSI_DCS_SHORT_WRITE;
+		break;
+	case 2:
+		msg.type = MIPI_DSI_DCS_SHORT_WRITE_PARAM;
+		break;
+	default:
+		msg.type = MIPI_DSI_DCS_LONG_WRITE;
+		break;
+	}
+
+	if (dsi->mode_flags & MIPI_DSI_MODE_LPM)
+		msg.flags |= MIPI_DSI_MSG_USE_LPM;
+
+	return rk628_dsi_transfer(rk628, dsi, &msg);
+}
+
+int rk628_mipi_dsi_dcs_read(struct rk628 *rk628, u8 cmd, void *data, size_t len)
+{
+	const struct rk628_dsi *dsi = &rk628->dsi0;
+	struct mipi_dsi_msg msg;
+
+	memset(&msg, 0, sizeof(msg));
+	msg.channel = dsi->channel;
+	msg.type = MIPI_DSI_DCS_READ;
+	msg.tx_buf = &cmd;
+	msg.tx_len = 1;
+	msg.rx_buf = data;
+	msg.rx_len = len;
+
+	return rk628_dsi_transfer(rk628, dsi, &msg);
+}
+
+static int
+panel_simple_xfer_dsi_cmd_seq(struct rk628 *rk628, struct panel_cmds *cmds)
+{
+	u16 i;
+	int err;
+
+	if (!cmds)
+		return 0;
+
+	for (i = 0; i < cmds->cmd_cnt; i++) {
+		struct cmd_desc *cmd = &cmds->cmds[i];
+
+		switch (cmd->dchdr.dtype) {
+		case MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM:
+		case MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM:
+		case MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM:
+		case MIPI_DSI_GENERIC_LONG_WRITE:
+			err = rk628_mipi_dsi_generic_write(rk628, cmd->payload,
+							   cmd->dchdr.dlen);
+			break;
+		case MIPI_DSI_DCS_SHORT_WRITE:
+		case MIPI_DSI_DCS_SHORT_WRITE_PARAM:
+		case MIPI_DSI_DCS_LONG_WRITE:
+			err = rk628_mipi_dsi_dcs_write_buffer(rk628, cmd->payload,
+							      cmd->dchdr.dlen);
+			break;
+		default:
+			dev_err(rk628->dev, "panel cmd desc invalid data type\n");
+			return -EINVAL;
+		}
+
+		if (err < 0)
+			dev_err(rk628->dev, "failed to write cmd\n");
+
+		if (cmd->dchdr.wait)
+			mdelay(cmd->dchdr.wait);
+	}
+
+	return 0;
+}
+
+static u32 rk628_dsi_get_lane_rate(const struct rk628_dsi *dsi)
+{
+	const struct rk628_display_mode *mode = &dsi->rk628->dst_mode;
+	u32 lane_rate;
+	u32 max_lane_rate = 1500;
+	u8 bpp, lanes;
+
+	bpp = dsi->bpp;
+	lanes = dsi->slave ? dsi->lanes * 2 : dsi->lanes;
+	lane_rate = mode->clock / 1000 * bpp / lanes;
+	lane_rate = DIV_ROUND_UP(lane_rate * 5, 4);
+
+	if (lane_rate > max_lane_rate)
+		lane_rate = max_lane_rate;
+
+	return lane_rate;
+}
+
+static void testif_testclk_assert(struct rk628 *rk628,
+				  const struct rk628_dsi *dsi)
+{
+	rk628_i2c_update_bits(rk628, dsi->id ?
+			   GRF_MIPI_TX1_CON : GRF_MIPI_TX0_CON,
+			   PHY_TESTCLK, PHY_TESTCLK);
+	udelay(1);
+}
+
+static void testif_testclk_deassert(struct rk628 *rk628,
+				    const struct rk628_dsi *dsi)
+{
+	rk628_i2c_update_bits(rk628, dsi->id ?
+			   GRF_MIPI_TX1_CON : GRF_MIPI_TX0_CON,
+			   PHY_TESTCLK, 0);
+	udelay(1);
+}
+
+static void testif_testclr_assert(struct rk628 *rk628,
+				  const struct rk628_dsi *dsi)
+{
+	rk628_i2c_update_bits(rk628, dsi->id ?
+			   GRF_MIPI_TX1_CON : GRF_MIPI_TX0_CON,
+			   PHY_TESTCLR, PHY_TESTCLR);
+	udelay(1);
+}
+
+static void testif_testclr_deassert(struct rk628 *rk628,
+				    const struct rk628_dsi *dsi)
+{
+	rk628_i2c_update_bits(rk628, dsi->id ?
+			   GRF_MIPI_TX1_CON : GRF_MIPI_TX0_CON,
+			   PHY_TESTCLR, 0);
+	udelay(1);
+}
+
+static void testif_set_data(struct rk628 *rk628,
+			    const struct rk628_dsi *dsi, u8 data)
+{
+	rk628_i2c_update_bits(rk628, dsi->id ?
+			   GRF_MIPI_TX1_CON : GRF_MIPI_TX0_CON,
+			   PHY_TESTDIN_MASK, PHY_TESTDIN(data));
+	udelay(1);
+}
+
+static void testif_testen_assert(struct rk628 *rk628,
+				 const struct rk628_dsi *dsi)
+{
+	rk628_i2c_update_bits(rk628, dsi->id ?
+			   GRF_MIPI_TX1_CON : GRF_MIPI_TX0_CON,
+			   PHY_TESTEN, PHY_TESTEN);
+	udelay(1);
+}
+
+static void testif_testen_deassert(struct rk628 *rk628,
+				   const struct rk628_dsi *dsi)
+{
+	rk628_i2c_update_bits(rk628,  dsi->id ?
+			   GRF_MIPI_TX1_CON : GRF_MIPI_TX0_CON,
+			   PHY_TESTEN, 0);
+	udelay(1);
+}
+
+static void testif_test_code_write(struct rk628 *rk628,
+				   const struct rk628_dsi *dsi, u8 test_code)
+{
+	testif_testclk_assert(rk628, dsi);
+	testif_set_data(rk628, dsi, test_code);
+	testif_testen_assert(rk628, dsi);
+	testif_testclk_deassert(rk628, dsi);
+	testif_testen_deassert(rk628, dsi);
+}
+
+static void testif_test_data_write(struct rk628 *rk628,
+				   const struct rk628_dsi *dsi, u8 test_data)
+{
+	testif_testclk_deassert(rk628, dsi);
+	testif_set_data(rk628, dsi, test_data);
+	testif_testclk_assert(rk628, dsi);
+}
+
+static u8 testif_get_data(struct rk628 *rk628, const struct rk628_dsi *dsi)
+{
+	u32 data = 0;
+
+	rk628_i2c_read(rk628, dsi->id ? GRF_DPHY1_STATUS : GRF_DPHY0_STATUS, &data);
+
+	return data >> PHY_TESTDOUT_SHIFT;
+}
+
+static void testif_write(struct rk628 *rk628, const struct rk628_dsi *dsi,
+			 u8 reg, u8 value)
+{
+	u8 monitor_data;
+
+	testif_test_code_write(rk628, dsi, reg);
+	testif_test_data_write(rk628, dsi, value);
+	monitor_data = testif_get_data(rk628, dsi);
+	dev_info(rk628->dev, "monitor_data: 0x%x\n", monitor_data);
+}
+
+static void mipi_dphy_init(struct rk628 *rk628, const struct rk628_dsi *dsi)
+{
+	const struct {
+		unsigned long max_lane_mbps;
+		u8 hsfreqrange;
+	} hsfreqrange_table[] = {
+		{  90, 0x00}, { 100, 0x10}, { 110, 0x20}, { 130, 0x01},
+		{ 140, 0x11}, { 150, 0x21}, { 170, 0x02}, { 180, 0x12},
+		{ 200, 0x22}, { 220, 0x03}, { 240, 0x13}, { 250, 0x23},
+		{ 270, 0x04}, { 300, 0x14}, { 330, 0x05}, { 360, 0x15},
+		{ 400, 0x25}, { 450, 0x06}, { 500, 0x16}, { 550, 0x07},
+		{ 600, 0x17}, { 650, 0x08}, { 700, 0x18}, { 750, 0x09},
+		{ 800, 0x19}, { 850, 0x29}, { 900, 0x39}, { 950, 0x0a},
+		{1000, 0x1a}, {1050, 0x2a}, {1100, 0x3a}, {1150, 0x0b},
+		{1200, 0x1b}, {1250, 0x2b}, {1300, 0x3b}, {1350, 0x0c},
+		{1400, 0x1c}, {1450, 0x2c}, {1500, 0x3c}
+	};
+	u8 hsfreqrange;
+	unsigned int index;
+
+	for (index = 0; index < ARRAY_SIZE(hsfreqrange_table); index++)
+		if (lane_mbps <= hsfreqrange_table[index].max_lane_mbps)
+			break;
+
+	if (index == ARRAY_SIZE(hsfreqrange_table))
+		--index;
+
+	hsfreqrange = hsfreqrange_table[index].hsfreqrange;
+	testif_write(rk628, dsi, 0x44, HSFREQRANGE(hsfreqrange));
+}
+
+static void mipi_dphy_power_on(struct rk628 *rk628, const struct rk628_dsi *dsi)
+{
+	int dev_id;
+	unsigned int dsi_base;
+	unsigned int val, mask;
+	int ret;
+
+	dev_id = dsi->id ? RK628_DEV_DSI1 : RK628_DEV_DSI0;
+	dsi_base = dsi->id ? DSI1_BASE : DSI0_BASE;
+
+	dsi_update_bits(rk628, dsi, DSI_PHY_RSTZ, PHY_ENABLECLK, 0);
+	dsi_update_bits(rk628, dsi, DSI_PHY_RSTZ, PHY_SHUTDOWNZ, 0);
+	dsi_update_bits(rk628, dsi, DSI_PHY_RSTZ, PHY_RSTZ, 0);
+	testif_testclr_assert(rk628, dsi);
+
+	/* Set all REQUEST inputs to zero */
+	rk628_i2c_update_bits(rk628, dsi->id ?
+			      GRF_MIPI_TX1_CON : GRF_MIPI_TX0_CON,
+			      FORCERXMODE_MASK | FORCETXSTOPMODE_MASK,
+			      FORCETXSTOPMODE(0) | FORCERXMODE(0));
+	udelay(1);
+
+	testif_testclr_deassert(rk628, dsi);
+
+	mipi_dphy_init(rk628, dsi);
+
+	dsi_update_bits(rk628, dsi, DSI_PHY_RSTZ,
+			PHY_ENABLECLK, PHY_ENABLECLK);
+	dsi_update_bits(rk628, dsi, DSI_PHY_RSTZ,
+			PHY_SHUTDOWNZ, PHY_SHUTDOWNZ);
+	dsi_update_bits(rk628, dsi, DSI_PHY_RSTZ, PHY_RSTZ, PHY_RSTZ);
+	usleep_range(1500, 2000);
+
+	rk628_combtxphy_power_on(rk628);
+
+	ret = regmap_read_poll_timeout(rk628->regmap[dev_id],
+				       dsi_base + DSI_PHY_STATUS,
+				       val, val & PHY_LOCK, 0, 1000);
+	if (ret < 0)
+		dev_err(rk628->dev, "PHY is not locked\n");
+
+	usleep_range(100, 200);
+
+	mask = PHY_STOPSTATELANE;
+	ret = regmap_read_poll_timeout(rk628->regmap[dev_id],
+				       dsi_base + DSI_PHY_STATUS,
+				       val, (val & mask) == mask,
+				       0, 1000);
+	if (ret < 0)
+		dev_err(rk628->dev, "lane module is not in stop state\n");
+
+	udelay(10);
+}
+
+void rk628_dsi0_reset_control_assert(struct rk628 *rk628)
+{
+	rk628_i2c_write(rk628, CRU_SOFTRST_CON02, 0x400040);
+}
+
+void rk628_dsi0_reset_control_deassert(struct rk628 *rk628)
+{
+	rk628_i2c_write(rk628, CRU_SOFTRST_CON02, 0x400000);
+}
+
+void rk628_dsi1_reset_control_assert(struct rk628 *rk628)
+{
+	rk628_i2c_write(rk628, CRU_SOFTRST_CON02, 0x800080);
+}
+
+void rk628_dsi1_reset_control_deassert(struct rk628 *rk628)
+{
+	rk628_i2c_write(rk628, CRU_SOFTRST_CON02, 0x800000);
+}
+
+void rk628_dsi_bridge_pre_enable(struct rk628 *rk628,
+				 const struct rk628_dsi *dsi)
+{
+	u32 val;
+
+	dsi_write(rk628, dsi, DSI_PWR_UP, RESET);
+	dsi_write(rk628, dsi, DSI_MODE_CFG, CMD_VIDEO_MODE(COMMAND_MODE));
+
+	val = DIV_ROUND_UP(lane_mbps >> 3, 20);
+	dsi_write(rk628, dsi, DSI_CLKMGR_CFG,
+		  TO_CLK_DIVISION(10) | TX_ESC_CLK_DIVISION(val));
+
+	val = CRC_RX_EN | ECC_RX_EN | BTA_EN | EOTP_TX_EN;
+	if (dsi->mode_flags & MIPI_DSI_MODE_EOT_PACKET)
+		val &= ~EOTP_TX_EN;
+
+	dsi_write(rk628, dsi, DSI_PCKHDL_CFG, val);
+
+	dsi_write(rk628, dsi, DSI_TO_CNT_CFG,
+		  HSTX_TO_CNT(1000) | LPRX_TO_CNT(1000));
+	dsi_write(rk628, dsi, DSI_BTA_TO_CNT, 0xd00);
+	dsi_write(rk628, dsi, DSI_PHY_TMR_CFG,
+		  PHY_HS2LP_TIME(0x14) | PHY_LP2HS_TIME(0x10) |
+		  MAX_RD_TIME(10000));
+	dsi_write(rk628, dsi, DSI_PHY_TMR_LPCLK_CFG,
+		  PHY_CLKHS2LP_TIME(0x40) | PHY_CLKLP2HS_TIME(0x40));
+	dsi_write(rk628, dsi, DSI_PHY_IF_CFG,
+		  PHY_STOP_WAIT_TIME(0x20) | N_LANES(dsi->lanes - 1));
+
+	mipi_dphy_power_on(rk628, dsi);
+
+	dsi_write(rk628, dsi, DSI_PWR_UP, POWER_UP);
+}
+
+static void rk628_dsi_set_vid_mode(struct rk628 *rk628,
+				   const struct rk628_dsi *dsi,
+				   const struct rk628_display_mode *mode)
+{
+	unsigned int lanebyteclk = (lane_mbps * 1000L) >> 3;
+	unsigned int dpipclk = mode->clock;
+	u32 hline, hsa, hbp, hline_time, hsa_time, hbp_time;
+	u32 vactive, vsa, vfp, vbp;
+	u32 val;
+	int pkt_size;
+
+	val = LP_HFP_EN | LP_HBP_EN | LP_VACT_EN | LP_VFP_EN | LP_VBP_EN |
+	      LP_VSA_EN;
+
+	if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_HFP)
+		val &= ~LP_HFP_EN;
+
+	if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_HBP)
+		val &= ~LP_HBP_EN;
+
+	if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_BURST)
+		val |= VID_MODE_TYPE_BURST;
+	else if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE)
+		val |= VID_MODE_TYPE_NON_BURST_SYNC_PULSES;
+	else
+		val |= VID_MODE_TYPE_NON_BURST_SYNC_EVENTS;
+
+	dsi_write(rk628, dsi, DSI_VID_MODE_CFG, val);
+
+	if (dsi->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS)
+		dsi_update_bits(rk628, dsi, DSI_LPCLK_CTRL,
+				AUTO_CLKLANE_CTRL, AUTO_CLKLANE_CTRL);
+
+	if (dsi->slave || dsi->master)
+		pkt_size = VID_PKT_SIZE(mode->hdisplay / 2);
+	else
+		pkt_size = VID_PKT_SIZE(mode->hdisplay);
+
+	dsi_write(rk628, dsi, DSI_VID_PKT_SIZE, pkt_size);
+
+	vactive = mode->vdisplay;
+	vsa = mode->vsync_end - mode->vsync_start;
+	vfp = mode->vsync_start - mode->vdisplay;
+	vbp = mode->vtotal - mode->vsync_end;
+	hsa = mode->hsync_end - mode->hsync_start;
+	hbp = mode->htotal - mode->hsync_end;
+	hline = mode->htotal;
+
+	//hline_time = hline * lanebyteclk / dpipclk;
+	hline_time = DIV_ROUND_CLOSEST_ULL(hline * lanebyteclk, dpipclk);
+	dsi_write(rk628, dsi, DSI_VID_HLINE_TIME,
+		  VID_HLINE_TIME(hline_time));
+	//hsa_time = hsa * lanebyteclk / dpipclk;
+	hsa_time = DIV_ROUND_CLOSEST_ULL(hsa * lanebyteclk, dpipclk);
+	dsi_write(rk628, dsi, DSI_VID_HSA_TIME, VID_HSA_TIME(hsa_time));
+	//hbp_time = hbp * lanebyteclk / dpipclk;
+	hbp_time = DIV_ROUND_CLOSEST_ULL(hbp * lanebyteclk, dpipclk);
+	dsi_write(rk628, dsi, DSI_VID_HBP_TIME, VID_HBP_TIME(hbp_time));
+
+	dsi_write(rk628, dsi, DSI_VID_VACTIVE_LINES, vactive);
+	dsi_write(rk628, dsi, DSI_VID_VSA_LINES, vsa);
+	dsi_write(rk628, dsi, DSI_VID_VFP_LINES, vfp);
+	dsi_write(rk628, dsi, DSI_VID_VBP_LINES, vbp);
+
+	dsi_write(rk628, dsi, DSI_MODE_CFG, CMD_VIDEO_MODE(VIDEO_MODE));
+}
+
+static void rk628_dsi_set_cmd_mode(struct rk628 *rk628,
+				   const struct rk628_dsi *dsi,
+				   const struct rk628_display_mode *mode)
+{
+	dsi_update_bits(rk628, dsi, DSI_CMD_MODE_CFG, DCS_LW_TX, 0);
+	dsi_write(rk628, dsi, DSI_EDPI_CMD_SIZE,
+		  EDPI_ALLOWED_CMD_SIZE(mode->hdisplay));
+	dsi_write(rk628, dsi, DSI_MODE_CFG, CMD_VIDEO_MODE(COMMAND_MODE));
+}
+
+static void rk628_dsi_bridge_enable(struct rk628 *rk628,
+				    const struct rk628_dsi *dsi)
+{
+	u32 val;
+	const struct rk628_display_mode *mode = &rk628->dst_mode;
+
+	dsi_write(rk628, dsi, DSI_PWR_UP, RESET);
+
+	switch (dsi->bus_format) {
+	case MIPI_DSI_FMT_RGB666:
+		val = DPI_COLOR_CODING(DPI_COLOR_CODING_18BIT_2) |
+		      LOOSELY18_EN;
+		break;
+	case MIPI_DSI_FMT_RGB666_PACKED:
+		val = DPI_COLOR_CODING(DPI_COLOR_CODING_18BIT_1);
+		break;
+	case MIPI_DSI_FMT_RGB565:
+		val = DPI_COLOR_CODING(DPI_COLOR_CODING_16BIT_1);
+		break;
+	case MIPI_DSI_FMT_RGB888:
+	default:
+		val = DPI_COLOR_CODING(DPI_COLOR_CODING_24BIT);
+		break;
+	}
+
+	dsi_write(rk628, dsi, DSI_DPI_COLOR_CODING, val);
+
+	val = 0;
+	if (mode->flags & DRM_MODE_FLAG_NVSYNC)
+		val |= VSYNC_ACTIVE_LOW;
+	if (mode->flags & DRM_MODE_FLAG_NHSYNC)
+		val |= HSYNC_ACTIVE_LOW;
+
+	dsi_write(rk628, dsi, DSI_DPI_CFG_POL, val);
+
+	dsi_write(rk628, dsi, DSI_DPI_VCID, DPI_VID(0));
+	dsi_write(rk628, dsi, DSI_DPI_LP_CMD_TIM,
+		  OUTVACT_LPCMD_TIME(4) | INVACT_LPCMD_TIME(4));
+	dsi_update_bits(rk628, dsi, DSI_LPCLK_CTRL,
+			PHY_TXREQUESTCLKHS, PHY_TXREQUESTCLKHS);
+
+	if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO)
+		rk628_dsi_set_vid_mode(rk628, dsi, mode);
+	else
+		rk628_dsi_set_cmd_mode(rk628, dsi, mode);
+
+	dsi_write(rk628, dsi, DSI_PWR_UP, POWER_UP);
+}
+
+void rk628_mipi_dsi_pre_enable(struct rk628 *rk628)
+{
+	const struct rk628_dsi *dsi = &rk628->dsi0;
+	const struct rk628_dsi *dsi1 = &rk628->dsi1;
+	u32 rate = rk628_dsi_get_lane_rate(dsi);
+	int bus_width;
+
+	rk628_i2c_update_bits(rk628, GRF_SYSTEM_CON0, SW_OUTPUT_MODE_MASK,
+			      SW_OUTPUT_MODE(OUTPUT_MODE_DSI));
+	rk628_i2c_update_bits(rk628, GRF_POST_PROC_CON, SW_SPLIT_EN,
+			      dsi->slave ? SW_SPLIT_EN : 0);
+
+	bus_width = rate << 8;
+	if (dsi->slave)
+		bus_width |= COMBTXPHY_MODULEA_EN | COMBTXPHY_MODULEB_EN;
+	else if (dsi->id)
+		bus_width |= COMBTXPHY_MODULEB_EN;
+	else
+		bus_width |= COMBTXPHY_MODULEA_EN;
+
+	rk628_combtxphy_set_bus_width(rk628, bus_width);
+	rk628_combtxphy_set_mode(rk628, PHY_MODE_VIDEO_MIPI);
+	lane_mbps = rk628_combtxphy_get_bus_width(rk628);
+
+	if (dsi->slave)
+		lane_mbps = rk628_combtxphy_get_bus_width(rk628);
+
+	/* rst for dsi0 */
+	rk628_dsi0_reset_control_assert(rk628);
+	usleep_range(20, 40);
+	rk628_dsi0_reset_control_deassert(rk628);
+	usleep_range(20, 40);
+
+	rk628_dsi_bridge_pre_enable(rk628, dsi);
+
+	if (dsi->slave) {
+		/* rst for dsi1 */
+		rk628_dsi1_reset_control_assert(rk628);
+		usleep_range(20, 40);
+		rk628_dsi1_reset_control_deassert(rk628);
+		usleep_range(20, 40);
+
+		rk628_dsi_bridge_pre_enable(rk628, dsi1);
+	}
+
+	rk628_panel_prepare(rk628);
+	panel_simple_xfer_dsi_cmd_seq(rk628, rk628->panel->on_cmds);
+
+#ifdef DSI_READ_POWER_MODE
+	u8 mode;
+
+	rk628_mipi_dsi_dcs_read(rk628, MIPI_DCS_GET_POWER_MODE,
+				&mode, sizeof(mode));
+
+	dev_info(rk628->dev, "dsi: mode: 0x%x\n", mode);
+#endif
+
+	dev_info(rk628->dev, "rk628_dsi final DSI-Link bandwidth: %d x %d\n",
+		 lane_mbps, dsi->slave ? dsi->lanes * 2 : dsi->lanes);
+}
+
+void rk628_mipi_dsi_enable(struct rk628 *rk628)
+{
+	const struct rk628_dsi *dsi = &rk628->dsi0;
+	const struct rk628_dsi *dsi1 = &rk628->dsi1;
+
+	rk628_dsi_bridge_enable(rk628, dsi);
+
+	if (dsi->slave)
+		rk628_dsi_bridge_enable(rk628, dsi1);
+
+	rk628_panel_enable(rk628);
+}
+
+void rk628_dsi_disable(struct rk628 *rk628)
+{
+	const struct rk628_dsi *dsi = &rk628->dsi0;
+	const struct rk628_dsi *dsi1 = &rk628->dsi1;
+
+	rk628_panel_disable(rk628);
+
+	dsi_write(rk628, dsi, DSI_PWR_UP, RESET);
+	dsi_write(rk628, dsi, DSI_LPCLK_CTRL, 0);
+	dsi_write(rk628, dsi, DSI_EDPI_CMD_SIZE, 0);
+	dsi_write(rk628, dsi, DSI_MODE_CFG, CMD_VIDEO_MODE(COMMAND_MODE));
+	dsi_write(rk628, dsi, DSI_PWR_UP, POWER_UP);
+
+	//dsi_write(rk628, dsi, DSI_PHY_RSTZ, 0);
+
+	if (dsi->slave) {
+		dsi_write(rk628, dsi1, DSI_PWR_UP, RESET);
+		dsi_write(rk628, dsi1, DSI_LPCLK_CTRL, 0);
+		dsi_write(rk628, dsi1, DSI_EDPI_CMD_SIZE, 0);
+		dsi_write(rk628, dsi1, DSI_MODE_CFG,
+			  CMD_VIDEO_MODE(COMMAND_MODE));
+		dsi_write(rk628, dsi1, DSI_PWR_UP, POWER_UP);
+
+		//dsi_write(rk628, dsi1, DSI_PHY_RSTZ, 0);
+	}
+
+	panel_simple_xfer_dsi_cmd_seq(rk628, rk628->panel->off_cmds);
+	rk628_panel_unprepare(rk628);
+	rk628_combtxphy_power_off(rk628);
+}
diff --git a/kernel/drivers/misc/rk628/rk628_dsi.h b/kernel/drivers/misc/rk628/rk628_dsi.h
new file mode 100644
index 0000000..7753b49
--- /dev/null
+++ b/kernel/drivers/misc/rk628/rk628_dsi.h
@@ -0,0 +1,158 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2020 Rockchip Electronics Co. Ltd.
+ *
+ * Author: Guochun Huang <hero.huang@rock-chips.com>
+ */
+
+#ifndef RK628_DSI_H
+#define RK628_DSI_H
+#include "rk628.h"
+
+#define DSI0_BASE           0x50000
+#define DSI1_BASE           0x60000
+
+#define DSI_VERSION			0x0000
+#define DSI_PWR_UP			0x0004
+#define RESET				0
+#define POWER_UP			BIT(0)
+#define DSI_CLKMGR_CFG			0x0008
+#define TO_CLK_DIVISION(x)		UPDATE(x, 15,  8)
+#define TX_ESC_CLK_DIVISION(x)		UPDATE(x,  7,  0)
+#define DSI_DPI_VCID			0x000c
+#define DPI_VID(x)			UPDATE(x,  1,  0)
+#define DSI_DPI_COLOR_CODING		0x0010
+#define LOOSELY18_EN			BIT(8)
+#define DPI_COLOR_CODING(x)		UPDATE(x,  3,  0)
+#define DSI_DPI_CFG_POL			0x0014
+#define COLORM_ACTIVE_LOW		BIT(4)
+#define SHUTD_ACTIVE_LOW		BIT(3)
+#define HSYNC_ACTIVE_LOW		BIT(2)
+#define VSYNC_ACTIVE_LOW		BIT(1)
+#define DATAEN_ACTIVE_LOW		BIT(0)
+#define DSI_DPI_LP_CMD_TIM		0x0018
+#define OUTVACT_LPCMD_TIME(x)		UPDATE(x, 23, 16)
+#define INVACT_LPCMD_TIME(x)		UPDATE(x,  7,  0)
+#define DSI_PCKHDL_CFG			0x002c
+#define CRC_RX_EN			BIT(4)
+#define ECC_RX_EN			BIT(3)
+#define BTA_EN				BIT(2)
+#define EOTP_RX_EN			BIT(1)
+#define EOTP_TX_EN			BIT(0)
+#define DSI_GEN_VCID			0x0030
+#define DSI_MODE_CFG			0x0034
+#define CMD_VIDEO_MODE(x)		UPDATE(x,  0,  0)
+#define DSI_VID_MODE_CFG		0x0038
+#define VPG_EN				BIT(16)
+#define LP_CMD_EN			BIT(15)
+#define FRAME_BTA_ACK_EN		BIT(14)
+#define LP_HFP_EN			BIT(13)
+#define LP_HBP_EN			BIT(12)
+#define LP_VACT_EN			BIT(11)
+#define LP_VFP_EN			BIT(10)
+#define LP_VBP_EN			BIT(9)
+#define LP_VSA_EN			BIT(8)
+#define VID_MODE_TYPE(x)		UPDATE(x,  1,  0)
+#define DSI_VID_PKT_SIZE		0x003c
+#define VID_PKT_SIZE(x)			UPDATE(x, 13,  0)
+#define DSI_VID_NUM_CHUNKS		0x0040
+#define DSI_VID_NULL_SIZE		0x0044
+#define DSI_VID_HSA_TIME		0x0048
+#define VID_HSA_TIME(x)			UPDATE(x, 11,  0)
+#define DSI_VID_HBP_TIME		0x004c
+#define VID_HBP_TIME(x)			UPDATE(x, 11,  0)
+#define DSI_VID_HLINE_TIME		0x0050
+#define VID_HLINE_TIME(x)		UPDATE(x, 14,  0)
+#define DSI_VID_VSA_LINES		0x0054
+#define VSA_LINES(x)			UPDATE(x,  9,  0)
+#define DSI_VID_VBP_LINES		0x0058
+#define VBP_LINES(x)			UPDATE(x,  9,  0)
+#define DSI_VID_VFP_LINES		0x005c
+#define VFP_LINES(x)			UPDATE(x,  9,  0)
+#define DSI_VID_VACTIVE_LINES		0x0060
+#define V_ACTIVE_LINES(x)		UPDATE(x, 13,  0)
+#define DSI_EDPI_CMD_SIZE		0x0064
+#define EDPI_ALLOWED_CMD_SIZE(x)	UPDATE(x, 15,  0)
+#define DSI_CMD_MODE_CFG		0x0068
+#define MAX_RD_PKT_SIZE			BIT(24)
+#define DCS_LW_TX			BIT(19)
+#define DCS_SR_0P_TX			BIT(18)
+#define DCS_SW_1P_TX			BIT(17)
+#define DCS_SW_0P_TX			BIT(16)
+#define GEN_LW_TX			BIT(14)
+#define GEN_SR_2P_TX			BIT(13)
+#define GEN_SR_1P_TX			BIT(12)
+#define GEN_SR_0P_TX			BIT(11)
+#define GEN_SW_2P_TX			BIT(10)
+#define GEN_SW_1P_TX			BIT(9)
+#define GEN_SW_0P_TX			BIT(8)
+#define ACK_RQST_EN			BIT(1)
+#define TEAR_FX_EN			BIT(0)
+#define DSI_GEN_HDR			0x006c
+#define GEN_WC_MSBYTE(x)		UPDATE(x, 23, 16)
+#define GEN_WC_LSBYTE(x)		UPDATE(x, 15,  8)
+#define GEN_VC(x)			UPDATE(x,  7,  6)
+#define GEN_DT(x)			UPDATE(x,  5,  0)
+#define DSI_GEN_PLD_DATA		0x0070
+#define DSI_CMD_PKT_STATUS		0x0074
+#define GEN_RD_CMD_BUSY			BIT(6)
+#define GEN_PLD_R_FULL			BIT(5)
+#define GEN_PLD_R_EMPTY			BIT(4)
+#define GEN_PLD_W_FULL			BIT(3)
+#define GEN_PLD_W_EMPTY			BIT(2)
+#define GEN_CMD_FULL			BIT(1)
+#define GEN_CMD_EMPTY			BIT(0)
+#define DSI_TO_CNT_CFG			0x0078
+#define HSTX_TO_CNT(x)			UPDATE(x, 31, 16)
+#define LPRX_TO_CNT(x)			UPDATE(x, 15,  0)
+#define DSI_HS_RD_TO_CNT		0x007c
+#define HS_RD_TO_CNT(x)			UPDATE(x, 15,  0)
+#define DSI_LP_RD_TO_CNT		0x0080
+#define LP_RD_TO_CNT(x)			UPDATE(x, 15,  0)
+#define DSI_HS_WR_TO_CNT		0x0084
+#define HS_WR_TO_CNT(x)			UPDATE(x, 15,  0)
+#define DSI_LP_WR_TO_CNT		0x0088
+#define LP_WR_TO_CNT(x)			UPDATE(x, 15,  0)
+#define DSI_BTA_TO_CNT			0x008c
+#define BTA_TO_CNT(x)			UPDATE(x, 15,  0)
+#define DSI_SDF_3D			0x0090
+#define DSI_LPCLK_CTRL			0x0094
+#define AUTO_CLKLANE_CTRL		BIT(1)
+#define PHY_TXREQUESTCLKHS		BIT(0)
+#define DSI_PHY_TMR_LPCLK_CFG		0x0098
+#define PHY_CLKHS2LP_TIME(x)		UPDATE(x, 25, 16)
+#define PHY_CLKLP2HS_TIME(x)		UPDATE(x,  9,  0)
+#define DSI_PHY_TMR_CFG			0x009c
+#define PHY_HS2LP_TIME(x)		UPDATE(x, 31, 24)
+#define PHY_LP2HS_TIME(x)		UPDATE(x, 23, 16)
+#define MAX_RD_TIME(x)			UPDATE(x, 14,  0)
+#define DSI_PHY_RSTZ			0x00a0
+#define PHY_FORCEPLL			BIT(3)
+#define PHY_ENABLECLK			BIT(2)
+#define PHY_RSTZ			BIT(1)
+#define PHY_SHUTDOWNZ			BIT(0)
+#define DSI_PHY_IF_CFG			0x00a4
+#define PHY_STOP_WAIT_TIME(x)		UPDATE(x, 15,  8)
+#define N_LANES(x)			UPDATE(x,  1,  0)
+#define DSI_PHY_STATUS			0x00b0
+#define PHY_STOPSTATE3LANE		BIT(11)
+#define PHY_STOPSTATE2LANE		BIT(9)
+#define PHY_STOPSTATE1LANE		BIT(7)
+#define PHY_STOPSTATE0LANE		BIT(4)
+#define PHY_STOPSTATECLKLANE		BIT(2)
+#define PHY_LOCK			BIT(0)
+#define PHY_STOPSTATELANE		(PHY_STOPSTATE0LANE | \
+					 PHY_STOPSTATECLKLANE)
+#define DSI_INT_ST0			0x00bc
+#define DSI_INT_ST1			0x00c0
+#define DSI_INT_MSK0			0x00c4
+#define DSI_INT_MSK1			0x00c8
+#define DSI_INT_FORCE0			0x00d8
+#define DSI_INT_FORCE1			0x00dc
+#define DSI_MAX_REGISTER		DSI_INT_FORCE1
+
+int rk628_dsi_parse(struct rk628 *rk628, struct device_node *dsi_np);
+void rk628_mipi_dsi_pre_enable(struct rk628 *rk628);
+void rk628_mipi_dsi_enable(struct rk628 *rk628);
+void rk628_dsi_disable(struct rk628 *rk628);
+#endif
diff --git a/kernel/drivers/misc/rk628/rk628_gpio.h b/kernel/drivers/misc/rk628/rk628_gpio.h
new file mode 100644
index 0000000..5a61adb
--- /dev/null
+++ b/kernel/drivers/misc/rk628/rk628_gpio.h
@@ -0,0 +1,296 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2020 Rockchip Electronics Co. Ltd.
+ *
+ * Author: Wyon Bi <bivvy.bi@rock-chips.com>
+ */
+
+#ifndef RK628_GPIO_H
+#define RK628_GPIO_H
+
+#define RK628_GPIO0_BASE      0x000D0000
+#define RK628_GPIO1_BASE      0x000E0000
+#define RK628_GPIO2_BASE      0x000F0000
+#define RK628_GPIO3_BASE      0x00100000
+#define RK628_GPIO_MAX_REGISTER       (RK628_GPIO3_BASE + GPIO_VER_ID)
+
+/* GPIO control registers */
+#define GPIO_SWPORT_DR_L        0x00
+#define GPIO_SWPORT_DR_H        0x04
+#define GPIO_SWPORT_DDR_L       0x08
+#define GPIO_SWPORT_DDR_H       0x0c
+#define GPIO_INTEN_L            0x10
+#define GPIO_INTEN_H            0x14
+#define GPIO_INTMASK_L          0x18
+#define GPIO_INTMASK_H          0x1c
+#define GPIO_INTTYPE_L          0x20
+#define GPIO_INTTYPE_H          0x24
+#define GPIO_INT_POLARITY_L     0x28
+#define GPIO_INT_POLARITY_H     0x2c
+#define GPIO_INT_BOTHEDGE_L     0x30
+#define GPIO_INT_BOTHEDGE_H     0x34
+#define GPIO_DEBOUNCE_L         0x38
+#define GPIO_DEBOUNCE_H         0x3c
+#define GPIO_DBCLK_DIV_EN_L     0x40
+#define GPIO_DBCLK_DIV_EN_H     0x44
+#define GPIO_INT_STATUS         0x50
+#define GPIO_INT_RAWSTATUS      0x58
+#define GPIO_PORTS_EOI_L        0x60
+#define GPIO_PORTS_EOI_H        0x64
+#define GPIO_EXT_PORT           0x70
+#define GPIO_VER_ID             0x78
+
+#define GPIO_REG_LOW            0x0
+#define GPIO_REG_HIGH           0x1
+
+/* GPIO control registers */
+#define GPIO_INTMASK            0x34
+#define GPIO_PORTS_EOI          0x4c
+
+#define BANK_OFFSET 32
+
+#define GPIO_DIRECTION_OUT 1
+#define GPIO_DIRECTION_IN 0
+
+enum {
+	GPIO_HIGH_Z,
+	GPIO_PULL_UP,
+	GPIO_PULL_DOWN,
+};
+
+enum {
+	GPIO_BANK0 = 0,
+	GPIO_BANK1,
+	GPIO_BANK2,
+	GPIO_BANK3,
+	GPIO_BANKX = 9,
+};
+
+
+enum {
+	GPIO0_A0 = BANK_OFFSET * 0,
+	GPIO0_A1,
+	GPIO0_A2,
+	GPIO0_A3,
+	GPIO0_A4,
+	GPIO0_A5,
+	GPIO0_A6,
+	GPIO0_A7,
+	GPIO0_B0,
+	GPIO0_B1,
+	GPIO0_B2,
+	GPIO0_B3,
+	GPIO1_A0 = BANK_OFFSET * 1,
+	GPIO1_A1,
+	GPIO1_A2,
+	GPIO1_A3,
+	GPIO1_A4,
+	GPIO1_A5,
+	GPIO1_A6,
+	GPIO1_A7,
+	GPIO1_B0,
+	GPIO1_B1,
+	GPIO1_B2,
+	GPIO1_B3,
+	GPIO1_B4,
+	GPIO1_B5,
+	GPIO2_A0 = BANK_OFFSET * 2,
+	GPIO2_A1,
+	GPIO2_A2,
+	GPIO2_A3,
+	GPIO2_A4,
+	GPIO2_A5,
+	GPIO2_A6,
+	GPIO2_A7,
+	GPIO2_B0,
+	GPIO2_B1,
+	GPIO2_B2,
+	GPIO2_B3,
+	GPIO2_B4,
+	GPIO2_B5,
+	GPIO2_B6,
+	GPIO2_B7,
+	GPIO2_C0,
+	GPIO2_C1,
+	GPIO2_C2,
+	GPIO2_C3,
+	GPIO2_C4,
+	GPIO2_C5,
+	GPIO2_C6,
+	GPIO2_C7,
+	GPIO3_A0 = BANK_OFFSET * 3,
+	GPIO3_A1,
+	GPIO3_A2,
+	GPIO3_A3,
+	GPIO3_A4,
+	GPIO3_A5,
+	GPIO3_A6,
+	GPIO3_A7,
+	GPIO3_B0,
+	GPIO3_B1,
+	GPIO3_B2,
+	GPIO3_B3,
+	GPIO3_B4,
+	PIN_I2SM_SCK = BANK_OFFSET * 4 + 2,
+	PIN_I2SM_D,
+	PIN_I2SM_LR,
+	PIN_RXDDC_SCL,
+	PIN_RXDDC_SDA,
+	PIN_HDMIRX_CE,
+	PIN_JTAG_EN,
+	PIN_UART_SEL,
+	PIN_UART_RTS_EN,
+	PIN_UART_CTS_EN,
+	PIN_MUX,
+};
+
+struct rk628_pin_iomux_group {
+	unsigned int pins;
+	int bank;
+	int mux;
+	int iomux_base;
+	int gpio_base;
+	int pull_reg;
+};
+
+
+#define PINCTRL_GROUP(a, b, c, d, e, f) \
+	{.pins = a, .bank = b, .mux = c, .iomux_base = d, .gpio_base = e, .pull_reg = f}
+
+
+static const struct rk628_pin_iomux_group rk628_pin_iomux_groups[] = {
+	PINCTRL_GROUP(GPIO0_A0, GPIO_BANK0, 1, GRF_GPIO0AB_SEL_CON,
+		      RK628_GPIO0_BASE, GRF_GPIO0A_P_CON),
+	PINCTRL_GROUP(GPIO0_A1, GPIO_BANK0, 1, GRF_GPIO0AB_SEL_CON,
+		      RK628_GPIO0_BASE, GRF_GPIO0A_P_CON),
+	PINCTRL_GROUP(GPIO0_A2, GPIO_BANK0, 1, GRF_GPIO0AB_SEL_CON, RK628_GPIO0_BASE, 0),
+	PINCTRL_GROUP(GPIO0_A3, GPIO_BANK0, 1, GRF_GPIO0AB_SEL_CON,
+		      RK628_GPIO0_BASE, GRF_GPIO0A_P_CON),
+	PINCTRL_GROUP(GPIO0_A4, GPIO_BANK0, 1, GRF_GPIO0AB_SEL_CON,
+		      RK628_GPIO0_BASE, GRF_GPIO0A_P_CON),
+	PINCTRL_GROUP(GPIO0_A5, GPIO_BANK0, 1, GRF_GPIO0AB_SEL_CON,
+		      RK628_GPIO0_BASE, GRF_GPIO0A_P_CON),
+	PINCTRL_GROUP(GPIO0_A6, GPIO_BANK0, 1, GRF_GPIO0AB_SEL_CON,
+		      RK628_GPIO0_BASE, GRF_GPIO0A_P_CON),
+	PINCTRL_GROUP(GPIO0_A7, GPIO_BANK0, 1, GRF_GPIO0AB_SEL_CON,
+		      RK628_GPIO0_BASE, GRF_GPIO0A_P_CON),
+	PINCTRL_GROUP(GPIO0_B0, GPIO_BANK0, 1, GRF_GPIO0AB_SEL_CON, RK628_GPIO0_BASE, 0),
+	PINCTRL_GROUP(GPIO0_B1, GPIO_BANK0, 1, GRF_GPIO0AB_SEL_CON, RK628_GPIO0_BASE, 0),
+	PINCTRL_GROUP(GPIO0_B2, GPIO_BANK0, 1, GRF_GPIO0AB_SEL_CON, RK628_GPIO0_BASE, 0),
+	PINCTRL_GROUP(GPIO0_B3, GPIO_BANK0, 1, GRF_GPIO0AB_SEL_CON, RK628_GPIO0_BASE, 0),
+
+	PINCTRL_GROUP(GPIO1_A0, GPIO_BANK1, 1, GRF_GPIO1AB_SEL_CON,
+		      RK628_GPIO1_BASE, GRF_GPIO1A_P_CON),
+	PINCTRL_GROUP(GPIO1_A1, GPIO_BANK1, 1, GRF_GPIO1AB_SEL_CON,
+		      RK628_GPIO1_BASE, GRF_GPIO1A_P_CON),
+	PINCTRL_GROUP(GPIO1_A2, GPIO_BANK1, 1, GRF_GPIO1AB_SEL_CON,
+		      RK628_GPIO1_BASE, GRF_GPIO1A_P_CON),
+	PINCTRL_GROUP(GPIO1_A3, GPIO_BANK1, 1, GRF_GPIO1AB_SEL_CON,
+		      RK628_GPIO1_BASE, GRF_GPIO1A_P_CON),
+	PINCTRL_GROUP(GPIO1_A4, GPIO_BANK1, 1, GRF_GPIO1AB_SEL_CON,
+		      RK628_GPIO1_BASE, GRF_GPIO1A_P_CON),
+	PINCTRL_GROUP(GPIO1_A5, GPIO_BANK1, 1, GRF_GPIO1AB_SEL_CON,
+		      RK628_GPIO1_BASE, GRF_GPIO1A_P_CON),
+	PINCTRL_GROUP(GPIO1_A6, GPIO_BANK1, 1, GRF_GPIO1AB_SEL_CON,
+		      RK628_GPIO1_BASE, GRF_GPIO1A_P_CON),
+	PINCTRL_GROUP(GPIO1_A7, GPIO_BANK1, 1, GRF_GPIO1AB_SEL_CON,
+		      RK628_GPIO1_BASE, GRF_GPIO1A_P_CON),
+	PINCTRL_GROUP(GPIO1_B0, GPIO_BANK1, 1, GRF_GPIO1AB_SEL_CON, RK628_GPIO1_BASE, 0),
+	PINCTRL_GROUP(GPIO1_B1, GPIO_BANK1, 1, GRF_GPIO1AB_SEL_CON, RK628_GPIO1_BASE, 0),
+	PINCTRL_GROUP(GPIO1_B2, GPIO_BANK1, 1, GRF_GPIO1AB_SEL_CON, RK628_GPIO1_BASE, 0),
+	PINCTRL_GROUP(GPIO1_B3, GPIO_BANK1, 1, GRF_GPIO1AB_SEL_CON, RK628_GPIO1_BASE, 0),
+	PINCTRL_GROUP(GPIO1_B4, GPIO_BANK1, 1, GRF_GPIO1AB_SEL_CON, RK628_GPIO1_BASE, 0),
+	PINCTRL_GROUP(GPIO1_B5, GPIO_BANK1, 1, GRF_GPIO1AB_SEL_CON, RK628_GPIO1_BASE, 0),
+
+	PINCTRL_GROUP(GPIO2_A0, GPIO_BANK2, 1, GRF_GPIO2AB_SEL_CON,
+		      RK628_GPIO2_BASE, GRF_GPIO2A_P_CON),
+	PINCTRL_GROUP(GPIO2_A1, GPIO_BANK2, 1, GRF_GPIO2AB_SEL_CON,
+		      RK628_GPIO2_BASE, GRF_GPIO2A_P_CON),
+	PINCTRL_GROUP(GPIO2_A2, GPIO_BANK2, 1, GRF_GPIO2AB_SEL_CON,
+		      RK628_GPIO2_BASE, GRF_GPIO2A_P_CON),
+	PINCTRL_GROUP(GPIO2_A3, GPIO_BANK2, 1, GRF_GPIO2AB_SEL_CON,
+		      RK628_GPIO2_BASE, GRF_GPIO2A_P_CON),
+	PINCTRL_GROUP(GPIO2_A4, GPIO_BANK2, 1, GRF_GPIO2AB_SEL_CON,
+		      RK628_GPIO2_BASE, GRF_GPIO2A_P_CON),
+	PINCTRL_GROUP(GPIO2_A5, GPIO_BANK2, 1, GRF_GPIO2AB_SEL_CON,
+		      RK628_GPIO2_BASE, GRF_GPIO2A_P_CON),
+	PINCTRL_GROUP(GPIO2_A6, GPIO_BANK2, 1, GRF_GPIO2AB_SEL_CON,
+		      RK628_GPIO2_BASE, GRF_GPIO2A_P_CON),
+	PINCTRL_GROUP(GPIO2_A7, GPIO_BANK2, 1, GRF_GPIO2AB_SEL_CON,
+		      RK628_GPIO2_BASE, GRF_GPIO2A_P_CON),
+	PINCTRL_GROUP(GPIO2_B0, GPIO_BANK2, 1, GRF_GPIO2AB_SEL_CON,
+		      RK628_GPIO2_BASE, GRF_GPIO2B_P_CON),
+	PINCTRL_GROUP(GPIO2_B1, GPIO_BANK2, 1, GRF_GPIO2AB_SEL_CON,
+		      RK628_GPIO2_BASE, GRF_GPIO2B_P_CON),
+	PINCTRL_GROUP(GPIO2_B2, GPIO_BANK2, 1, GRF_GPIO2AB_SEL_CON,
+		      RK628_GPIO2_BASE, GRF_GPIO2B_P_CON),
+	PINCTRL_GROUP(GPIO2_B3, GPIO_BANK2, 1, GRF_GPIO2AB_SEL_CON,
+		      RK628_GPIO2_BASE, GRF_GPIO2B_P_CON),
+	PINCTRL_GROUP(GPIO2_B4, GPIO_BANK2, 1, GRF_GPIO2AB_SEL_CON,
+		      RK628_GPIO2_BASE, GRF_GPIO2B_P_CON),
+	PINCTRL_GROUP(GPIO2_B5, GPIO_BANK2, 1, GRF_GPIO2AB_SEL_CON,
+		      RK628_GPIO2_BASE, GRF_GPIO2B_P_CON),
+	PINCTRL_GROUP(GPIO2_B6, GPIO_BANK2, 1, GRF_GPIO2AB_SEL_CON,
+		      RK628_GPIO2_BASE, GRF_GPIO2B_P_CON),
+	PINCTRL_GROUP(GPIO2_B7, GPIO_BANK2, 1, GRF_GPIO2AB_SEL_CON,
+		      RK628_GPIO2_BASE, GRF_GPIO2B_P_CON),
+	PINCTRL_GROUP(GPIO2_C0, GPIO_BANK2, 1, GRF_GPIO2C_SEL_CON,
+		      RK628_GPIO2_BASE, GRF_GPIO2C_P_CON),
+	PINCTRL_GROUP(GPIO2_C1, GPIO_BANK2, 1, GRF_GPIO2C_SEL_CON,
+		      RK628_GPIO2_BASE, GRF_GPIO2C_P_CON),
+	PINCTRL_GROUP(GPIO2_C2, GPIO_BANK2, 1, GRF_GPIO2C_SEL_CON,
+		      RK628_GPIO2_BASE, GRF_GPIO2C_P_CON),
+	PINCTRL_GROUP(GPIO2_C3, GPIO_BANK2, 1, GRF_GPIO2C_SEL_CON,
+		      RK628_GPIO2_BASE, GRF_GPIO2C_P_CON),
+	PINCTRL_GROUP(GPIO2_C4, GPIO_BANK2, 1, GRF_GPIO2C_SEL_CON,
+		      RK628_GPIO2_BASE, GRF_GPIO2C_P_CON),
+	PINCTRL_GROUP(GPIO2_C5, GPIO_BANK2, 1, GRF_GPIO2C_SEL_CON,
+		      RK628_GPIO2_BASE, GRF_GPIO2C_P_CON),
+	PINCTRL_GROUP(GPIO2_C6, GPIO_BANK2, 1, GRF_GPIO2C_SEL_CON,
+		      RK628_GPIO2_BASE, GRF_GPIO2C_P_CON),
+	PINCTRL_GROUP(GPIO2_C7, GPIO_BANK2, 1, GRF_GPIO2C_SEL_CON,
+		      RK628_GPIO2_BASE, GRF_GPIO2C_P_CON),
+
+	PINCTRL_GROUP(GPIO3_A0, GPIO_BANK3, 1, GRF_GPIO3AB_SEL_CON,
+		      RK628_GPIO3_BASE, GRF_GPIO3A_P_CON),
+	PINCTRL_GROUP(GPIO3_A1, GPIO_BANK3, 1, GRF_GPIO3AB_SEL_CON,
+		      RK628_GPIO3_BASE, GRF_GPIO3A_P_CON),
+	PINCTRL_GROUP(GPIO3_A2, GPIO_BANK3, 1, GRF_GPIO3AB_SEL_CON,
+		      RK628_GPIO3_BASE, GRF_GPIO3A_P_CON),
+	PINCTRL_GROUP(GPIO3_A3, GPIO_BANK3, 1, GRF_GPIO3AB_SEL_CON,
+		      RK628_GPIO3_BASE, GRF_GPIO3A_P_CON),
+	PINCTRL_GROUP(GPIO3_A4, GPIO_BANK3, 1, GRF_GPIO3AB_SEL_CON,
+		      RK628_GPIO3_BASE, 0),
+	PINCTRL_GROUP(GPIO3_A5, GPIO_BANK3, 1, GRF_GPIO3AB_SEL_CON,
+		      RK628_GPIO3_BASE, 0),
+	PINCTRL_GROUP(GPIO3_A6, GPIO_BANK3, 1, GRF_GPIO3AB_SEL_CON,
+		      RK628_GPIO3_BASE, 0),
+	PINCTRL_GROUP(GPIO3_A7, GPIO_BANK3, 1, GRF_GPIO3AB_SEL_CON,
+		      RK628_GPIO3_BASE, 0),
+	PINCTRL_GROUP(GPIO3_B0, GPIO_BANK3, 1, GRF_GPIO3AB_SEL_CON,
+		      RK628_GPIO3_BASE, GRF_GPIO3B_P_CON),
+	PINCTRL_GROUP(GPIO3_B1, GPIO_BANK3, 1, GRF_GPIO3AB_SEL_CON,
+		      RK628_GPIO3_BASE, GRF_GPIO3B_P_CON),
+	PINCTRL_GROUP(GPIO3_B2, GPIO_BANK3, 1, GRF_GPIO3AB_SEL_CON,
+		      RK628_GPIO3_BASE, GRF_GPIO3B_P_CON),
+	PINCTRL_GROUP(GPIO3_B3, GPIO_BANK3, 1, GRF_GPIO3AB_SEL_CON,
+		      RK628_GPIO3_BASE, GRF_GPIO3B_P_CON),
+	PINCTRL_GROUP(GPIO3_B4, GPIO_BANK3, 1, GRF_GPIO3AB_SEL_CON,
+		      RK628_GPIO3_BASE, GRF_GPIO3B_P_CON),
+
+	PINCTRL_GROUP(PIN_I2SM_SCK, GPIO_BANKX, 1, GRF_SYSTEM_CON3, 0, 0),
+	PINCTRL_GROUP(PIN_I2SM_D, GPIO_BANKX, 1, GRF_SYSTEM_CON3, 0, 0),
+	PINCTRL_GROUP(PIN_I2SM_LR, GPIO_BANKX, 1, GRF_SYSTEM_CON3, 0, 0),
+	PINCTRL_GROUP(PIN_RXDDC_SCL, GPIO_BANKX, 1, GRF_SYSTEM_CON3, 0, 0),
+	PINCTRL_GROUP(PIN_RXDDC_SDA, GPIO_BANKX, 1, GRF_SYSTEM_CON3, 0, 0),
+	PINCTRL_GROUP(PIN_HDMIRX_CE, GPIO_BANKX, 1, GRF_SYSTEM_CON3, 0, 0),
+	PINCTRL_GROUP(PIN_JTAG_EN, GPIO_BANKX, 1, GRF_SYSTEM_CON3, 0, 0),
+	PINCTRL_GROUP(PIN_UART_SEL, GPIO_BANKX, 1, GRF_SYSTEM_CON3, 0, 0),
+	PINCTRL_GROUP(PIN_UART_RTS_EN, GPIO_BANKX, 1, GRF_SYSTEM_CON3, 0, 0),
+	PINCTRL_GROUP(PIN_UART_CTS_EN, GPIO_BANKX, 1, GRF_SYSTEM_CON3, 0, 0),
+
+};
+
+#endif // RK628_GPIO_H
+
+
diff --git a/kernel/drivers/misc/rk628/rk628_grf.h b/kernel/drivers/misc/rk628/rk628_grf.h
new file mode 100644
index 0000000..54d46cf
--- /dev/null
+++ b/kernel/drivers/misc/rk628/rk628_grf.h
@@ -0,0 +1,263 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2020 Rockchip Electronics Co. Ltd.
+ *
+ * Author: Wyon Bi <bivvy.bi@rock-chips.com>
+ */
+
+#ifndef RK628_GRF_H
+#define RK628_GRF_H
+
+#define GPIO_FUNC 0
+#define MUX_FUNC1 1
+#define MUX_FUNC2 2
+#define MUX_FUNC3 3
+
+/* GRF_SYSTEM_CON3 */
+#define UART_CTS_DISABLE 0xB0
+#define UART_CTS_ENABLE 0xB1
+
+#define UART_RTS_DISABLE 0xA0
+#define UART_RTS_ENABLE 0xA1
+
+#define UART_IOMUX_DISABLE 0x90
+#define UART_IOMUX_ENABLE 0x91
+
+#define JTAG_DISABLE 0x80
+#define JTAG_ENABLE 0x81
+
+#define HDMIRX_CEC0 0x70
+#define HDMIRX_CEC1 0x71
+
+#define SELECT_RXDDC_SDA0 0x60
+#define SELECT_RXDDC_SDA1 0x61
+
+#define SELECT_RXDDC_SCL0 0x50
+#define SELECT_RXDDC_SCL1 0x51
+
+#define SELECT_I2S_LRM0 0x40
+#define SELECT_I2S_LRM1 0x41
+
+#define SELECT_I2S_DM0 0x30
+#define SELECT_I2S_DM1 0x31
+
+#define SELECT_I2S_SCKM0 0x20
+#define SELECT_I2S_SCKM1 0x21
+
+/* GPIO0_A */
+#define GPIO_0A2 0x0a20
+#define I2S_SCKM0 0x0a21
+
+#define GPIO0A3 0x0a30
+#define I2SLR_M0 0x0a31
+
+#define GPIO0A4 0x0a40
+#define I2SM0D0 0x0a41
+#define UART_TXM1 0x0a42
+
+#define GPIO0A5 0x0a50
+#define I2SM0D1 0x0a51
+#define UART_RXM1 0x0a52
+
+#define GPIO0A6 0x0a60
+#define I2SM0D2 0x0a61
+#define UART_CTSNM1 0x0a62
+
+#define GPIO0A7 0x0a70
+#define I2SM0D3 0x0a71
+#define UART_RTSNM1 0x0a72
+
+
+/* GPIO0_B */
+#define GPIO0B0 0x0b00
+#define HPDIN 0x0b01
+
+#define GPIO0B1 0x0b10
+#define DDCSDATX 0x0b11
+
+#define GPIO0B2 0x0b20
+#define DDCSCLTX 0x0b21
+
+#define GPIO0B3 0x0b30
+#define CECTX 0x0b31
+
+
+/* GPIO1_A */
+#define GPIO1A0 0x1a00
+#define TESTCLKOUT 0x1a01
+
+#define GPIO1A1 0x1a10
+#define XIPSFC_SCLK 0x1a11
+
+#define GPIO1A2 0x1a20
+#define I2SSCKM1 0x1a21
+
+#define GPIO1A3 0x1a30
+#define I2SM1LR 0x1a31
+
+#define GPIO1A4 0x1a40
+#define I2SM1D0 0x1a41
+
+#define GPIO1A5 0x1a50
+#define I2SM1D1 0x1a51
+
+#define GPIO1A6 0x1a60
+#define I2SM1D2 0x1a61
+
+#define GPIO1A7 0x1a70
+#define I2SM1D3 0x1a71
+
+
+/* GPIO1_B */
+#define GPIO1B0 0x1b00
+#define HPDM0OUT 0x1b01
+
+#define GPIO1B1 0x1b10
+#define DDCM0SDARX 0x1b11
+
+#define GPIO1B2 0x1b20
+#define DDCM0SCLRX 0x1b21
+
+#define GPIO1B3 0x1b30
+#define CECM0RX 0x1b31
+
+#define GPIO1B4 0x1b40
+#define I2CS_SCL 0x1b41
+#define I2CM_SCL 0x1b42
+
+#define GPIO1B5 0x1b50
+#define I2CS_SDA 0x1b51
+#define I2CM_SDA 0x1b52
+
+
+/* GPIO2_A */
+#define GPIO2A0 0x2a00
+#define VOPD0 0x2a01
+
+#define GPIO2A1 0x2a10
+#define VOPD1 0x2a11
+
+#define GPIO2A2 0x2a20
+#define VOPD2 0x2a21
+
+#define GPIO2A3 0x2a30
+#define VOPD3 0x2a31
+
+#define GPIO2A4 0x2a40
+#define VOPD4 0x2a41
+
+#define GPIO2A5 0x2a50
+#define VOPD5 0x2a51
+
+#define GPIO2A6 0x2a60
+#define VOPD6 0x2a61
+
+#define GPIO2A7 0x2a70
+#define VOPD7 0x2a71
+
+
+/* GPIO2_B */
+#define GPIO2B0 0x2b00
+#define VOPD8 0x2b01
+
+#define GPIO2B1 0x2b10
+#define VOPD9 0x2b11
+
+#define GPIO2B2 0x2b20
+#define VOPD10 0x2b21
+
+#define GPIO2B3 0x2b30
+#define VOPD11 0x2b31
+
+#define GPIO2B4 0x2b40
+#define VOPD12 0x2b41
+
+#define GPIO2B5 0x2b50
+#define VOPD13 0x2b51
+
+#define GPIO2B6 0x2b60
+#define VOPD14 0x2b61
+
+#define GPIO2B7 0x2b70
+#define VOPD15 0x2b71
+
+
+/* GPIO2_C */
+#define GPIO2C0 0x2c00
+#define VOPD16 0x2c01
+#define XIPSFC_CSN 0x2c02
+
+#define GPIO2C1 0x2c10
+#define VOPD17 0x2c11
+#define XIPSFC_MISO 0x2c12
+
+#define GPIO2C2 0x2c20
+#define VOPD18 0x2c21
+#define XIPSFC_MOSI 0x2c22
+
+#define GPIO2C3 0x2c30
+#define VOPD19 0x2c31
+#define RISVJTAG_TDO 0x2c32
+#define UART_TXM0 0x2c33
+
+#define GPIO2C4 0x2c40
+#define VOPD20 0x2c41
+#define RISVJTAG_TDI 0x2c42
+#define UART_RXM0 0x2c43
+
+#define GPIO2C5 0x2c50
+#define VOPD21 0x2c51
+#define RISVJTAG_TMS 0x2c52
+#define UART_CTSNM0 0x2c53
+
+#define GPIO2C6 0x2c60
+#define VOPD22 0x2c61
+#define RISVJTAG_TCK 0x2c62
+#define UART_RTSNM0 0x2c63
+
+#define GPIO2C7 0x2c70
+#define VOPD23 0x2c71
+#define RISVJTAG_TRSTN 0x2c72
+
+
+/* GPIO3_A */
+#define GPIO3A0 0x3a00
+#define VOPDEN 0x3a01
+
+#define GPIO3A1 0x3a10
+#define VOPHSYNC 0x3a11
+
+#define GPIO3A3 0x3a30
+#define VOPVSYNC 0x3a31
+
+#define GPIO3A4 0x3a40
+#define HPDM1OUT 0x3a41
+
+#define GPIO3A5 0x3a50
+#define DDCM1SDARX 0x3a51
+
+#define GPIO3A6 0x3a60
+#define DDCM1SCLRX 0x3a61
+
+#define GPIO3A7 0x3a70
+#define CECM1RX 0x3a71
+
+
+/* GPIO3_B */
+#define GPIO3B0 0x3b00
+#define VOPDCLK 0x3b01
+
+#define GPIO3B1 0x3b10
+#define GVIHPD 0x3b11
+
+#define GPIO3B2 0x3b20
+#define GVILOCK 0x3b21
+
+#define GPIO3B4 0x3b40
+#define SPIBOOT 0x3b41
+#define INT 0x3b42
+
+
+#endif // RK628_GRF_H
+
+
diff --git a/kernel/drivers/misc/rk628/rk628_gvi.c b/kernel/drivers/misc/rk628/rk628_gvi.c
new file mode 100644
index 0000000..7539cb7
--- /dev/null
+++ b/kernel/drivers/misc/rk628/rk628_gvi.c
@@ -0,0 +1,230 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2020 Rockchip Electronics Co. Ltd.
+ *
+ * Author: Guochun Huang <hero.huang@rock-chips.com>
+ */
+
+#include "linux/printk.h"
+#include "rk628.h"
+#include "rk628_config.h"
+#include "rk628_combtxphy.h"
+#include "rk628_gvi.h"
+#include "panel.h"
+
+int rk628_gvi_parse(struct rk628 *rk628, struct device_node *gvi_np)
+{
+	const char *string;
+	u32 val;
+	int ret;
+
+	if (!of_device_is_available(gvi_np))
+		return -EINVAL;
+
+	rk628->output_mode = OUTPUT_MODE_GVI;
+
+	if (!of_property_read_u32(gvi_np, "gvi,lanes", &val))
+		rk628->gvi.lanes = val;
+
+	if (of_property_read_bool(gvi_np, "rockchip,division-mode"))
+		rk628->gvi.division_mode = true;
+	else
+		rk628->gvi.division_mode = false;
+
+	if (of_property_read_bool(gvi_np, "rockchip,gvi-frm-rst"))
+		rk628->gvi.frm_rst = true;
+	else
+		rk628->gvi.frm_rst = false;
+
+	if (!of_property_read_string(gvi_np, "bus-format", &string)) {
+		if (!strcmp(string, "rgb666"))
+			rk628->gvi.bus_format = GVI_MEDIA_BUS_FMT_RGB666_1X18;
+		else if (!strcmp(string, "rgb101010"))
+			rk628->gvi.bus_format = GVI_MEDIA_BUS_FMT_RGB101010_1X30;
+		else if (!strcmp(string, "yuyv8"))
+			rk628->gvi.bus_format = GVI_MEDIA_BUS_FMT_YUYV8_1X16;
+		else if (!strcmp(string, "yuyv10"))
+			rk628->gvi.bus_format = GVI_MEDIA_BUS_FMT_YUYV10_1X20;
+		else
+			rk628->gvi.bus_format = GVI_MEDIA_BUS_FMT_RGB888_1X24;
+	}
+
+	ret = rk628_panel_info_get(rk628, gvi_np);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static void rk628_gvi_get_info(struct rk628_gvi *gvi)
+{
+	switch (gvi->bus_format) {
+	case GVI_MEDIA_BUS_FMT_RGB666_1X18:
+		gvi->byte_mode = 3;
+		gvi->color_depth = COLOR_DEPTH_RGB_YUV444_18BIT;
+		break;
+	case GVI_MEDIA_BUS_FMT_RGB888_1X24:
+		gvi->byte_mode = 4;
+		gvi->color_depth = COLOR_DEPTH_RGB_YUV444_24BIT;
+		break;
+	case GVI_MEDIA_BUS_FMT_RGB101010_1X30:
+		gvi->byte_mode = 4;
+		gvi->color_depth = COLOR_DEPTH_RGB_YUV444_30BIT;
+		break;
+	case GVI_MEDIA_BUS_FMT_YUYV8_1X16:
+		gvi->byte_mode = 3;
+		gvi->color_depth = COLOR_DEPTH_YUV422_16BIT;
+		break;
+	case GVI_MEDIA_BUS_FMT_YUYV10_1X20:
+		gvi->byte_mode = 3;
+		gvi->color_depth = COLOR_DEPTH_YUV422_20BIT;
+		break;
+	default:
+		gvi->byte_mode = 3;
+		gvi->color_depth = COLOR_DEPTH_RGB_YUV444_24BIT;
+		pr_err("unsupported bus_format: 0x%x\n", gvi->bus_format);
+		break;
+	}
+}
+
+static unsigned int rk628_gvi_get_lane_rate(struct rk628 *rk628)
+{
+	const struct rk628_display_mode *mode = &rk628->dst_mode;
+	struct rk628_gvi *gvi = &rk628->gvi;
+	u32 lane_bit_rate, min_lane_rate = 500000, max_lane_rate = 4000000;
+	u64 total_bw;
+
+	/**
+	 * [ENCODER TOTAL BIT-RATE](bps) = [byte mode](byte) x 10 / [pixel clock](HZ)
+	 *
+	 * lane_bit_rate = [total bit-rate](bps) / [lane number]
+	 *
+	 * 500Mbps <= lane_bit_rate <= 4Gbps
+	 */
+	total_bw = (u64)gvi->byte_mode * 10 * mode->clock;/* Kbps */
+	do_div(total_bw, gvi->lanes);
+	lane_bit_rate = total_bw;
+
+	if (lane_bit_rate < min_lane_rate)
+		lane_bit_rate = min_lane_rate;
+	if (lane_bit_rate > max_lane_rate)
+		lane_bit_rate = max_lane_rate;
+
+	return lane_bit_rate;
+}
+
+static void rk628_gvi_pre_enable(struct rk628 *rk628, struct rk628_gvi *gvi)
+{
+	/* gvi reset */
+	rk628_i2c_update_bits(rk628, GVI_SYS_RST, SYS_RST_SOFT_RST,
+			      SYS_RST_SOFT_RST);
+	udelay(10);
+	rk628_i2c_update_bits(rk628, GVI_SYS_RST, SYS_RST_SOFT_RST, 0);
+	udelay(10);
+
+	rk628_i2c_update_bits(rk628, GVI_SYS_CTRL0, SYS_CTRL0_LANE_NUM_MASK,
+			      SYS_CTRL0_LANE_NUM(gvi->lanes - 1));
+	rk628_i2c_update_bits(rk628, GVI_SYS_CTRL0, SYS_CTRL0_BYTE_MODE_MASK,
+			      SYS_CTRL0_BYTE_MODE(gvi->byte_mode ==
+			      3 ? 0 : (gvi->byte_mode == 4 ? 1 : 2)));
+	rk628_i2c_update_bits(rk628, GVI_SYS_CTRL0, SYS_CTRL0_SECTION_NUM_MASK,
+			      SYS_CTRL0_SECTION_NUM(gvi->division_mode));
+	rk628_i2c_update_bits(rk628, GRF_POST_PROC_CON, SW_SPLIT_EN,
+			      gvi->division_mode ? SW_SPLIT_EN : 0);
+	rk628_i2c_update_bits(rk628, GVI_SYS_CTRL1, SYS_CTRL1_DUAL_PIXEL_EN,
+			      gvi->division_mode ? SYS_CTRL1_DUAL_PIXEL_EN : 0);
+
+	rk628_i2c_update_bits(rk628, GVI_SYS_CTRL0, SYS_CTRL0_FRM_RST_EN,
+			      gvi->frm_rst ? SYS_CTRL0_FRM_RST_EN : 0);
+	rk628_i2c_update_bits(rk628, GVI_SYS_CTRL1, SYS_CTRL1_LANE_ALIGN_EN, 0);
+}
+
+static void rk628_gvi_enable_color_bar(struct rk628 *rk628,
+				       struct rk628_gvi *gvi)
+{
+	const struct rk628_display_mode *mode = &rk628->dst_mode;
+	u16 vm_hactive, vm_hback_porch, vm_hsync_len;
+	u16 vm_vactive, vm_vback_porch, vm_vsync_len;
+	u16 hsync_len, hact_st, hact_end, htotal;
+	u16 vsync_len, vact_st, vact_end, vtotal;
+
+	vm_hactive = mode->hdisplay;
+	vm_hsync_len = mode->hsync_end - mode->hsync_start;
+	vm_hback_porch = mode->htotal - mode->hsync_end;
+
+	vm_vactive = mode->vdisplay;
+	vm_vsync_len = mode->vsync_end - mode->vsync_start;
+	vm_vback_porch = mode->vtotal - mode->vsync_end;
+
+	if (gvi->division_mode) {
+		hsync_len = vm_hsync_len / 2;
+		hact_st = (vm_hsync_len + vm_hback_porch) / 2;
+		hact_end = (vm_hsync_len + vm_hback_porch + vm_hactive) / 2;
+		htotal = mode->htotal / 2;
+	} else {
+		hsync_len = vm_hsync_len;
+		hact_st = vm_hsync_len + vm_hback_porch;
+		hact_end = vm_hsync_len + vm_hback_porch + vm_hactive;
+		htotal = mode->htotal;
+	}
+	vsync_len = vm_vsync_len;
+	vact_st = vsync_len + vm_vback_porch;
+	vact_end = vact_st + vm_vactive;
+	vtotal = mode->vtotal;
+
+	rk628_i2c_write(rk628, GVI_COLOR_BAR_HTIMING0,
+			hact_st << 16 | hsync_len);
+	rk628_i2c_write(rk628, GVI_COLOR_BAR_HTIMING1,
+			(htotal - 1) << 16 | hact_end);
+	rk628_i2c_write(rk628, GVI_COLOR_BAR_VTIMING0,
+			vact_st << 16 | vsync_len);
+	rk628_i2c_write(rk628, GVI_COLOR_BAR_VTIMING1,
+			(vtotal - 1) << 16 | vact_end);
+	rk628_i2c_update_bits(rk628, GVI_COLOR_BAR_CTRL, COLOR_BAR_EN, 0);
+}
+
+
+static void rk628_gvi_post_enable(struct rk628 *rk628, struct rk628_gvi *gvi)
+{
+	u32 val;
+
+	val = SYS_CTRL0_GVI_EN | SYS_CTRL0_AUTO_GATING;
+	rk628_i2c_update_bits(rk628, GVI_SYS_CTRL0, val, 3);
+}
+
+void rk628_gvi_enable(struct rk628 *rk628)
+{
+	struct rk628_gvi *gvi = &rk628->gvi;
+	unsigned int rate;
+
+	rk628_gvi_get_info(gvi);
+	rate = rk628_gvi_get_lane_rate(rk628);
+
+	/* set gvi_hpd and gvi_lock mux */
+	rk628_i2c_write(rk628, GRF_GPIO3AB_SEL_CON, 0x06000600);
+	rk628_i2c_update_bits(rk628, GRF_SYSTEM_CON0, SW_OUTPUT_MODE_MASK,
+			      SW_OUTPUT_MODE(OUTPUT_MODE_GVI));
+	rk628_combtxphy_set_bus_width(rk628, rate);
+	rk628_combtxphy_set_gvi_division_mode(rk628, gvi->division_mode);
+	rk628_combtxphy_set_mode(rk628, PHY_MODE_VIDEO_GVI);
+	rate = rk628_combtxphy_get_bus_width(rk628);
+	rk628_combtxphy_power_on(rk628);
+	rk628_gvi_pre_enable(rk628, gvi);
+	rk628_panel_prepare(rk628);
+	rk628_gvi_enable_color_bar(rk628, gvi);
+	rk628_gvi_post_enable(rk628, gvi);
+	rk628_panel_enable(rk628);
+	dev_info(rk628->dev,
+		 "GVI-Link bandwidth: %d x %d Mbps, Byte mode: %d, Color Depty: %d, %s division mode\n",
+		 rate, gvi->lanes, gvi->byte_mode, gvi->color_depth,
+		 gvi->division_mode ? "two" : "one");
+}
+
+void rk628_gvi_disable(struct rk628 *rk628)
+{
+	rk628_panel_disable(rk628);
+	rk628_panel_unprepare(rk628);
+	rk628_i2c_update_bits(rk628, GVI_SYS_CTRL0, SYS_CTRL0_GVI_EN, 0);
+	rk628_combtxphy_power_off(rk628);
+}
+
diff --git a/kernel/drivers/misc/rk628/rk628_gvi.h b/kernel/drivers/misc/rk628/rk628_gvi.h
new file mode 100644
index 0000000..451d0cd
--- /dev/null
+++ b/kernel/drivers/misc/rk628/rk628_gvi.h
@@ -0,0 +1,218 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2020 Rockchip Electronics Co. Ltd.
+ *
+ * Author: Guochun Huang <hero.huang@rock-chips.com>
+ */
+
+#ifndef RK628_GVI_H
+#define RK628_GVI_H
+
+#include "rk628.h"
+
+#define GVI_BASE					0x80000
+#define HOSTREG(x)					((x) + GVI_BASE)
+#define GVI_SYS_CTRL0					HOSTREG(0x0000)
+#define GVI_SYS_CTRL1					HOSTREG(0x0004)
+#define GVI_SYS_CTRL2					HOSTREG(0x0008)
+#define GVI_SYS_CTRL3					HOSTREG(0x000c)
+#define GVI_VERSION					HOSTREG(0x0010)
+#define GVI_SYS_RST					HOSTREG(0x0014)
+#define GVI_LINE_FLAG					HOSTREG(0x0018)
+#define GVI_STATUS					HOSTREG(0x001c)
+#define GVI_PLL_LOCK_TIMEOUT				HOSTREG(0x0030)
+#define GVI_HTPDN_TIMEOUT				HOSTREG(0x0034)
+#define GVI_LOCKN_TIMEOUT				HOSTREG(0x0038)
+#define GVI_WAIT_LOCKN					HOSTREG(0x003C)
+#define GVI_WAIT_HTPDN					HOSTREG(0x0040)
+#define GVI_INTR_EN					HOSTREG(0x0050)
+#define GVI_INTR_CLR					HOSTREG(0x0054)
+#define GVI_INTR_RAW_STATUS				HOSTREG(0x0058)
+#define GVI_INTR_STATUS					HOSTREG(0x005c)
+#define GVI_COLOR_BAR_CTRL				HOSTREG(0x0060)
+#define GVI_COLOR_BAR_HTIMING0				HOSTREG(0x0070)
+#define GVI_COLOR_BAR_HTIMING1				HOSTREG(0x0074)
+#define GVI_COLOR_BAR_VTIMING0				HOSTREG(0x0078)
+#define GVI_COLOR_BAR_VTIMING1				HOSTREG(0x007c)
+
+/* SYS_CTRL0 */
+#define SYS_CTRL0_GVI_EN				BIT(0)
+#define SYS_CTRL0_AUTO_GATING				BIT(1)
+#define SYS_CTRL0_FRM_RST_EN				BIT(2)
+#define SYS_CTRL0_FRM_RST_MODE				BIT(3)
+#define SYS_CTRL0_LANE_NUM_MASK				GENMASK(7, 4)
+#define SYS_CTRL0_LANE_NUM(x)				UPDATE(x, 7, 4)
+#define SYS_CTRL0_BYTE_MODE_MASK			GENMASK(9, 8)
+#define SYS_CTRL0_BYTE_MODE(x)				UPDATE(x, 9, 8)
+#define SYS_CTRL0_SECTION_NUM_MASK			GENMASK(11, 10)
+#define SYS_CTRL0_SECTION_NUM(x)			UPDATE(x, 11, 10)
+#define SYS_CTRL0_CDR_ENDIAN_SWAP			BIT(12)
+#define SYS_CTRL0_PACK_BYTE_SWAP			BIT(13)
+#define SYS_CTRL0_PACK_ENDIAN_SWAP			BIT(14)
+#define SYS_CTRL0_ENC8B10B_ENDIAN_SWAP			BIT(15)
+#define SYS_CTRL0_CDR_EN				BIT(16)
+#define SYS_CTRL0_ALN_EN				BIT(17)
+#define SYS_CTRL0_NOR_EN				BIT(18)
+#define SYS_CTRL0_ALN_NOR_MODE				BIT(19)
+#define SYS_CTRL0_GVI_MASK				GENMASK(19, 16)
+#define SYS_CTRL0_GVI_GN_EN(x)				UPDATE(x, 19, 16)
+
+#define SYS_CTRL0_SCRAMBLER_EN				BIT(20)
+#define SYS_CTRL0_ENCODE8B10B_EN			BIT(21)
+#define SYS_CTRL0_INIT_RD_EN				BIT(22)
+#define SYS_CTRL0_INIT_RD_VALUE				BIT(23)
+#define SYS_CTRL0_FORCE_HTPDN_EN			BIT(24)
+#define SYS_CTRL0_FORCE_HTPDN_VALUE			BIT(25)
+#define SYS_CTRL0_FORCE_PLL_EN				BIT(26)
+#define SYS_CTRL0_FORCE_PLL_VALUE			BIT(27)
+#define SYS_CTRL0_FORCE_LOCKN_EN			BIT(28)
+#define SYS_CTRL0_FORCE_LOCKN_VALUE			BIT(29)
+
+/* SYS_CTRL1 */
+#define SYS_CTRL1_COLOR_DEPTH_MASK			GENMASK(3, 0)
+#define SYS_CTRL1_COLOR_DEPTH(x)			UPDATE(x, 3, 0)
+#define SYS_CTRL1_DUAL_PIXEL_EN				BIT(4)
+#define SYS_CTRL1_TIMING_ALIGN_EN			BIT(8)
+#define SYS_CTRL1_LANE_ALIGN_EN				BIT(9)
+
+#define SYS_CTRL1_DUAL_PIXEL_SWAP			BIT(12)
+#define SYS_CTRL1_RB_SWAP				BIT(13)
+#define SYS_CTRL1_YC_SWAP				BIT(14)
+#define SYS_CTRL1_WHOLE_FRM_EN				BIT(16)
+#define SYS_CTRL1_NOR_PROTECT				BIT(17)
+#define SYS_CTRL1_RD_WCNT_UPDATE			BIT(31)
+
+/* SYS_CTRL2 */
+#define SYS_CTRL2_AFIFO_READ_THOLD_MASK			GENMASK(7, 0)
+#define SYS_CTRL2_AFIFO_READ_THOLD(x)			UPDATE(x, 7, 0)
+#define SYS_CTRL2_AFIFO_ALMOST_FULL_THOLD_MASK		GENMASK(23, 16)
+#define SYS_CTRL2_AFIFO_ALMOST_FULL_THOLD(x)		UPDATE(x, 23, 16)
+#define SYS_CTRL2_AFIFO_ALMOST_EMPTY_THOLD_MASK		GENMASK(31, 24)
+#define SYS_CTRL2_AFIFO_ALMOST_EMPTY_THOLD(x)		UPDATE(x, 31, 24)
+
+/* SYS_CTRL3 */
+#define SYS_CTRL3_LANE0_SEL_MASK			GENMASK(2, 0)
+#define SYS_CTRL3_LANE0_SEL(x)				UPDATE(x, 2, 0)
+#define SYS_CTRL3_LANE1_SEL_MASK			GENMASK(6, 4)
+#define SYS_CTRL3_LANE1_SEL(x)				UPDATE(x, 6, 4)
+#define SYS_CTRL3_LANE2_SEL_MASK			GENMASK(10, 8)
+#define SYS_CTRL3_LANE2_SEL(x)				UPDATE(x, 10, 8)
+#define SYS_CTRL3_LANE3_SEL_MASK			GENMASK(14, 12)
+#define SYS_CTRL3_LANE3_SEL(x)				UPDATE(x, 14, 12)
+#define SYS_CTRL3_LANE4_SEL_MASK			GENMASK(18, 16)
+#define SYS_CTRL3_LANE4_SEL(x)				UPDATE(x, 18, 16)
+#define SYS_CTRL3_LANE5_SEL_MASK			GENMASK(22, 20)
+#define SYS_CTRL3_LANE5_SEL(x)				UPDATE(x, 22, 20)
+#define SYS_CTRL3_LANE6_SEL_MASK			GENMASK(26, 24)
+#define SYS_CTRL3_LANE6_SEL(x)				UPDATE(x, 26, 24)
+#define SYS_CTRL3_LANE7_SEL_MASK			GENMASK(30, 28)
+#define SYS_CTRL3_LANE7_SEL(x)				UPDATE(x, 30, 28)
+/* VERSIION */
+#define VERSION_VERSION(x)				UPDATE(x, 31, 0)
+/* SYS_RESET*/
+#define SYS_RST_SOFT_RST				BIT(0)
+/* LINE_FLAG */
+#define LINE_FLAG_LANE_FLAG0_MASK			GENMASK(15, 0)
+#define LINE_FLAG_LANE_FLAG0(x)				UPDATE(x, 15, 0)
+#define LINE_FLAG_LANE_FLAG1_MASK			GENMASK(31, 16)
+#define LINE_FLAG_LANE_FLAG1(x)				UPDATE(x, 31, 16)
+/* STATUS */
+#define STATUS_HTDPN					BIT(4)
+#define STATUS_LOCKN					BIT(5)
+#define STATUS_PLL_LOCKN				BIT(6)
+#define STATUS_AFIFO0_WCNT_MASK				GENMASK(23, 16)
+#define STATUS_AFIFO0_WCNT(x)				UPDATE(x, 23, 16)
+#define STATUS_AFIFO1_WCNT_MASK				GENMASK(31, 24)
+#define STATUS_AFIFO1_WCNT(x)				UPDATE(x, 31, 24)
+/* PLL_LTIMEOUT */
+#define PLL_LOCK_TIMEOUT_PLL_LOCK_TIME_OUT_MASK		GENMASK(31, 0)
+#define PLL_LOCK_TIMEOUT_PLL_LOCK_TIME_OUT(x)		UPDATE(x, 31, 0)
+/* HTPDNEOUT */
+#define HTPDN_TIMEOUT_HTPDN_TIME_OUT_MASK		GENMASK(31, 0)
+#define HTPDN_TIMEOUT_HTPDN_TIME_OUT(x)			UPDATE(x, 31, 0)
+/* LOCKNEOUT */
+#define LOCKN_TIMEOUT_LOCKN_TIME_OUT_MASK		GENMASK(31, 0)
+#define LOCKN_TIMEOUT_LOCKN_TIME_OUT(x)			UPDATE(x, 31, 0)
+/* WAIT_LOCKN */
+#define WAIT_LOCKN_WAIT_LOCKN_TIME_MASK			GENMASK(30, 0)
+#define WAIT_LOCKN_WAIT_LOCKN_TIME(x)			UPDATE(x, 30, 0)
+#define WAIT_LOCKN_WAIT_LOCKN_TIME_EN			BIT(31)
+/* WAIT_HTPDN */
+#define WAIT_HTPDN_WAIT_HTPDN_TIME_MASK			GENMASK(30, 0)
+#define WAIT_HTPDN_WAIT_HTPDN_TIME(x)			UPDATE(x, 30, 0)
+#define WAIT_HTPDN_WAIT_HTPDN_EN			BIT(31)
+/* INTR_EN */
+#define INTR_EN_INTR_FRM_ST_EN				BIT(0)
+#define INTR_EN_INTR_PLL_LOCK_EN			BIT(1)
+#define INTR_EN_INTR_HTPDN_EN				BIT(2)
+#define INTR_EN_INTR_LOCKN_EN				BIT(3)
+#define INTR_EN_INTR_PLL_TIMEOUT_EN			BIT(4)
+#define INTR_EN_INTR_HTPDN_TIMEOUT_EN			BIT(5)
+#define INTR_EN_INTR_LOCKN_TIMEOUT_EN			BIT(6)
+#define INTR_EN_INTR_LINE_FLAG0_EN			BIT(8)
+#define INTR_EN_INTR_LINE_FLAG1_EN			BIT(9)
+#define INTR_EN_INTR_AFIFO_OVERFLOW_EN			BIT(10)
+#define INTR_EN_INTR_AFIFO_UNDERFLOW_EN			BIT(11)
+#define INTR_EN_INTR_PLL_ERR_EN				BIT(12)
+#define INTR_EN_INTR_HTPDN_ERR_EN			BIT(13)
+#define INTR_EN_INTR_LOCKN_ERR_EN			BIT(14)
+/* INTR_CLR*/
+#define INTR_CLR_INTR_FRM_ST_CLR			BIT(0)
+#define INTR_CLR_INTR_PLL_LOCK_CLR			BIT(1)
+#define INTR_CLR_INTR_HTPDN_CLR				BIT(2)
+#define INTR_CLR_INTR_LOCKN_CLR				BIT(3)
+#define INTR_CLR_INTR_PLL_TIMEOUT_CLR			BIT(4)
+#define INTR_CLR_INTR_HTPDN_TIMEOUT_CLR			BIT(5)
+#define INTR_CLR_INTR_LOCKN_TIMEOUT_CLR			BIT(6)
+#define INTR_CLR_INTR_LINE_FLAG0_CLR			BIT(8)
+#define INTR_CLR_INTR_LINE_FLAG1_CLR			BIT(9)
+#define INTR_CLR_INTR_AFIFO_OVERFLOW_CLR		BIT(10)
+#define INTR_CLR_INTR_AFIFO_UNDERFLOW_CLR		BIT(11)
+#define INTR_CLR_INTR_PLL_ERR_CLR			BIT(12)
+#define INTR_CLR_INTR_HTPDN_ERR_CLR			BIT(13)
+#define INTR_CLR_INTR_LOCKN_ERR_CLR			BIT(14)
+/* INTR_RAW_STATUS */
+#define INTR_RAW_STATUS_RAW_INTR_FRM_ST			BIT(0)
+#define INTR_RAW_STATUS_RAW_INTR_PLL_LOCK		BIT(1)
+#define INTR_RAW_STATUS_RAW_INTR_HTPDN			BIT(2)
+#define INTR_RAW_STATUS_RAW_INTR_LOCKN			BIT(3)
+#define INTR_RAW_STATUS_RAW_INTR_PLL_TIMEOUT		BIT(4)
+#define INTR_RAW_STATUS_RAW_INTR_HTPDN_TIMEOUT		BIT(5)
+#define INTR_RAW_STATUS_RAW_INTR_LOCKN_TIMEOUT		BIT(6)
+#define INTR_RAW_STATUS_RAW_INTR_LINE_FLAG0		BIT(8)
+#define INTR_RAW_STATUS_RAW_INTR_LINE_FLAG1		BIT(9)
+#define INTR_RAW_STATUS_RAW_INTR_AFIFO_OVERFLOW		BIT(10)
+#define INTR_RAW_STATUS_RAW_INTR_AFIFO_UNDERFLOW	BIT(11)
+#define INTR_RAW_STATUS_RAW_INTR_PLL_ERR		BIT(12)
+#define INTR_RAW_STATUS_RAW_INTR_HTPDN_ERR		BIT(13)
+#define INTR_RAW_STATUS_RAW_INTR_LOCKN_ERR		BIT(14)
+/* INTR_STATUS */
+#define INTR_STATUS_INTR_FRM_ST				BIT(0)
+#define INTR_STATUS_INTR_PLL_LOCK			BIT(1)
+#define INTR_STATUS_INTR_HTPDN				BIT(2)
+#define INTR_STATUS_INTR_LOCKN				BIT(3)
+#define INTR_STATUS_INTR_PLL_TIMEOUT			BIT(4)
+#define INTR_STATUS_INTR_HTPDN_TIMEOUT			BIT(5)
+#define INTR_STATUS_INTR_LOCKN_TIMEOUT			BIT(6)
+#define INTR_STATUS_INTR_LINE_FLAG0			BIT(8)
+#define INTR_STATUS_INTR_LINE_FLAG1			BIT(9)
+#define INTR_STATUS_INTR_AFIFO_OVERFLOW			BIT(10)
+#define INTR_STATUS_INTR_AFIFO_UNDERFLOW		BIT(11)
+#define INTR_STATUS_INTR_PLL_ERR			BIT(12)
+#define INTR_STATUS_INTR_HTPDN_ERR			BIT(13)
+#define INTR_STATUS_INTR_LOCKN_ERR			BIT(14)
+
+/* COLOR_BAR_CTRL */
+#define COLOR_BAR_EN					BIT(0)
+
+#define COLOR_DEPTH_RGB_YUV444_18BIT			0
+#define COLOR_DEPTH_RGB_YUV444_24BIT			1
+#define COLOR_DEPTH_RGB_YUV444_30BIT			2
+#define COLOR_DEPTH_YUV422_16BIT			8
+#define COLOR_DEPTH_YUV422_20BIT			9
+
+int rk628_gvi_parse(struct rk628 *rk628, struct device_node *gvi_np);
+void rk628_gvi_enable(struct rk628 *rk628);
+void rk628_gvi_disable(struct rk628 *rk628);
+
+#endif
diff --git a/kernel/drivers/misc/rk628/rk628_hdmirx.c b/kernel/drivers/misc/rk628/rk628_hdmirx.c
new file mode 100644
index 0000000..bfb8866
--- /dev/null
+++ b/kernel/drivers/misc/rk628/rk628_hdmirx.c
@@ -0,0 +1,777 @@
+// SPDX-License-Identifier: BSD-3-Clause
+/*
+ * Copyright (c) 2021 Rockchip Electronics Co. Ltd.
+ *
+ * Author: Chen Shunqing <csq@rock-chips.com>
+ */
+#include <linux/gpio/consumer.h>
+#include <linux/of_device.h>
+
+#include "rk628.h"
+#include "rk628_combrxphy.h"
+#include "rk628_config.h"
+#include "rk628_hdmirx.h"
+
+#define POLL_INTERVAL_MS			1000
+#define MODETCLK_CNT_NUM			1000
+#define MODETCLK_HZ				49500000
+#define RXPHY_CFG_MAX_TIMES			1
+
+static u8 debug;
+
+static u8 edid_init_data[] = {
+	0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00,
+	0x49, 0x78, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+	0x12, 0x1E, 0x01, 0x03, 0x80, 0x00, 0x00, 0x78,
+	0x0A, 0x0D, 0xC9, 0xA0, 0x57, 0x47, 0x98, 0x27,
+	0x12, 0x48, 0x4C, 0x00, 0x00, 0x00, 0x01, 0x01,
+	0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+	0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3A,
+	0x80, 0x18, 0x71, 0x38, 0x2D, 0x40, 0x58, 0x2C,
+	0x45, 0x00, 0xC4, 0x8E, 0x21, 0x00, 0x00, 0x1E,
+	0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x50,
+	0x72, 0x6F, 0x6A, 0x65, 0x63, 0x74, 0x6F, 0x72,
+	0x0A, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xFD,
+	0x00, 0x14, 0x78, 0x01, 0xFF, 0x1D, 0x00, 0x0A,
+	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x01, 0x18,
+
+	0x02, 0x03, 0x13, 0x71, 0x40, 0x23, 0x09, 0x07,
+	0x01, 0x83, 0x01, 0x00, 0x00, 0x65, 0x03, 0x0C,
+	0x00, 0x10, 0x00, 0x02, 0x3A, 0x80, 0x18, 0x71,
+	0x38, 0x2D, 0x40, 0x58, 0x2C, 0x45, 0x00, 0x20,
+	0xC2, 0x31, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17,
+
+};
+
+struct rk628_hdmi_mode {
+	u32 hdisplay;
+	u32 hstart;
+	u32 hend;
+	u32 htotal;
+	u32 vdisplay;
+	u32 vstart;
+	u32 vend;
+	u32 vtotal;
+	u32 clock;
+	unsigned int flags;
+};
+
+struct rk628_hdmirx {
+	bool plugin;
+	bool res_change;
+	struct rk628_hdmi_mode mode;
+	u32 input_format;
+	u32 fs_audio;
+	bool audio_present;
+	bool hpd_output_inverted;
+	bool src_mode_4K_yuv420;
+	bool phy_lock;
+};
+
+static void rk628_hdmirx_ctrl_enable(struct rk628 *rk628)
+{
+
+	rk628_i2c_update_bits(rk628, GRF_SYSTEM_CON0,
+	     SW_INPUT_MODE_MASK,
+	     SW_INPUT_MODE(INPUT_MODE_HDMI));
+
+	rk628_i2c_write(rk628, HDMI_RX_HDMI20_CONTROL, 0x10001f10);
+	rk628_i2c_write(rk628, HDMI_RX_HDMI_MODE_RECOVER, 0x00000021);
+	rk628_i2c_write(rk628, HDMI_RX_PDEC_CTRL, 0xbfff8011);
+	rk628_i2c_write(rk628, HDMI_RX_PDEC_ASP_CTRL, 0x00000040);
+	rk628_i2c_write(rk628, HDMI_RX_HDMI_RESMPL_CTRL, 0x00000001);
+	rk628_i2c_write(rk628, HDMI_RX_HDMI_SYNC_CTRL, 0x00000014);
+	rk628_i2c_write(rk628, HDMI_RX_PDEC_ERR_FILTER, 0x00000008);
+	rk628_i2c_write(rk628, HDMI_RX_SCDC_I2CCONFIG, 0x01000000);
+	rk628_i2c_write(rk628, HDMI_RX_SCDC_CONFIG, 0x00000001);
+	rk628_i2c_write(rk628, HDMI_RX_SCDC_WRDATA0, 0xabcdef01);
+	rk628_i2c_write(rk628, HDMI_RX_CHLOCK_CONFIG, 0x0030c15c);
+	rk628_i2c_write(rk628, HDMI_RX_HDMI_ERROR_PROTECT, 0x000d0c98);
+	rk628_i2c_write(rk628, HDMI_RX_MD_HCTRL1, 0x00000010);
+	rk628_i2c_write(rk628, HDMI_RX_MD_HCTRL2, 0x00001738);
+	rk628_i2c_write(rk628, HDMI_RX_MD_VCTRL, 0x00000002);
+	rk628_i2c_write(rk628, HDMI_RX_MD_VTH, 0x0000073a);
+	rk628_i2c_write(rk628, HDMI_RX_MD_IL_POL, 0x00000004);
+	rk628_i2c_write(rk628, HDMI_RX_PDEC_ACRM_CTRL, 0x00000000);
+	rk628_i2c_write(rk628, HDMI_RX_HDMI_DCM_CTRL, 0x00040414);
+	rk628_i2c_write(rk628, HDMI_RX_HDMI_CKM_EVLTM, 0x00103e70);
+	rk628_i2c_write(rk628, HDMI_RX_HDMI_CKM_F, 0x0c1c0b54);
+	rk628_i2c_write(rk628, HDMI_RX_HDMI_RESMPL_CTRL, 0x00000001);
+
+	rk628_i2c_update_bits(rk628, HDMI_RX_HDCP_SETTINGS,
+	     HDMI_RESERVED_MASK |
+	     FAST_I2C_MASK |
+	     ONE_DOT_ONE_MASK |
+	     FAST_REAUTH_MASK,
+	     HDMI_RESERVED(1) |
+	     FAST_I2C(0) |
+	     ONE_DOT_ONE(1) |
+	     FAST_REAUTH(1));
+}
+
+static void rk628_hdmirx_video_unmute(struct rk628 *rk628, u8 unmute)
+{
+	rk628_i2c_update_bits(rk628, HDMI_RX_DMI_DISABLE_IF, VID_ENABLE_MASK, VID_ENABLE(unmute));
+}
+
+static void rk628_hdmirx_hpd_ctrl(struct rk628 *rk628, bool en)
+{
+	u8 en_level, set_level;
+	struct rk628_hdmirx *hdmirx = rk628->hdmirx;
+
+	dev_dbg(rk628->dev, "%s: %sable, hpd invert:%d\n", __func__,
+			en ? "en" : "dis", hdmirx->hpd_output_inverted);
+	en_level = hdmirx->hpd_output_inverted ? 0 : 1;
+	set_level = en ? en_level : !en_level;
+	rk628_i2c_update_bits(rk628, HDMI_RX_HDMI_SETUP_CTRL,
+			HOT_PLUG_DETECT_MASK, HOT_PLUG_DETECT(set_level));
+}
+
+static void rk628_hdmirx_disable_edid(struct rk628 *rk628)
+{
+	rk628_hdmirx_hpd_ctrl(rk628, false);
+	rk628_hdmirx_video_unmute(rk628, 0);
+}
+
+static void rk628_hdmirx_enable_edid(struct rk628 *rk628)
+{
+	rk628_hdmirx_hpd_ctrl(rk628, true);
+}
+
+static int tx_5v_power_present(struct rk628 *rk628)
+{
+	bool ret;
+	int val, i, cnt;
+
+	/* Direct Mode */
+	if (!rk628->plugin_det_gpio)
+		return 1;
+
+	cnt = 0;
+	for (i = 0; i < 5; i++) {
+		val = gpiod_get_value(rk628->plugin_det_gpio);
+		if (val > 0)
+			cnt++;
+		usleep_range(500, 600);
+	}
+
+	ret = (cnt >= 3) ? 1 : 0;
+	dev_dbg(rk628->dev, "%s: %d\n", __func__, ret);
+
+	return ret;
+}
+
+static int rk628_hdmirx_init_edid(struct rk628 *rk628)
+{
+	struct rk628_display_mode *src_mode;
+	struct rk628_hdmirx *hdmirx = rk628->hdmirx;
+	u32 val;
+	u8 csum = 0;
+	int i, base, j;
+
+	src_mode = rk628_display_get_src_mode(rk628);
+	for (j = 0, base = 0x36; j < 2; j++) {
+		csum = 0;
+		/* clock-frequency */
+		edid_init_data[base + 1] = ((src_mode->clock / 10) & 0xff00) >> 8;
+		edid_init_data[base] = (src_mode->clock / 10) & 0xff;
+		/* hactive low 8 bits */
+		edid_init_data[base + 2]  = src_mode->hdisplay & 0xff;
+
+		/* hblanking low 8 bits */
+		val = src_mode->htotal - src_mode->hdisplay;
+		edid_init_data[base + 3] = val & 0xff;
+
+		/* hactive high 4 bits & hblanking low 4 bits */
+		edid_init_data[base + 4] =
+			((src_mode->hdisplay & 0xf00) >> 4) + ((val & 0xf00) >> 8);
+
+		/* vactive low 8 bits */
+		edid_init_data[base + 5] = src_mode->vdisplay & 0xff;
+
+		/* vblanking low 8 bits */
+		val = src_mode->vtotal - src_mode->vdisplay;
+		edid_init_data[base + 6] = val & 0xff;
+
+		/* vactive high 4 bits & vblanking low 4 bits */
+		edid_init_data[base + 7] =
+			((src_mode->vdisplay & 0xf00) >> 4) + ((val & 0xf00) >> 8);
+
+		/* hsync pulse offset low 8 bits */
+		val = src_mode->hsync_start - src_mode->hdisplay;
+		edid_init_data[base + 8] = val & 0xff;
+
+		/* hsync pulse width low 8 bits */
+		val = src_mode->hsync_end - src_mode->hsync_start;
+		edid_init_data[base + 9] = val & 0xff;
+
+		/* vsync pulse offset low 4 bits & vsync pulse width low 4 bits */
+		val = ((src_mode->vsync_start - src_mode->vdisplay) & 0xf) << 4;
+		edid_init_data[base + 10] = val;
+		edid_init_data[base + 10] += (src_mode->vsync_end - src_mode->vsync_start) & 0xf;
+
+		/* 6~7bits:hsync pulse offset;
+		 * 4~6bits:hsync pulse width;
+		 * 2~3bits:vsync pulse offset;
+		 * 0~1bits:vsync pulse width
+		 */
+		edid_init_data[base + 11] =
+			((src_mode->hsync_start - src_mode->hdisplay) & 0x300) >> 2;
+		edid_init_data[base + 11] +=
+			((src_mode->hsync_end - src_mode->hsync_start) & 0x700) >> 4;
+		edid_init_data[base + 11] +=
+			((src_mode->vsync_start - src_mode->vdisplay) & 0x30) >> 2;
+		edid_init_data[base + 11] +=
+			((src_mode->vsync_end - src_mode->vsync_start) & 0x30) >> 4;
+
+		edid_init_data[base + 17]  = 0x18;
+		if (src_mode->flags & DRM_MODE_FLAG_PHSYNC)
+			edid_init_data[base + 17] |= 0x2;
+
+		if (src_mode->flags & DRM_MODE_FLAG_PVSYNC)
+			edid_init_data[base + 17] |= 0x4;
+
+		if (hdmirx->src_mode_4K_yuv420 && src_mode->clock == 594000) {
+			edid_init_data[0x80 + 0x2] = 0x16;
+			edid_init_data[0x80 + 0x13] = 0xe2;
+			edid_init_data[0x80 + 0x14] = 0x0E;
+			edid_init_data[0x80 + 0x15] = 0x61;
+			base += (0x5d + 0x3);
+		} else {
+			base += 0x5d;
+		}
+
+		for (i = 0; i < 127; i++)
+			csum += edid_init_data[i + j * 128];
+
+		edid_init_data[127 +  j * 128] = (u8)0 - csum;
+	}
+
+	return 0;
+}
+
+static int rk628_hdmirx_set_edid(struct rk628 *rk628)
+{
+	int i;
+	u32 val;
+	u16 edid_len;
+
+	rk628_hdmirx_disable_edid(rk628);
+
+	if (!rk628->plugin_det_gpio)
+		return 0;
+
+	/* edid access by apb when write, i2c slave addr: 0x0 */
+	rk628_i2c_update_bits(rk628, GRF_SYSTEM_CON0,
+	     SW_ADAPTER_I2CSLADR_MASK |
+	     SW_EDID_MODE_MASK,
+	     SW_ADAPTER_I2CSLADR(0) |
+	     SW_EDID_MODE(1));
+
+	rk628_hdmirx_init_edid(rk628);
+
+	edid_len = ARRAY_SIZE(edid_init_data);
+	for (i = 0; i < edid_len; i++)
+		rk628_i2c_write(rk628, EDID_BASE + i * 4, edid_init_data[i]);
+
+	/* read out for debug */
+	if (debug >= 3) {
+		pr_info("====== Read EDID: ======\n");
+		for (i = 0; i < edid_len; i++) {
+			rk628_i2c_read(rk628, EDID_BASE + i * 4, &val);
+			pr_info("0x%02x ", val);
+			if ((i + 1) % 8 == 0)
+				pr_info("\n");
+		}
+		pr_info("============\n");
+	}
+
+	/* edid access by RX's i2c, i2c slave addr: 0x0 */
+	rk628_i2c_update_bits(rk628, GRF_SYSTEM_CON0,
+	     SW_ADAPTER_I2CSLADR_MASK |
+	     SW_EDID_MODE_MASK,
+	     SW_ADAPTER_I2CSLADR(0) |
+	     SW_EDID_MODE(0));
+
+	mdelay(1);
+
+	return 0;
+}
+
+static int rk628_hdmirx_phy_power_on(struct rk628 *rk628, int f)
+{
+	int ret;
+	bool rxphy_pwron = false;
+
+	if (rxphy_pwron) {
+		dev_info(rk628->dev, "rxphy already power on, power off!\n");
+		ret = rk628_combrxphy_power_off(rk628);
+		if (ret)
+			dev_info(rk628->dev, "hdmi rxphy power off failed!\n");
+		else
+			rxphy_pwron = false;
+	}
+
+	udelay(1000);
+	if (rxphy_pwron == false) {
+		ret = rk628_combrxphy_power_on(rk628, f);
+		if (ret) {
+			rxphy_pwron = false;
+			dev_info(rk628->dev, "hdmi rxphy power on failed\n");
+		} else {
+			rxphy_pwron = true;
+			dev_info(rk628->dev, "hdmi rxphy power on success\n");
+		}
+	}
+
+	dev_info(rk628->dev, "%s:rxphy_pwron=%d\n", __func__, rxphy_pwron);
+	return ret;
+}
+
+static void rk628_hdmirx_get_timing(struct rk628 *rk628)
+{
+	u32 hact, vact, htotal, vtotal, fps, status;
+	u32 val;
+	u32 modetclk_cnt_hs, modetclk_cnt_vs, hs, vs;
+	u32 hofs_pix, hbp, hfp, vbp, vfp;
+	u32 tmds_clk, tmdsclk_cnt;
+	u64 tmp_data;
+	u32 interlaced;
+	u32 hfrontporch, hsync, hbackporch, vfrontporch, vsync, vbackporch;
+	unsigned long long pixelclock;
+	unsigned long flags = 0;
+	struct rk628_hdmirx *hdmirx = rk628->hdmirx;
+
+	rk628_i2c_read(rk628, HDMI_RX_SCDC_REGS1, &val);
+	status = val;
+
+	rk628_i2c_read(rk628, HDMI_RX_MD_STS, &val);
+	interlaced = val & ILACE_STS ? 1 : 0;
+
+	rk628_i2c_read(rk628, HDMI_RX_MD_HACT_PX, &val);
+	hact = val & 0xffff;
+	rk628_i2c_read(rk628, HDMI_RX_MD_VAL, &val);
+	vact = val & 0xffff;
+	rk628_i2c_read(rk628, HDMI_RX_MD_HT1, &val);
+	htotal = (val >> 16) & 0xffff;
+	rk628_i2c_read(rk628, HDMI_RX_MD_VTL, &val);
+	vtotal = val & 0xffff;
+	rk628_i2c_read(rk628, HDMI_RX_MD_HT1, &val);
+	hofs_pix = val & 0xffff;
+	rk628_i2c_read(rk628, HDMI_RX_MD_VOL, &val);
+	vbp = (val & 0xffff) + 1;
+
+	rk628_i2c_read(rk628, HDMI_RX_HDMI_CKM_RESULT, &val);
+	tmdsclk_cnt = val & 0xffff;
+	tmp_data = tmdsclk_cnt;
+	tmp_data = ((tmp_data * MODETCLK_HZ) + MODETCLK_CNT_NUM / 2);
+	do_div(tmp_data, MODETCLK_CNT_NUM);
+	tmds_clk = tmp_data;
+	if (!(htotal && vtotal)) {
+		dev_info(rk628->dev, "timing err, htotal:%d, vtotal:%d\n", htotal, vtotal);
+		return;
+	}
+	fps = (tmds_clk + (htotal * vtotal) / 2) / (htotal * vtotal);
+
+	rk628_i2c_read(rk628, HDMI_RX_MD_HT0, &val);
+	modetclk_cnt_hs = val & 0xffff;
+	hs = (tmdsclk_cnt * modetclk_cnt_hs + MODETCLK_CNT_NUM / 2) /
+		MODETCLK_CNT_NUM;
+
+	rk628_i2c_read(rk628, HDMI_RX_MD_VSC, &val);
+	modetclk_cnt_vs = val & 0xffff;
+	vs = (tmdsclk_cnt * modetclk_cnt_vs + MODETCLK_CNT_NUM / 2) /
+		MODETCLK_CNT_NUM;
+	vs = (vs + htotal / 2) / htotal;
+
+	rk628_i2c_read(rk628, HDMI_RX_HDMI_STS, &val);
+	if (val & BIT(8))
+		flags |= DRM_MODE_FLAG_PHSYNC;
+	else
+		flags |= DRM_MODE_FLAG_NHSYNC;
+	if (val & BIT(9))
+		flags |= DRM_MODE_FLAG_PVSYNC;
+	else
+		flags |= DRM_MODE_FLAG_NVSYNC;
+
+	if ((hofs_pix < hs) || (htotal < (hact + hofs_pix)) ||
+	    (vtotal < (vact + vs + vbp))) {
+		dev_info(rk628->dev,
+			 "timing err, total:%dx%d, act:%dx%d, hofs:%d, hs:%d, vs:%d, vbp:%d\n",
+			 htotal, vtotal, hact, vact, hofs_pix, hs, vs, vbp);
+		return;
+	}
+	hbp = hofs_pix - hs;
+	hfp = htotal - hact - hofs_pix;
+	vfp = vtotal - vact - vs - vbp;
+
+	dev_info(rk628->dev, "cnt_num:%d, tmds_cnt:%d, hs_cnt:%d, vs_cnt:%d, hofs:%d\n",
+		 MODETCLK_CNT_NUM, tmdsclk_cnt, modetclk_cnt_hs, modetclk_cnt_vs, hofs_pix);
+
+	hfrontporch = hfp;
+	hsync = hs;
+	hbackporch = hbp;
+	vfrontporch = vfp;
+	vsync = vs;
+	vbackporch = vbp;
+	pixelclock = htotal * vtotal * fps;
+
+	if (interlaced == 1) {
+		vsync = vsync + 1;
+		pixelclock /= 2;
+	}
+
+	hdmirx->mode.clock = pixelclock / 1000;
+	hdmirx->mode.hdisplay = hact;
+	hdmirx->mode.hstart = hdmirx->mode.hdisplay + hfrontporch;
+	hdmirx->mode.hend = hdmirx->mode.hstart + hsync;
+	hdmirx->mode.htotal = hdmirx->mode.hend + hbackporch;
+
+	hdmirx->mode.vdisplay = vact;
+	hdmirx->mode.vstart = hdmirx->mode.vdisplay + vfrontporch;
+	hdmirx->mode.vend = hdmirx->mode.vstart + vsync;
+	hdmirx->mode.vtotal = hdmirx->mode.vend + vbackporch;
+	hdmirx->mode.flags = flags;
+
+	dev_info(rk628->dev, "SCDC_REGS1:%#x, act:%dx%d, total:%dx%d, fps:%d, pixclk:%llu\n",
+		 status, hact, vact, htotal, vtotal, fps, pixelclock);
+	dev_info(rk628->dev, "hfp:%d, hs:%d, hbp:%d, vfp:%d, vs:%d, vbp:%d, interlace:%d\n",
+		 hfrontporch, hsync, hbackporch, vfrontporch, vsync, vbackporch, interlaced);
+}
+
+static int rk628_hdmirx_phy_setup(struct rk628 *rk628)
+{
+	u32 i, cnt, val;
+	u32 width, height, frame_width, frame_height, status;
+	struct rk628_display_mode *src_mode;
+	struct rk628_hdmirx *hdmirx = rk628->hdmirx;
+	int f;
+	struct rk628_display_mode *dst_mode;
+
+	/* Bit31 is used to distinguish HDMI cable mode and direct connection
+	 * mode in the rk628_combrxphy driver.
+	 * Bit31: 0 -direct connection mode;
+	 *    1 -cable mode;
+	 * The cable mode is to know the input clock frequency through cdr_mode
+	 * in the rk628_combrxphy driver, and the cable mode supports up to
+	 * 297M, so 297M is passed uniformly here.
+	 */
+	f = (297000 | BIT(31));
+	dst_mode = rk628_display_get_dst_mode(rk628);
+	/*
+	 * force 594m mode to yuv420 format.
+	 * bit30 is used to indicate whether it is yuv420 format.
+	 */
+	if (hdmirx->src_mode_4K_yuv420 && dst_mode->clock == 594000)
+		f |= BIT(30);
+
+	for (i = 0; i < RXPHY_CFG_MAX_TIMES; i++) {
+		rk628_hdmirx_phy_power_on(rk628, f);
+		cnt = 0;
+
+		do {
+			cnt++;
+			udelay(2000);
+			rk628_i2c_read(rk628, HDMI_RX_MD_HACT_PX, &val);
+			width = val & 0xffff;
+			rk628_i2c_read(rk628, HDMI_RX_MD_HT1, &val);
+			frame_width = (val >> 16) & 0xffff;
+
+			rk628_i2c_read(rk628, HDMI_RX_MD_VAL, &val);
+			height = val & 0xffff;
+			rk628_i2c_read(rk628, HDMI_RX_MD_VTL, &val);
+			frame_height = val & 0xffff;
+
+			rk628_i2c_read(rk628, HDMI_RX_SCDC_REGS1, &val);
+			status = val;
+
+			dev_info(rk628->dev,
+				 "%s read wxh:%dx%d, total:%dx%d, SCDC_REGS1:%#x, cnt:%d\n",
+				 __func__, width, height, frame_width,
+				 frame_height, status, cnt);
+
+			if (cnt >= 15)
+				break;
+		} while ((status & 0xfff) != 0xf00);
+
+		if ((status & 0xfff) != 0xf00) {
+			dev_info(rk628->dev, "%s hdmi rxphy lock failed, retry:%d\n",
+				 __func__, i);
+			continue;
+		} else {
+			rk628_hdmirx_get_timing(rk628);
+
+			src_mode = rk628_display_get_src_mode(rk628);
+			src_mode->clock = hdmirx->mode.clock;
+			src_mode->hdisplay = hdmirx->mode.hdisplay;
+			src_mode->hsync_start = hdmirx->mode.hstart;
+			src_mode->hsync_end = hdmirx->mode.hend;
+			src_mode->htotal = hdmirx->mode.htotal;
+
+			src_mode->vdisplay = hdmirx->mode.vdisplay;
+			src_mode->vsync_start = hdmirx->mode.vstart;
+			src_mode->vsync_end = hdmirx->mode.vend;
+			src_mode->vtotal = hdmirx->mode.vtotal;
+			src_mode->flags = hdmirx->mode.flags;
+			if (hdmirx->src_mode_4K_yuv420 && dst_mode->clock == 594000) {
+				rk628_mode_copy(src_mode, dst_mode);
+				src_mode->flags = DRM_MODE_FLAG_PHSYNC|DRM_MODE_FLAG_PVSYNC;
+			}
+
+			break;
+		}
+	}
+
+	if (i == RXPHY_CFG_MAX_TIMES) {
+		hdmirx->phy_lock = false;
+		return -1;
+	}
+	hdmirx->phy_lock = true;
+
+	return 0;
+}
+
+static u32 rk628_hdmirx_get_input_format(struct rk628 *rk628)
+{
+	u32 val, format, avi_pb = 0;
+	u8 i;
+	u8 cnt = 0, max_cnt = 2;
+	struct rk628_hdmirx *hdmirx = rk628->hdmirx;
+
+	rk628_i2c_read(rk628, HDMI_RX_PDEC_ISTS, &val);
+	if (val & AVI_RCV_ISTS) {
+		for (i = 0; i < 100; i++) {
+			rk628_i2c_read(rk628, HDMI_RX_PDEC_AVI_PB, &format);
+			dev_dbg(rk628->dev, "%s PDEC_AVI_PB:%#x\n", __func__, format);
+			if (format && format == avi_pb) {
+				if (++cnt >= max_cnt)
+					break;
+			} else {
+				cnt = 0;
+				avi_pb = format;
+			}
+			msleep(30);
+		}
+		format  = (avi_pb & VIDEO_FORMAT) >> 5;
+		switch (format) {
+		case 0:
+			hdmirx->input_format = BUS_FMT_RGB;
+			break;
+		case 1:
+			hdmirx->input_format = BUS_FMT_YUV422;
+			break;
+		case 2:
+			hdmirx->input_format = BUS_FMT_YUV444;
+			break;
+		case 3:
+			hdmirx->input_format = BUS_FMT_YUV420;
+			break;
+		default:
+			hdmirx->input_format = BUS_FMT_RGB;
+			break;
+		}
+		rk628_i2c_write(rk628, HDMI_RX_PDEC_ICLR, AVI_RCV_ISTS);
+	}
+
+	return hdmirx->input_format;
+}
+
+static int rk628_check_signal(struct rk628 *rk628)
+{
+	u32 hact, vact, val;
+
+	rk628_i2c_read(rk628, HDMI_RX_MD_HACT_PX, &val);
+	hact = val & 0xffff;
+	rk628_i2c_read(rk628, HDMI_RX_MD_VAL, &val);
+	vact = val & 0xffff;
+
+	if (!hact || !vact) {
+		dev_info(rk628->dev, "no signal\n");
+		return 0;
+	}
+
+	return 1;
+}
+
+static bool rk628_hdmirx_status_change(struct rk628 *rk628)
+{
+	u32 hact, vact, val;
+	struct rk628_hdmirx *hdmirx = rk628->hdmirx;
+
+	rk628_i2c_read(rk628, HDMI_RX_MD_HACT_PX, &val);
+	hact = val & 0xffff;
+	rk628_i2c_read(rk628, HDMI_RX_MD_VAL, &val);
+	vact = val & 0xffff;
+	if (!rk628->plugin_det_gpio && !hact && !vact)
+		return true;
+
+	if (hact != hdmirx->mode.hdisplay || vact != hdmirx->mode.vdisplay) {
+		dev_info(rk628->dev, "new: hdisplay=%d, vdisplay=%d\n", hact, vact);
+		dev_info(rk628->dev, "old: hdisplay=%d, vdisplay=%d\n",
+			 hdmirx->mode.hdisplay, hdmirx->mode.vdisplay);
+		return true;
+	}
+
+	rk628_hdmirx_get_input_format(rk628);
+	if (hdmirx->input_format != rk628_get_input_bus_format(rk628))
+		return true;
+
+	return false;
+}
+
+static int rk628_hdmirx_init(struct rk628 *rk628)
+{
+	struct rk628_hdmirx *hdmirx;
+	struct device *dev = rk628->dev;
+
+	hdmirx = devm_kzalloc(rk628->dev, sizeof(*hdmirx), GFP_KERNEL);
+	if (!hdmirx)
+		return -ENOMEM;
+	rk628->hdmirx = hdmirx;
+
+	hdmirx->hpd_output_inverted = of_property_read_bool(dev->of_node,
+		"hpd-output-inverted");
+
+	hdmirx->src_mode_4K_yuv420 = of_property_read_bool(dev->of_node,
+		"src-mode-4k-yuv420");
+
+	/* HDMIRX IOMUX */
+	rk628_i2c_write(rk628, GRF_GPIO1AB_SEL_CON, HIWORD_UPDATE(0x7, 10, 8));
+	//i2s pinctrl
+	rk628_i2c_write(rk628, GRF_GPIO0AB_SEL_CON, 0x155c155c);
+
+	/* if GVI and HDMITX OUT, HDMIRX missing signal */
+	rk628_i2c_update_bits(rk628, GRF_SYSTEM_CON0,
+			      SW_OUTPUT_MODE_MASK, SW_OUTPUT_MODE(OUTPUT_MODE_RGB));
+	rk628_i2c_update_bits(rk628, GRF_SYSTEM_CON0,
+			      SW_INPUT_MODE_MASK, SW_INPUT_MODE(INPUT_MODE_HDMI));
+	rk628_hdmirx_set_edid(rk628);
+	/* clear avi rcv interrupt */
+	rk628_i2c_write(rk628, HDMI_RX_PDEC_ICLR, AVI_RCV_ISTS);
+
+	dev_info(rk628->dev, "hdmirx driver version: %s\n", DRIVER_VERSION);
+
+	return 0;
+}
+
+void rk628_hdmirx_enable_interrupts(struct rk628 *rk628, bool en)
+{
+	u32 pdec_ien, md_ien;
+	u32 md_mask = 0;
+
+	md_mask = VACT_LIN_ENSET | HACT_PIX_ENSET | HS_CLK_ENSET;
+	dev_dbg(rk628->dev, "%s: %sable\n", __func__, en ? "en" : "dis");
+	/* clr irq */
+	rk628_i2c_write(rk628, HDMI_RX_MD_ICLR, md_mask);
+	if (en) {
+		rk628_i2c_write(rk628, HDMI_RX_MD_IEN_SET, md_mask);
+	} else {
+		rk628_i2c_write(rk628, HDMI_RX_MD_IEN_CLR, md_mask);
+		rk628_i2c_write(rk628, HDMI_RX_AUD_FIFO_IEN_CLR, 0x1f);
+	}
+	usleep_range(5000, 5000);
+	rk628_i2c_read(rk628, HDMI_RX_MD_IEN, &md_ien);
+	rk628_i2c_read(rk628, HDMI_RX_PDEC_IEN, &pdec_ien);
+	dev_dbg(rk628->dev, "%s MD_IEN:%#x, PDEC_IEN:%#x\n", __func__, md_ien, pdec_ien);
+}
+
+int rk628_hdmirx_enable(struct rk628 *rk628)
+{
+	int ret;
+	struct rk628_hdmirx *hdmirx;
+
+	if (!rk628->hdmirx) {
+		ret = rk628_hdmirx_init(rk628);
+		if (ret < 0)
+			return HDMIRX_PLUGOUT;
+	}
+
+	hdmirx = rk628->hdmirx;
+	if (tx_5v_power_present(rk628)) {
+		hdmirx->plugin = true;
+		rk628_hdmirx_enable_edid(rk628);
+		rk628_hdmirx_ctrl_enable(rk628);
+		rk628_hdmirx_phy_setup(rk628);
+		rk628_hdmirx_get_input_format(rk628);
+		rk628_set_input_bus_format(rk628, hdmirx->input_format);
+		dev_info(rk628->dev, "hdmirx plug in\n");
+		dev_info(rk628->dev, "input: %d, output: %d\n", hdmirx->input_format,
+			 rk628_get_output_bus_format(rk628));
+		if (!rk628_check_signal(rk628))
+			return HDMIRX_PLUGIN | HDMIRX_NOSIGNAL;
+
+		rk628_hdmirx_video_unmute(rk628, 1);
+		return HDMIRX_PLUGIN;
+	}
+
+	hdmirx->plugin = false;
+	rk628_hdmirx_disable_edid(rk628);
+	rk628_i2c_update_bits(rk628, GRF_SYSTEM_CON0, SW_I2S_DATA_OEN_MASK, SW_I2S_DATA_OEN(1));
+
+	return HDMIRX_PLUGOUT;
+}
+
+void rk628_hdmirx_disable(struct rk628 *rk628)
+{
+	int ret;
+	struct rk628_hdmirx *hdmirx;
+
+	if (!rk628->hdmirx) {
+		ret = rk628_hdmirx_init(rk628);
+		if (ret < 0)
+			return;
+	}
+
+	hdmirx = rk628->hdmirx;
+	if (!tx_5v_power_present(rk628)) {
+		hdmirx->plugin = false;
+		rk628_hdmirx_disable_edid(rk628);
+		rk628_i2c_update_bits(rk628, GRF_SYSTEM_CON0, SW_I2S_DATA_OEN_MASK,
+				      SW_I2S_DATA_OEN(1));
+		dev_info(rk628->dev, "hdmirx plug out\n");
+	}
+}
+
+int rk628_hdmirx_detect(struct rk628 *rk628)
+{
+	int ret = 0;
+	struct rk628_hdmirx *hdmirx;
+
+	if (!rk628->hdmirx) {
+		ret = rk628_hdmirx_init(rk628);
+		if (ret < 0 || !rk628->hdmirx)
+			return HDMIRX_PLUGOUT;
+	}
+	hdmirx = rk628->hdmirx;
+
+	if (tx_5v_power_present(rk628)) {
+		ret |= HDMIRX_PLUGIN;
+		if (!hdmirx->plugin)
+			ret |= HDMIRX_CHANGED;
+		if (rk628_hdmirx_status_change(rk628))
+			ret |= HDMIRX_CHANGED;
+		if (!hdmirx->phy_lock)
+			ret |= HDMIRX_NOLOCK;
+		hdmirx->plugin = true;
+	} else {
+		ret |= HDMIRX_PLUGOUT;
+		if (hdmirx->plugin)
+			ret |= HDMIRX_CHANGED;
+		hdmirx->plugin = false;
+	}
+
+	return ret;
+}
diff --git a/kernel/drivers/misc/rk628/rk628_hdmirx.h b/kernel/drivers/misc/rk628/rk628_hdmirx.h
new file mode 100644
index 0000000..cb7407a
--- /dev/null
+++ b/kernel/drivers/misc/rk628/rk628_hdmirx.h
@@ -0,0 +1,633 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2021 Rockchip Electronics Co. Ltd.
+ *
+ * Author: Chen Shunqing <csq@rock-chips.com>
+ */
+
+#ifndef HDMIRX_H
+#define HDMIRX_H
+
+#include "rk628.h"
+
+#define HDMIRX_REG(x)		((x) + 0x30000)
+
+/* --------- EDID and HDCP KEY ------- */
+#define EDID_BASE				0x000a0000
+#define HDCP_KEY_BASE				0x000a8000
+#define KEY_MAX_REGISTER			0x000a8490
+
+#define HDMI_RX_HDMI_SETUP_CTRL			HDMIRX_REG(0x0000)
+#define HOT_PLUG_DETECT_MASK			BIT(0)
+#define HOT_PLUG_DETECT(x)			UPDATE(x, 0, 0)
+#define HDMI_RX_HDMI_OVR_CTRL			HDMIRX_REG(0x0004)
+#define HDMI_RX_HDMI_TIMER_CTRL			HDMIRX_REG(0x0008)
+#define HDMI_RX_HDMI_RES_OVR			HDMIRX_REG(0x0010)
+#define HDMI_RX_HDMI_RES_STS			HDMIRX_REG(0x0014)
+#define HDMI_RX_HDMI_PLL_CTRL			HDMIRX_REG(0x0018)
+#define HDMI_RX_HDMI_PLL_FRQSET1		HDMIRX_REG(0x001c)
+#define HDMI_RX_HDMI_PLL_FRQSET2		HDMIRX_REG(0x0020)
+#define HDMI_RX_HDMI_PLL_PAR1			HDMIRX_REG(0x0024)
+#define HDMI_RX_HDMI_PLL_PAR2			HDMIRX_REG(0x0028)
+#define HDMI_RX_HDMI_PLL_PAR3			HDMIRX_REG(0x002c)
+#define HDMI_RX_HDMI_PLL_LCK_STS		HDMIRX_REG(0x0030)
+#define HDMI_RX_HDMI_CLK_CTRL			HDMIRX_REG(0x0034)
+#define HDMI_RX_HDMI_PCB_CTRL			HDMIRX_REG(0x0038)
+#define SEL_PIXCLKSRC_MASK			GENMASK(19, 18)
+#define SEL_PIXCLKSRC(x)			UPDATE(x, 19, 18)
+#define HDMI_RX_HDMI_PHS_CTR			HDMIRX_REG(0x0040)
+#define HDMI_RX_HDMI_PHS_USED			HDMIRX_REG(0x0044)
+#define HDMI_RX_HDMI_MISC_CTRL			HDMIRX_REG(0x0048)
+#define HDMI_RX_HDMI_EQOFF_CTRL			HDMIRX_REG(0x004c)
+#define HDMI_RX_HDMI_EQGAIN_CTRL		HDMIRX_REG(0x0050)
+#define HDMI_RX_HDMI_EQCAL_STS			HDMIRX_REG(0x0054)
+#define HDMI_RX_HDMI_EQRESULT			HDMIRX_REG(0x0058)
+#define HDMI_RX_HDMI_EQ_MEAS_CTRL		HDMIRX_REG(0x005c)
+#define HDMI_RX_HDMI_WR_CFG			HDMIRX_REG(0x0060)
+#define HDMI_RX_HDMI_CTRL			HDMIRX_REG(0x0064)
+#define HDMI_RX_HDMI_MODE_RECOVER		HDMIRX_REG(0x0080)
+#define PREAMBLE_CNT_LIMIT_MASK			GENMASK(31, 27)
+#define PREAMBLE_CNT_LIMIT(x)			UPDATE(x, 31, 27)
+#define OESSCTL3_THR_MASK			GENMASK(20, 19)
+#define OESSCTL3_THR(x)				UPDATE(x, 20, 19)
+#define SPIKE_FILTER_EN_MASK			BIT(18)
+#define SPIKE_FILTER_EN(x)			UPDATE(x, 18, 18)
+#define DVI_MODE_HYST_MASK			GENMASK(17, 13)
+#define DVI_MODE_HYST(x)			UPDATE(x, 17, 13)
+#define HDMI_MODE_HYST_MASK			GENMASK(12, 8)
+#define HDMI_MODE_HYST(x)			UPDATE(x, 12, 8)
+#define HDMI_MODE_MASK				GENMASK(7, 6)
+#define HDMI_MODE(x)				UPDATE(x, 7, 6)
+#define GB_DET_MASK				GENMASK(5, 4)
+#define GB_DET(x)				UPDATE(x, 5, 4)
+#define EESS_OESS_MASK				GENMASK(3, 2)
+#define EESS_OESS(x)				UPDATE(x, 3, 2)
+#define SEL_CTL01_MASK				GENMASK(1, 0)
+#define SEL_CTL01(x)				UPDATE(x, 1, 0)
+#define HDMI_RX_HDMI_ERROR_PROTECT		HDMIRX_REG(0x0084)
+#define RG_BLOCK_OFF_MASK			BIT(20)
+#define RG_BLOCK_OFF(x)				UPDATE(x, 20, 20)
+#define BLOCK_OFF_MASK				BIT(19)
+#define BLOCK_OFF(x)				UPDATE(x, 19, 19)
+#define VALID_MODE_MASK				GENMASK(18, 16)
+#define VALID_MODE(x)				UPDATE(x, 18, 16)
+#define CTRL_FILT_SEN_MASK			GENMASK(13, 12)
+#define CTRL_FILT_SEN(x)			UPDATE(x, 13, 12)
+#define VS_FILT_SENS_MASK			GENMASK(11, 10)
+#define VS_FILT_SENS(x)				UPDATE(x, 11, 10)
+#define HS_FILT_SENS_MASK			GENMASK(9, 8)
+#define HS_FILT_SENS(x)				UPDATE(x, 9, 8)
+#define DE_MEASURE_MODE_MASK			GENMASK(7, 6)
+#define DE_MEASURE_MODE(x)			UPDATE(x, 7, 6)
+#define DE_REGEN_MASK				BIT(5)
+#define DE_REGEN(x)				UPDATE(x, 5, 5)
+#define DE_FILTER_SENS_MASK			GENMASK(4, 3)
+#define DE_FILTER_SENS(x)			UPDATE(x, 4, 3)
+#define HDMI_RX_HDMI_ERD_STS			HDMIRX_REG(0x0088)
+#define HDMI_RX_HDMI_SYNC_CTRL			HDMIRX_REG(0x0090)
+#define VS_POL_ADJ_MODE_MASK			GENMASK(4, 3)
+#define VS_POL_ADJ_MODE(x)			UPDATE(x, 4, 3)
+#define HS_POL_ADJ_MODE_MASK			GENMASK(2, 1)
+#define HS_POL_ADJ_MODE(x)			UPDATE(x, 2, 1)
+#define HDMI_RX_HDMI_CKM_EVLTM			HDMIRX_REG(0x0094)
+#define LOCK_HYST_MASK				GENMASK(21, 20)
+#define LOCK_HYST(x)				UPDATE(x, 21, 20)
+#define CLK_HYST_MASK				GENMASK(18, 16)
+#define CLK_HYST(x)				UPDATE(x, 18, 16)
+#define EVAL_TIME_MASK				GENMASK(15, 4)
+#define EVAL_TIME(x)				UPDATE(x, 15, 4)
+#define HDMI_RX_HDMI_CKM_F			HDMIRX_REG(0x0098)
+#define HDMIRX_MAXFREQ_MASK			GENMASK(31, 16)
+#define HDMIRX_MAXFREQ(x)			UPDATE(x, 31, 16)
+#define MINFREQ_MASK				GENMASK(15, 0)
+#define MINFREQ(x)				UPDATE(x, 15, 0)
+#define HDMI_RX_HDMI_CKM_RESULT			HDMIRX_REG(0x009c)
+#define HDMI_RX_HDMI_PVO_CONFIG			HDMIRX_REG(0x00a0)
+#define HDMI_RX_HDMI_RESMPL_CTRL		HDMIRX_REG(0x00a4)
+#define MAN_VID_DEREPEAT_MASK			GENMASK(4, 1)
+#define MAN_VID_DEREPEAT(x)			UPDATE(x, 4, 1)
+#define AUTO_DEREPEAT_MASK			BIT(0)
+#define AUTO_DEREPEAT(x)			UPDATE(x, 0, 0)
+#define HDMI_RX_HDMI_DCM_CTRL			HDMIRX_REG(0x00a8)
+#define DCM_DEFAULT_PHASE_MASK			BIT(18)
+#define DCM_DEFAULT_PHASE(x)			UPDATE(x, 18, 18)
+#define DCM_COLOUR_DEPTH_SEL_MASK		BIT(12)
+#define DCM_COLOUR_DEPTH_SEL(x)			UPDATE(x, 12, 12)
+#define DCM_COLOUR_DEPTH_MASK			GENMASK(11, 8)
+#define DCM_COLOUR_DEPTH(x)			UPDATE(x, 11, 8)
+#define DCM_GCP_ZERO_FIELDS_MASK		GENMASK(5, 2)
+#define DCM_GCP_ZERO_FIELDS(x)			UPDATE(x, 5, 2)
+#define HDMI_RX_HDMI_VM_CFG_CH_0_1		HDMIRX_REG(0x00b0)
+#define HDMI_RX_HDMI_VM_CFG_CH2			HDMIRX_REG(0x00b4)
+#define HDMI_RX_HDMI_SPARE			HDMIRX_REG(0x00b8)
+#define HDMI_RX_HDMI_STS			HDMIRX_REG(0x00bc)
+#define HDMI_RX_HDCP_CTRL			HDMIRX_REG(0x00c0)
+#define HDCP_ENABLE_MASK			BIT(24)
+#define HDCP_ENABLE(x)				UPDATE(x, 24, 24)
+#define FREEZE_HDCP_FSM_MASK			BIT(21)
+#define FREEZE_HDCP_FSM(x)			UPDATE(x, 21, 21)
+#define FREEZE_HDCP_STATE_MASK			GENMASK(20, 15)
+#define FREEZE_HDCP_STATE(x)			UPDATE(x, 20, 15)
+#define HDCP_CTL_MASK				GENMASK(9, 8)
+#define HDCP_CTL(x)				UPDATE(x, 9, 8)
+#define HDCP_RI_RATE_MASK			GENMASK(7, 6)
+#define HDCP_RI_RATE(x)				UPDATE(x, 7, 6)
+#define KEY_DECRYPT_ENABLE_MASK			BIT(1)
+#define KEY_DECRYPT_ENABLE(x)			UPDATE(x, 1, 1)
+#define HDCP_ENC_EN_MASK			BIT(0)
+#define HDCP_ENC_EN(x)				UPDATE(x, 0, 0)
+#define HDMI_RX_HDCP_SETTINGS			HDMIRX_REG(0x00c4)
+#define HDMI_RESERVED(x)			UPDATE(x, 13, 13)
+#define HDMI_RESERVED_MASK			BIT(13)
+#define FAST_I2C(x)				UPDATE(x, 12, 12)
+#define FAST_I2C_MASK				BIT(12)
+#define ONE_DOT_ONE(x)				UPDATE(x, 9, 9)
+#define ONE_DOT_ONE_MASK			BIT(9)
+#define FAST_REAUTH(x)				UPDATE(x, 8, 8)
+#define FAST_REAUTH_MASK			BIT(8)
+#define HDMI_RX_HDCP_SEED			HDMIRX_REG(0x00c8)
+#define HDMI_RX_HDCP_BKSV1			HDMIRX_REG(0x00cc)
+#define HDMI_RX_HDCP_BKSV0			HDMIRX_REG(0x00d0)
+#define HDMI_RX_HDCP_KIDX			HDMIRX_REG(0x00d4)
+#define HDMI_RX_HDCP_KEY1			HDMIRX_REG(0x00d8)
+#define HDMI_RX_HDCP_KEY0			HDMIRX_REG(0x00dc)
+#define HDMI_RX_HDCP_DBG			HDMIRX_REG(0x00e0)
+#define HDMI_RX_HDCP_AKSV1			HDMIRX_REG(0x00e4)
+#define HDMI_RX_HDCP_AKSV0			HDMIRX_REG(0x00e8)
+#define HDMI_RX_HDCP_AN1			HDMIRX_REG(0x00ec)
+#define HDMI_RX_HDCP_AN0			HDMIRX_REG(0x00f0)
+#define HDMI_RX_HDCP_EESS_WOO			HDMIRX_REG(0x00f4)
+#define HDMI_RX_HDCP_I2C_TIMEOUT		HDMIRX_REG(0x00f8)
+#define HDMI_RX_HDCP_STS			HDMIRX_REG(0x00fc)
+#define HDMI_RX_MD_HCTRL1			HDMIRX_REG(0x0140)
+#define HACT_PIX_ITH_MASK			GENMASK(10, 8)
+#define HACT_PIX_ITH(x)				UPDATE(x, 10, 8)
+#define HACT_PIX_SRC_MASK			BIT(5)
+#define HACT_PIX_SRC(x)				UPDATE(x, 5, 5)
+#define HTOT_PIX_SRC_MASK			BIT(4)
+#define HTOT_PIX_SRC(x)				UPDATE(x, 4, 4)
+#define HDMI_RX_MD_HCTRL2			HDMIRX_REG(0x0144)
+#define HS_CLK_ITH_MASK				GENMASK(14, 12)
+#define HS_CLK_ITH(x)				UPDATE(x, 14, 12)
+#define HTOT32_CLK_ITH_MASK			GENMASK(9, 8)
+#define HTOT32_CLK_ITH(x)			UPDATE(x, 9, 8)
+#define VS_ACT_TIME_MASK			BIT(5)
+#define VS_ACT_TIME(x)				UPDATE(x, 5, 5)
+#define HS_ACT_TIME_MASK			GENMASK(4, 3)
+#define HS_ACT_TIME(x)				UPDATE(x, 4, 3)
+#define H_START_POS_MASK			GENMASK(1, 0)
+#define H_START_POS(x)				UPDATE(x, 1, 0)
+#define HDMI_RX_MD_HT0				HDMIRX_REG(0x0148)
+#define HDMI_RX_MD_HT1				HDMIRX_REG(0x014c)
+#define HDMI_RX_MD_HACT_PX			HDMIRX_REG(0x0150)
+#define HDMI_RX_MD_HACT_RSV			HDMIRX_REG(0x0154)
+#define HDMI_RX_MD_VCTRL			HDMIRX_REG(0x0158)
+#define V_OFFS_LIN_MODE_MASK			BIT(4)
+#define V_OFFS_LIN_MODE(x)			UPDATE(x, 4, 4)
+#define V_EDGE_MASK				BIT(1)
+#define V_EDGE(x)				UPDATE(x, 1, 1)
+#define V_MODE_MASK				BIT(0)
+#define V_MODE(x)				UPDATE(x, 0, 0)
+#define HDMI_RX_MD_VSC				HDMIRX_REG(0x015c)
+#define HDMI_RX_MD_VTC				HDMIRX_REG(0x0160)
+#define HDMI_RX_MD_VOL				HDMIRX_REG(0x0164)
+#define HDMI_RX_MD_VAL				HDMIRX_REG(0x0168)
+#define HDMI_RX_MD_VTH				HDMIRX_REG(0x016c)
+#define VOFS_LIN_ITH_MASK			GENMASK(11, 10)
+#define VOFS_LIN_ITH(x)				UPDATE(x, 11, 10)
+#define VACT_LIN_ITH_MASK			GENMASK(9, 8)
+#define VACT_LIN_ITH(x)				UPDATE(x, 9, 8)
+#define VTOT_LIN_ITH_MASK			GENMASK(7, 6)
+#define VTOT_LIN_ITH(x)				UPDATE(x, 7, 6)
+#define VS_CLK_ITH_MASK				GENMASK(5, 3)
+#define VS_CLK_ITH(x)				UPDATE(x, 5, 3)
+#define VTOT_CLK_ITH_MASK			GENMASK(2, 0)
+#define VTOT_CLK_ITH(x)				UPDATE(x, 2, 0)
+#define HDMI_RX_MD_VTL				HDMIRX_REG(0x0170)
+#define HDMI_RX_MD_IL_CTRL			HDMIRX_REG(0x0174)
+#define HDMI_RX_MD_IL_SKEW			HDMIRX_REG(0x0178)
+#define HDMI_RX_MD_IL_POL			HDMIRX_REG(0x017c)
+#define FAFIELDDET_EN_MASK			BIT(2)
+#define FAFIELDDET_EN(x)			UPDATE(x, 2, 2)
+#define FIELD_POL_MODE_MASK			GENMASK(1, 0)
+#define FIELD_POL_MODE(x)			UPDATE(x, 1, 0)
+#define HDMI_RX_MD_STS				HDMIRX_REG(0x0180)
+#define ILACE_STS				BIT(3)
+#define HDMI_RX_AUD_CTRL			HDMIRX_REG(0x0200)
+#define HDMI_RX_AUD_PLL_CTRL			HDMIRX_REG(0x0208)
+#define PLL_LOCK_TOGGLE_DIV_MASK		GENMASK(27, 24)
+#define PLL_LOCK_TOGGLE_DIV(x)			UPDATE(x, 27, 24)
+#define HDMI_RX_AUD_CLK_CTRL			HDMIRX_REG(0x0214)
+#define CTS_N_REF_MASK				BIT(4)
+#define CTS_N_REF(x)				UPDATE(x, 4, 4)
+#define HDMI_RX_AUD_CLK_STS			HDMIRX_REG(0x023c)
+#define HDMI_RX_AUD_FIFO_CTRL			HDMIRX_REG(0x0240)
+#define AFIF_SUBPACKET_DESEL_MASK		GENMASK(27, 24)
+#define AFIF_SUBPACKET_DESEL(x)			UPDATE(x, 27, 24)
+#define AFIF_SUBPACKETS_MASK			BIT(16)
+#define AFIF_SUBPACKETS(x)			UPDATE(x, 16, 16)
+#define MSA_CHANNEL_DESELECT			BIT(24)
+#define HDMI_RX_AUD_FIFO_TH			HDMIRX_REG(0x0244)
+#define AFIF_TH_START_MASK			GENMASK(26, 18)
+#define AFIF_TH_START(x)			UPDATE(x, 26, 18)
+#define AFIF_TH_MAX_MASK			GENMASK(17, 9)
+#define AFIF_TH_MAX(x)				UPDATE(x, 17, 9)
+#define AFIF_TH_MIN_MASK			GENMASK(8, 0)
+#define AFIF_TH_MIN(x)				UPDATE(x, 8, 0)
+#define HDMI_RX_AUD_FIFO_FILL_S			HDMIRX_REG(0x0248)
+#define HDMI_RX_AUD_FIFO_CLR_MM			HDMIRX_REG(0x024c)
+#define HDMI_RX_AUD_FIFO_FILLSTS		HDMIRX_REG(0x0250)
+#define HDMI_RX_AUD_CHEXTR_CTRL			HDMIRX_REG(0x0254)
+#define AUD_LAYOUT_CTRL(x)			UPDATE(x, 1, 0)
+#define HDMI_RX_AUD_MUTE_CTRL			HDMIRX_REG(0x0258)
+#define APPLY_INT_MUTE_MASK			BIT(31)
+#define APPLY_INT_MUTE(x)			UPDATE(x, 31, 31)
+#define APORT_SHDW_CTRL_MASK			GENMASK(22, 21)
+#define APORT_SHDW_CTRL(x)			UPDATE(x, 22, 21)
+#define AUTO_ACLK_MUTE_MASK			GENMASK(20, 19)
+#define AUTO_ACLK_MUTE(x)			UPDATE(x, 20, 19)
+#define AUD_MUTE_SPEED_MASK			GENMASK(16, 10)
+#define AUD_MUTE_SPEED(x)			UPDATE(x, 16, 10)
+#define AUD_AVMUTE_EN_MASK			BIT(7)
+#define AUD_AVMUTE_EN(x)			UPDATE(x, 7, 7)
+#define AUD_MUTE_SEL_MASK			GENMASK(6, 5)
+#define AUD_MUTE_SEL(x)				UPDATE(x, 6, 5)
+#define AUD_MUTE_MODE_MASK			GENMASK(4, 3)
+#define AUD_MUTE_MODE(x)			UPDATE(x, 4, 3)
+#define HDMI_RX_AUD_FIFO_FILLSTS1		HDMIRX_REG(0x025c)
+#define HDMI_RX_AUD_SAO_CTRL			HDMIRX_REG(0x0260)
+#define I2S_LPCM_BPCUV_MASK			BIT(11)
+#define I2S_LPCM_BPCUV(x)			UPDATE(x, 11, 11)
+#define I2S_32_16_MASK				BIT(0)
+#define I2S_32_16(x)				UPDATE(x, 0, 0)
+#define HDMI_RX_AUD_PAO_CTRL			HDMIRX_REG(0x0264)
+#define PAO_RATE_MASK				GENMASK(17, 16)
+#define PAO_RATE(x)				UPDATE(x, 17, 16)
+#define HDMI_RX_AUD_SPARE			HDMIRX_REG(0x0268)
+#define HDMI_RX_AUD_FIFO_STS			HDMIRX_REG(0x027c)
+#define HDMI_RX_AUDPLL_GEN_CTS			HDMIRX_REG(0x0280)
+#define AUDPLL_CTS_MANUAL(x)			UPDATE(x, 19, 0)
+#define HDMI_RX_AUDPLL_GEN_N			HDMIRX_REG(0x0284)
+#define AUDPLL_N_MANUAL(x)			UPDATE(x, 19, 0)
+#define HDMI_RX_AUDPLL_GEN_CTRL_RW1		HDMIRX_REG(0x0288)
+#define HDMI_RX_AUDPLL_GEN_CTRL_RW2		HDMIRX_REG(0x028c)
+#define HDMI_RX_AUDPLL_GEN_CTRL_W1		HDMIRX_REG(0x0298)
+#define HDMI_RX_AUDPLL_GEN_STS_RO1		HDMIRX_REG(0x02a0)
+#define HDMI_RX_AUDPLL_GEN_STS_RO2		HDMIRX_REG(0x02a4)
+#define HDMI_RX_AUDPLL_SC_NDIVCTSTH		HDMIRX_REG(0x02a8)
+#define HDMI_RX_AUDPLL_SC_CTS			HDMIRX_REG(0x02ac)
+#define HDMI_RX_AUDPLL_SC_N			HDMIRX_REG(0x02b0)
+#define HDMI_RX_AUDPLL_SC_CTRL			HDMIRX_REG(0x02b4)
+#define HDMI_RX_AUDPLL_SC_STS1			HDMIRX_REG(0x02b8)
+#define HDMI_RX_AUDPLL_SC_STS2			HDMIRX_REG(0x02bc)
+#define HDMI_RX_SNPS_PHYG3_CTRL			HDMIRX_REG(0x02c0)
+#define PORTSELECT_MASK				GENMASK(3, 2)
+#define PORTSELECT(x)				UPDATE(x, 3, 2)
+#define HDMI_RX_I2CM_PHYG3_SLAVE		HDMIRX_REG(0x02c4)
+#define HDMI_RX_I2CM_PHYG3_ADDRESS		HDMIRX_REG(0x02c8)
+#define HDMI_RX_I2CM_PHYG3_DATAO		HDMIRX_REG(0x02cc)
+#define HDMI_RX_I2CM_PHYG3_DATAI		HDMIRX_REG(0x02d0)
+#define HDMI_RX_I2CM_PHYG3_OPERATION		HDMIRX_REG(0x02d4)
+#define HDMI_RX_I2CM_PHYG3_MODE			HDMIRX_REG(0x02d8)
+#define HDMI_RX_I2CM_PHYG3_SOFTRST		HDMIRX_REG(0x02dc)
+#define HDMI_RX_I2CM_PHYG3_SS_CNTS		HDMIRX_REG(0x02e0)
+#define HDMI_RX_I2CM_PHYG3_FS_HCNT		HDMIRX_REG(0x02e4)
+#define HDMI_RX_JTAG_CONF			HDMIRX_REG(0x02ec)
+#define HDMI_RX_JTAG_TAP_TCLK			HDMIRX_REG(0x02f0)
+#define HDMI_RX_JTAG_TAP_IN			HDMIRX_REG(0x02f4)
+#define HDMI_RX_JTAG_TAP_OUT			HDMIRX_REG(0x02f8)
+#define HDMI_RX_JTAG_ADDR			HDMIRX_REG(0x02fc)
+#define HDMI_RX_PDEC_CTRL			HDMIRX_REG(0x0300)
+#define PFIFO_SCORE_FILTER_EN			BIT(31)
+#define PFIFO_SCORE_HDP_IF			BIT(29)
+#define PFIFO_SCORE_AMP_IF			BIT(28)
+#define PFIFO_SCORE_NTSCVBI_IF			BIT(27)
+#define PFIFO_SCORE_MPEGS_IF			BIT(26)
+#define PFIFO_SCORE_AUD_IF			BIT(25)
+#define PFIFO_SCORE_SPD_IF			BIT(24)
+#define PFIFO_SCORE_AVI_IF			BIT(23)
+#define PFIFO_SCORE_VS_IF			BIT(22)
+#define PFIFO_SCORE_GMTP			BIT(21)
+#define PFIFO_SCORE_ISRC2			BIT(20)
+#define PFIFO_SCORE_ISRC1			BIT(19)
+#define PFIFO_SCORE_ACP				BIT(18)
+#define PFIFO_SCORE_GCP				BIT(17)
+#define PFIFO_SCORE_ACR				BIT(16)
+#define GCP_GLOBAVMUTE				BIT(15)
+#define PD_FIFO_WE				BIT(4)
+#define PDEC_BCH_EN				BIT(0)
+#define HDMI_RX_PDEC_FIFO_CFG			HDMIRX_REG(0x0304)
+#define PD_FIFO_TH_START_MASK			GENMASK(29, 20)
+#define PD_FIFO_TH_START(x)			UPDATE(x, 29, 20)
+#define PD_FIFO_TH_MAX_MASK			GENMASK(19, 10)
+#define PD_FIFO_TH_MAX(x)			UPDATE(x, 19, 10)
+#define PD_FIFO_TH_MIN_MASK			GENMASK(9, 0)
+#define PD_FIFO_TH_MIN(x)			UPDATE(x, 9, 0)
+#define HDMI_RX_PDEC_FIFO_STS			HDMIRX_REG(0x0308)
+#define HDMI_RX_PDEC_FIFO_DATA			HDMIRX_REG(0x030c)
+#define HDMI_RX_PDEC_AUDIODET_CTRL		HDMIRX_REG(0x0310)
+#define AUDIODET_THRESHOLD_MASK			GENMASK(13, 9)
+#define AUDIODET_THRESHOLD(x)			UPDATE(x, 13, 9)
+#define HDMI_RX_PDEC_DBG_ACP			HDMIRX_REG(0x031c)
+#define HDMI_RX_PDEC_DBG_ERR_CORR		HDMIRX_REG(0x0320)
+#define HDMI_RX_PDEC_FIFO_STS1			HDMIRX_REG(0x0324)
+#define HDMI_RX_PDEC_ACRM_CTRL			HDMIRX_REG(0x0330)
+#define DELTACTS_IRQTRIG_MASK			GENMASK(4, 2)
+#define DELTACTS_IRQTRIG(x)			UPDATE(x, 4, 2)
+#define HDMI_RX_PDEC_ACRM_MAX			HDMIRX_REG(0x0334)
+#define HDMI_RX_PDEC_ACRM_MIN			HDMIRX_REG(0x0338)
+#define HDMI_RX_PDEC_ERR_FILTER			HDMIRX_REG(0x033c)
+#define HDMI_RX_PDEC_ASP_CTRL			HDMIRX_REG(0x0340)
+#define HDMI_RX_PDEC_ASP_ERR			HDMIRX_REG(0x0344)
+#define HDMI_RX_PDEC_STS			HDMIRX_REG(0x0360)
+#define HDMI_RX_PDEC_AUD_STS			HDMIRX_REG(0x0364)
+#define HDMI_RX_PDEC_VSI_PAYLOAD0		HDMIRX_REG(0x0368)
+#define HDMI_RX_PDEC_VSI_PAYLOAD1		HDMIRX_REG(0x036c)
+#define HDMI_RX_PDEC_VSI_PAYLOAD2		HDMIRX_REG(0x0370)
+#define HDMI_RX_PDEC_VSI_PAYLOAD3		HDMIRX_REG(0x0374)
+#define HDMI_RX_PDEC_VSI_PAYLOAD4		HDMIRX_REG(0x0378)
+#define HDMI_RX_PDEC_VSI_PAYLOAD5		HDMIRX_REG(0x037c)
+#define HDMI_RX_PDEC_GCP_AVMUTE			HDMIRX_REG(0x0380)
+#define PKTDEC_GCP_CD_MASK			GENMASK(7, 4)
+#define HDMI_RX_PDEC_ACR_CTS			HDMIRX_REG(0x0390)
+#define HDMI_RX_PDEC_ACR_N			HDMIRX_REG(0x0394)
+#define HDMI_RX_PDEC_AVI_HB			HDMIRX_REG(0x03a0)
+#define HDMI_RX_PDEC_AVI_PB			HDMIRX_REG(0x03a4)
+#define VID_IDENT_CODE_VIC7			BIT(31)
+#define VID_IDENT_CODE				GENMASK(30, 24)
+#define VIDEO_FORMAT				GENMASK(6, 5)
+#define HDMI_RX_PDEC_AVI_TBB			HDMIRX_REG(0x03a8)
+#define HDMI_RX_PDEC_AVI_LRB			HDMIRX_REG(0x03ac)
+#define HDMI_RX_PDEC_AIF_CTRL			HDMIRX_REG(0x03c0)
+#define FC_LFE_EXCHG				BIT(18)
+#define HDMI_RX_PDEC_AIF_HB			HDMIRX_REG(0x03c4)
+#define HDMI_RX_PDEC_AIF_PB0			HDMIRX_REG(0x03c8)
+#define HDMI_RX_PDEC_AIF_PB1			HDMIRX_REG(0x03cc)
+#define HDMI_RX_PDEC_GMD_HB			HDMIRX_REG(0x03d0)
+#define HDMI_RX_PDEC_GMD_PB			HDMIRX_REG(0x03d4)
+#define HDMI_RX_PDEC_VSI_ST0			HDMIRX_REG(0x03e0)
+#define HDMI_RX_PDEC_VSI_ST1			HDMIRX_REG(0x03e4)
+#define HDMI_RX_PDEC_VSI_PB0			HDMIRX_REG(0x03e8)
+#define HDMI_RX_PDEC_VSI_PB1			HDMIRX_REG(0x03ec)
+#define HDMI_RX_PDEC_VSI_PB2			HDMIRX_REG(0x03f0)
+#define HDMI_RX_PDEC_VSI_PB3			HDMIRX_REG(0x03f4)
+#define HDMI_RX_PDEC_VSI_PB4			HDMIRX_REG(0x03f8)
+#define HDMI_RX_PDEC_VSI_PB5			HDMIRX_REG(0x03fc)
+#define HDMI_RX_CEAVID_CONFIG			HDMIRX_REG(0x0400)
+#define HDMI_RX_CEAVID_3DCONFIG			HDMIRX_REG(0x0404)
+#define HDMI_RX_CEAVID_HCONFIG_LO		HDMIRX_REG(0x0408)
+#define HDMI_RX_CEAVID_HCONFIG_HI		HDMIRX_REG(0x040c)
+#define HDMI_RX_CEAVID_VCONFIG_LO		HDMIRX_REG(0x0410)
+#define HDMI_RX_CEAVID_VCONFIG_HI		HDMIRX_REG(0x0414)
+#define HDMI_RX_CEAVID_STATUS			HDMIRX_REG(0x0418)
+#define HDMI_RX_PDEC_AMP_HB			HDMIRX_REG(0x0480)
+#define HDMI_RX_PDEC_AMP_PAYLOAD0		HDMIRX_REG(0x0484)
+#define HDMI_RX_PDEC_AMP_PAYLOAD1		HDMIRX_REG(0x0488)
+#define HDMI_RX_PDEC_AMP_PAYLOAD2		HDMIRX_REG(0x048c)
+#define HDMI_RX_PDEC_AMP_PAYLOAD3		HDMIRX_REG(0x0490)
+#define HDMI_RX_PDEC_AMP_PAYLOAD4		HDMIRX_REG(0x0494)
+#define HDMI_RX_PDEC_AMP_PAYLOAD5		HDMIRX_REG(0x0498)
+#define HDMI_RX_PDEC_AMP_PAYLOAD6		HDMIRX_REG(0x049c)
+#define HDMI_RX_PDEC_NTSCVBI_HB			HDMIRX_REG(0x04a0)
+#define HDMI_RX_PDEC_NTSCVBI_PAYLOAD0		HDMIRX_REG(0x04a4)
+#define HDMI_RX_PDEC_NTSCVBI_PAYLOAD1		HDMIRX_REG(0x04a8)
+#define HDMI_RX_PDEC_NTSCVBI_PAYLOAD2		HDMIRX_REG(0x04ac)
+#define HDMI_RX_PDEC_NTSCVBI_PAYLOAD3		HDMIRX_REG(0x04b0)
+#define HDMI_RX_PDEC_NTSCVBI_PAYLOAD4		HDMIRX_REG(0x04b4)
+#define HDMI_RX_PDEC_NTSCVBI_PAYLOAD5		HDMIRX_REG(0x04b8)
+#define HDMI_RX_PDEC_NTSCVBI_PAYLOAD6		HDMIRX_REG(0x04bc)
+#define HDMI_RX_PDEC_DRM_HB			HDMIRX_REG(0x04c0)
+#define HDMI_RX_PDEC_DRM_PAYLOAD0		HDMIRX_REG(0x04c4)
+#define HDMI_RX_PDEC_DRM_PAYLOAD1		HDMIRX_REG(0x04c8)
+#define HDMI_RX_PDEC_DRM_PAYLOAD2		HDMIRX_REG(0x04cc)
+#define HDMI_RX_PDEC_DRM_PAYLOAD3		HDMIRX_REG(0x04d0)
+#define HDMI_RX_PDEC_DRM_PAYLOAD4		HDMIRX_REG(0x04d4)
+#define HDMI_RX_PDEC_DRM_PAYLOAD5		HDMIRX_REG(0x04d8)
+#define HDMI_RX_PDEC_DRM_PAYLOAD6		HDMIRX_REG(0x04dc)
+#define HDMI_RX_MHLMODE_CTRL			HDMIRX_REG(0x0500)
+#define HDMI_RX_CDSENSE_STATUS			HDMIRX_REG(0x0504)
+#define HDMI_RX_DESERFIFO_CTRL			HDMIRX_REG(0x0508)
+#define HDMI_RX_DESER_INTTRSHCTRL		HDMIRX_REG(0x050c)
+#define HDMI_RX_DESER_INTCNTCTRL		HDMIRX_REG(0x0510)
+#define HDMI_RX_DESER_INTCNT			HDMIRX_REG(0x0514)
+#define HDMI_RX_HDCP_RPT_CTRL			HDMIRX_REG(0x0600)
+#define HDMI_RX_HDCP_RPT_BSTATUS		HDMIRX_REG(0x0604)
+#define HDMI_RX_HDCP_RPT_KSVFIFO_CTRL		HDMIRX_REG(0x0608)
+#define HDMI_RX_HDCP_RPT_KSVFIFO1		HDMIRX_REG(0x060c)
+#define HDMI_RX_HDCP_RPT_KSVFIFO0		HDMIRX_REG(0x0610)
+#define HDMI_RX_HDMI20_CONTROL			HDMIRX_REG(0x0800)
+#define HDMI_RX_SCDC_I2CCONFIG			HDMIRX_REG(0x0804)
+#define I2CSPIKESUPPR_MASK			GENMASK(25, 24)
+#define I2CSPIKESUPPR(x)			UPDATE(x, 25, 24)
+#define HDMI_RX_SCDC_CONFIG			HDMIRX_REG(0x0808)
+#define HDMI_RX_CHLOCK_CONFIG			HDMIRX_REG(0x080c)
+#define CHLOCKMAXER_MASK			GENMASK(29, 20)
+#define CHLOCKMAXER(x)				UPDATE(x, 29, 20)
+#define MILISECTIMERLIMIT_MASK			GENMASK(15, 0)
+#define MILISECTIMERLIMIT(x)			UPDATE(x, 15, 0)
+#define HDMI_RX_HDCP22_CONTROL			HDMIRX_REG(0x081c)
+#define HDMI_RX_SCDC_REGS0			HDMIRX_REG(0x0820)
+#define HDMI_RX_SCDC_REGS1			HDMIRX_REG(0x0824)
+#define HDMI_RX_SCDC_REGS2			HDMIRX_REG(0x0828)
+#define HDMI_RX_SCDC_REGS3			HDMIRX_REG(0x082c)
+#define HDMI_RX_SCDC_MANSPEC0			HDMIRX_REG(0x0840)
+#define HDMI_RX_SCDC_MANSPEC1			HDMIRX_REG(0x0844)
+#define HDMI_RX_SCDC_MANSPEC2			HDMIRX_REG(0x0848)
+#define HDMI_RX_SCDC_MANSPEC3			HDMIRX_REG(0x084c)
+#define HDMI_RX_SCDC_MANSPEC4			HDMIRX_REG(0x0850)
+#define HDMI_RX_SCDC_WRDATA0			HDMIRX_REG(0x0860)
+#define MANUFACTUREROUI_MASK			GENMASK(31, 8)
+#define MANUFACTUREROUI(x)			UPDATE(x, 31, 8)
+#define SINKVERSION_MASK			GENMASK(7, 0)
+#define SINKVERSION(x)				UPDATE(x, 7, 0)
+#define HDMI_RX_SCDC_WRDATA1			HDMIRX_REG(0x0864)
+#define HDMI_RX_SCDC_WRDATA2			HDMIRX_REG(0x0868)
+#define HDMI_RX_SCDC_WRDATA3			HDMIRX_REG(0x086c)
+#define HDMI_RX_SCDC_WRDATA4			HDMIRX_REG(0x0870)
+#define HDMI_RX_SCDC_WRDATA5			HDMIRX_REG(0x0874)
+#define HDMI_RX_SCDC_WRDATA6			HDMIRX_REG(0x0878)
+#define HDMI_RX_SCDC_WRDATA7			HDMIRX_REG(0x087c)
+#define HDMI_RX_HDMI20_STATUS			HDMIRX_REG(0x08e0)
+#define HDMI_RX_HDCP2_ESM_GLOBAL_GPIO_IN	HDMIRX_REG(0x08e8)
+#define HDMI_RX_HDCP2_ESM_GLOBAL_GPIO_OUT	HDMIRX_REG(0x08ec)
+#define HDMI_RX_HDCP2_ESM_P0_GPIO_IN		HDMIRX_REG(0x08f0)
+#define HDMI_RX_HDCP2_ESM_P0_GPIO_OUT		HDMIRX_REG(0x08f4)
+#define HDMI_RX_HDCP22_STATUS			HDMIRX_REG(0x08fc)
+#define HDMI_RX_HDMI2_IEN_CLR			HDMIRX_REG(0x0f60)
+#define HDMI_RX_HDMI2_IEN_SET			HDMIRX_REG(0x0f64)
+#define HDMI_RX_HDMI2_ISTS			HDMIRX_REG(0x0f68)
+#define HDMI_RX_HDMI2_IEN			HDMIRX_REG(0x0f6c)
+#define HDMI_RX_HDMI2_ICLR                      HDMIRX_REG(0x0f70)
+#define HDMI_RX_HDMI2_ISET			HDMIRX_REG(0x0f74)
+#define HDMI_RX_PDEC_IEN_CLR			HDMIRX_REG(0x0f78)
+#define HDMI_RX_PDEC_IEN_SET			HDMIRX_REG(0x0f7c)
+#define ACR_N_CHG_IEN				BIT(23)
+#define ACR_CTS_CHG_IEN				BIT(22)
+#define GCP_AV_MUTE_CHG_ENSET			BIT(21)
+#define AIF_RCV_ENSET				BIT(19)
+#define AVI_RCV_ENSET				BIT(18)
+#define GCP_RCV_ENSET				BIT(16)
+#define AMP_RCV_ENSET				BIT(14)
+#define HDMI_RX_PDEC_ISTS			HDMIRX_REG(0x0f80)
+#define AVI_RCV_ISTS				BIT(18)
+#define HDMI_RX_PDEC_IEN			HDMIRX_REG(0x0f84)
+#define HDMI_RX_PDEC_ICLR			HDMIRX_REG(0x0f88)
+#define AVI_RCV_ICLR				BIT(18)
+#define HDMI_RX_PDEC_ISET			HDMIRX_REG(0x0f8c)
+#define HDMI_RX_AUD_CEC_IEN_CLR			HDMIRX_REG(0x0f90)
+#define HDMI_RX_AUD_CEC_IEN_SET			HDMIRX_REG(0x0f94)
+#define HDMI_RX_AUD_CEC_ISTS			HDMIRX_REG(0x0f98)
+#define HDMI_RX_AUD_CEC_IEN			HDMIRX_REG(0x0f9c)
+#define HDMI_RX_AUD_CEC_ICLR			HDMIRX_REG(0x0fa0)
+#define HDMI_RX_AUD_CEC_ISET			HDMIRX_REG(0x0fa4)
+#define HDMI_RX_AUD_FIFO_IEN_CLR		HDMIRX_REG(0x0fa8)
+#define HDMI_RX_AUD_FIFO_IEN_SET		HDMIRX_REG(0x0fac)
+#define HDMI_RX_AUD_FIFO_ISTS			HDMIRX_REG(0x0fb0)
+#define HDMI_RX_AUD_FIFO_IEN			HDMIRX_REG(0x0fb4)
+#define HDMI_RX_AUD_FIFO_ICLR			HDMIRX_REG(0x0fb8)
+#define HDMI_RX_AUD_FIFO_ISET			HDMIRX_REG(0x0fbc)
+#define HDMI_RX_MD_IEN_CLR			HDMIRX_REG(0x0fc0)
+#define HDMI_RX_MD_IEN_SET			HDMIRX_REG(0x0fc4)
+#define VACT_LIN_ENSET				BIT(9)
+#define HACT_PIX_ENSET				BIT(6)
+#define HS_CLK_ENSET				BIT(5)
+#define DE_ACTIVITY_ENSET			BIT(2)
+#define VS_ACT_ENSET				BIT(1)
+#define HS_ACT_ENSET				BIT(0)
+#define HDMI_RX_MD_ISTS				HDMIRX_REG(0x0fc8)
+#define HDMI_RX_MD_IEN				HDMIRX_REG(0x0fcc)
+#define HDMI_RX_MD_ICLR				HDMIRX_REG(0x0fd0)
+#define HDMI_RX_MD_ISET				HDMIRX_REG(0x0fd4)
+#define HDMI_RX_HDMI_IEN_CLR			HDMIRX_REG(0x0fd8)
+#define HDMI_RX_HDMI_IEN_SET			HDMIRX_REG(0x0fdc)
+#define HDCP_DKSET_DONE_ENCLR_MASK		BIT(31)
+#define HDCP_DKSET_DONE_ENCLR(x)		UPDATE(x, 31, 31)
+#define HDMI_RX_HDMI_ISTS			HDMIRX_REG(0x0fe0)
+#define HDMI_RX_HDMI_IEN			HDMIRX_REG(0x0fe4)
+#define HDMI_RX_HDMI_ICLR			HDMIRX_REG(0x0fe8)
+#define HDMI_RX_HDMI_ISET			HDMIRX_REG(0x0fec)
+#define HDMI_RX_DMI_SW_RST			HDMIRX_REG(0x0ff0)
+#define HDMI_RX_DMI_DISABLE_IF			HDMIRX_REG(0x0ff4)
+#define MAIN_ENABLE				BIT(0)
+#define MODET_ENABLE				BIT(1)
+#define HDMI_ENABLE				BIT(2)
+#define BUS_ENABLE				BIT(3)
+#define AUD_ENABLE				BIT(4)
+#define CEC_ENABLE				BIT(5)
+#define PIXEL_ENABLE				BIT(6)
+#define VID_ENABLE_MASK				BIT(7)
+#define VID_ENABLE(x)				UPDATE(x, 7, 7)
+#define TMDS_ENABLE_MASK			BIT(16)
+#define TMDS_ENABLE(x)				UPDATE(x, 16, 16)
+#define HDMI_RX_DMI_MODULE_ID_EXT		HDMIRX_REG(0x0ff8)
+#define HDMI_RX_DMI_MODULE_ID			HDMIRX_REG(0x0ffc)
+#define HDMI_RX_CEC_CTRL			HDMIRX_REG(0x1f00)
+#define HDMI_RX_CEC_MASK			HDMIRX_REG(0x1f08)
+#define HDMI_RX_CEC_ADDR_L			HDMIRX_REG(0x1f14)
+#define HDMI_RX_CEC_ADDR_H			HDMIRX_REG(0x1f18)
+#define HDMI_RX_CEC_TX_CNT			HDMIRX_REG(0x1f1c)
+#define HDMI_RX_CEC_RX_CNT			HDMIRX_REG(0x1f20)
+#define HDMI_RX_CEC_TX_DATA_0			HDMIRX_REG(0x1f40)
+#define HDMI_RX_CEC_TX_DATA_1			HDMIRX_REG(0x1f44)
+#define HDMI_RX_CEC_TX_DATA_2			HDMIRX_REG(0x1f48)
+#define HDMI_RX_CEC_TX_DATA_3			HDMIRX_REG(0x1f4c)
+#define HDMI_RX_CEC_TX_DATA_4			HDMIRX_REG(0x1f50)
+#define HDMI_RX_CEC_TX_DATA_5			HDMIRX_REG(0x1f54)
+#define HDMI_RX_CEC_TX_DATA_6			HDMIRX_REG(0x1f58)
+#define HDMI_RX_CEC_TX_DATA_7			HDMIRX_REG(0x1f5c)
+#define HDMI_RX_CEC_TX_DATA_8			HDMIRX_REG(0x1f60)
+#define HDMI_RX_CEC_TX_DATA_9			HDMIRX_REG(0x1f64)
+#define HDMI_RX_CEC_TX_DATA_10			HDMIRX_REG(0x1f68)
+#define HDMI_RX_CEC_TX_DATA_11			HDMIRX_REG(0x1f6c)
+#define HDMI_RX_CEC_TX_DATA_12			HDMIRX_REG(0x1f70)
+#define HDMI_RX_CEC_TX_DATA_13			HDMIRX_REG(0x1f74)
+#define HDMI_RX_CEC_TX_DATA_14			HDMIRX_REG(0x1f78)
+#define HDMI_RX_CEC_TX_DATA_15			HDMIRX_REG(0x1f7c)
+#define HDMI_RX_CEC_RX_DATA_0			HDMIRX_REG(0x1f80)
+#define HDMI_RX_CEC_RX_DATA_1			HDMIRX_REG(0x1f84)
+#define HDMI_RX_CEC_RX_DATA_2			HDMIRX_REG(0x1f88)
+#define HDMI_RX_CEC_RX_DATA_3			HDMIRX_REG(0x1f8c)
+#define HDMI_RX_CEC_RX_DATA_4			HDMIRX_REG(0x1f90)
+#define HDMI_RX_CEC_RX_DATA_5			HDMIRX_REG(0x1f94)
+#define HDMI_RX_CEC_RX_DATA_6			HDMIRX_REG(0x1f98)
+#define HDMI_RX_CEC_RX_DATA_7			HDMIRX_REG(0x1f9c)
+#define HDMI_RX_CEC_RX_DATA_8			HDMIRX_REG(0x1fa0)
+#define HDMI_RX_CEC_RX_DATA_9			HDMIRX_REG(0x1fa4)
+#define HDMI_RX_CEC_RX_DATA_10			HDMIRX_REG(0x1fa8)
+#define HDMI_RX_CEC_RX_DATA_11			HDMIRX_REG(0x1fac)
+#define HDMI_RX_CEC_RX_DATA_12			HDMIRX_REG(0x1fb0)
+#define HDMI_RX_CEC_RX_DATA_13			HDMIRX_REG(0x1fb4)
+#define HDMI_RX_CEC_RX_DATA_14			HDMIRX_REG(0x1fb8)
+#define HDMI_RX_CEC_RX_DATA_15			HDMIRX_REG(0x1fbc)
+#define HDMI_RX_CEC_LOCK			HDMIRX_REG(0x1fc0)
+#define HDMI_RX_CEC_WAKEUPCTRL			HDMIRX_REG(0x1fc4)
+#define HDMI_RX_CBUSSWRESETREQ			HDMIRX_REG(0x3000)
+#define HDMI_RX_CBUSENABLEIF			HDMIRX_REG(0x3004)
+#define HDMI_RX_CB_LOCKONCLOCK_STS		HDMIRX_REG(0x3010)
+#define HDMI_RX_CB_LOCKONCLOCKCLR		HDMIRX_REG(0x3014)
+#define HDMI_RX_CBUSIOCTRL			HDMIRX_REG(0x3020)
+#define HDMI_RX_DD_CTRL				HDMIRX_REG(0x3040)
+#define HDMI_RX_DD_OP_CTRL			HDMIRX_REG(0x3044)
+#define HDMI_RX_DD_STS				HDMIRX_REG(0x3048)
+#define HDMI_RX_DD_BYPASS_EN			HDMIRX_REG(0x304c)
+#define HDMI_RX_DD_BYPASS_CTRL			HDMIRX_REG(0x3050)
+#define HDMI_RX_DD_BYPASS_CBUS			HDMIRX_REG(0x3054)
+#define HDMI_RX_LL_TXPCKFIFO			HDMIRX_REG(0x3080)
+#define HDMI_RX_LL_RXPCKFIFO_RD_CLR		HDMIRX_REG(0x3084)
+#define HDMI_RX_LL_RXPCKFIFO_A			HDMIRX_REG(0x3088)
+#define HDMI_RX_LL_RXPCKFIFO_B			HDMIRX_REG(0x308c)
+#define HDMI_RX_LL_TXPCKCTRL_0			HDMIRX_REG(0x3090)
+#define HDMI_RX_LL_TXPCKCTRL_1			HDMIRX_REG(0x3094)
+#define HDMI_RX_LL_PCKFIFO_STS			HDMIRX_REG(0x309c)
+#define HDMI_RX_LL_RXPCKCTRL_0			HDMIRX_REG(0x30a0)
+#define HDMI_RX_LL_RXPCKCTRL_1			HDMIRX_REG(0x30a4)
+#define HDMI_RX_LL_INTTRSHLDCTRL		HDMIRX_REG(0x30b0)
+#define HDMI_RX_LL_INTCNTCTRL			HDMIRX_REG(0x30b4)
+#define HDMI_RX_LL_INTCNT_0			HDMIRX_REG(0x30b8)
+#define HDMI_RX_LL_INTCNT_1			HDMIRX_REG(0x30bc)
+#define HDMI_RX_CBHDCP_OPCTRL			HDMIRX_REG(0x3100)
+#define HDMI_RX_CBHDCP_WDATA_0			HDMIRX_REG(0x3104)
+#define HDMI_RX_CBHDCP_WDATA_1			HDMIRX_REG(0x3108)
+#define HDMI_RX_CBHDCP_RDATA_0			HDMIRX_REG(0x310c)
+#define HDMI_RX_CBHDCP_RDATA_1			HDMIRX_REG(0x3110)
+#define HDMI_RX_CBHDCP_STATUS			HDMIRX_REG(0x3114)
+#define HDMI_RX_CBHDCP_DDC_REPORT		HDMIRX_REG(0x3118)
+#define HDMI_RX_ISTAT_CB_DD			HDMIRX_REG(0x3200)
+#define HDMI_RX_IMASK_CB_DD			HDMIRX_REG(0x3204)
+#define HDMI_RX_IFORCE_CB_DD			HDMIRX_REG(0x3208)
+#define HDMI_RX_ICLEAR_CB_DD			HDMIRX_REG(0x320c)
+#define HDMI_RX_IMUTE_CB_DD			HDMIRX_REG(0x3210)
+#define HDMI_RX_ISTAT_CB_LL			HDMIRX_REG(0x3220)
+#define HDMI_RX_IMASK_CB_LL			HDMIRX_REG(0x3224)
+#define HDMI_RX_IFORCE_CB_LL			HDMIRX_REG(0x3228)
+#define HDMI_RX_ICLEAR_CB_LL			HDMIRX_REG(0x322c)
+#define HDMI_RX_IMUTE_CB_LL			HDMIRX_REG(0x3230)
+#define HDMI_RX_ISTAT_CB_HDCP			HDMIRX_REG(0x3240)
+#define HDMI_RX_IMASK_CB_HDCP			HDMIRX_REG(0x3244)
+#define HDMI_RX_IFORCE_CB_HDCP			HDMIRX_REG(0x3248)
+#define HDMI_RX_ICLEAR_CB_HDCP			HDMIRX_REG(0x324c)
+#define HDMI_RX_IMUTE_CB_HDCP			HDMIRX_REG(0x3250)
+#define HDMI_RX_ISTAT_CB_MCTRL			HDMIRX_REG(0x3260)
+#define HDMI_RX_IMASK_CB_MCTRL			HDMIRX_REG(0x3264)
+#define HDMI_RX_IFORCE_CB_MCTRL			HDMIRX_REG(0x3268)
+#define HDMI_RX_ICLEAR_CB_MCTRL			HDMIRX_REG(0x326c)
+#define HDMI_RX_IMUTE_CB_MCTRL			HDMIRX_REG(0x3270)
+#define HDMI_RX_IMASTER_MUTE_CB			HDMIRX_REG(0x32e0)
+#define HDMI_RX_IVECTOR_INDEX_CB		HDMIRX_REG(0x32e4)
+#define HDMI_RX_MAX_REGISTER			HDMI_RX_IVECTOR_INDEX_CB
+
+#define EDID_NUM_BLOCKS_MAX			2
+#define EDID_BLOCK_SIZE				128
+
+#define HDMIRX_PLUGIN				BIT(0)
+#define HDMIRX_PLUGOUT				BIT(1)
+#define HDMIRX_CHANGED				BIT(2)
+#define HDMIRX_NOSIGNAL				BIT(3)
+#define HDMIRX_NOLOCK				BIT(4)
+
+void rk628_hdmirx_enable_interrupts(struct rk628 *rk628, bool en);
+int rk628_hdmirx_enable(struct rk628 *rk628);
+void rk628_hdmirx_disable(struct rk628 *rk628);
+int rk628_hdmirx_detect(struct rk628 *rk628);
+
+#endif
diff --git a/kernel/drivers/misc/rk628/rk628_hdmitx.c b/kernel/drivers/misc/rk628/rk628_hdmitx.c
new file mode 100644
index 0000000..eaa1c2e
--- /dev/null
+++ b/kernel/drivers/misc/rk628/rk628_hdmitx.c
@@ -0,0 +1,1198 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2020 Rockchip Electronics Co. Ltd.
+ *
+ * Author: Chen Shunqing <csq@rock-chips.com>
+ */
+
+#include "rk628.h"
+#include <linux/irq.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/extcon.h>
+#include <linux/extcon-provider.h>
+#include <linux/hdmi.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+#ifdef CONFIG_SWITCH
+#include <linux/switch.h>
+#endif
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_of.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_edid.h>
+#include <sound/hdmi-codec.h>
+
+#include "../../gpu/drm/rockchip/rockchip_drm_drv.h"
+#include "rk628_config.h"
+#include "rk628_hdmitx.h"
+#include "rk628_post_process.h"
+
+#include <linux/extcon.h>
+#include <linux/extcon-provider.h>
+
+
+struct audio_info {
+	int sample_rate;
+	int channels;
+	int sample_width;
+};
+
+struct hdmi_data_info {
+	int vic;
+	bool sink_is_hdmi;
+	bool sink_has_audio;
+	unsigned int enc_in_format;
+	unsigned int enc_out_format;
+	unsigned int colorimetry;
+};
+
+struct rk628_hdmi_i2c {
+	struct i2c_adapter adap;
+	u8 ddc_addr;
+	u8 segment_addr;
+	/* i2c lock */
+	struct mutex lock;
+};
+
+struct rk628_hdmi_phy_config {
+	unsigned long mpixelclock;
+	u8 pre_emphasis;	/* pre-emphasis value */
+	u8 vlev_ctr;		/* voltage level control */
+};
+
+struct rk628_hdmi {
+	struct rk628 *rk628;
+	struct device *dev;
+	int irq;
+
+	struct drm_bridge bridge;
+	struct drm_connector connector;
+
+	struct rk628_hdmi_i2c *i2c;
+	struct i2c_adapter *ddc;
+
+	unsigned int tmds_rate;
+
+	struct platform_device *audio_pdev;
+	bool audio_enable;
+
+	struct hdmi_data_info	hdmi_data;
+	struct drm_display_mode previous_mode;
+
+	struct rockchip_drm_sub_dev sub_dev;
+	struct extcon_dev *extcon;
+};
+
+static const unsigned int rk628_hdmi_cable[] = {
+	EXTCON_DISP_HDMI,
+	EXTCON_NONE,
+};
+
+enum {
+	CSC_ITU601_16_235_TO_RGB_0_255_8BIT,
+	CSC_ITU601_0_255_TO_RGB_0_255_8BIT,
+	CSC_ITU709_16_235_TO_RGB_0_255_8BIT,
+	CSC_RGB_0_255_TO_ITU601_16_235_8BIT,
+	CSC_RGB_0_255_TO_ITU709_16_235_8BIT,
+	CSC_RGB_0_255_TO_RGB_16_235_8BIT,
+};
+
+static const char coeff_csc[][24] = {
+	/*
+	 * YUV2RGB:601 SD mode(Y[16:235], UV[16:240], RGB[0:255]):
+	 *   R = 1.164*Y + 1.596*V - 204
+	 *   G = 1.164*Y - 0.391*U - 0.813*V + 154
+	 *   B = 1.164*Y + 2.018*U - 258
+	 */
+	{
+		0x04, 0xa7, 0x00, 0x00, 0x06, 0x62, 0x02, 0xcc,
+		0x04, 0xa7, 0x11, 0x90, 0x13, 0x40, 0x00, 0x9a,
+		0x04, 0xa7, 0x08, 0x12, 0x00, 0x00, 0x03, 0x02
+	},
+	/*
+	 * YUV2RGB:601 SD mode(YUV[0:255],RGB[0:255]):
+	 *   R = Y + 1.402*V - 248
+	 *   G = Y - 0.344*U - 0.714*V + 135
+	 *   B = Y + 1.772*U - 227
+	 */
+	{
+		0x04, 0x00, 0x00, 0x00, 0x05, 0x9b, 0x02, 0xf8,
+		0x04, 0x00, 0x11, 0x60, 0x12, 0xdb, 0x00, 0x87,
+		0x04, 0x00, 0x07, 0x16, 0x00, 0x00, 0x02, 0xe3
+	},
+	/*
+	 * YUV2RGB:709 HD mode(Y[16:235],UV[16:240],RGB[0:255]):
+	 *   R = 1.164*Y + 1.793*V - 248
+	 *   G = 1.164*Y - 0.213*U - 0.534*V + 77
+	 *   B = 1.164*Y + 2.115*U - 289
+	 */
+	{
+		0x04, 0xa7, 0x00, 0x00, 0x07, 0x2c, 0x02, 0xf8,
+		0x04, 0xa7, 0x10, 0xda, 0x12, 0x22, 0x00, 0x4d,
+		0x04, 0xa7, 0x08, 0x74, 0x00, 0x00, 0x03, 0x21
+	},
+
+	/*
+	 * RGB2YUV:601 SD mode:
+	 *   Cb = -0.291G - 0.148R + 0.439B + 128
+	 *   Y  = 0.504G  + 0.257R + 0.098B + 16
+	 *   Cr = -0.368G + 0.439R - 0.071B + 128
+	 */
+	{
+		0x11, 0x5f, 0x01, 0x82, 0x10, 0x23, 0x00, 0x80,
+		0x02, 0x1c, 0x00, 0xa1, 0x00, 0x36, 0x00, 0x1e,
+		0x11, 0x29, 0x10, 0x59, 0x01, 0x82, 0x00, 0x80
+	},
+	/*
+	 * RGB2YUV:709 HD mode:
+	 *   Cb = - 0.338G - 0.101R + 0.439B + 128
+	 *   Y  = 0.614G   + 0.183R + 0.062B + 16
+	 *   Cr = - 0.399G + 0.439R - 0.040B + 128
+	 */
+	{
+		0x11, 0x98, 0x01, 0xc1, 0x10, 0x28, 0x00, 0x80,
+		0x02, 0x74, 0x00, 0xbb, 0x00, 0x3f, 0x00, 0x10,
+		0x11, 0x5a, 0x10, 0x67, 0x01, 0xc1, 0x00, 0x80
+	},
+	/*
+	 * RGB[0:255]2RGB[16:235]:
+	 *   R' = R x (235-16)/255 + 16;
+	 *   G' = G x (235-16)/255 + 16;
+	 *   B' = B x (235-16)/255 + 16;
+	 */
+	{
+		0x00, 0x00, 0x03, 0x6F, 0x00, 0x00, 0x00, 0x10,
+		0x03, 0x6F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,
+		0x00, 0x00, 0x00, 0x00, 0x03, 0x6F, 0x00, 0x10
+	},
+};
+
+static inline struct rk628_hdmi *bridge_to_hdmi(struct drm_bridge *b)
+{
+	return container_of(b, struct rk628_hdmi, bridge);
+}
+
+static inline struct rk628_hdmi *connector_to_hdmi(struct drm_connector *c)
+{
+	return container_of(c, struct rk628_hdmi, connector);
+}
+
+static u32 hdmi_readb(struct rk628_hdmi *hdmi, u32 reg)
+{
+	u32 val;
+
+	rk628_i2c_read(hdmi->rk628, reg, &val);
+
+	return val;
+}
+
+static void hdmi_writeb(struct rk628_hdmi *hdmi, u32 reg, u32 val)
+{
+	rk628_i2c_write(hdmi->rk628, reg, val);
+}
+
+static void hdmi_modb(struct rk628_hdmi *hdmi, u32 offset,
+			     u32 msk, u32 val)
+{
+	u8 temp = hdmi_readb(hdmi, offset) & ~msk;
+
+	temp |= val & msk;
+	hdmi_writeb(hdmi, offset, temp);
+}
+
+static void rk628_hdmi_i2c_init(struct rk628_hdmi *hdmi)
+{
+	int ddc_bus_freq;
+
+	ddc_bus_freq = (hdmi->tmds_rate >> 2) / HDMI_SCL_RATE;
+
+	hdmi_writeb(hdmi, DDC_BUS_FREQ_L, ddc_bus_freq & 0xFF);
+	hdmi_writeb(hdmi, DDC_BUS_FREQ_H, (ddc_bus_freq >> 8) & 0xFF);
+
+	/* Clear the EDID interrupt flag and mute the interrupt */
+	hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, 0);
+	hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, INT_EDID_READY);
+}
+
+static void rk628_hdmi_sys_power(struct rk628_hdmi *hdmi, bool enable)
+{
+	if (enable)
+		hdmi_modb(hdmi, HDMI_SYS_CTRL, POWER_MASK, PWR_OFF(0));
+	else
+		hdmi_modb(hdmi, HDMI_SYS_CTRL, POWER_MASK, PWR_OFF(1));
+}
+
+static struct rk628_hdmi_phy_config rk628_hdmi_phy_config[] = {
+	/* pixelclk pre-emp vlev */
+	{ 74250000,  0x3f, 0x88 },
+	{ 165000000, 0x3f, 0x88 },
+	{ ~0UL,	     0x00, 0x00 }
+};
+
+static void rk628_hdmi_set_pwr_mode(struct rk628_hdmi *hdmi, int mode)
+{
+	const struct rk628_hdmi_phy_config *phy_config =
+						rk628_hdmi_phy_config;
+
+	switch (mode) {
+	case NORMAL:
+		rk628_hdmi_sys_power(hdmi, false);
+		for (; phy_config->mpixelclock != ~0UL; phy_config++)
+			if (hdmi->tmds_rate <= phy_config->mpixelclock)
+				break;
+		if (!phy_config->mpixelclock)
+			return;
+		hdmi_writeb(hdmi, HDMI_PHY_PRE_EMPHASIS,
+			    phy_config->pre_emphasis);
+		hdmi_writeb(hdmi, HDMI_PHY_DRIVER, phy_config->vlev_ctr);
+
+		hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x15);
+		hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x14);
+		hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x10);
+		hdmi_writeb(hdmi, HDMI_PHY_CHG_PWR, 0x0f);
+		hdmi_writeb(hdmi, HDMI_PHY_SYNC, 0x00);
+		hdmi_writeb(hdmi, HDMI_PHY_SYNC, 0x01);
+
+		rk628_hdmi_sys_power(hdmi, true);
+		break;
+
+	case LOWER_PWR:
+		rk628_hdmi_sys_power(hdmi, false);
+		hdmi_writeb(hdmi, HDMI_PHY_DRIVER, 0x00);
+		hdmi_writeb(hdmi, HDMI_PHY_PRE_EMPHASIS, 0x00);
+		hdmi_writeb(hdmi, HDMI_PHY_CHG_PWR, 0x00);
+		hdmi_writeb(hdmi, HDMI_PHY_SYS_CTL, 0x15);
+		break;
+
+	default:
+		dev_err(hdmi->dev, "Unknown power mode %d\n", mode);
+	}
+}
+
+static void rk628_hdmi_reset(struct rk628_hdmi *hdmi)
+{
+	u32 val;
+	u32 msk;
+
+	hdmi_modb(hdmi, HDMI_SYS_CTRL, RST_DIGITAL_MASK, NOT_RST_DIGITAL(1));
+	usleep_range(100, 110);
+
+	hdmi_modb(hdmi, HDMI_SYS_CTRL, RST_ANALOG_MASK, NOT_RST_ANALOG(1));
+	usleep_range(100, 110);
+
+	msk = REG_CLK_INV_MASK | REG_CLK_SOURCE_MASK | POWER_MASK |
+	      INT_POL_MASK;
+	val = REG_CLK_INV(1) | REG_CLK_SOURCE(1) | PWR_OFF(0) | INT_POL(1);
+	hdmi_modb(hdmi, HDMI_SYS_CTRL, msk, val);
+
+	rk628_hdmi_set_pwr_mode(hdmi, NORMAL);
+}
+
+static int rk628_hdmi_upload_frame(struct rk628_hdmi *hdmi, int setup_rc,
+				   union hdmi_infoframe *frame, u32 frame_index,
+				   u32 mask, u32 disable, u32 enable)
+{
+	if (mask)
+		hdmi_modb(hdmi, HDMI_PACKET_SEND_AUTO, mask, disable);
+
+	hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_BUF_INDEX, frame_index);
+
+	if (setup_rc >= 0) {
+		u8 packed_frame[HDMI_MAXIMUM_INFO_FRAME_SIZE];
+		ssize_t rc, i;
+
+		rc = hdmi_infoframe_pack(frame, packed_frame,
+					 sizeof(packed_frame));
+		if (rc < 0)
+			return rc;
+
+		for (i = 0; i < rc; i++)
+			hdmi_writeb(hdmi, HDMI_CONTROL_PACKET_ADDR + (i * 4),
+				    packed_frame[i]);
+
+		if (mask)
+			hdmi_modb(hdmi, HDMI_PACKET_SEND_AUTO, mask, enable);
+	}
+
+	return setup_rc;
+}
+
+static int rk628_hdmi_config_video_vsi(struct rk628_hdmi *hdmi,
+				       struct drm_display_mode *mode)
+{
+	union hdmi_infoframe frame;
+	int rc;
+
+	rc = drm_hdmi_vendor_infoframe_from_display_mode(&frame.vendor.hdmi,
+							 &hdmi->connector,
+							 mode);
+
+	return rk628_hdmi_upload_frame(hdmi, rc, &frame,
+				       INFOFRAME_VSI, PACKET_VSI_EN_MASK,
+				       PACKET_VSI_EN(0), PACKET_VSI_EN(1));
+}
+
+static int rk628_hdmi_config_video_avi(struct rk628_hdmi *hdmi,
+				       struct drm_display_mode *mode)
+{
+	union hdmi_infoframe frame;
+	int rc;
+
+	rc = drm_hdmi_avi_infoframe_from_display_mode(&frame.avi,
+						      &hdmi->connector, mode);
+
+	if (hdmi->hdmi_data.enc_out_format == HDMI_COLORSPACE_YUV444)
+		frame.avi.colorspace = HDMI_COLORSPACE_YUV444;
+	else if (hdmi->hdmi_data.enc_out_format == HDMI_COLORSPACE_YUV422)
+		frame.avi.colorspace = HDMI_COLORSPACE_YUV422;
+	else
+		frame.avi.colorspace = HDMI_COLORSPACE_RGB;
+
+	if (frame.avi.colorspace != HDMI_COLORSPACE_RGB)
+		frame.avi.colorimetry = hdmi->hdmi_data.colorimetry;
+
+	frame.avi.scan_mode = HDMI_SCAN_MODE_NONE;
+
+	return rk628_hdmi_upload_frame(hdmi, rc, &frame,
+				       INFOFRAME_AVI, 0, 0, 0);
+}
+
+static int rk628_hdmi_config_audio_aai(struct rk628_hdmi *hdmi,
+				       struct audio_info *audio)
+{
+	struct hdmi_audio_infoframe *faudio;
+	union hdmi_infoframe frame;
+	int rc;
+
+	rc = hdmi_audio_infoframe_init(&frame.audio);
+	faudio = (struct hdmi_audio_infoframe *)&frame;
+
+	faudio->channels = audio->channels;
+
+	return rk628_hdmi_upload_frame(hdmi, rc, &frame,
+				       INFOFRAME_AAI, 0, 0, 0);
+}
+
+static int rk628_hdmi_config_video_csc(struct rk628_hdmi *hdmi)
+{
+	struct hdmi_data_info *data = &hdmi->hdmi_data;
+	int c0_c2_change = 0;
+	int csc_enable = 0;
+	int csc_mode = 0;
+	int auto_csc = 0;
+	int value;
+	int i;
+	int out_fmt;
+
+	/* Input video mode is SDR RGB24bit, data enable signal from external */
+	hdmi_writeb(hdmi, HDMI_VIDEO_CONTROL1, DE_SOURCE(1) |
+		    VIDEO_INPUT_SDR_RGB444);
+
+	if (hdmi->hdmi_data.enc_out_format == HDMI_COLORSPACE_YUV444)
+		out_fmt = VIDEO_OUTPUT_YCBCR444;
+	else
+		out_fmt = VIDEO_OUTPUT_RRGB444;
+	/* Input color hardcode to RGB, and output color hardcode to RGB888 */
+	value = VIDEO_INPUT_8BITS | out_fmt | VIDEO_INPUT_CSP(0);
+	hdmi_writeb(hdmi, HDMI_VIDEO_CONTROL2, value);
+
+	if (data->enc_in_format == data->enc_out_format) {
+		if ((data->enc_in_format == HDMI_COLORSPACE_RGB) ||
+		    (data->enc_in_format >= HDMI_COLORSPACE_YUV444)) {
+			value = SOF_DISABLE(1) | COLOR_DEPTH_NOT_INDICATED(1);
+			hdmi_writeb(hdmi, HDMI_VIDEO_CONTROL3, value);
+
+			hdmi_modb(hdmi, HDMI_VIDEO_CONTROL,
+				  VIDEO_AUTO_CSC_MASK | VIDEO_C0_C2_SWAP_MASK,
+				  VIDEO_AUTO_CSC(AUTO_CSC_DISABLE) |
+				  VIDEO_C0_C2_SWAP(C0_C2_CHANGE_DISABLE));
+			return 0;
+		}
+	}
+
+	if (data->colorimetry == HDMI_COLORIMETRY_ITU_601) {
+		if ((data->enc_in_format == HDMI_COLORSPACE_RGB) &&
+		    (data->enc_out_format == HDMI_COLORSPACE_YUV444)) {
+			csc_mode = CSC_RGB_0_255_TO_ITU601_16_235_8BIT;
+			auto_csc = AUTO_CSC_DISABLE;
+			c0_c2_change = C0_C2_CHANGE_DISABLE;
+			csc_enable = CSC_ENABLE(1);
+		} else if ((data->enc_in_format == HDMI_COLORSPACE_YUV444) &&
+			   (data->enc_out_format == HDMI_COLORSPACE_RGB)) {
+			csc_mode = CSC_ITU601_16_235_TO_RGB_0_255_8BIT;
+			auto_csc = AUTO_CSC_ENABLE;
+			c0_c2_change = C0_C2_CHANGE_DISABLE;
+			csc_enable = CSC_ENABLE(0);
+		}
+	} else {
+		if ((data->enc_in_format == HDMI_COLORSPACE_RGB) &&
+		    (data->enc_out_format == HDMI_COLORSPACE_YUV444)) {
+			csc_mode = CSC_RGB_0_255_TO_ITU709_16_235_8BIT;
+			auto_csc = AUTO_CSC_DISABLE;
+			c0_c2_change = C0_C2_CHANGE_DISABLE;
+			csc_enable = CSC_ENABLE(1);
+		} else if ((data->enc_in_format == HDMI_COLORSPACE_YUV444) &&
+			   (data->enc_out_format == HDMI_COLORSPACE_RGB)) {
+			csc_mode = CSC_ITU709_16_235_TO_RGB_0_255_8BIT;
+			auto_csc = AUTO_CSC_ENABLE;
+			c0_c2_change = C0_C2_CHANGE_DISABLE;
+			csc_enable = CSC_ENABLE(0);
+		}
+	}
+
+	for (i = 0; i < 24; i++)
+		hdmi_writeb(hdmi, HDMI_VIDEO_CSC_COEF + (i * 4),
+			    coeff_csc[csc_mode][i]);
+
+	value = SOF_DISABLE(1) | csc_enable | COLOR_DEPTH_NOT_INDICATED(1);
+	hdmi_writeb(hdmi, HDMI_VIDEO_CONTROL3, value);
+	hdmi_modb(hdmi, HDMI_VIDEO_CONTROL,
+		  VIDEO_AUTO_CSC_MASK | VIDEO_C0_C2_SWAP_MASK,
+		  VIDEO_AUTO_CSC(auto_csc) | VIDEO_C0_C2_SWAP(c0_c2_change));
+
+	return 0;
+}
+
+static int rk628_hdmi_config_video_timing(struct rk628_hdmi *hdmi,
+					  struct drm_display_mode *mode)
+{
+	int value;
+
+	/* Set detail external video timing polarity and interlace mode */
+	value = EXTERANL_VIDEO(1);
+	value |= mode->flags & DRM_MODE_FLAG_PHSYNC ?
+		 HSYNC_POLARITY(1) : HSYNC_POLARITY(0);
+	value |= mode->flags & DRM_MODE_FLAG_PVSYNC ?
+		 VSYNC_POLARITY(1) : VSYNC_POLARITY(0);
+	value |= mode->flags & DRM_MODE_FLAG_INTERLACE ?
+		 INETLACE(1) : INETLACE(0);
+	hdmi_writeb(hdmi, HDMI_VIDEO_TIMING_CTL, value);
+
+	/* Set detail external video timing */
+	value = mode->htotal;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HTOTAL_L, value & 0xFF);
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HTOTAL_H, (value >> 8) & 0xFF);
+
+	value = mode->htotal - mode->hdisplay;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HBLANK_L, value & 0xFF);
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HBLANK_H, (value >> 8) & 0xFF);
+
+	value = mode->htotal - mode->hsync_start;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDELAY_L, value & 0xFF);
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDELAY_H, (value >> 8) & 0xFF);
+
+	value = mode->hsync_end - mode->hsync_start;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDURATION_L, value & 0xFF);
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_HDURATION_H, (value >> 8) & 0xFF);
+
+	value = mode->vtotal;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VTOTAL_L, value & 0xFF);
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VTOTAL_H, (value >> 8) & 0xFF);
+
+	value = mode->vtotal - mode->vdisplay;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VBLANK, value & 0xFF);
+
+	value = mode->vtotal - mode->vsync_start;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VDELAY, value & 0xFF);
+
+	value = mode->vsync_end - mode->vsync_start;
+	hdmi_writeb(hdmi, HDMI_VIDEO_EXT_VDURATION, value & 0xFF);
+
+	hdmi_writeb(hdmi, HDMI_PHY_PRE_DIV_RATIO, 0x1e);
+	hdmi_writeb(hdmi, PHY_FEEDBACK_DIV_RATIO_LOW, 0x2c);
+	hdmi_writeb(hdmi, PHY_FEEDBACK_DIV_RATIO_HIGH, 0x01);
+
+	return 0;
+}
+
+static int rk628_hdmi_setup(struct rk628_hdmi *hdmi,
+			    struct drm_display_mode *mode)
+{
+	hdmi->hdmi_data.vic = drm_match_cea_mode(mode);
+
+	hdmi->hdmi_data.enc_in_format = HDMI_COLORSPACE_RGB;
+	hdmi->hdmi_data.enc_out_format = HDMI_COLORSPACE_RGB;
+
+	if ((hdmi->hdmi_data.vic == 6) || (hdmi->hdmi_data.vic == 7) ||
+	    (hdmi->hdmi_data.vic == 21) || (hdmi->hdmi_data.vic == 22) ||
+	    (hdmi->hdmi_data.vic == 2) || (hdmi->hdmi_data.vic == 3) ||
+	    (hdmi->hdmi_data.vic == 17) || (hdmi->hdmi_data.vic == 18))
+		hdmi->hdmi_data.colorimetry = HDMI_COLORIMETRY_ITU_601;
+	else
+		hdmi->hdmi_data.colorimetry = HDMI_COLORIMETRY_ITU_709;
+
+	/* Mute video and audio output */
+	hdmi_modb(hdmi, HDMI_AV_MUTE, AUDIO_MUTE_MASK | VIDEO_BLACK_MASK,
+		  AUDIO_MUTE(1) | VIDEO_MUTE(1));
+
+	/* Set HDMI Mode */
+	hdmi_writeb(hdmi, HDMI_HDCP_CTRL,
+		    HDMI_DVI(hdmi->hdmi_data.sink_is_hdmi));
+
+	rk628_hdmi_config_video_timing(hdmi, mode);
+
+	rk628_hdmi_config_video_csc(hdmi);
+
+	if (hdmi->hdmi_data.sink_is_hdmi) {
+		rk628_hdmi_config_video_avi(hdmi, mode);
+		rk628_hdmi_config_video_vsi(hdmi, mode);
+	}
+
+	/*
+	 * When IP controller have configured to an accurate video
+	 * timing, then the TMDS clock source would be switched to
+	 * DCLK_LCDC, so we need to init the TMDS rate to mode pixel
+	 * clock rate, and reconfigure the DDC clock.
+	 */
+	hdmi->tmds_rate = mode->clock * 1000;
+	rk628_hdmi_i2c_init(hdmi);
+
+	/* Unmute video and audio output */
+	hdmi_modb(hdmi, HDMI_AV_MUTE, VIDEO_BLACK_MASK, VIDEO_MUTE(0));
+	if (hdmi->audio_enable)
+		hdmi_modb(hdmi, HDMI_AV_MUTE, AUDIO_MUTE_MASK, AUDIO_MUTE(0));
+
+	return 0;
+}
+
+static enum drm_connector_status
+rk628_hdmi_connector_detect(struct drm_connector *connector, bool force)
+{
+	struct rk628_hdmi *hdmi = connector_to_hdmi(connector);
+	int status;
+
+	status = hdmi_readb(hdmi, HDMI_STATUS) & HOTPLUG_STATUS;
+
+	if (status)
+		extcon_set_state_sync(hdmi->extcon, EXTCON_DISP_HDMI, true);
+	else
+		extcon_set_state_sync(hdmi->extcon, EXTCON_DISP_HDMI, false);
+
+	return status ? connector_status_connected :
+			connector_status_disconnected;
+}
+
+static int rk628_hdmi_connector_get_modes(struct drm_connector *connector)
+{
+	struct rk628_hdmi *hdmi = connector_to_hdmi(connector);
+	struct drm_display_info *info = &connector->display_info;
+	struct edid *edid = NULL;
+	int ret = 0;
+
+	if (!hdmi->ddc)
+		return 0;
+
+	if ((hdmi_readb(hdmi, HDMI_STATUS) & HOTPLUG_STATUS))
+		edid = drm_get_edid(connector, hdmi->ddc);
+
+	if (edid) {
+		hdmi->hdmi_data.sink_is_hdmi = drm_detect_hdmi_monitor(edid);
+		hdmi->hdmi_data.sink_has_audio = drm_detect_monitor_audio(edid);
+
+		drm_connector_update_edid_property(connector, edid);
+
+		ret = drm_add_edid_modes(connector, edid);
+		kfree(edid);
+	} else {
+		hdmi->hdmi_data.sink_is_hdmi = true;
+		hdmi->hdmi_data.sink_has_audio = true;
+
+		ret = rockchip_drm_add_modes_noedid(connector);
+
+		info->edid_hdmi_dc_modes = 0;
+		info->hdmi.y420_dc_modes = 0;
+		info->color_formats = 0;
+
+		dev_info(hdmi->dev, "failed to get edid\n");
+	}
+
+	return ret;
+}
+
+static enum drm_mode_status
+rk628_hdmi_connector_mode_valid(struct drm_connector *connector,
+				struct drm_display_mode *mode)
+{
+	if ((mode->hdisplay == 1920 && mode->vdisplay == 1080) ||
+	    (mode->hdisplay == 1280 && mode->vdisplay == 720))
+		return MODE_OK;
+	else
+		return MODE_BAD;
+}
+
+static struct drm_encoder *
+rk628_hdmi_connector_best_encoder(struct drm_connector *connector)
+{
+	struct rk628_hdmi *hdmi = connector_to_hdmi(connector);
+
+	return hdmi->bridge.encoder;
+}
+
+static int
+rk628_hdmi_probe_single_connector_modes(struct drm_connector *connector,
+					u32 maxX, u32 maxY)
+{
+	return drm_helper_probe_single_connector_modes(connector, 1920, 1080);
+}
+
+static const struct drm_connector_funcs rk628_hdmi_connector_funcs = {
+	.fill_modes = rk628_hdmi_probe_single_connector_modes,
+	.detect = rk628_hdmi_connector_detect,
+	.destroy = drm_connector_cleanup,
+	.reset = drm_atomic_helper_connector_reset,
+	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+static const struct drm_connector_helper_funcs
+rk628_hdmi_connector_helper_funcs = {
+	.get_modes = rk628_hdmi_connector_get_modes,
+	.mode_valid = rk628_hdmi_connector_mode_valid,
+	.best_encoder = rk628_hdmi_connector_best_encoder,
+};
+
+static void rk628_hdmi_bridge_mode_set(struct drm_bridge *bridge,
+				       const struct drm_display_mode *mode,
+				       const struct drm_display_mode *adj_mode)
+{
+	struct rk628_hdmi *hdmi = bridge_to_hdmi(bridge);
+	struct rk628 *rk628 = hdmi->rk628;
+	struct rk628_display_mode *src = rk628_display_get_src_mode(rk628);
+	struct rk628_display_mode *dst = rk628_display_get_dst_mode(rk628);
+
+	/* Store the display mode for plugin/DPMS poweron events */
+	memcpy(&hdmi->previous_mode, mode, sizeof(hdmi->previous_mode));
+	dst->clock = mode->clock;
+	dst->hdisplay = mode->hdisplay;
+	dst->hsync_start = mode->hsync_start;
+	dst->hsync_end = mode->hsync_end;
+	dst->htotal = mode->htotal;
+	dst->vdisplay = mode->vdisplay;
+	dst->vsync_start = mode->vsync_start;
+	dst->vsync_end = mode->vsync_end;
+	dst->vtotal = mode->vtotal;
+	dst->flags = mode->flags;
+	rk628_mode_copy(src, dst);
+}
+
+static bool
+rk628_hdmi_bridge_mode_fixup(struct drm_bridge *bridge,
+			     const struct drm_display_mode *mode,
+			     struct drm_display_mode *adj)
+{
+	struct rk628_hdmi *hdmi = bridge_to_hdmi(bridge);
+	struct rk628 *rk628 = hdmi->rk628;
+
+	if (rk628->sync_pol == MODE_FLAG_NSYNC) {
+		adj->flags &= ~(DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC);
+		adj->flags |= (DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC);
+	} else {
+		adj->flags &= ~(DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC);
+		adj->flags |= (DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC);
+	}
+
+	return true;
+}
+
+static void rk628_hdmi_bridge_enable(struct drm_bridge *bridge)
+{
+	struct rk628_hdmi *hdmi = bridge_to_hdmi(bridge);
+
+	rk628_post_process_init(hdmi->rk628);
+	rk628_post_process_enable(hdmi->rk628);
+	rk628_hdmi_setup(hdmi, &hdmi->previous_mode);
+	rk628_hdmi_set_pwr_mode(hdmi, NORMAL);
+}
+
+static void rk628_hdmi_bridge_disable(struct drm_bridge *bridge)
+{
+	struct rk628_hdmi *hdmi = bridge_to_hdmi(bridge);
+
+	rk628_hdmi_set_pwr_mode(hdmi, LOWER_PWR);
+}
+
+static int rk628_hdmi_bridge_attach(struct drm_bridge *bridge,
+				    enum drm_bridge_attach_flags flags)
+{
+	struct rk628_hdmi *hdmi = bridge_to_hdmi(bridge);
+	struct drm_connector *connector = &hdmi->connector;
+	struct drm_device *drm = bridge->dev;
+	int ret;
+
+	if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)
+		return 0;
+
+	connector->polled = DRM_CONNECTOR_POLL_HPD;
+
+	ret = drm_connector_init(drm, connector, &rk628_hdmi_connector_funcs,
+				 DRM_MODE_CONNECTOR_HDMIA);
+	if (ret) {
+		dev_err(hdmi->dev, "Failed to initialize connector with drm\n");
+		return ret;
+	}
+
+	drm_connector_helper_add(connector,
+				 &rk628_hdmi_connector_helper_funcs);
+	drm_connector_attach_encoder(connector, bridge->encoder);
+
+	hdmi->sub_dev.connector = &hdmi->connector;
+	hdmi->sub_dev.of_node = hdmi->dev->of_node;
+	rockchip_drm_register_sub_dev(&hdmi->sub_dev);
+
+	return 0;
+}
+
+static void rk628_hdmi_bridge_detach(struct drm_bridge *bridge)
+{
+	struct rk628_hdmi *hdmi = bridge_to_hdmi(bridge);
+
+	rockchip_drm_unregister_sub_dev(&hdmi->sub_dev);
+}
+
+static const struct drm_bridge_funcs rk628_hdmi_bridge_funcs = {
+	.attach = rk628_hdmi_bridge_attach,
+	.detach = rk628_hdmi_bridge_detach,
+	.mode_set = rk628_hdmi_bridge_mode_set,
+	.mode_fixup = rk628_hdmi_bridge_mode_fixup,
+	.enable = rk628_hdmi_bridge_enable,
+	.disable = rk628_hdmi_bridge_disable,
+};
+
+static int
+rk628_hdmi_audio_config_set(struct rk628_hdmi *hdmi, struct audio_info *audio)
+{
+	int rate, N, channel;
+
+	if (audio->channels < 3)
+		channel = I2S_CHANNEL_1_2;
+	else if (audio->channels < 5)
+		channel = I2S_CHANNEL_3_4;
+	else if (audio->channels < 7)
+		channel = I2S_CHANNEL_5_6;
+	else
+		channel = I2S_CHANNEL_7_8;
+
+	switch (audio->sample_rate) {
+	case 32000:
+		rate = AUDIO_32K;
+		N = N_32K;
+		break;
+	case 44100:
+		rate = AUDIO_441K;
+		N = N_441K;
+		break;
+	case 48000:
+		rate = AUDIO_48K;
+		N = N_48K;
+		break;
+	case 88200:
+		rate = AUDIO_882K;
+		N = N_882K;
+		break;
+	case 96000:
+		rate = AUDIO_96K;
+		N = N_96K;
+		break;
+	case 176400:
+		rate = AUDIO_1764K;
+		N = N_1764K;
+		break;
+	case 192000:
+		rate = AUDIO_192K;
+		N = N_192K;
+		break;
+	default:
+		dev_err(hdmi->dev, "[%s] not support such sample rate %d\n",
+			__func__, audio->sample_rate);
+		return -ENOENT;
+	}
+
+	/* set_audio source I2S */
+	hdmi_writeb(hdmi, HDMI_AUDIO_CTRL1, 0x01);
+	hdmi_writeb(hdmi, AUDIO_SAMPLE_RATE, rate);
+	hdmi_writeb(hdmi, AUDIO_I2S_MODE,
+		    I2S_MODE(I2S_STANDARD) | I2S_CHANNEL(channel));
+
+	hdmi_writeb(hdmi, AUDIO_I2S_MAP, 0x00);
+	hdmi_writeb(hdmi, AUDIO_I2S_SWAPS_SPDIF, 0);
+
+	/* Set N value */
+	hdmi_writeb(hdmi, AUDIO_N_H, (N >> 16) & 0x0F);
+	hdmi_writeb(hdmi, AUDIO_N_M, (N >> 8) & 0xFF);
+	hdmi_writeb(hdmi, AUDIO_N_L, N & 0xFF);
+
+	/*Set hdmi nlpcm mode to support hdmi bitstream*/
+	hdmi_writeb(hdmi, HDMI_AUDIO_CHANNEL_STATUS, AUDIO_STATUS_NLPCM(0));
+
+	return rk628_hdmi_config_audio_aai(hdmi, audio);
+}
+
+static int rk628_hdmi_audio_hw_params(struct device *dev, void *d,
+				      struct hdmi_codec_daifmt *daifmt,
+				      struct hdmi_codec_params *params)
+{
+	struct rk628_hdmi *hdmi = dev_get_drvdata(dev);
+	struct audio_info audio = {
+		.sample_width = params->sample_width,
+		.sample_rate = params->sample_rate,
+		.channels = params->channels,
+	};
+
+	if (!hdmi->hdmi_data.sink_has_audio) {
+		dev_err(hdmi->dev, "Sink do not support audio!\n");
+		return -ENODEV;
+	}
+
+	if (!hdmi->bridge.encoder->crtc)
+		return -ENODEV;
+
+	switch (daifmt->fmt) {
+	case HDMI_I2S:
+		break;
+	default:
+		dev_err(dev, "%s: Invalid format %d\n", __func__, daifmt->fmt);
+		return -EINVAL;
+	}
+
+	return rk628_hdmi_audio_config_set(hdmi, &audio);
+}
+
+static void rk628_hdmi_audio_shutdown(struct device *dev, void *d)
+{
+	/* do nothing */
+}
+
+static int rk628_hdmi_audio_mute(struct device *dev, void *d, bool mute,
+				 int direction)
+{
+	struct rk628_hdmi *hdmi = dev_get_drvdata(dev);
+
+	if (!hdmi->hdmi_data.sink_has_audio) {
+		dev_err(hdmi->dev, "Sink do not support audio!\n");
+		return -ENODEV;
+	}
+
+	hdmi->audio_enable = !mute;
+
+	if (mute)
+		hdmi_modb(hdmi, HDMI_AV_MUTE, AUDIO_MUTE_MASK | AUDIO_PD_MASK,
+			  AUDIO_MUTE(1) | AUDIO_PD(1));
+	else
+		hdmi_modb(hdmi, HDMI_AV_MUTE, AUDIO_MUTE_MASK | AUDIO_PD_MASK,
+			  AUDIO_MUTE(0) | AUDIO_PD(0));
+
+	return 0;
+}
+
+static int rk628_hdmi_audio_get_eld(struct device *dev, void *d,
+				    u8 *buf, size_t len)
+{
+	struct rk628_hdmi *hdmi = dev_get_drvdata(dev);
+	struct drm_mode_config *config = &hdmi->bridge.dev->mode_config;
+	struct drm_connector *connector;
+	int ret = -ENODEV;
+
+	mutex_lock(&config->mutex);
+	list_for_each_entry(connector, &config->connector_list, head) {
+		if (hdmi->bridge.encoder == connector->encoder) {
+			memcpy(buf, connector->eld,
+			       min(sizeof(connector->eld), len));
+			ret = 0;
+		}
+	}
+	mutex_unlock(&config->mutex);
+
+	return ret;
+}
+
+static const struct hdmi_codec_ops audio_codec_ops = {
+	.hw_params = rk628_hdmi_audio_hw_params,
+	.audio_shutdown = rk628_hdmi_audio_shutdown,
+	.mute_stream = rk628_hdmi_audio_mute,
+	.no_capture_mute = 1,
+	.get_eld = rk628_hdmi_audio_get_eld,
+};
+
+static int rk628_hdmi_audio_codec_init(struct rk628_hdmi *hdmi,
+				       struct device *dev)
+{
+	struct hdmi_codec_pdata codec_data = {
+		.i2s = 1,
+		.ops = &audio_codec_ops,
+		.max_i2s_channels = 8,
+	};
+
+	hdmi->audio_enable = false;
+	hdmi->audio_pdev = platform_device_register_data(dev,
+				HDMI_CODEC_DRV_NAME, PLATFORM_DEVID_NONE,
+				&codec_data, sizeof(codec_data));
+
+	return PTR_ERR_OR_ZERO(hdmi->audio_pdev);
+}
+
+static irqreturn_t rk628_hdmi_irq(int irq, void *dev_id)
+{
+	struct rk628_hdmi *hdmi = dev_id;
+	u8 interrupt;
+
+	/* clear interrupts */
+	rk628_i2c_write(hdmi->rk628, GRF_INTR0_CLR_EN, 0x00040004);
+	interrupt = hdmi_readb(hdmi, HDMI_STATUS);
+	if (!(interrupt & INT_HOTPLUG))
+		return IRQ_HANDLED;
+
+	hdmi_modb(hdmi, HDMI_STATUS, INT_HOTPLUG, INT_HOTPLUG);
+	if (hdmi->connector.dev)
+		drm_helper_hpd_irq_event(hdmi->connector.dev);
+
+	return IRQ_HANDLED;
+}
+
+static int rk628_hdmi_i2c_read(struct rk628_hdmi *hdmi, struct i2c_msg *msgs)
+{
+	int length = msgs->len;
+	u8 *buf = msgs->buf;
+	int i;
+	u32 c;
+
+	for (i = 0; i < 5; i++) {
+		msleep(20);
+		c = hdmi_readb(hdmi, HDMI_INTERRUPT_STATUS1);
+		if (c & INT_EDID_READY)
+			break;
+	}
+	if ((c & INT_EDID_READY) == 0)
+		return -EAGAIN;
+
+	while (length--)
+		*buf++ = hdmi_readb(hdmi, HDMI_EDID_FIFO_ADDR);
+
+	return 0;
+}
+
+static int rk628_hdmi_i2c_write(struct rk628_hdmi *hdmi, struct i2c_msg *msgs)
+{
+	/*
+	 * The DDC module only support read EDID message, so
+	 * we assume that each word write to this i2c adapter
+	 * should be the offset of EDID word address.
+	 */
+	if ((msgs->len != 1) ||
+	    ((msgs->addr != DDC_ADDR) && (msgs->addr != DDC_SEGMENT_ADDR)))
+		return -EINVAL;
+
+	if (msgs->addr == DDC_ADDR)
+		hdmi->i2c->ddc_addr = msgs->buf[0];
+	if (msgs->addr == DDC_SEGMENT_ADDR) {
+		hdmi->i2c->segment_addr = msgs->buf[0];
+		return 0;
+	}
+
+	/* Set edid fifo first addr */
+	hdmi_writeb(hdmi, HDMI_EDID_FIFO_OFFSET, 0x00);
+
+	/* Set edid word address 0x00/0x80 */
+	hdmi_writeb(hdmi, HDMI_EDID_WORD_ADDR, hdmi->i2c->ddc_addr);
+
+	/* Set edid segment pointer */
+	hdmi_writeb(hdmi, HDMI_EDID_SEGMENT_POINTER, hdmi->i2c->segment_addr);
+
+	return 0;
+}
+
+static int rk628_hdmi_i2c_xfer(struct i2c_adapter *adap,
+			       struct i2c_msg *msgs, int num)
+{
+	struct rk628_hdmi *hdmi = i2c_get_adapdata(adap);
+	struct rk628_hdmi_i2c *i2c = hdmi->i2c;
+	int i, ret = 0;
+
+	mutex_lock(&i2c->lock);
+
+	hdmi->i2c->ddc_addr = 0;
+	hdmi->i2c->segment_addr = 0;
+
+	/* Clear the EDID interrupt flag and unmute the interrupt */
+	hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, INT_EDID_READY);
+	hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, INT_EDID_READY_MASK);
+
+	for (i = 0; i < num; i++) {
+		dev_dbg(hdmi->dev, "xfer: num: %d/%d, len: %d, flags: %#x\n",
+			i + 1, num, msgs[i].len, msgs[i].flags);
+
+		if (msgs[i].flags & I2C_M_RD)
+			ret = rk628_hdmi_i2c_read(hdmi, &msgs[i]);
+		else
+			ret = rk628_hdmi_i2c_write(hdmi, &msgs[i]);
+
+		if (ret < 0)
+			break;
+	}
+
+	if (!ret)
+		ret = num;
+
+	/* Mute HDMI EDID interrupt */
+	hdmi_writeb(hdmi, HDMI_INTERRUPT_MASK1, 0);
+	hdmi_writeb(hdmi, HDMI_INTERRUPT_STATUS1, INT_EDID_READY);
+
+	mutex_unlock(&i2c->lock);
+
+	return ret;
+}
+
+static u32 rk628_hdmi_i2c_func(struct i2c_adapter *adapter)
+{
+	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static const struct i2c_algorithm rk628_hdmi_algorithm = {
+	.master_xfer	= rk628_hdmi_i2c_xfer,
+	.functionality	= rk628_hdmi_i2c_func,
+};
+
+static struct i2c_adapter *rk628_hdmi_i2c_adapter(struct rk628_hdmi *hdmi)
+{
+	struct i2c_adapter *adap;
+	struct rk628_hdmi_i2c *i2c;
+	int ret;
+
+	i2c = devm_kzalloc(hdmi->dev, sizeof(*i2c), GFP_KERNEL);
+	if (!i2c)
+		return ERR_PTR(-ENOMEM);
+
+	mutex_init(&i2c->lock);
+
+	adap = &i2c->adap;
+	adap->class = I2C_CLASS_DDC;
+	adap->owner = THIS_MODULE;
+	adap->dev.parent = hdmi->dev;
+	adap->dev.of_node = hdmi->dev->of_node;
+	adap->algo = &rk628_hdmi_algorithm;
+	strscpy(adap->name, "RK628 HDMI", sizeof(adap->name));
+	i2c_set_adapdata(adap, hdmi);
+
+	ret = i2c_add_adapter(adap);
+	if (ret) {
+		dev_warn(hdmi->dev, "cannot add %s I2C adapter\n", adap->name);
+		devm_kfree(hdmi->dev, i2c);
+		return ERR_PTR(ret);
+	}
+
+	hdmi->i2c = i2c;
+
+	dev_info(hdmi->dev, "registered %s I2C bus driver\n", adap->name);
+
+	return adap;
+}
+
+int rk628_hdmitx_enable(struct rk628 *rk628)
+{
+	struct device *dev = rk628->dev;
+	struct rk628_hdmi *hdmi;
+	int irq;
+	int ret;
+
+	if (!of_device_is_available(dev->of_node))
+		return -ENODEV;
+
+	hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL);
+	if (!hdmi)
+		return -ENOMEM;
+
+
+	hdmi->dev = dev;
+	hdmi->rk628 = rk628;
+
+	irq = rk628->client->irq;
+	if (irq < 0)
+		return irq;
+	dev_set_drvdata(dev, hdmi);
+
+	/* selete int io function */
+	rk628_i2c_write(rk628, GRF_GPIO0AB_SEL_CON, 0x70007000);
+	rk628_i2c_write(rk628, GRF_GPIO0AB_SEL_CON, 0x055c055c);
+
+	/* hdmitx vclk pllref select Pin_vclk */
+	rk628_i2c_update_bits(rk628, GRF_POST_PROC_CON,
+			   SW_HDMITX_VCLK_PLLREF_SEL_MASK,
+			   SW_HDMITX_VCLK_PLLREF_SEL(1));
+	/* set output mode to HDMI */
+	rk628_i2c_update_bits(rk628, GRF_SYSTEM_CON0, SW_OUTPUT_MODE_MASK,
+			   SW_OUTPUT_MODE(OUTPUT_MODE_HDMI));
+
+	rk628_hdmi_reset(hdmi);
+
+	hdmi->ddc = rk628_hdmi_i2c_adapter(hdmi);
+	if (IS_ERR(hdmi->ddc)) {
+		ret = PTR_ERR(hdmi->ddc);
+		hdmi->ddc = NULL;
+		goto fail;
+	}
+
+	/*
+	 * When IP controller haven't configured to an accurate video
+	 * timing, then the TMDS clock source would be switched to
+	 * PCLK_HDMI, so we need to init the TMDS rate to PCLK rate,
+	 * and reconfigure the DDC clock.
+	 */
+	hdmi->tmds_rate = 24000 * 1000;
+
+	/* hdmitx int en */
+	rk628_i2c_write(rk628, GRF_INTR0_EN, 0x00040004);
+	rk628_hdmi_i2c_init(hdmi);
+
+	rk628_hdmi_audio_codec_init(hdmi, dev);
+
+	ret = devm_request_threaded_irq(dev, irq, NULL,
+					rk628_hdmi_irq,
+					IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+					dev_name(dev), hdmi);
+	if (ret) {
+		dev_err(dev, "failed to request hdmi irq: %d\n", ret);
+		goto fail;
+	}
+
+	/* Unmute hotplug interrupt */
+	hdmi_modb(hdmi, HDMI_STATUS, MASK_INT_HOTPLUG_MASK,
+		  MASK_INT_HOTPLUG(1));
+	hdmi->bridge.funcs = &rk628_hdmi_bridge_funcs;
+	hdmi->bridge.of_node = dev->of_node;
+
+	drm_bridge_add(&hdmi->bridge);
+
+	hdmi->extcon = devm_extcon_dev_allocate(hdmi->dev, rk628_hdmi_cable);
+	if (IS_ERR(hdmi->extcon)) {
+		dev_err(hdmi->dev, "allocate extcon failed\n");
+		ret = PTR_ERR(hdmi->extcon);
+		goto fail;
+	}
+
+	ret = devm_extcon_dev_register(hdmi->dev, hdmi->extcon);
+	if (ret) {
+		dev_err(dev, "failed to register extcon: %d\n", ret);
+		goto fail;
+	}
+
+	ret = extcon_set_property_capability(hdmi->extcon, EXTCON_DISP_HDMI,
+					     EXTCON_PROP_DISP_HPD);
+	if (ret) {
+		dev_err(dev, "failed to set USB property capability: %d\n",
+			ret);
+		goto fail;
+	}
+
+	return 0;
+
+fail:
+	return ret;
+}
+
+MODULE_AUTHOR("Chen Shunqing <csq@rock-chips.com>");
+MODULE_DESCRIPTION("Rockchip RK628 HDMI driver");
+MODULE_LICENSE("GPL");
diff --git a/kernel/drivers/misc/rk628/rk628_hdmitx.h b/kernel/drivers/misc/rk628/rk628_hdmitx.h
new file mode 100644
index 0000000..9a1b3b8
--- /dev/null
+++ b/kernel/drivers/misc/rk628/rk628_hdmitx.h
@@ -0,0 +1,350 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2020 Rockchip Electronics Co. Ltd.
+ *
+ * Author: Chen Shunqing <csq@rock-chips.com>
+ */
+
+#ifndef HDMITX_H
+#define HDMITX_H
+
+#include "rk628.h"
+
+#define HDMI_BASE			0x70000
+#define HDMI_REG_STRIDE			4
+#define HDMITX_REG(x)			((x * HDMI_REG_STRIDE) + HDMI_BASE)
+
+#define DDC_SEGMENT_ADDR		0x30
+
+enum PWR_MODE {
+	NORMAL,
+	LOWER_PWR,
+};
+
+#define HDMI_SCL_RATE			(100 * 1000)
+#define DDC_BUS_FREQ_L			HDMITX_REG(0x4b)
+#define DDC_BUS_FREQ_H			HDMITX_REG(0x4c)
+
+#define HDMI_SYS_CTRL			HDMITX_REG(0x00)
+#define RST_ANALOG_MASK			BIT(6)
+#define NOT_RST_ANALOG(x)		UPDATE(x, 6, 6)
+#define RST_DIGITAL_MASK		BIT(5)
+#define NOT_RST_DIGITAL(x)		UPDATE(x, 5, 5)
+#define REG_CLK_INV_MASK		BIT(4)
+#define REG_CLK_INV(x)			UPDATE(x, 4, 4)
+#define VCLK_INV_MASK			BIT(3)
+#define VCLK_INV(x)			UPDATE(x, 3, 3)
+#define REG_CLK_SOURCE_MASK		BIT(2)
+#define REG_CLK_SOURCE(x)		UPDATE(x, 2, 2)
+#define POWER_MASK			BIT(1)
+#define PWR_OFF(x)			UPDATE(x, 1, 1)
+#define INT_POL_MASK			BIT(0)
+#define INT_POL(x)			UPDATE(x, 0, 0)
+
+#define HDMI_VIDEO_CONTROL1		HDMITX_REG(0x01)
+#define VIDEO_INPUT_FORMAT_MASK		GENMASK(3, 1)
+#define VIDEO_INPUT_SDR_RGB444		UPDATE(0x0, 3, 1)
+#define VIDEO_INPUT_DDR_RGB444		UPDATE(0x5, 3, 1)
+#define VIDEO_INPUT_DDR_YCBCR422	UPDATE(0x6, 3, 1)
+#define DE_SOURCE_MASK			BIT(0)
+#define DE_SOURCE(x)			UPDATE(x, 0, 0)
+
+#define HDMI_VIDEO_CONTROL2		HDMITX_REG(0x02)
+#define VIDEO_OUTPUT_COLOR_MASK		GENMASK(7, 6)
+#define VIDEO_OUTPUT_RRGB444		UPDATE(0x0, 7, 6)
+#define VIDEO_OUTPUT_YCBCR444		UPDATE(0x1, 7, 6)
+#define VIDEO_OUTPUT_YCBCR422		UPDATE(0x2, 7, 6)
+#define VIDEO_INPUT_BITS_MASK		GENMASK(5, 4)
+#define VIDEO_INPUT_12BITS		UPDATE(0x0, 5, 4)
+#define VIDEO_INPUT_10BITS		UPDATE(0x1, 5, 4)
+#define VIDEO_INPUT_REVERT		UPDATE(0x2, 5, 4)
+#define VIDEO_INPUT_8BITS		UPDATE(0x3, 5, 4)
+#define VIDEO_INPUT_CSP_MASK		BIT(1)
+#define VIDEO_INPUT_CSP(x)		UPDATE(x, 0, 0)
+
+#define HDMI_VIDEO_CONTROL		HDMITX_REG(0x03)
+#define VIDEO_AUTO_CSC_MASK		BIT(7)
+#define VIDEO_AUTO_CSC(x)		UPDATE(x, 7, 7)
+#define VIDEO_C0_C2_SWAP_MASK		BIT(0)
+#define VIDEO_C0_C2_SWAP(x)		UPDATE(x, 0, 0)
+enum {
+	C0_C2_CHANGE_ENABLE = 0,
+	C0_C2_CHANGE_DISABLE = 1,
+	AUTO_CSC_DISABLE = 0,
+	AUTO_CSC_ENABLE = 1,
+};
+
+#define HDMI_VIDEO_CONTROL3		HDMITX_REG(0x04)
+#define COLOR_DEPTH_NOT_INDICATED_MASK	BIT(4)
+#define COLOR_DEPTH_NOT_INDICATED(x)	UPDATE(x, 4, 4)
+#define SOF_MASK			BIT(3)
+#define SOF_DISABLE(x)			UPDATE(x, 3, 3)
+#define CSC_MASK			BIT(0)
+#define CSC_ENABLE(x)			UPDATE(x, 0, 0)
+
+#define HDMI_AV_MUTE			HDMITX_REG(0x05)
+#define AVMUTE_CLEAR_MASK		BIT(7)
+#define AVMUTE_CLEAR(x)			UPDATE(x, 7, 7)
+#define AVMUTE_ENABLE_MASK		BIT(6)
+#define AVMUTE_ENABLE(x)		UPDATE(x, 6, 6)
+#define AUDIO_PD_MASK			BIT(2)
+#define AUDIO_PD(x)			UPDATE(x, 2, 2)
+#define AUDIO_MUTE_MASK			BIT(1)
+#define AUDIO_MUTE(x)			UPDATE(x, 1, 1)
+#define VIDEO_BLACK_MASK		BIT(0)
+#define VIDEO_MUTE(x)			UPDATE(x, 0, 0)
+
+#define HDMI_VIDEO_TIMING_CTL		HDMITX_REG(0x08)
+#define HSYNC_POLARITY(x)		UPDATE(x, 3, 3)
+#define VSYNC_POLARITY(x)		UPDATE(x, 2, 2)
+#define INETLACE(x)			UPDATE(x, 1, 1)
+#define EXTERANL_VIDEO(x)		UPDATE(x, 0, 0)
+
+#define HDMI_VIDEO_EXT_HTOTAL_L		HDMITX_REG(0x09)
+#define HDMI_VIDEO_EXT_HTOTAL_H		HDMITX_REG(0x0a)
+#define HDMI_VIDEO_EXT_HBLANK_L		HDMITX_REG(0x0b)
+#define HDMI_VIDEO_EXT_HBLANK_H		HDMITX_REG(0x0c)
+#define HDMI_VIDEO_EXT_HDELAY_L		HDMITX_REG(0x0d)
+#define HDMI_VIDEO_EXT_HDELAY_H		HDMITX_REG(0x0e)
+#define HDMI_VIDEO_EXT_HDURATION_L	HDMITX_REG(0x0f)
+#define HDMI_VIDEO_EXT_HDURATION_H	HDMITX_REG(0x10)
+#define HDMI_VIDEO_EXT_VTOTAL_L		HDMITX_REG(0x11)
+#define HDMI_VIDEO_EXT_VTOTAL_H		HDMITX_REG(0x12)
+#define HDMI_VIDEO_EXT_VBLANK		HDMITX_REG(0x13)
+#define HDMI_VIDEO_EXT_VDELAY		HDMITX_REG(0x14)
+#define HDMI_VIDEO_EXT_VDURATION	HDMITX_REG(0x15)
+
+#define HDMI_VIDEO_CSC_COEF		HDMITX_REG(0x18)
+
+#define HDMI_AUDIO_CTRL1		HDMITX_REG(0x35)
+enum {
+	CTS_SOURCE_INTERNAL = 0,
+	CTS_SOURCE_EXTERNAL = 1,
+};
+
+#define CTS_SOURCE(x)			UPDATE(x, 7, 7)
+
+enum {
+	DOWNSAMPLE_DISABLE = 0,
+	DOWNSAMPLE_1_2 = 1,
+	DOWNSAMPLE_1_4 = 2,
+};
+
+#define DOWN_SAMPLE(x)			UPDATE(x, 6, 5)
+
+enum {
+	AUDIO_SOURCE_IIS = 0,
+	AUDIO_SOURCE_SPDIF = 1,
+};
+
+#define AUDIO_SOURCE(x)			UPDATE(x, 4, 3)
+#define MCLK_ENABLE(x)			UPDATE(x, 2, 2)
+
+enum {
+	MCLK_128FS = 0,
+	MCLK_256FS = 1,
+	MCLK_384FS = 2,
+	MCLK_512FS = 3,
+};
+
+#define MCLK_RATIO(x)			UPDATE(x, 1, 0)
+
+#define AUDIO_SAMPLE_RATE		HDMITX_REG(0x37)
+enum {
+	AUDIO_32K = 0x3,
+	AUDIO_441K = 0x0,
+	AUDIO_48K = 0x2,
+	AUDIO_882K = 0x8,
+	AUDIO_96K = 0xa,
+	AUDIO_1764K = 0xc,
+	AUDIO_192K = 0xe,
+};
+
+#define AUDIO_I2S_MODE			HDMITX_REG(0x38)
+enum {
+	I2S_CHANNEL_1_2 = 1,
+	I2S_CHANNEL_3_4 = 3,
+	I2S_CHANNEL_5_6 = 7,
+	I2S_CHANNEL_7_8 = 0xf
+};
+
+#define I2S_CHANNEL(x)			UPDATE(x, 5, 2)
+
+enum {
+	I2S_STANDARD = 0,
+	I2S_LEFT_JUSTIFIED = 1,
+	I2S_RIGHT_JUSTIFIED = 2,
+};
+
+#define I2S_MODE(x)			UPDATE(x, 1, 0)
+
+#define AUDIO_I2S_MAP			HDMITX_REG(0x39)
+#define AUDIO_I2S_SWAPS_SPDIF		HDMITX_REG(0x3a)
+#define N_32K				0x1000
+#define N_441K				0x1880
+#define N_882K				0x3100
+#define N_1764K				0x6200
+#define N_48K				0x1800
+#define N_96K				0x3000
+#define N_192K				0x6000
+
+#define HDMI_AUDIO_CHANNEL_STATUS	HDMITX_REG(0x3e)
+#define AUDIO_STATUS_NLPCM_MASK		BIT(7)
+#define AUDIO_STATUS_NLPCM(x)		UPDATE(x, 7, 7)
+#define AUDIO_STATUS_USE_MASK		BIT(6)
+#define AUDIO_STATUS_COPYRIGHT_MASK	BIT(5)
+#define AUDIO_STATUS_ADDITION_MASK	GENMASK(3, 2)
+#define AUDIO_STATUS_CLK_ACCURACY_MASK	GENMASK(1, 1)
+
+#define AUDIO_N_H			HDMITX_REG(0x3f)
+#define AUDIO_N_M			HDMITX_REG(0x40)
+#define AUDIO_N_L			HDMITX_REG(0x41)
+
+#define HDMI_AUDIO_CTS_H		HDMITX_REG(0x45)
+#define HDMI_AUDIO_CTS_M		HDMITX_REG(0x46)
+#define HDMI_AUDIO_CTS_L		HDMITX_REG(0x47)
+
+#define HDMI_DDC_CLK_L			HDMITX_REG(0x4b)
+#define HDMI_DDC_CLK_H			HDMITX_REG(0x4c)
+
+#define HDMI_EDID_SEGMENT_POINTER	HDMITX_REG(0x4d)
+#define HDMI_EDID_WORD_ADDR		HDMITX_REG(0x4e)
+#define HDMI_EDID_FIFO_OFFSET		HDMITX_REG(0x4f)
+#define HDMI_EDID_FIFO_ADDR		HDMITX_REG(0x50)
+
+#define HDMI_PACKET_SEND_MANUAL		HDMITX_REG(0x9c)
+#define HDMI_PACKET_SEND_AUTO		HDMITX_REG(0x9d)
+#define PACKET_GCP_EN_MASK		BIT(7)
+#define PACKET_GCP_EN(x)		UPDATE(x, 7, 7)
+#define PACKET_MSI_EN_MASK		BIT(6)
+#define PACKET_MSI_EN(x)		UPDATE(x, 6, 6)
+#define PACKET_SDI_EN_MASK		BIT(5)
+#define PACKET_SDI_EN(x)		UPDATE(x, 5, 5)
+#define PACKET_VSI_EN_MASK		BIT(4)
+#define PACKET_VSI_EN(x)		UPDATE(x, 4, 4)
+
+#define HDMI_CONTROL_PACKET_BUF_INDEX	HDMITX_REG(0x9f)
+enum {
+	INFOFRAME_VSI = 0x05,
+	INFOFRAME_AVI = 0x06,
+	INFOFRAME_AAI = 0x08,
+};
+
+#define HDMI_CONTROL_PACKET_ADDR	HDMITX_REG(0xa0)
+#define HDMI_MAXIMUM_INFO_FRAME_SIZE	0x11
+enum {
+	AVI_COLOR_MODE_RGB = 0,
+	AVI_COLOR_MODE_YCBCR422 = 1,
+	AVI_COLOR_MODE_YCBCR444 = 2,
+	AVI_COLORIMETRY_NO_DATA = 0,
+
+	AVI_COLORIMETRY_SMPTE_170M = 1,
+	AVI_COLORIMETRY_ITU709 = 2,
+	AVI_COLORIMETRY_EXTENDED = 3,
+
+	AVI_CODED_FRAME_ASPECT_NO_DATA = 0,
+	AVI_CODED_FRAME_ASPECT_4_3 = 1,
+	AVI_CODED_FRAME_ASPECT_16_9 = 2,
+
+	ACTIVE_ASPECT_RATE_SAME_AS_CODED_FRAME = 0x08,
+	ACTIVE_ASPECT_RATE_4_3 = 0x09,
+	ACTIVE_ASPECT_RATE_16_9 = 0x0A,
+	ACTIVE_ASPECT_RATE_14_9 = 0x0B,
+};
+
+#define HDMI_HDCP_CTRL			HDMITX_REG(0x52)
+#define HDMI_DVI_MASK			BIT(1)
+#define HDMI_DVI(x)			UPDATE(x, 1, 1)
+
+#define HDMI_INTERRUPT_MASK1		HDMITX_REG(0xc0)
+#define INT_EDID_READY_MASK		BIT(2)
+#define HDMI_INTERRUPT_STATUS1		HDMITX_REG(0xc1)
+#define	INT_ACTIVE_VSYNC_MASK		BIT(5)
+#define INT_EDID_READY			BIT(2)
+
+#define HDMI_INTERRUPT_MASK2		HDMITX_REG(0xc2)
+#define HDMI_INTERRUPT_STATUS2		HDMITX_REG(0xc3)
+#define INT_HDCP_ERR			BIT(7)
+#define INT_BKSV_FLAG			BIT(6)
+#define INT_HDCP_OK			BIT(4)
+
+#define HDMI_STATUS			HDMITX_REG(0xc8)
+#define HOTPLUG_STATUS			BIT(7)
+#define MASK_INT_HOTPLUG_MASK		BIT(5)
+#define MASK_INT_HOTPLUG(x)		UPDATE(x, 5, 5)
+#define INT_HOTPLUG			BIT(1)
+
+#define HDMI_COLORBAR                   HDMITX_REG(0xc9)
+
+#define HDMI_PHY_SYNC			HDMITX_REG(0xce)
+#define HDMI_PHY_SYS_CTL		HDMITX_REG(0xe0)
+#define TMDS_CLK_SOURCE_MASK		BIT(5)
+#define TMDS_CLK_SOURCE(x)		UPDATE(x, 5, 5)
+#define PHASE_CLK_MASK			BIT(4)
+#define PHASE_CLK(x)			UPDATE(x, 4, 4)
+#define TMDS_PHASE_SEL_MASK		BIT(3)
+#define TMDS_PHASE_SEL(x)		UPDATE(x, 3, 3)
+#define BANDGAP_PWR_MASK		BIT(2)
+#define BANDGAP_PWR(x)			UPDATE(x, 2, 2)
+#define PLL_PWR_DOWN_MASK		BIT(1)
+#define PLL_PWR_DOWN(x)			UPDATE(x, 1, 1)
+#define TMDS_CHG_PWR_DOWN_MASK		BIT(0)
+#define TMDS_CHG_PWR_DOWN(x)		UPDATE(x, 0, 0)
+
+#define HDMI_PHY_CHG_PWR		HDMITX_REG(0xe1)
+#define CLK_CHG_PWR(x)			UPDATE(x, 3, 3)
+#define DATA_CHG_PWR(x)			UPDATE(x, 2, 0)
+
+#define HDMI_PHY_DRIVER			HDMITX_REG(0xe2)
+#define CLK_MAIN_DRIVER(x)		UPDATE(x, 7, 4)
+#define DATA_MAIN_DRIVER(x)		UPDATE(x, 3, 0)
+
+#define HDMI_PHY_PRE_EMPHASIS		HDMITX_REG(0xe3)
+#define PRE_EMPHASIS(x)			UPDATE(x, 6, 4)
+#define CLK_PRE_DRIVER(x)		UPDATE(x, 3, 2)
+#define DATA_PRE_DRIVER(x)		UPDATE(x, 1, 0)
+
+#define PHY_FEEDBACK_DIV_RATIO_LOW	HDMITX_REG(0xe7)
+#define FEEDBACK_DIV_LOW(x)		UPDATE(x, 7, 0)
+#define PHY_FEEDBACK_DIV_RATIO_HIGH	HDMITX_REG(0xe8)
+#define FEEDBACK_DIV_HIGH(x)		UPDATE(x, 0, 0)
+
+#define HDMI_PHY_PRE_DIV_RATIO		HDMITX_REG(0xed)
+#define PRE_DIV_RATIO(x)		UPDATE(x, 4, 0)
+
+#define HDMI_CEC_CTRL			HDMITX_REG(0xd0)
+#define ADJUST_FOR_HISENSE_MASK		BIT(6)
+#define REJECT_RX_BROADCAST_MASK	BIT(5)
+#define BUSFREETIME_ENABLE_MASK		BIT(2)
+#define REJECT_RX_MASK			BIT(1)
+#define START_TX_MASK			BIT(0)
+
+#define HDMI_CEC_DATA			HDMITX_REG(0xd1)
+#define HDMI_CEC_TX_OFFSET		HDMITX_REG(0xd2)
+#define HDMI_CEC_RX_OFFSET		HDMITX_REG(0xd3)
+#define HDMI_CEC_CLK_H			HDMITX_REG(0xd4)
+#define HDMI_CEC_CLK_L			HDMITX_REG(0xd5)
+#define HDMI_CEC_TX_LENGTH		HDMITX_REG(0xd6)
+#define HDMI_CEC_RX_LENGTH		HDMITX_REG(0xd7)
+#define HDMI_CEC_TX_INT_MASK		HDMITX_REG(0xd8)
+#define TX_DONE_MASK			BIT(3)
+#define TX_NOACK_MASK			BIT(2)
+#define TX_BROADCAST_REJ_MASK		BIT(1)
+#define TX_BUSNOTFREE_MASK		BIT(0)
+
+#define HDMI_CEC_RX_INT_MASK		HDMITX_REG(0xd9)
+#define RX_LA_ERR_MASK			BIT(4)
+#define RX_GLITCH_MASK			BIT(3)
+#define RX_DONE_MASK			BIT(0)
+
+#define HDMI_CEC_TX_INT			HDMITX_REG(0xda)
+#define HDMI_CEC_RX_INT			HDMITX_REG(0xdb)
+#define HDMI_CEC_BUSFREETIME_L		HDMITX_REG(0xdc)
+#define HDMI_CEC_BUSFREETIME_H		HDMITX_REG(0xdd)
+#define HDMI_CEC_LOGICADDR		HDMITX_REG(0xde)
+
+#define HDMI_MAX_REG			HDMITX_REG(0xed)
+
+int rk628_hdmitx_enable(struct rk628 *rk628);
+
+#endif
diff --git a/kernel/drivers/misc/rk628/rk628_lvds.c b/kernel/drivers/misc/rk628/rk628_lvds.c
new file mode 100644
index 0000000..b0587b4
--- /dev/null
+++ b/kernel/drivers/misc/rk628/rk628_lvds.c
@@ -0,0 +1,126 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2021 Rockchip Electronics Co. Ltd.
+ *
+ * Author: Guochun Huang <hero.huang@rock-chips.com>
+ */
+
+#include "rk628.h"
+
+#include "rk628_combtxphy.h"
+#include "rk628_config.h"
+#include "panel.h"
+
+static inline void lvds_write(struct rk628 *rk628, u32 reg, u32 val)
+{
+	rk628_i2c_write(rk628, reg, val);
+}
+
+static inline void lvds_update_bits(struct rk628 *rk628, u32 reg,
+				    u32 mask, u32 val)
+{
+	rk628_i2c_update_bits(rk628, reg, mask, val);
+}
+
+int rk628_lvds_parse(struct rk628 *rk628, struct device_node *lvds_np)
+{
+	const char *string;
+	int ret;
+
+	if (!of_device_is_available(lvds_np))
+		return -EINVAL;
+
+	rk628->output_mode = OUTPUT_MODE_LVDS;
+
+	if (!of_property_read_string(lvds_np, "bus-format", &string)) {
+		if (!strcmp(string, "jeida_24"))
+			rk628->lvds.format = LVDS_FORMAT_JEIDA_24BIT;
+		else if (!strcmp(string, "jeida_18"))
+			rk628->lvds.format = LVDS_FORMAT_JEIDA_18BIT;
+		else if (!strcmp(string, "vesa_18"))
+			rk628->lvds.format = LVDS_FORMAT_VESA_18BIT;
+		else
+			rk628->lvds.format = LVDS_FORMAT_VESA_24BIT;
+	}
+
+	if (!of_property_read_string(lvds_np, "link-type", &string)) {
+		if (!strcmp(string, "dual_link_odd_even_pixels"))
+			rk628->lvds.link_type = LVDS_DUAL_LINK_ODD_EVEN_PIXELS;
+		else if (!strcmp(string, "dual_link_even_odd_pixels"))
+			rk628->lvds.link_type = LVDS_DUAL_LINK_EVEN_ODD_PIXELS;
+		else if (!strcmp(string, "dual_link_left_right_pixels"))
+			rk628->lvds.link_type = LVDS_DUAL_LINK_LEFT_RIGHT_PIXELS;
+		else if (!strcmp(string, "dual_link_right_left_pixels"))
+			rk628->lvds.link_type = LVDS_DUAL_LINK_RIGHT_LEFT_PIXELS;
+		else
+			rk628->lvds.link_type = LVDS_SINGLE_LINK;
+	}
+
+	ret = rk628_panel_info_get(rk628, lvds_np);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+void rk628_lvds_enable(struct rk628 *rk628)
+{
+	enum lvds_link_type link_type = rk628->lvds.link_type;
+	enum lvds_format format = rk628->lvds.format;
+	const struct rk628_display_mode *mode = &rk628->dst_mode;
+	u32 val, bus_width;
+
+	lvds_update_bits(rk628, GRF_SYSTEM_CON0, SW_OUTPUT_MODE_MASK,
+			 SW_OUTPUT_MODE(OUTPUT_MODE_LVDS));
+
+	switch (link_type) {
+	case LVDS_DUAL_LINK_ODD_EVEN_PIXELS:
+		val = SW_LVDS_CON_CHASEL(1) | SW_LVDS_CON_STARTSEL(0) |
+		      SW_LVDS_CON_DUAL_SEL(0);
+		bus_width = COMBTXPHY_MODULEA_EN | COMBTXPHY_MODULEB_EN;
+		break;
+	case LVDS_DUAL_LINK_EVEN_ODD_PIXELS:
+		val = SW_LVDS_CON_CHASEL(1) | SW_LVDS_CON_STARTSEL(1) |
+		      SW_LVDS_CON_DUAL_SEL(0);
+		bus_width = COMBTXPHY_MODULEA_EN | COMBTXPHY_MODULEB_EN;
+		break;
+	case LVDS_DUAL_LINK_LEFT_RIGHT_PIXELS:
+		val = SW_LVDS_CON_CHASEL(1) | SW_LVDS_CON_STARTSEL(0) |
+		      SW_LVDS_CON_DUAL_SEL(1);
+		lvds_update_bits(rk628, GRF_POST_PROC_CON,
+				 SW_SPLIT_EN, SW_SPLIT_EN);
+		bus_width = COMBTXPHY_MODULEA_EN | COMBTXPHY_MODULEB_EN;
+		break;
+	case LVDS_DUAL_LINK_RIGHT_LEFT_PIXELS:
+		val = SW_LVDS_CON_CHASEL(1) | SW_LVDS_CON_STARTSEL(1) |
+		      SW_LVDS_CON_DUAL_SEL(1);
+		lvds_update_bits(rk628, GRF_POST_PROC_CON,
+				 SW_SPLIT_EN, SW_SPLIT_EN);
+		bus_width = COMBTXPHY_MODULEA_EN | COMBTXPHY_MODULEB_EN;
+		break;
+	case LVDS_SINGLE_LINK:
+	default:
+		val = SW_LVDS_CON_CHASEL(0) | SW_LVDS_CON_STARTSEL(0) |
+		      SW_LVDS_CON_DUAL_SEL(0);
+		bus_width = COMBTXPHY_MODULEA_EN;
+		break;
+	}
+
+	val |= SW_LVDS_CON_SELECT(format) | SW_LVDS_CON_MSBSEL(0) |
+	       SW_LVDS_CON_CLKINV(0);
+	lvds_write(rk628, GRF_LVDS_TX_CON, val);
+
+	bus_width |= (mode->clock / 1000) << 8;
+	rk628_combtxphy_set_bus_width(rk628, bus_width);
+	rk628_combtxphy_set_mode(rk628, PHY_MODE_VIDEO_LVDS);
+	rk628_combtxphy_power_on(rk628);
+	rk628_panel_prepare(rk628);
+	rk628_panel_enable(rk628);
+}
+
+void rk628_lvds_disable(struct rk628 *rk628)
+{
+	rk628_panel_disable(rk628);
+	rk628_panel_unprepare(rk628);
+	rk628_combtxphy_power_off(rk628);
+}
diff --git a/kernel/drivers/misc/rk628/rk628_lvds.h b/kernel/drivers/misc/rk628/rk628_lvds.h
new file mode 100644
index 0000000..dbae5b6
--- /dev/null
+++ b/kernel/drivers/misc/rk628/rk628_lvds.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2021 Rockchip Electronics Co. Ltd.
+ *
+ * Author: Guochun Huang <hero.huang@rock-chips.com>
+ */
+
+#ifndef RK628_LVDS_H
+#define RK628_LVDS_H
+
+int rk628_lvds_parse(struct rk628 *rk628, struct device_node *lvds_np);
+void rk628_lvds_enable(struct rk628 *rk628);
+void rk628_lvds_disable(struct rk628 *rk628);
+
+#endif
diff --git a/kernel/drivers/misc/rk628/rk628_pinctrl.c b/kernel/drivers/misc/rk628/rk628_pinctrl.c
new file mode 100644
index 0000000..115f904
--- /dev/null
+++ b/kernel/drivers/misc/rk628/rk628_pinctrl.c
@@ -0,0 +1,326 @@
+// SPDX-License-Identifier: BSD-3-Clause
+/*
+ * Copyright (c) 2021 Rockchip Electronics Co. Ltd.
+ *
+ * Author: Wyon Bi <bivvy.bi@rock-chips.com>
+ */
+
+#include "rk628.h"
+
+static int rk628_calc_mux_offset(struct rk628 *rk628, int mux, int reg, int offset)
+{
+	int val = 0, orig;
+
+	switch (reg) {
+	case GRF_SYSTEM_CON3:
+		rk628_i2c_read(rk628, reg, &orig);
+		if (mux)
+			val = BIT(offset) | orig;
+		else
+			val = ~BIT(offset) & orig;
+		break;
+	case GRF_GPIO0AB_SEL_CON:
+		if (offset >= 4 && offset < 8) {
+			offset += offset - 4;
+			val = 0x3 << (offset + 16) | (mux << offset);
+		} else if (offset > 7) {
+			offset += 4;
+			val = BIT(offset + 16) | (mux << offset);
+		} else {
+			val = BIT(offset + 16) | (mux << offset);
+		}
+		break;
+	case GRF_GPIO1AB_SEL_CON:
+		if (offset == 13)
+			offset++;
+		if (offset > 11)
+			val = 0x3 << (offset + 16) | (mux << offset);
+		else
+			val = BIT(offset + 16) | (mux << offset);
+		break;
+	case GRF_GPIO2AB_SEL_CON:
+		val = BIT(offset + 16) | (mux << offset);
+		break;
+	case GRF_GPIO2C_SEL_CON:
+		offset -= 16;
+		val = 0x3 << ((offset*2) + 16) | (mux << (offset*2));
+		break;
+	case GRF_GPIO3AB_SEL_CON:
+		if (offset > 11)
+			val = 0x3 << (offset + 16) | (mux << offset);
+		else
+			val = BIT(offset + 16) | (mux << offset);
+		break;
+	default:
+		break;
+	}
+
+	return val;
+}
+
+int rk628_misc_pinctrl_set_mux(struct rk628 *rk628, int gpio, int mux)
+{
+	int i, iomux_base, offset, val;
+
+	mux &= 0x3;
+
+	for (i = 0; i < ARRAY_SIZE(rk628_pin_iomux_groups); i++) {
+		if (rk628_pin_iomux_groups[i].pins == gpio) {
+			iomux_base = rk628_pin_iomux_groups[i].iomux_base;
+			offset = rk628_pin_iomux_groups[i].pins % BANK_OFFSET;
+			break;
+		}
+	}
+
+	if ((i == ARRAY_SIZE(rk628_pin_iomux_groups)) || (!iomux_base)) {
+		pr_info("%s invalid gpio or iomux_base\n", __func__);
+		return -1;
+	}
+
+	val = rk628_calc_mux_offset(rk628, mux, iomux_base, offset);
+
+	rk628_i2c_write(rk628, iomux_base, val);
+
+	return 0;
+
+}
+
+
+/* generic gpio chip */
+int rk628_misc_gpio_get_value(struct rk628 *rk628, int gpio)
+{
+	int i, data_reg, offset, val;
+
+	for (i = 0; i < ARRAY_SIZE(rk628_pin_iomux_groups); i++) {
+		if (rk628_pin_iomux_groups[i].pins == gpio) {
+			data_reg = rk628_pin_iomux_groups[i].gpio_base + GPIO_EXT_PORT;
+			offset = rk628_pin_iomux_groups[i].pins % BANK_OFFSET;
+			break;
+		}
+	}
+
+	if ((i == ARRAY_SIZE(rk628_pin_iomux_groups)) || (!data_reg)) {
+		pr_info("%s invalid gpio or data_reg\n", __func__);
+		return -1;
+	}
+
+	rk628_i2c_read(rk628, data_reg, &val);
+
+	val >>= offset;
+	val &= 1;
+
+	return val;
+}
+
+int rk628_misc_gpio_set_value(struct rk628 *rk628, int gpio, int value)
+{
+	int i, data_reg, offset, val;
+
+	for (i = 0; i < ARRAY_SIZE(rk628_pin_iomux_groups); i++) {
+		if (rk628_pin_iomux_groups[i].pins == gpio) {
+			offset = rk628_pin_iomux_groups[i].pins % BANK_OFFSET;
+			if (offset >= 16) {
+				data_reg = rk628_pin_iomux_groups[i].gpio_base + GPIO_SWPORT_DR_H;
+				offset -= 16;
+			} else {
+				data_reg = rk628_pin_iomux_groups[i].gpio_base + GPIO_SWPORT_DR_L;
+			}
+			break;
+		}
+	}
+
+	if ((i == ARRAY_SIZE(rk628_pin_iomux_groups)) || (!data_reg)) {
+		pr_info("%s invalid gpio or data_reg\n", __func__);
+		return -1;
+	}
+
+	if (value)
+		val = BIT(offset + 16) | BIT(offset);
+	else
+		val = BIT(offset + 16) | (0xffff & ~BIT(offset));
+
+	rk628_i2c_write(rk628, data_reg, val);
+
+	return 0;
+}
+
+
+
+int rk628_misc_gpio_set_direction(struct rk628 *rk628, int gpio, int direction)
+{
+	int i, dir_reg, offset, val;
+
+	for (i = 0; i < ARRAY_SIZE(rk628_pin_iomux_groups); i++) {
+		if (rk628_pin_iomux_groups[i].pins == gpio) {
+			offset = rk628_pin_iomux_groups[i].pins % BANK_OFFSET;
+			if (offset >= 16) {
+				dir_reg = rk628_pin_iomux_groups[i].gpio_base + GPIO_SWPORT_DDR_H;
+				offset -= 16;
+			} else {
+				dir_reg = rk628_pin_iomux_groups[i].gpio_base + GPIO_SWPORT_DDR_L;
+			}
+			break;
+		}
+	}
+
+	if ((i == ARRAY_SIZE(rk628_pin_iomux_groups)) || (!dir_reg)) {
+		pr_info("%s invalid gpio or dir_reg\n", __func__);
+		return -1;
+	}
+
+	if (!direction)
+		val = BIT(offset + 16) | (0xffff & ~BIT(offset));
+	else
+		val = BIT(offset + 16) | BIT(offset);
+
+	rk628_i2c_write(rk628, dir_reg, val);
+
+	return 0;
+}
+
+
+int rk628_misc_iomux_init(struct rk628 *rk628)
+{
+	int i, iomux_base, offset, val, mux;
+
+	for (i = 0; i < ARRAY_SIZE(rk628_pin_iomux_groups); i++) {
+		mux = rk628_pin_iomux_groups[i].mux;
+		iomux_base = rk628_pin_iomux_groups[i].iomux_base;
+		offset = rk628_pin_iomux_groups[i].pins % BANK_OFFSET;
+		if (iomux_base) {
+			val = rk628_calc_mux_offset(rk628, mux, iomux_base, offset);
+			rk628_i2c_write(rk628, iomux_base, val);
+		}
+	}
+
+	return 0;
+}
+
+
+int rk628_misc_gpio_direction_input(struct rk628 *rk628, int gpio)
+{
+	rk628_misc_pinctrl_set_mux(rk628, gpio, GPIO_FUNC);
+
+	rk628_misc_gpio_set_direction(rk628, gpio, GPIO_DIRECTION_IN);
+
+	return 0;
+}
+
+
+int rk628_misc_gpio_direction_output(struct rk628 *rk628, int gpio, int value)
+{
+
+	rk628_misc_pinctrl_set_mux(rk628, gpio, GPIO_FUNC);
+	rk628_misc_gpio_set_value(rk628, gpio, value);
+	rk628_misc_gpio_set_direction(rk628, gpio, GPIO_DIRECTION_OUT);
+	return 0;
+}
+
+
+int rk628_misc_gpio_set_pull_highz_up_down(struct rk628 *rk628, int gpio, int pull)
+{
+	int i, bank, pull_reg = 0, offset, val = 0;
+	int valid_pinnum[] = { 8, 8, 24, 13 };
+
+	for (i = 0; i < ARRAY_SIZE(rk628_pin_iomux_groups); i++) {
+		if (rk628_pin_iomux_groups[i].pins == gpio) {
+			bank = rk628_pin_iomux_groups[i].bank;
+			pull_reg = rk628_pin_iomux_groups[i].pull_reg;
+			offset = rk628_pin_iomux_groups[i].pins % BANK_OFFSET;
+			break;
+		}
+	}
+
+	if ((i == ARRAY_SIZE(rk628_pin_iomux_groups))  || (!pull_reg)) {
+		pr_info("rk628_gpio_pull_highz_up_down invalid gpio or pull_reg\n");
+		return -1;
+	}
+
+	switch (bank) {
+	case GPIO_BANK0:
+		if (pull == GPIO_PULL_UP)
+			return -1;
+
+		if (offset == 2)
+			return -1;
+
+		if (offset < valid_pinnum[bank])
+			val = 0x3 << (2 * offset + 16) | pull << (2 * offset);
+		break;
+	case GPIO_BANK1:
+		if (pull == GPIO_PULL_UP)
+			return -1;
+
+		if (offset == 2)
+			return -1;
+
+		if (offset < valid_pinnum[bank])
+			val = 0x3 << (2 * offset + 16) | pull << (2 * offset);
+		break;
+	case GPIO_BANK2:
+		if (pull == GPIO_PULL_UP)
+			pull = GPIO_PULL_DOWN;
+		else if (pull == GPIO_PULL_DOWN)
+			pull = GPIO_PULL_UP;
+
+		if (offset < valid_pinnum[bank]) {
+			offset = offset % 8;
+			val = 0x3 << (2 * offset + 16) | pull << (2 * offset);
+		}
+		break;
+	case GPIO_BANK3:
+		if (pull == GPIO_PULL_UP && (offset == 2 || offset == 11 || offset == 12))
+			return -1;
+		else if (pull == GPIO_PULL_DOWN && (offset == 9 || offset == 10))
+			return -1;
+
+		if (offset == 0 || offset == 1 || offset == 3 || offset == 8) {
+			if (pull == GPIO_PULL_UP)
+				pull = GPIO_PULL_DOWN;
+			else if (pull == GPIO_PULL_DOWN)
+				pull = GPIO_PULL_UP;
+		}
+
+		if ((offset > 7 && offset < valid_pinnum[bank]) || offset < 4) {
+			offset = offset % 8;
+			val = 0x3 << (2 * offset + 16) | pull << (2 * offset);
+		}
+		break;
+	default:
+		break;
+	}
+
+	rk628_i2c_write(rk628, pull_reg, val);
+
+	return 0;
+}
+
+
+
+int rk628_misc_gpio_test_all(struct rk628 *rk628)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(rk628_pin_iomux_groups); i++) {
+		if (rk628_pin_iomux_groups[i].pins && (rk628_pin_iomux_groups[i].pins != GPIO1_A1)
+		    && (rk628_pin_iomux_groups[i].pins != GPIO2_C0)
+		    && (rk628_pin_iomux_groups[i].pins != GPIO2_C1)
+		    && (rk628_pin_iomux_groups[i].pins != GPIO2_C2)
+		    && (rk628_pin_iomux_groups[i].pins != GPIO2_C3)
+		    && (rk628_pin_iomux_groups[i].pins != GPIO2_C4))
+			rk628_misc_gpio_direction_output(rk628, rk628_pin_iomux_groups[i].pins, 1);
+	}
+
+	for (i = 0; i < ARRAY_SIZE(rk628_pin_iomux_groups); i++) {
+		if (rk628_pin_iomux_groups[i].pins && (rk628_pin_iomux_groups[i].pins != GPIO1_A1)
+		    && (rk628_pin_iomux_groups[i].pins != GPIO2_C0)
+		    && (rk628_pin_iomux_groups[i].pins != GPIO2_C1)
+		    && (rk628_pin_iomux_groups[i].pins != GPIO2_C2)
+		    && (rk628_pin_iomux_groups[i].pins != GPIO2_C3)
+		    && (rk628_pin_iomux_groups[i].pins != GPIO2_C4))
+			rk628_misc_gpio_direction_output(rk628, rk628_pin_iomux_groups[i].pins, 0);
+	}
+
+	return 0;
+}
+
diff --git a/kernel/drivers/misc/rk628/rk628_pinctrl.h b/kernel/drivers/misc/rk628/rk628_pinctrl.h
new file mode 100644
index 0000000..b2c2b24
--- /dev/null
+++ b/kernel/drivers/misc/rk628/rk628_pinctrl.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: BSD-3-Clause */
+/*
+ * Copyright (c) 2021 Rockchip Electronics Co. Ltd.
+ *
+ * Author: Wyon Bi <bivvy.bi@rock-chips.com>
+ */
+
+#ifndef RK628_PINCTRL_H
+#define RK628_PINCTRL_H
+
+int rk628_misc_pinctrl_set_mux(struct rk628 *rk628, int gpio, int mux);
+int rk628_misc_gpio_get_value(struct rk628 *rk628, int gpio);
+int rk628_misc_gpio_set_value(struct rk628 *rk628, int gpio, int value);
+int rk628_misc_gpio_set_direction(struct rk628 *rk628, int gpio, int direction);
+int rk628_misc_gpio_direction_input(struct rk628 *rk628, int gpio);
+int rk628_misc_gpio_direction_output(struct rk628 *rk628, int gpio, int value);
+int rk628_misc_gpio_set_pull_highz_up_down(struct rk628 *rk628, int gpio, int pull);
+
+#endif // RK628_PINCTRL_H
diff --git a/kernel/drivers/misc/rk628/rk628_post_process.c b/kernel/drivers/misc/rk628/rk628_post_process.c
new file mode 100644
index 0000000..d5ffa3d
--- /dev/null
+++ b/kernel/drivers/misc/rk628/rk628_post_process.c
@@ -0,0 +1,268 @@
+// SPDX-License-Identifier: BSD-3-Clause
+/*
+ * Copyright (c) 2021 Rockchip Electronics Co. Ltd.
+ *
+ * Author: Wyon Bi <bivvy.bi@rock-chips.com>
+ */
+#include "rk628.h"
+#include "rk628_config.h"
+#include "rk628_cru.h"
+
+static void calc_dsp_frm_hst_vst(const struct rk628_display_mode *src,
+				 const struct rk628_display_mode *dst,
+				 u32 *dsp_frame_hst,
+				 u32 *dsp_frame_vst)
+{
+	u32 bp_in, bp_out;
+	u32 v_scale_ratio;
+	u64 t_frm_st;
+	u64 t_bp_in, t_bp_out, t_delta, tin;
+	u32 src_pixclock, dst_pixclock;
+	u32 dst_htotal, dst_hsync_len, dst_hback_porch;
+	u32 dst_vsync_len, dst_vback_porch, dst_vactive;
+	u32 src_htotal, src_hsync_len, src_hback_porch;
+	u32 src_vtotal, src_vsync_len, src_vback_porch, src_vactive;
+	u32 rem;
+	u32 x;
+
+	src_pixclock = div_u64(1000000000llu, src->clock);
+	dst_pixclock = div_u64(1000000000llu, dst->clock);
+
+	src_hsync_len = src->hsync_end - src->hsync_start;
+	src_hback_porch = src->htotal - src->hsync_end;
+	src_htotal = src->htotal;
+	src_vsync_len = src->vsync_end - src->vsync_start;
+	src_vback_porch = src->vtotal - src->vsync_end;
+	src_vactive = src->vdisplay;
+	src_vtotal = src->vtotal;
+
+	dst_hsync_len = dst->hsync_end - dst->hsync_start;
+	dst_hback_porch = dst->htotal - dst->hsync_end;
+	dst_htotal = dst->htotal;
+	dst_vsync_len = dst->vsync_end - dst->vsync_start;
+	dst_vback_porch = dst->vtotal - dst->vsync_end;
+	dst_vactive = dst->vdisplay;
+
+	bp_in = (src_vback_porch + src_vsync_len) * src_htotal +
+		src_hsync_len + src_hback_porch;
+	bp_out = (dst_vback_porch + dst_vsync_len) * dst_htotal +
+		 dst_hsync_len + dst_hback_porch;
+
+	t_bp_in = bp_in * src_pixclock;
+	t_bp_out = bp_out * dst_pixclock;
+	tin = src_vtotal * src_htotal * src_pixclock;
+
+	v_scale_ratio = src_vactive / dst_vactive;
+	x = 5;
+__retry:
+	if (v_scale_ratio <= 2)
+		t_delta = x * src_htotal * src_pixclock;
+	else
+		t_delta = 12 * src_htotal * src_pixclock;
+
+	if (t_bp_in + t_delta > t_bp_out)
+		t_frm_st = (t_bp_in + t_delta - t_bp_out);
+	else
+		t_frm_st = tin - (t_bp_out - (t_bp_in + t_delta));
+
+	do_div(t_frm_st, src_pixclock);
+	rem = do_div(t_frm_st, src_htotal);
+	if ((t_frm_st < 2 || t_frm_st > 14) && x < 12) {
+		x++;
+		goto __retry;
+	}
+	if (t_frm_st < 2 || t_frm_st > 14)
+		t_frm_st = 4;
+
+	*dsp_frame_hst = rem;
+	*dsp_frame_vst = t_frm_st;
+}
+
+static void rk628_post_process_scaler_init(struct rk628 *rk628,
+					   struct rk628_display_mode *src,
+					   const struct rk628_display_mode *dst)
+{
+	u32 dsp_frame_hst, dsp_frame_vst;
+	u32 scl_hor_mode, scl_ver_mode;
+	u32 scl_v_factor, scl_h_factor;
+	u32 dsp_htotal, dsp_hs_end, dsp_hact_st, dsp_hact_end;
+	u32 dsp_vtotal, dsp_vs_end, dsp_vact_st, dsp_vact_end;
+	u32 dsp_hbor_end, dsp_hbor_st, dsp_vbor_end, dsp_vbor_st;
+	u16 bor_right = 0, bor_left = 0, bor_up = 0, bor_down = 0;
+	u8 hor_down_mode = 0, ver_down_mode = 0;
+	u32 dst_hsync_len, dst_hback_porch, dst_hfront_porch, dst_hactive;
+	u32 dst_vsync_len, dst_vback_porch, dst_vfront_porch, dst_vactive;
+	u32 src_hactive;
+	u32 src_vactive;
+
+	src_hactive = src->hdisplay;
+	src_vactive = src->vdisplay;
+
+	dst_hactive = dst->hdisplay;
+	dst_hsync_len = dst->hsync_end - dst->hsync_start;
+	dst_hback_porch = dst->htotal - dst->hsync_end;
+	dst_hfront_porch = dst->hsync_start - dst->hdisplay;
+	dst_vsync_len = dst->vsync_end - dst->vsync_start;
+	dst_vback_porch = dst->vtotal - dst->vsync_end;
+	dst_vfront_porch = dst->vsync_start - dst->vdisplay;
+	dst_vactive = dst->vdisplay;
+
+	dsp_htotal = dst_hsync_len + dst_hback_porch +
+		     dst_hactive + dst_hfront_porch;
+	dsp_vtotal = dst_vsync_len + dst_vback_porch +
+		     dst_vactive + dst_vfront_porch;
+	dsp_hs_end = dst_hsync_len;
+	dsp_vs_end = dst_vsync_len;
+	dsp_hbor_end = dst_hsync_len + dst_hback_porch + dst_hactive;
+	dsp_hbor_st = dst_hsync_len + dst_hback_porch;
+	dsp_vbor_end = dst_vsync_len + dst_vback_porch + dst_vactive;
+	dsp_vbor_st = dst_vsync_len + dst_vback_porch;
+	dsp_hact_st = dsp_hbor_st + bor_left;
+	dsp_hact_end = dsp_hbor_end - bor_right;
+	dsp_vact_st = dsp_vbor_st + bor_up;
+	dsp_vact_end = dsp_vbor_end - bor_down;
+
+	calc_dsp_frm_hst_vst(src, dst, &dsp_frame_hst, &dsp_frame_vst);
+	dev_info(rk628->dev, "dsp_frame_vst:%d  dsp_frame_hst:%d\n",
+		 dsp_frame_vst, dsp_frame_hst);
+
+	if (src_hactive > dst_hactive) {
+		scl_hor_mode = 2;
+
+		if (hor_down_mode == 0) {
+			if ((src_hactive - 1) / (dst_hactive - 1) > 2)
+				scl_h_factor = ((src_hactive - 1) << 14) /
+						(dst_hactive - 1);
+			else
+				scl_h_factor = ((src_hactive - 2) << 14) /
+						(dst_hactive - 1);
+		} else {
+			scl_h_factor = (dst_hactive << 16) / (src_hactive - 1);
+		}
+
+	} else if (src_hactive == dst_hactive) {
+		scl_hor_mode = 0;
+		scl_h_factor = 0;
+	} else {
+		scl_hor_mode = 1;
+		scl_h_factor = ((src_hactive - 1) << 16) / (dst_hactive - 1);
+	}
+
+	if (src_vactive > dst_vactive) {
+		scl_ver_mode = 2;
+
+		if (ver_down_mode == 0) {
+			if ((src_vactive - 1) / (dst_vactive - 1) > 2)
+				scl_v_factor = ((src_vactive - 1) << 14) /
+						(dst_vactive - 1);
+			else
+				scl_v_factor = ((src_vactive - 2) << 14) /
+						(dst_vactive - 1);
+		} else {
+			scl_v_factor = (dst_vactive << 16) / (src_vactive - 1);
+		}
+
+	} else if (src_vactive == dst_vactive) {
+		scl_ver_mode = 0;
+		scl_v_factor = 0;
+	} else {
+		scl_ver_mode = 1;
+		scl_v_factor = ((src_vactive - 1) << 16) / (dst_vactive - 1);
+	}
+
+	rk628_i2c_update_bits(rk628, GRF_RGB_DEC_CON0, SW_HRES_MASK,
+			      SW_HRES(src_hactive));
+	rk628_i2c_write(rk628, GRF_SCALER_CON0, SCL_VER_DOWN_MODE(ver_down_mode) |
+			SCL_HOR_DOWN_MODE(hor_down_mode) |
+			SCL_VER_MODE(scl_ver_mode) |
+			SCL_HOR_MODE(scl_hor_mode));
+	rk628_i2c_write(rk628, GRF_SCALER_CON1, SCL_V_FACTOR(scl_v_factor) |
+			SCL_H_FACTOR(scl_h_factor));
+	rk628_i2c_write(rk628, GRF_SCALER_CON2, DSP_FRAME_VST(dsp_frame_vst) |
+			DSP_FRAME_HST(dsp_frame_hst));
+	rk628_i2c_write(rk628, GRF_SCALER_CON3, DSP_HS_END(dsp_hs_end) |
+			DSP_HTOTAL(dsp_htotal));
+	rk628_i2c_write(rk628, GRF_SCALER_CON4, DSP_HACT_END(dsp_hact_end) |
+			DSP_HACT_ST(dsp_hact_st));
+	rk628_i2c_write(rk628, GRF_SCALER_CON5, DSP_VS_END(dsp_vs_end) |
+			DSP_VTOTAL(dsp_vtotal));
+	rk628_i2c_write(rk628, GRF_SCALER_CON6, DSP_VACT_END(dsp_vact_end) |
+			DSP_VACT_ST(dsp_vact_st));
+	rk628_i2c_write(rk628, GRF_SCALER_CON7, DSP_HBOR_END(dsp_hbor_end) |
+			DSP_HBOR_ST(dsp_hbor_st));
+	rk628_i2c_write(rk628, GRF_SCALER_CON8, DSP_VBOR_END(dsp_vbor_end) |
+			DSP_VBOR_ST(dsp_vbor_st));
+}
+
+void rk628_post_process_init(struct rk628 *rk628)
+{
+	struct rk628_display_mode *src = &rk628->src_mode;
+	const struct rk628_display_mode *dst = &rk628->dst_mode;
+	u64 dst_rate, src_rate;
+
+	src_rate = src->clock * 1000;
+	dst_rate = src_rate * dst->vdisplay * dst->htotal;
+	do_div(dst_rate, (src->vdisplay * src->htotal));
+	do_div(dst_rate, 1000);
+	dev_info(rk628->dev, "src %dx%d clock:%d\n",
+		 src->hdisplay, src->vdisplay, src->clock);
+
+	dev_info(rk628->dev, "dst %dx%d clock:%llu\n",
+		 dst->hdisplay, dst->vdisplay, dst_rate);
+
+	rk628_cru_clk_set_rate(rk628, CGU_CLK_RX_READ, src->clock * 1000);
+	rk628_cru_clk_set_rate(rk628, CGU_SCLK_VOP, dst_rate * 1000);
+
+	if (rk628->output_mode == OUTPUT_MODE_HDMI) {
+		rk628_i2c_update_bits(rk628, GRF_SYSTEM_CON0, SW_VSYNC_POL_MASK,
+				      SW_VSYNC_POL(rk628->sync_pol));
+		rk628_i2c_update_bits(rk628, GRF_SYSTEM_CON0, SW_HSYNC_POL_MASK,
+				      SW_HSYNC_POL(rk628->sync_pol));
+	} else {
+		if (src->flags & DRM_MODE_FLAG_PVSYNC)
+			rk628_i2c_update_bits(rk628, GRF_SYSTEM_CON0,
+					      SW_VSYNC_POL_MASK, SW_VSYNC_POL(1));
+		if (src->flags & DRM_MODE_FLAG_PHSYNC)
+			rk628_i2c_update_bits(rk628, GRF_SYSTEM_CON0,
+					      SW_HSYNC_POL_MASK,
+					      SW_HSYNC_POL(1));
+	}
+
+	rk628_post_process_scaler_init(rk628, src, dst);
+}
+
+static void rk628_post_process_csc(struct rk628 *rk628)
+{
+	enum bus_format in_fmt, out_fmt;
+
+	in_fmt = rk628_get_input_bus_format(rk628);
+	out_fmt = rk628_get_output_bus_format(rk628);
+
+	if (in_fmt == out_fmt) {
+		if (out_fmt == BUS_FMT_YUV422) {
+			rk628_i2c_write(rk628, GRF_CSC_CTRL_CON,
+					SW_YUV2VYU_SWP(1) |
+					SW_R2Y_EN(0));
+			return;
+		}
+		rk628_i2c_write(rk628, GRF_CSC_CTRL_CON, SW_R2Y_EN(0));
+		rk628_i2c_write(rk628, GRF_CSC_CTRL_CON, SW_Y2R_EN(0));
+		return;
+	}
+
+	if (in_fmt == BUS_FMT_RGB)
+		rk628_i2c_write(rk628, GRF_CSC_CTRL_CON, SW_R2Y_EN(1));
+	else if (out_fmt == BUS_FMT_RGB)
+		rk628_i2c_write(rk628, GRF_CSC_CTRL_CON, SW_Y2R_EN(1));
+}
+
+void rk628_post_process_enable(struct rk628 *rk628)
+{
+	rk628_post_process_csc(rk628);
+	rk628_i2c_write(rk628, GRF_SCALER_CON0, SCL_EN(1));
+}
+
+void rk628_post_process_disable(struct rk628 *rk628)
+{
+	rk628_i2c_write(rk628, GRF_SCALER_CON0, SCL_EN(0));
+}
diff --git a/kernel/drivers/misc/rk628/rk628_post_process.h b/kernel/drivers/misc/rk628/rk628_post_process.h
new file mode 100644
index 0000000..62a7fd0
--- /dev/null
+++ b/kernel/drivers/misc/rk628/rk628_post_process.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2020 Rockchip Electronics Co. Ltd.
+ *
+ * Author: Wyon Bi <bivvy.bi@rock-chips.com>
+ */
+
+#ifndef POST_PROCESS_H
+#define POST_PROCESS_H
+
+void rk628_post_process_init(struct rk628 *rk628);
+void rk628_post_process_enable(struct rk628 *rk628);
+void rk628_post_process_disable(struct rk628 *rk628);
+
+#endif
diff --git a/kernel/drivers/misc/rk628/rk628_rgb.c b/kernel/drivers/misc/rk628/rk628_rgb.c
new file mode 100644
index 0000000..23c8d25
--- /dev/null
+++ b/kernel/drivers/misc/rk628/rk628_rgb.c
@@ -0,0 +1,166 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2020 Rockchip Electronics Co. Ltd.
+ *
+ * Author: Guochun Huang <hero.huang@rock-chips.com>
+ */
+
+#include "rk628.h"
+#include "rk628_cru.h"
+#include "rk628_config.h"
+#include "panel.h"
+
+void rk628_rgb_decoder_enable(struct rk628 *rk628)
+{
+		/* config sw_input_mode RGB */
+	rk628_i2c_update_bits(rk628, GRF_SYSTEM_CON0, SW_INPUT_MODE_MASK,
+			      SW_INPUT_MODE(INPUT_MODE_RGB));
+
+	/* pinctrl for vop pin */
+	rk628_i2c_write(rk628, GRF_GPIO2AB_SEL_CON, 0xffffffff);
+	rk628_i2c_write(rk628, GRF_GPIO2C_SEL_CON, 0xffff5555);
+	rk628_i2c_write(rk628, GRF_GPIO3AB_SEL_CON, 0x10b010b);
+
+	/* rk628: modify IO drive strength for RGB */
+	rk628_i2c_write(rk628, GRF_GPIO2A_D0_CON, 0xffff1111);
+	rk628_i2c_write(rk628, GRF_GPIO2A_D1_CON, 0xffff1111);
+	rk628_i2c_write(rk628, GRF_GPIO2B_D0_CON, 0xffff1111);
+	rk628_i2c_write(rk628, GRF_GPIO2B_D1_CON, 0xffff1111);
+	rk628_i2c_write(rk628, GRF_GPIO2C_D0_CON, 0xffff1111);
+	rk628_i2c_write(rk628, GRF_GPIO2C_D1_CON, 0xffff1111);
+	rk628_i2c_write(rk628, GRF_GPIO3A_D0_CON, 0xffff1011);
+	rk628_i2c_write(rk628, GRF_GPIO3B_D_CON, 0x10001);
+}
+
+void rk628_rgb_encoder_enable(struct rk628 *rk628)
+{
+	rk628_i2c_update_bits(rk628, GRF_SYSTEM_CON0,
+			      SW_BT_DATA_OEN_MASK | SW_OUTPUT_MODE_MASK,
+			      SW_OUTPUT_MODE(OUTPUT_MODE_RGB));
+	rk628_i2c_update_bits(rk628, GRF_POST_PROC_CON, SW_DCLK_OUT_INV_EN,
+			      SW_DCLK_OUT_INV_EN);
+}
+
+void rk628_rgb_encoder_disable(struct rk628 *rk628)
+{
+	rk628_panel_disable(rk628);
+	rk628_panel_unprepare(rk628);
+}
+
+
+void rk628_rgb_rx_enable(struct rk628 *rk628)
+{
+
+	rk628_rgb_decoder_enable(rk628);
+
+}
+
+void rk628_rgb_tx_enable(struct rk628 *rk628)
+{
+	rk628_rgb_encoder_enable(rk628);
+
+	rk628_panel_prepare(rk628);
+	rk628_panel_enable(rk628);
+}
+
+void rk628_rgb_tx_disable(struct rk628 *rk628)
+{
+	rk628_panel_disable(rk628);
+}
+
+void rk628_bt1120_decoder_enable(struct rk628 *rk628)
+{
+	struct rk628_display_mode *mode = rk628_display_get_src_mode(rk628);
+
+	/* pinctrl for vop pin */
+	rk628_i2c_write(rk628, GRF_GPIO2AB_SEL_CON, 0xffffffff);
+	rk628_i2c_write(rk628, GRF_GPIO2C_SEL_CON, 0xffff5555);
+	rk628_i2c_write(rk628, GRF_GPIO3AB_SEL_CON, 0x10b010b);
+
+	/* rk628: modify IO drive strength for RGB */
+	rk628_i2c_write(rk628, GRF_GPIO2A_D0_CON, 0xffff1111);
+	rk628_i2c_write(rk628, GRF_GPIO2A_D1_CON, 0xffff1111);
+	rk628_i2c_write(rk628, GRF_GPIO2B_D0_CON, 0xffff1111);
+	rk628_i2c_write(rk628, GRF_GPIO2B_D1_CON, 0xffff1111);
+	rk628_i2c_write(rk628, GRF_GPIO2C_D0_CON, 0xffff1111);
+	rk628_i2c_write(rk628, GRF_GPIO2C_D1_CON, 0xffff1111);
+	rk628_i2c_write(rk628, GRF_GPIO3A_D0_CON, 0xffff1011);
+	rk628_i2c_write(rk628, GRF_GPIO3B_D_CON, 0x10001);
+
+	/* config sw_input_mode bt1120 */
+	rk628_i2c_update_bits(rk628, GRF_SYSTEM_CON0, SW_INPUT_MODE_MASK,
+			      SW_INPUT_MODE(INPUT_MODE_BT1120));
+
+	/* operation resetn_bt1120dec */
+	rk628_i2c_write(rk628, CRU_SOFTRST_CON00, 0x10001000);
+	rk628_i2c_write(rk628, CRU_SOFTRST_CON00, 0x10000000);
+
+	rk628_cru_clk_set_rate(rk628, CGU_BT1120DEC, mode->clock * 1000);
+
+#ifdef BT1120_DUAL_EDGE
+	rk628_i2c_update_bits(rk628, GRF_RGB_DEC_CON0,
+			      DEC_DUALEDGE_EN, DEC_DUALEDGE_EN);
+	rk628_i2c_write(rk628, GRF_BT1120_DCLK_DELAY_CON0, 0x10000000);
+	rk628_i2c_write(rk628, GRF_BT1120_DCLK_DELAY_CON1, 0);
+#endif
+
+	rk628_i2c_update_bits(rk628, GRF_RGB_DEC_CON1, SW_SET_X_MASK,
+			      SW_SET_X(mode->hdisplay));
+	rk628_i2c_update_bits(rk628, GRF_RGB_DEC_CON2, SW_SET_Y_MASK,
+			      SW_SET_Y(mode->vdisplay));
+
+	rk628_i2c_update_bits(rk628, GRF_SYSTEM_CON0,
+			      SW_BT_DATA_OEN_MASK | SW_INPUT_MODE_MASK,
+			      SW_BT_DATA_OEN | SW_INPUT_MODE(INPUT_MODE_BT1120));
+	rk628_i2c_write(rk628, GRF_CSC_CTRL_CON, SW_Y2R_EN(1));
+	rk628_i2c_update_bits(rk628, GRF_RGB_DEC_CON0,
+			      SW_CAP_EN_PSYNC | SW_CAP_EN_ASYNC | SW_PROGRESS_EN,
+			      SW_CAP_EN_PSYNC | SW_CAP_EN_ASYNC | SW_PROGRESS_EN);
+}
+
+void rk628_bt1120_encoder_enable(struct rk628 *rk628)
+{
+	u32 val = 0;
+
+	/* pinctrl for vop pin */
+	rk628_i2c_write(rk628, GRF_GPIO2AB_SEL_CON, 0xffffffff);
+	rk628_i2c_write(rk628, GRF_GPIO2C_SEL_CON, 0xffff5555);
+	rk628_i2c_write(rk628, GRF_GPIO3AB_SEL_CON, 0x10b010b);
+
+	/* rk628: modify IO drive strength for RGB */
+	rk628_i2c_write(rk628, GRF_GPIO2A_D0_CON, 0xffff1111);
+	rk628_i2c_write(rk628, GRF_GPIO2A_D1_CON, 0xffff1111);
+	rk628_i2c_write(rk628, GRF_GPIO2B_D0_CON, 0xffff1111);
+	rk628_i2c_write(rk628, GRF_GPIO2B_D1_CON, 0xffff1111);
+	rk628_i2c_write(rk628, GRF_GPIO2C_D0_CON, 0xffff1111);
+	rk628_i2c_write(rk628, GRF_GPIO2C_D1_CON, 0xffff1111);
+	rk628_i2c_write(rk628, GRF_GPIO3A_D0_CON, 0xffff1011);
+	rk628_i2c_write(rk628, GRF_GPIO3B_D_CON, 0x10001);
+
+	/* config sw_input_mode bt1120 */
+	rk628_i2c_update_bits(rk628, GRF_SYSTEM_CON0,
+			      SW_BT_DATA_OEN_MASK | SW_OUTPUT_MODE_MASK,
+			      SW_OUTPUT_MODE(OUTPUT_MODE_BT1120));
+	rk628_i2c_write(rk628, GRF_CSC_CTRL_CON, SW_R2Y_EN(1));
+	rk628_i2c_update_bits(rk628, GRF_POST_PROC_CON,
+			      SW_DCLK_OUT_INV_EN, SW_DCLK_OUT_INV_EN);
+
+#ifdef BT1120_DUAL_EDGE
+	val |= ENC_DUALEDGE_EN(1);
+	rk628_i2c_write(rk628, GRF_BT1120_DCLK_DELAY_CON0, 0x10000000);
+	rk628_i2c_write(rk628, GRF_BT1120_DCLK_DELAY_CON1, 0);
+#endif
+	val |= BT1120_UV_SWAP(1);
+	rk628_i2c_write(rk628, GRF_RGB_ENC_CON, val);
+}
+
+void rk628_bt1120_rx_enable(struct rk628 *rk628)
+{
+	rk628_bt1120_decoder_enable(rk628);
+}
+
+void rk628_bt1120_tx_enable(struct rk628 *rk628)
+{
+	rk628_bt1120_encoder_enable(rk628);
+}
+
diff --git a/kernel/drivers/misc/rk628/rk628_rgb.h b/kernel/drivers/misc/rk628/rk628_rgb.h
new file mode 100644
index 0000000..e4bd486
--- /dev/null
+++ b/kernel/drivers/misc/rk628/rk628_rgb.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2021 Rockchip Electronics Co. Ltd.
+ *
+ * Author: Guochun Huang <hero.huang@rock-chips.com>
+ */
+
+#ifndef RK628_RGB_H
+#define RK628_RGB_H
+#include "rk628.h"
+
+void rk628_rgb_rx_enable(struct rk628 *rk628);
+void rk628_rgb_tx_enable(struct rk628 *rk628);
+void rk628_rgb_tx_disable(struct rk628 *rk628);
+void rk628_bt1120_rx_enable(struct rk628 *rk628);
+void rk628_bt1120_tx_enable(struct rk628 *rk628);
+#endif
diff --git a/kernel/drivers/mmc/host/dw_mmc-rockchip.c b/kernel/drivers/mmc/host/dw_mmc-rockchip.c
index cdc97f8..a88e8f1 100644
--- a/kernel/drivers/mmc/host/dw_mmc-rockchip.c
+++ b/kernel/drivers/mmc/host/dw_mmc-rockchip.c
@@ -166,7 +166,7 @@
 	 * It's impossible all 4 fixed phase won't be able to work.
 	 */
 	for (i = 0; i < ARRAY_SIZE(degrees); i++) {
-		degree = degrees[i] + priv->last_degree;
+		degree = degrees[i] + priv->last_degree + 90;
 		degree = degree % 360;
 		clk_set_phase(priv->sample_clk, degree);
 		if (!mmc_send_tuning(mmc, opcode, NULL))
@@ -174,12 +174,12 @@
 	}
 
 	if (i == ARRAY_SIZE(degrees)) {
-		dev_warn(host->dev, "All phases bad!");
+		dev_warn(host->dev, "V2 All phases bad!");
 		return -EIO;
 	}
 
 done:
-	dev_info(host->dev, "Successfully tuned phase to %d\n", degrees[i]);
+	dev_info(host->dev, "Successfully tuned phase to %d\n", degree);
 	priv->last_degree = degree;
 	return 0;
 }
@@ -208,8 +208,7 @@
 	}
 
 	if (priv->use_v2_tuning) {
-		ret = dw_mci_v2_execute_tuning(slot, opcode);
-		if (!ret)
+		if (!dw_mci_v2_execute_tuning(slot, opcode))
 			return 0;
 		/* Otherwise we continue using fine tuning */
 	}
@@ -448,8 +447,10 @@
 	if (!pdev->dev.of_node)
 		return -ENODEV;
 
-	if (!device_property_read_bool(&pdev->dev, "non-removable") &&
-	    !device_property_read_bool(&pdev->dev, "cd-gpios"))
+	if ((!device_property_read_bool(&pdev->dev, "non-removable") &&
+	     !device_property_read_bool(&pdev->dev, "cd-gpios")) ||
+	    (device_property_read_bool(&pdev->dev, "no-sd") &&
+	     device_property_read_bool(&pdev->dev, "no-mmc")))
 		use_rpm = false;
 
 	match = of_match_node(dw_mci_rockchip_match, pdev->dev.of_node);
diff --git a/kernel/drivers/mmc/host/dw_mmc.c b/kernel/drivers/mmc/host/dw_mmc.c
index fde4946..a88d9b9 100644
--- a/kernel/drivers/mmc/host/dw_mmc.c
+++ b/kernel/drivers/mmc/host/dw_mmc.c
@@ -1468,7 +1468,7 @@
 {
 	struct dw_mci_slot *slot = mmc_priv(mmc);
 	const struct dw_mci_drv_data *drv_data = slot->host->drv_data;
-	u32 regs;
+	u32 regs, power_off_delay;
 	int ret;
 
 	switch (ios->bus_width) {
@@ -1507,8 +1507,14 @@
 
 	switch (ios->power_mode) {
 	case MMC_POWER_UP:
-		if (!IS_ERR_OR_NULL(slot->host->pinctrl))
-			pinctrl_select_state(slot->host->pinctrl, slot->host->idle_state);
+		if (dw_mci_get_cd(mmc) && !IS_ERR_OR_NULL(slot->host->pinctrl)) {
+			if (!pinctrl_select_state(slot->host->pinctrl, slot->host->idle_state)) {
+				if (device_property_read_u32(slot->host->dev, "power-off-delay-ms",
+				    &power_off_delay))
+					power_off_delay = 200;
+				msleep(power_off_delay);
+			}
+		}
 
 		if (!IS_ERR(mmc->supply.vmmc)) {
 			ret = mmc_regulator_set_ocr(mmc, mmc->supply.vmmc,
diff --git a/kernel/drivers/mmc/host/rk_sdmmc_ops.c b/kernel/drivers/mmc/host/rk_sdmmc_ops.c
index 96f20d2..d0ef613 100644
--- a/kernel/drivers/mmc/host/rk_sdmmc_ops.c
+++ b/kernel/drivers/mmc/host/rk_sdmmc_ops.c
@@ -22,6 +22,8 @@
 #include <linux/seq_file.h>
 #include <linux/mutex.h>
 #include <linux/miscdevice.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
 #include "../core/block.h"
 #include "../core/card.h"
 #include "../core/core.h"
@@ -131,7 +133,7 @@
 /*
  * Transfer a single sector of kernel addressable data
  */
-int rk_emmc_transfer(u8 *buffer, unsigned addr, unsigned blksz, int write)
+int rk_emmc_transfer(u8 *buffer, unsigned int addr, unsigned int datasz, int write)
 {
 	int ret = 0;
 	enum emmc_area_type areatype;
@@ -150,11 +152,18 @@
 	mrq.data = &data;
 	mrq.stop = &stop;
 
-	sg_init_one(&sg, buffer, blksz);
+	sg_init_one(&sg, buffer, datasz);
 
-	rk_emmc_prepare_mrq(&mrq, &sg, 1, addr, 1, blksz, write);
+	rk_emmc_prepare_mrq(&mrq, &sg, 1, addr, datasz / BLKSZ, BLKSZ, write);
 
+	pm_runtime_get_sync(&this_card->dev);
 	mmc_claim_host(this_card->host);
+
+	if (this_card->ext_csd.cmdq_en) {
+		ret = mmc_cmdq_disable(this_card);
+		if (ret)
+			goto exit;
+	}
 
 	areatype = (enum emmc_area_type)this_card->ext_csd.part_config
 		    & EXT_CSD_PART_CONFIG_ACC_MASK;
@@ -186,7 +195,12 @@
 	}
 
 exit:
+	if (this_card->reenable_cmdq && !this_card->ext_csd.cmdq_en)
+		mmc_cmdq_enable(this_card);
+
 	mmc_release_host(this_card->host);
+	pm_runtime_put(&this_card->dev);
+
 	return ret;
 }
 EXPORT_SYMBOL(rk_emmc_transfer);
diff --git a/kernel/drivers/mmc/host/rk_sdmmc_ops.h b/kernel/drivers/mmc/host/rk_sdmmc_ops.h
index 8261d69..81890ab 100644
--- a/kernel/drivers/mmc/host/rk_sdmmc_ops.h
+++ b/kernel/drivers/mmc/host/rk_sdmmc_ops.h
@@ -6,6 +6,6 @@
 #ifndef _RK_SDMMC_OPS_H_
 #define _RK_SDMMC_OPS_H_
 
-int rk_emmc_transfer(u8 *buffer, unsigned int addr, unsigned int blksz, int write);
+int rk_emmc_transfer(u8 *buffer, unsigned int addr, unsigned int datasz, int write);
 
 #endif
diff --git a/kernel/drivers/mmc/host/sdhci-of-dwcmshc.c b/kernel/drivers/mmc/host/sdhci-of-dwcmshc.c
index 9db254e..ae9a2a9 100644
--- a/kernel/drivers/mmc/host/sdhci-of-dwcmshc.c
+++ b/kernel/drivers/mmc/host/sdhci-of-dwcmshc.c
@@ -335,17 +335,19 @@
 	sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_RXCLK);
 
 	txclk_tapnum = drv_data->hs200_tx_tap;
-	if ((drv_data->flags & RK_DLL_CMD_OUT) &&
-	    host->mmc->ios.timing == MMC_TIMING_MMC_HS400) {
+	if (host->mmc->ios.timing == MMC_TIMING_MMC_HS400) {
 		txclk_tapnum = drv_data->hs400_tx_tap;
-		extra = DLL_CMDOUT_SRC_CLK_NEG |
-			DLL_CMDOUT_BOTH_CLK_EDGE |
-			DWCMSHC_EMMC_DLL_DLYENA |
-			drv_data->hs400_cmd_tap |
-			DLL_CMDOUT_TAPNUM_FROM_SW;
-		if (drv_data->flags & RK_TAP_VALUE_SEL)
-			extra |= DLL_TAP_VALUE_SEL | dll_lock_value << DLL_TAP_VALUE_OFFSET;
-		sdhci_writel(host, extra, DECMSHC_EMMC_DLL_CMDOUT);
+
+		if (drv_data->flags & RK_DLL_CMD_OUT) {
+			extra = DLL_CMDOUT_SRC_CLK_NEG |
+				DLL_CMDOUT_BOTH_CLK_EDGE |
+				DWCMSHC_EMMC_DLL_DLYENA |
+				drv_data->hs400_cmd_tap |
+				DLL_CMDOUT_TAPNUM_FROM_SW;
+			if (drv_data->flags & RK_TAP_VALUE_SEL)
+				extra |= DLL_TAP_VALUE_SEL | dll_lock_value << DLL_TAP_VALUE_OFFSET;
+			sdhci_writel(host, extra, DECMSHC_EMMC_DLL_CMDOUT);
+		}
 	}
 	extra = DWCMSHC_EMMC_DLL_DLYENA |
 		DLL_TXCLK_TAPNUM_FROM_SW |
diff --git a/kernel/drivers/mtd/mtdoops.c b/kernel/drivers/mtd/mtdoops.c
index 6bc2c72..774970b 100644
--- a/kernel/drivers/mtd/mtdoops.c
+++ b/kernel/drivers/mtd/mtdoops.c
@@ -267,8 +267,7 @@
 }
 
 static void mtdoops_do_dump(struct kmsg_dumper *dumper,
-			    enum kmsg_dump_reason reason,
-			    struct kmsg_dumper_iter *iter)
+			    enum kmsg_dump_reason reason)
 {
 	struct mtdoops_context *cxt = container_of(dumper,
 			struct mtdoops_context, dump);
@@ -277,7 +276,7 @@
 	if (reason == KMSG_DUMP_OOPS && !dump_oops)
 		return;
 
-	kmsg_dump_get_buffer(iter, true, cxt->oops_buf + MTDOOPS_HEADER_SIZE,
+	kmsg_dump_get_buffer(dumper, true, cxt->oops_buf + MTDOOPS_HEADER_SIZE,
 			     record_size - MTDOOPS_HEADER_SIZE, NULL);
 
 	if (reason != KMSG_DUMP_OOPS) {
diff --git a/kernel/drivers/mtd/nand/spi/core.c b/kernel/drivers/mtd/nand/spi/core.c
index bd18e6d..f783f2c 100644
--- a/kernel/drivers/mtd/nand/spi/core.c
+++ b/kernel/drivers/mtd/nand/spi/core.c
@@ -527,7 +527,7 @@
 			     const struct nand_page_io_req *req,
 			     bool ecc_enabled)
 {
-	u8 status;
+	u8 status = 0;
 	int ret;
 
 	ret = spinand_load_page_op(spinand, req);
@@ -535,6 +535,13 @@
 		return ret;
 
 	ret = spinand_wait(spinand, &status);
+	/*
+	 * When there is data outside of OIP in the status, the status data is
+	 * inaccurate and needs to be reconfirmed
+	 */
+	if (spinand->id.data[0] == 0x01 && status && !ret)
+		ret = spinand_wait(spinand, &status);
+
 	if (ret < 0)
 		return ret;
 
@@ -1111,6 +1118,13 @@
 		if (ret)
 			return ret;
 
+		/* HWP_EN must be enabled first before block unlock region is set */
+		if (spinand->id.data[0] == 0x01) {
+			ret = spinand_lock_block(spinand, HWP_EN);
+			if (ret)
+				return ret;
+		}
+
 		ret = spinand_lock_block(spinand, BL_ALL_UNLOCKED);
 		if (ret)
 			return ret;
diff --git a/kernel/drivers/mtd/nand/spi/dosilicon.c b/kernel/drivers/mtd/nand/spi/dosilicon.c
index d6e38ae..cef6883 100644
--- a/kernel/drivers/mtd/nand/spi/dosilicon.c
+++ b/kernel/drivers/mtd/nand/spi/dosilicon.c
@@ -9,7 +9,7 @@
 
 #define SPINAND_MFR_DOSILICON			0xE5
 
-#define DOSICON_STATUS_ECC_MASK			GENMASK(7, 4)
+#define DOSICON_STATUS_ECC_MASK			GENMASK(6, 4)
 #define DOSICON_STATUS_ECC_NO_BITFLIPS		(0 << 4)
 #define DOSICON_STATUS_ECC_1TO3_BITFLIPS	(1 << 4)
 #define DOSICON_STATUS_ECC_4TO6_BITFLIPS	(3 << 4)
@@ -213,6 +213,15 @@
 		     SPINAND_HAS_QE_BIT,
 		     SPINAND_ECCINFO(&ds35xxgb_ooblayout,
 				     ds35xxgb_ecc_get_status)),
+	SPINAND_INFO("DS35Q1GD-IB",
+		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x51),
+		     NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
+		     NAND_ECCREQ(8, 512),
+		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+					      &write_cache_variants,
+					      &update_cache_variants),
+		     SPINAND_HAS_QE_BIT,
+		     SPINAND_ECCINFO(&ds35xxgb_ooblayout, ds35xxgb_ecc_get_status)),
 };
 
 static const struct spinand_manufacturer_ops dosilicon_spinand_manuf_ops = {
diff --git a/kernel/drivers/mtd/nand/spi/esmt.c b/kernel/drivers/mtd/nand/spi/esmt.c
index 588a7e2..f658de8 100644
--- a/kernel/drivers/mtd/nand/spi/esmt.c
+++ b/kernel/drivers/mtd/nand/spi/esmt.c
@@ -29,7 +29,7 @@
 		SPINAND_PROG_LOAD(false, 0, NULL, 0));
 
 static int f50lxx41x_ooblayout_ecc(struct mtd_info *mtd, int section,
-				  struct mtd_oob_region *region)
+				   struct mtd_oob_region *region)
 {
 	if (section > 3)
 		return -ERANGE;
@@ -41,7 +41,7 @@
 }
 
 static int f50lxx41x_ooblayout_free(struct mtd_info *mtd, int section,
-				   struct mtd_oob_region *region)
+				    struct mtd_oob_region *region)
 {
 	if (section > 3)
 		return -ERANGE;
@@ -57,6 +57,60 @@
 	.free = f50lxx41x_ooblayout_free,
 };
 
+static int f50l2g41ka_ooblayout_ecc(struct mtd_info *mtd, int section,
+				    struct mtd_oob_region *region)
+{
+	if (section)
+		return -ERANGE;
+
+	region->offset = mtd->oobsize / 2;
+	region->length = mtd->oobsize / 2;
+
+	return 0;
+}
+
+static int f50l2g41ka_ooblayout_free(struct mtd_info *mtd, int section,
+				     struct mtd_oob_region *region)
+{
+	if (section)
+		return -ERANGE;
+
+	region->offset = 2;
+	region->length = mtd->oobsize / 2 - 2;
+
+	return 0;
+}
+
+static const struct mtd_ooblayout_ops f50l2g41ka_ooblayout = {
+	.ecc = f50l2g41ka_ooblayout_ecc,
+	.free = f50l2g41ka_ooblayout_free,
+};
+
+/*
+ * ecc bits: 0xC0[4,6]
+ * [0b000], No bit errors were detected;
+ * [0b001] and [0b011], 1~6 Bit errors were detected and corrected. Not
+ *	reach Flipping Bits;
+ * [0b101], Bit error count equals the bit flip
+ *	detection threshold
+ * [0b010], Multiple bit errors were detected and
+ *	not corrected.
+ * others, Reserved.
+ */
+static int f50l2g41ka_ecc_ecc_get_status(struct spinand_device *spinand,
+					 u8 status)
+{
+	struct nand_device *nand = spinand_to_nand(spinand);
+	u8 eccsr = (status & GENMASK(6, 4)) >> 4;
+
+	if (eccsr <= 1 || eccsr == 3)
+		return eccsr;
+	else if (eccsr == 5)
+		return nanddev_get_ecc_requirements(nand)->strength;
+	else
+		return -EBADMSG;
+}
+
 static const struct spinand_info esmt_spinand_table[] = {
 	SPINAND_INFO("F50L1G41LB",
 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x01),
@@ -67,6 +121,15 @@
 					      &update_cache_variants),
 		     0,
 		     SPINAND_ECCINFO(&f50lxx41x_ooblayout, NULL)),
+	SPINAND_INFO("F50L2G41KA",
+		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x41, 0x7F),
+		     NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
+		     NAND_ECCREQ(8, 512),
+		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+					      &write_cache_variants,
+					      &update_cache_variants),
+		     0,
+		     SPINAND_ECCINFO(&f50l2g41ka_ooblayout, f50l2g41ka_ecc_ecc_get_status)),
 };
 
 static const struct spinand_manufacturer_ops esmt_spinand_manuf_ops = {
diff --git a/kernel/drivers/mtd/nand/spi/fmsh.c b/kernel/drivers/mtd/nand/spi/fmsh.c
index 0c5761b..ef4a586 100644
--- a/kernel/drivers/mtd/nand/spi/fmsh.c
+++ b/kernel/drivers/mtd/nand/spi/fmsh.c
@@ -80,6 +80,31 @@
 	.free = fm25s01_ooblayout_free,
 };
 
+/*
+ * ecc bits: 0xC0[4,6]
+ * [0b000], No bit errors were detected;
+ * [0b001] and [0b011], 1~6 Bit errors were detected and corrected. Not
+ *	reach Flipping Bits;
+ * [0b101], Bit error count equals the bit flip
+ *	detection threshold
+ * [0b010], Multiple bit errors were detected and
+ *	not corrected.
+ * others, Reserved.
+ */
+static int fm25s01bi3_ecc_ecc_get_status(struct spinand_device *spinand,
+					u8 status)
+{
+	struct nand_device *nand = spinand_to_nand(spinand);
+	u8 eccsr = (status & GENMASK(6, 4)) >> 4;
+
+	if (eccsr <= 1 || eccsr == 3)
+		return eccsr;
+	else if (eccsr == 5)
+		return nanddev_get_ecc_requirements(nand)->strength;
+	else
+		return -EBADMSG;
+}
+
 static const struct spinand_info fmsh_spinand_table[] = {
 	SPINAND_INFO("FM25S01A",
 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xE4),
@@ -97,11 +122,11 @@
 		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
 					      &write_cache_variants,
 					      &update_cache_variants),
-		     1,
+		     SPINAND_HAS_QE_BIT,
 		     SPINAND_ECCINFO(&fm25s01a_ooblayout, NULL)),
 	SPINAND_INFO("FM25S01",
 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xA1),
-		     NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
+		     NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
 		     NAND_ECCREQ(1, 512),
 		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
 					      &write_cache_variants,
@@ -110,13 +135,22 @@
 		     SPINAND_ECCINFO(&fm25s01_ooblayout, NULL)),
 	SPINAND_INFO("FM25LS01",
 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xA5),
-		     NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
+		     NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
 		     NAND_ECCREQ(1, 512),
 		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
 					      &write_cache_variants,
 					      &update_cache_variants),
 		     0,
 		     SPINAND_ECCINFO(&fm25s01_ooblayout, NULL)),
+	SPINAND_INFO("FM25S01BI3",
+		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xD4),
+		     NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
+		     NAND_ECCREQ(8, 512),
+		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+					      &write_cache_variants,
+					      &update_cache_variants),
+		     SPINAND_HAS_QE_BIT,
+		     SPINAND_ECCINFO(&fm25s01_ooblayout, fm25s01bi3_ecc_ecc_get_status)),
 };
 
 static const struct spinand_manufacturer_ops fmsh_spinand_manuf_ops = {
diff --git a/kernel/drivers/mtd/nand/spi/foresee.c b/kernel/drivers/mtd/nand/spi/foresee.c
index bb2e517..403c331 100644
--- a/kernel/drivers/mtd/nand/spi/foresee.c
+++ b/kernel/drivers/mtd/nand/spi/foresee.c
@@ -115,6 +115,24 @@
 					      &update_cache_variants),
 		     SPINAND_HAS_QE_BIT,
 		     SPINAND_ECCINFO(&fsxxndxxg_ooblayout, NULL)),
+	SPINAND_INFO("F35UQA002G-WWT",
+		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x62),
+		     NAND_MEMORG(1, 2048, 64, 64, 2048, 40, 1, 1, 1),
+		     NAND_ECCREQ(1, 512),
+		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+					      &write_cache_variants,
+					      &update_cache_variants),
+		     SPINAND_HAS_QE_BIT,
+		     SPINAND_ECCINFO(&fsxxndxxg_ooblayout, NULL)),
+	SPINAND_INFO("F35UQA001G-WWT",
+		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x61),
+		     NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
+		     NAND_ECCREQ(1, 512),
+		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+					      &write_cache_variants,
+					      &update_cache_variants),
+		     SPINAND_HAS_QE_BIT,
+		     SPINAND_ECCINFO(&fsxxndxxg_ooblayout, NULL)),
 };
 
 static const struct spinand_manufacturer_ops foresee_spinand_manuf_ops = {
diff --git a/kernel/drivers/mtd/nand/spi/gigadevice.c b/kernel/drivers/mtd/nand/spi/gigadevice.c
index 5e0df41..e8601e8 100644
--- a/kernel/drivers/mtd/nand/spi/gigadevice.c
+++ b/kernel/drivers/mtd/nand/spi/gigadevice.c
@@ -39,6 +39,22 @@
 		SPINAND_PAGE_READ_FROM_CACHE_OP_3A(true, 0, 1, NULL, 0),
 		SPINAND_PAGE_READ_FROM_CACHE_OP_3A(false, 0, 0, NULL, 0));
 
+static SPINAND_OP_VARIANTS(read_cache_variants_1gq5,
+		SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 2, NULL, 0),
+		SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
+		SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 1, NULL, 0),
+		SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0),
+		SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0),
+		SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0));
+
+static SPINAND_OP_VARIANTS(read_cache_variants_2gq5,
+		SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(0, 4, NULL, 0),
+		SPINAND_PAGE_READ_FROM_CACHE_X4_OP(0, 1, NULL, 0),
+		SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(0, 2, NULL, 0),
+		SPINAND_PAGE_READ_FROM_CACHE_X2_OP(0, 1, NULL, 0),
+		SPINAND_PAGE_READ_FROM_CACHE_OP(true, 0, 1, NULL, 0),
+		SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, NULL, 0));
+
 static SPINAND_OP_VARIANTS(write_cache_variants,
 		SPINAND_PROG_LOAD_X4(true, 0, NULL, 0),
 		SPINAND_PROG_LOAD(true, 0, NULL, 0));
@@ -349,6 +365,36 @@
 		     SPINAND_HAS_QE_BIT,
 		     SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
 				     gd5fxgq4uexxg_ecc_get_status)),
+	SPINAND_INFO("GD5F1GQ4RExxG",
+		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xc1),
+		     NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
+		     NAND_ECCREQ(8, 512),
+		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+					      &write_cache_variants,
+					      &update_cache_variants),
+		     SPINAND_HAS_QE_BIT,
+		     SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
+				     gd5fxgq4uexxg_ecc_get_status)),
+	SPINAND_INFO("GD5F2GQ4UExxG",
+		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xd2),
+		     NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
+		     NAND_ECCREQ(8, 512),
+		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+					      &write_cache_variants,
+					      &update_cache_variants),
+		     SPINAND_HAS_QE_BIT,
+		     SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
+				     gd5fxgq4uexxg_ecc_get_status)),
+	SPINAND_INFO("GD5F2GQ4RExxG",
+		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xc2),
+		     NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
+		     NAND_ECCREQ(8, 512),
+		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+					      &write_cache_variants,
+					      &update_cache_variants),
+		     SPINAND_HAS_QE_BIT,
+		     SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
+				     gd5fxgq4uexxg_ecc_get_status)),
 	SPINAND_INFO("GD5F1GQ4UFxxG",
 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE, 0xb1, 0x48),
 		     NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
@@ -363,42 +409,122 @@
 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x51),
 		     NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
 		     NAND_ECCREQ(4, 512),
-		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants_1gq5,
+					      &write_cache_variants,
+					      &update_cache_variants),
+		     SPINAND_HAS_QE_BIT,
+		     SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
+				     gd5fxgq5xexxg_ecc_get_status)),
+	SPINAND_INFO("GD5F1GQ5RExxG",
+		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x41, 0xc8),
+		     NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
+		     NAND_ECCREQ(4, 512),
+		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants_1gq5,
 					      &write_cache_variants,
 					      &update_cache_variants),
 		     SPINAND_HAS_QE_BIT,
 		     SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
 				     gd5fxgq5xexxg_ecc_get_status)),
 	SPINAND_INFO("GD5F2GQ5UExxG",
-		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x52),
+		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x52),
 		     NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
 		     NAND_ECCREQ(4, 512),
-		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants_2gq5,
 					      &write_cache_variants,
 					      &update_cache_variants),
 		     SPINAND_HAS_QE_BIT,
 		     SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
 				     gd5fxgq5xexxg_ecc_get_status)),
-	SPINAND_INFO("GD5F2GQ4UBxxG",
-		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xd2),
+	SPINAND_INFO("GD5F2GQ5RExxG",
+		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x42),
+		     NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
+		     NAND_ECCREQ(4, 512),
+		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants_2gq5,
+					      &write_cache_variants,
+					      &update_cache_variants),
+		     SPINAND_HAS_QE_BIT,
+		     SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
+				     gd5fxgq5xexxg_ecc_get_status)),
+	SPINAND_INFO("GD5F4GQ6UExxG",
+		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x55),
+		     NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 2, 1),
+		     NAND_ECCREQ(4, 512),
+		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants_2gq5,
+					      &write_cache_variants,
+					      &update_cache_variants),
+		     SPINAND_HAS_QE_BIT,
+		     SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
+				     gd5fxgq5xexxg_ecc_get_status)),
+	SPINAND_INFO("GD5F4GQ6RExxG",
+		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x45),
+		     NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 2, 1),
+		     NAND_ECCREQ(4, 512),
+		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants_2gq5,
+					      &write_cache_variants,
+					      &update_cache_variants),
+		     SPINAND_HAS_QE_BIT,
+		     SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
+				     gd5fxgq5xexxg_ecc_get_status)),
+	SPINAND_INFO("GD5F1GM7UExxG",
+		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x91),
+		     NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
+		     NAND_ECCREQ(8, 512),
+		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants_1gq5,
+					      &write_cache_variants,
+					      &update_cache_variants),
+		     SPINAND_HAS_QE_BIT,
+		     SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
+				     gd5fxgq4uexxg_ecc_get_status)),
+	SPINAND_INFO("GD5F1GM7RExxG",
+		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x81),
+		     NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
+		     NAND_ECCREQ(8, 512),
+		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants_1gq5,
+					      &write_cache_variants,
+					      &update_cache_variants),
+		     SPINAND_HAS_QE_BIT,
+		     SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
+				     gd5fxgq4uexxg_ecc_get_status)),
+	SPINAND_INFO("GD5F2GM7UExxG",
+		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x92),
 		     NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
 		     NAND_ECCREQ(8, 512),
-		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants_1gq5,
 					      &write_cache_variants,
 					      &update_cache_variants),
 		     SPINAND_HAS_QE_BIT,
 		     SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
-				     gd5fxgq4xa_ecc_get_status)),
-	SPINAND_INFO("GD5F4GQ6UExxG",
-		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x55),
+				     gd5fxgq4uexxg_ecc_get_status)),
+	SPINAND_INFO("GD5F2GM7RExxG",
+		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x82),
+		     NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
+		     NAND_ECCREQ(8, 512),
+		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants_1gq5,
+					      &write_cache_variants,
+					      &update_cache_variants),
+		     SPINAND_HAS_QE_BIT,
+		     SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
+				     gd5fxgq4uexxg_ecc_get_status)),
+	SPINAND_INFO("GD5F4GM8UExxG",
+		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x95),
 		     NAND_MEMORG(1, 2048, 128, 64, 4096, 80, 1, 1, 1),
-		     NAND_ECCREQ(4, 512),
-		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+		     NAND_ECCREQ(8, 512),
+		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants_1gq5,
 					      &write_cache_variants,
 					      &update_cache_variants),
 		     SPINAND_HAS_QE_BIT,
 		     SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
-				     gd5fxgq5xexxg_ecc_get_status)),
+				     gd5fxgq4uexxg_ecc_get_status)),
+	SPINAND_INFO("GD5F4GM8RExxG",
+		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x85),
+		     NAND_MEMORG(1, 2048, 128, 64, 4096, 80, 1, 1, 1),
+		     NAND_ECCREQ(8, 512),
+		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants_1gq5,
+					      &write_cache_variants,
+					      &update_cache_variants),
+		     SPINAND_HAS_QE_BIT,
+		     SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
+				     gd5fxgq4uexxg_ecc_get_status)),
 	SPINAND_INFO("GD5F1GQ4UExxH",
 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xd9),
 		     NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1),
@@ -408,56 +534,6 @@
 					      &update_cache_variants),
 		     SPINAND_HAS_QE_BIT,
 		     SPINAND_ECCINFO(&gd5fxgqx_variant3_ooblayout,
-				     gd5fxgq4xa_ecc_get_status)),
-	SPINAND_INFO("GD5F1GQ5RExxG",
-		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x41),
-		     NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
-		     NAND_ECCREQ(4, 512),
-		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
-					      &write_cache_variants,
-					      &update_cache_variants),
-		     SPINAND_HAS_QE_BIT,
-		     SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
-				     gd5fxgq5xexxg_ecc_get_status)),
-	SPINAND_INFO("GD5F2GQ5RExxG",
-		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x42),
-		     NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
-		     NAND_ECCREQ(4, 512),
-		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
-					      &write_cache_variants,
-					      &update_cache_variants),
-		     SPINAND_HAS_QE_BIT,
-		     SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
-				     gd5fxgq5xexxg_ecc_get_status)),
-	SPINAND_INFO("GD5F2GM7RxG",
-		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x82),
-		     NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
-		     NAND_ECCREQ(8, 512),
-		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
-					      &write_cache_variants,
-					      &update_cache_variants),
-		     SPINAND_HAS_QE_BIT,
-		     SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
-				     gd5fxgq4xa_ecc_get_status)),
-	SPINAND_INFO("GD5F1GM7UxG",
-		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x91),
-		     NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
-		     NAND_ECCREQ(8, 512),
-		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
-					      &write_cache_variants,
-					      &update_cache_variants),
-		     SPINAND_HAS_QE_BIT,
-		     SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
-				     gd5fxgq4xa_ecc_get_status)),
-	SPINAND_INFO("GD5F2GM7UxG",
-		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0x92),
-		     NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
-		     NAND_ECCREQ(8, 512),
-		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
-					      &write_cache_variants,
-					      &update_cache_variants),
-		     SPINAND_HAS_QE_BIT,
-		     SPINAND_ECCINFO(&gd5fxgqx_variant2_ooblayout,
 				     gd5fxgq4xa_ecc_get_status)),
 };
 
diff --git a/kernel/drivers/mtd/nand/spi/jsc.c b/kernel/drivers/mtd/nand/spi/jsc.c
index 93fed1c..9c421c8 100644
--- a/kernel/drivers/mtd/nand/spi/jsc.c
+++ b/kernel/drivers/mtd/nand/spi/jsc.c
@@ -70,12 +70,13 @@
 static int js28u1gqscahg_ecc_get_status(struct spinand_device *spinand,
 					u8 status)
 {
-	u8 eccsr = (status & GENMASK(6, 4)) >> 2;
+	struct nand_device *nand = spinand_to_nand(spinand);
+	u8 eccsr = (status & GENMASK(6, 4)) >> 4;
 
-	if (eccsr <= 7)
+	if (eccsr < 4)
 		return eccsr;
-	else if (eccsr == 12)
-		return 8;
+	else if (eccsr == 4)
+		return nanddev_get_ecc_requirements(nand)->strength;
 	else
 		return -EBADMSG;
 }
diff --git a/kernel/drivers/mtd/nand/spi/skyhigh.c b/kernel/drivers/mtd/nand/spi/skyhigh.c
index cda8408..3d075f1 100644
--- a/kernel/drivers/mtd/nand/spi/skyhigh.c
+++ b/kernel/drivers/mtd/nand/spi/skyhigh.c
@@ -26,8 +26,8 @@
 		SPINAND_PROG_LOAD(true, 0, NULL, 0));
 
 static SPINAND_OP_VARIANTS(update_cache_variants,
-		SPINAND_PROG_LOAD_X4(false, 0, NULL, 0),
-		SPINAND_PROG_LOAD(false, 0, NULL, 0));
+		SPINAND_PROG_LOAD_X4(true, 0, NULL, 0),
+		SPINAND_PROG_LOAD(true, 0, NULL, 0));
 
 static int s35ml04g3_ooblayout_ecc(struct mtd_info *mtd, int section,
 				   struct mtd_oob_region *region)
diff --git a/kernel/drivers/mtd/nand/spi/unim.c b/kernel/drivers/mtd/nand/spi/unim.c
index 659921e..ee78420 100644
--- a/kernel/drivers/mtd/nand/spi/unim.c
+++ b/kernel/drivers/mtd/nand/spi/unim.c
@@ -69,12 +69,13 @@
 static int tx25g01_ecc_get_status(struct spinand_device *spinand,
 				  u8 status)
 {
-	u8 eccsr = (status & GENMASK(6, 4)) >> 2;
+	struct nand_device *nand = spinand_to_nand(spinand);
+	u8 eccsr = (status & GENMASK(6, 4)) >> 4;
 
-	if (eccsr <= 7)
+	if (eccsr < 4)
 		return eccsr;
-	else if (eccsr == 12)
-		return 8;
+	else if (eccsr == 4)
+		return nanddev_get_ecc_requirements(nand)->strength;
 	else
 		return -EBADMSG;
 }
diff --git a/kernel/drivers/mtd/nand/spi/xtx.c b/kernel/drivers/mtd/nand/spi/xtx.c
index 28ffee0..a40ef58 100644
--- a/kernel/drivers/mtd/nand/spi/xtx.c
+++ b/kernel/drivers/mtd/nand/spi/xtx.c
@@ -203,6 +203,28 @@
 		return -EBADMSG;
 }
 
+static int xt26g11c_ecc_get_status(struct spinand_device *spinand,
+				   u8 status)
+{
+	struct nand_device *nand = spinand_to_nand(spinand);
+
+	switch (status & STATUS_ECC_MASK) {
+	case STATUS_ECC_NO_BITFLIPS:
+		return 0;
+
+	case STATUS_ECC_UNCOR_ERROR:
+		return -EBADMSG;
+
+	case STATUS_ECC_HAS_BITFLIPS:
+		return 1;
+
+	default:
+		return nanddev_get_ecc_requirements(nand)->strength;
+	}
+
+	return -EINVAL;
+}
+
 static const struct spinand_info xtx_spinand_table[] = {
 	SPINAND_INFO("XT26G01A",
 		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_ADDR, 0xE1),
@@ -293,7 +315,34 @@
 					      &update_cache_variants),
 		     SPINAND_HAS_QE_BIT,
 		     SPINAND_ECCINFO(&xt26g01c_ooblayout,
-				     xt26g01c_ecc_get_status)),
+				     xt26g11c_ecc_get_status)),
+	SPINAND_INFO("XT26Q02DWSIGA",
+		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x52),
+		     NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1),
+		     NAND_ECCREQ(8, 512),
+		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+					      &write_cache_variants,
+					      &update_cache_variants),
+		     SPINAND_HAS_QE_BIT,
+		     SPINAND_ECCINFO(&xt26g01c_ooblayout, xt26g11c_ecc_get_status)),
+	SPINAND_INFO("XT26Q01DWSIGA",
+		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x51),
+		     NAND_MEMORG(1, 2048, 128, 64, 1024, 20, 1, 1, 1),
+		     NAND_ECCREQ(8, 512),
+		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+					      &write_cache_variants,
+					      &update_cache_variants),
+		     SPINAND_HAS_QE_BIT,
+		     SPINAND_ECCINFO(&xt26g01c_ooblayout, xt26g11c_ecc_get_status)),
+	SPINAND_INFO("XT26Q04DWSIGA",
+		     SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0x53),
+		     NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1),
+		     NAND_ECCREQ(8, 512),
+		     SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+					      &write_cache_variants,
+					      &update_cache_variants),
+		     SPINAND_HAS_QE_BIT,
+		     SPINAND_ECCINFO(&xt26g01c_ooblayout, xt26g11c_ecc_get_status)),
 };
 
 static const struct spinand_manufacturer_ops xtx_spinand_manuf_ops = {
diff --git a/kernel/drivers/mtd/spi-nor/xmc.c b/kernel/drivers/mtd/spi-nor/xmc.c
index 864d3c0..7468f66 100644
--- a/kernel/drivers/mtd/spi-nor/xmc.c
+++ b/kernel/drivers/mtd/spi-nor/xmc.c
@@ -24,6 +24,8 @@
 			     SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
 	{ "XM25QH128C", INFO(0x204018, 0, 64 * 1024, 256,
 			     SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
+	{ "XM25QH256C", INFO(0x204019, 0, 64 * 1024, 512,
+			     SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
 	{ "XM25QU128C", INFO(0x204118, 0, 64 * 1024, 256,
 			     SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
 };
diff --git a/kernel/drivers/net/can/rockchip/rockchip_canfd.c b/kernel/drivers/net/can/rockchip/rockchip_canfd.c
index a73c1f5..abdcdb5 100644
--- a/kernel/drivers/net/can/rockchip/rockchip_canfd.c
+++ b/kernel/drivers/net/can/rockchip/rockchip_canfd.c
@@ -27,6 +27,7 @@
 #include <linux/can/led.h>
 #include <linux/reset.h>
 #include <linux/pm_runtime.h>
+#include <linux/rockchip/cpu.h>
 
 /* registers definition */
 enum rockchip_canfd_reg {
@@ -105,6 +106,7 @@
 	ROCKCHIP_CANFD_MODE = 0,
 	ROCKCHIP_CAN_MODE,
 	ROCKCHIP_RK3568_CAN_MODE,
+	ROCKCHIP_RK3568_CAN_MODE_V2,
 };
 
 #define DATE_LENGTH_12_BYTE	(0x9)
@@ -220,6 +222,7 @@
 struct rockchip_canfd {
 	struct can_priv can;
 	struct device *dev;
+	struct napi_struct napi;
 	struct clk_bulk_data *clks;
 	int num_clks;
 	struct reset_control *reset;
@@ -292,8 +295,6 @@
 
 	val = rockchip_canfd_read(rcan, CAN_MODE);
 	val |= WORK_MODE;
-	if (rcan->mode >= ROCKCHIP_CAN_MODE && rcan->txtorx)
-		val |= MODE_RXSTX;
 	rockchip_canfd_write(rcan, CAN_MODE, val);
 
 	netdev_dbg(ndev, "%s MODE=0x%08x\n", __func__,
@@ -427,8 +428,6 @@
 	if (rcan->can.ctrlmode & CAN_CTRLMODE_LOOPBACK)
 		val |= MODE_SELF_TEST | MODE_LBACK;
 
-	val |= MODE_AUTO_RETX;
-
 	rockchip_canfd_write(rcan, CAN_MODE, val);
 
 	rockchip_canfd_set_bittiming(ndev);
@@ -488,19 +487,13 @@
 {
 	struct rockchip_canfd *rcan =
 		container_of(work, struct rockchip_canfd, tx_err_work.work);
-	u32 mode, err_code, id;
+	u32 mode;
 
-	id = rockchip_canfd_read(rcan, CAN_TXID);
-	err_code = rockchip_canfd_read(rcan, CAN_ERR_CODE);
-	if (err_code & 0x1fe0000) {
-		mode = rockchip_canfd_read(rcan, CAN_MODE);
-		rockchip_canfd_write(rcan, CAN_MODE, 0);
-		rockchip_canfd_write(rcan, CAN_MODE, mode);
-		rockchip_canfd_write(rcan, CAN_CMD, CAN_TX0_REQ);
-		schedule_delayed_work(&rcan->tx_err_work, 1);
-	} else if (rcan->txtorx && rcan->mode >= ROCKCHIP_CAN_MODE && id & CAN_EFF_FLAG) {
-		schedule_delayed_work(&rcan->tx_err_work, 1);
-	}
+	mode = rockchip_canfd_read(rcan, CAN_MODE);
+	rockchip_canfd_write(rcan, CAN_MODE, 0);
+	rockchip_canfd_write(rcan, CAN_MODE, mode);
+	rockchip_canfd_write(rcan, CAN_CMD, CAN_TX0_REQ);
+	schedule_delayed_work(&rcan->tx_err_work, 1);
 }
 
 /* transmit a CAN message
@@ -552,7 +545,14 @@
 			dlc |= TX_FD_BRS_ENABLE;
 	}
 
-	if (!rcan->txtorx && rcan->mode >= ROCKCHIP_CAN_MODE && cf->can_id & CAN_EFF_FLAG) {
+	if (rcan->txtorx && rcan->mode <= ROCKCHIP_RK3568_CAN_MODE && cf->can_id & CAN_EFF_FLAG)
+		rockchip_canfd_write(rcan, CAN_MODE,
+				     rockchip_canfd_read(rcan, CAN_MODE) | MODE_RXSTX);
+	else
+		rockchip_canfd_write(rcan, CAN_MODE,
+				     rockchip_canfd_read(rcan, CAN_MODE) & (~MODE_RXSTX));
+
+	if (!rcan->txtorx && rcan->mode <= ROCKCHIP_RK3568_CAN_MODE && cf->can_id & CAN_EFF_FLAG) {
 		/* Two frames are sent consecutively.
 		 * Before the first frame is tx finished,
 		 * the register of the second frame is configured.
@@ -583,10 +583,9 @@
 		rockchip_canfd_write(rcan, CAN_TXDAT0 + i,
 				     *(u32 *)(cf->data + i));
 
-	rockchip_canfd_write(rcan, CAN_CMD, CAN_TX1_REQ);
+	rockchip_canfd_write(rcan, CAN_CMD, cmd);
 
-	if (rcan->txtorx && rcan->mode >= ROCKCHIP_CAN_MODE && cf->can_id & CAN_EFF_FLAG)
-		schedule_delayed_work(&rcan->tx_err_work, 1);
+	schedule_delayed_work(&rcan->tx_err_work, 1);
 
 	can_put_echo_skb(skb, ndev, 0);
 
@@ -602,15 +601,15 @@
 	u32 id_rockchip_canfd, dlc;
 	int i = 0;
 	u32 __maybe_unused ts, ret;
-	u32 data[16] = {0};
+	u32 data[16];
 
 	dlc = rockchip_canfd_read(rcan, CAN_RXFRD);
 	id_rockchip_canfd = rockchip_canfd_read(rcan, CAN_RXFRD);
 	ts = rockchip_canfd_read(rcan, CAN_RXFRD);
-	for (i = 0; i < 16; i++)
+	for (i = 0; i < ARRAY_SIZE(data); i++)
 		data[i] = rockchip_canfd_read(rcan, CAN_RXFRD);
 
-	if (rcan->mode >= ROCKCHIP_CAN_MODE) {
+	if (rcan->mode <= ROCKCHIP_RK3568_CAN_MODE) {
 		/* may be an empty frame */
 		if (!dlc && !id_rockchip_canfd)
 			return 1;
@@ -618,11 +617,10 @@
 		if (rcan->txtorx) {
 			if (rockchip_canfd_read(rcan, CAN_TX_CHECK_FIC) & FORMAT_MASK) {
 				ret = rockchip_canfd_read(rcan, CAN_TXID) & CAN_SFF_MASK;
-				if (id_rockchip_canfd == ret) {
+				if ((id_rockchip_canfd == ret) && !(dlc & FORMAT_MASK))
 					rockchip_canfd_write(rcan, CAN_TX_CHECK_FIC,
 							     ts | CAN_TX0_REQ);
-					return 1;
-				}
+				return 1;
 			}
 		}
 	}
@@ -675,6 +673,53 @@
 	return 1;
 }
 
+static int rockchip_canfd_get_rx_fifo_cnt(struct net_device *ndev)
+{
+	struct rockchip_canfd *rcan = netdev_priv(ndev);
+	int quota = 0;
+
+	if (read_poll_timeout_atomic(rockchip_canfd_read, quota,
+				     (quota & rcan->rx_fifo_mask) >> rcan->rx_fifo_shift,
+				     0, 500000, false, rcan, CAN_RXFC))
+		netdev_dbg(ndev, "Warning: get fifo cnt failed\n");
+
+	quota = (quota & rcan->rx_fifo_mask) >> rcan->rx_fifo_shift;
+
+	return quota;
+}
+
+/* rockchip_canfd_rx_poll - Poll routine for rx packets (NAPI)
+ * @napi:	napi structure pointer
+ * @quota:	Max number of rx packets to be processed.
+ *
+ * This is the poll routine for rx part.
+ * It will process the packets maximux quota value.
+ *
+ * Return: number of packets received
+ */
+static int rockchip_canfd_rx_poll(struct napi_struct *napi, int quota)
+{
+	struct net_device *ndev = napi->dev;
+	struct rockchip_canfd *rcan = netdev_priv(ndev);
+	int work_done = 0;
+
+	quota = rockchip_canfd_get_rx_fifo_cnt(ndev);
+	if (quota) {
+		while (work_done < quota)
+			work_done += rockchip_canfd_rx(ndev);
+	}
+
+	if (work_done)
+		can_led_event(ndev, CAN_LED_EVENT_RX);
+
+	if (work_done < 6) {
+		napi_complete_done(napi, work_done);
+		rockchip_canfd_write(rcan, CAN_INT_MASK, 0);
+	}
+
+	return work_done;
+}
+
 static int rockchip_canfd_err(struct net_device *ndev, u32 isr)
 {
 	struct rockchip_canfd *rcan = netdev_priv(ndev);
@@ -694,6 +739,9 @@
 		cf->data[6] = txerr;
 		cf->data[7] = rxerr;
 	}
+
+	if (isr & TX_LOSTARB_INT)
+		schedule_delayed_work(&rcan->tx_err_work, 1);
 
 	if (isr & BUS_OFF_INT) {
 		rcan->can.state = CAN_STATE_BUS_OFF;
@@ -754,12 +802,10 @@
 		else
 			stats->tx_bytes += (dlc & DLC_MASK);
 		stats->tx_packets++;
-		if (rcan->txtorx && rcan->mode >= ROCKCHIP_CAN_MODE && dlc & FORMAT_MASK) {
-			cancel_delayed_work(&rcan->tx_err_work);
+		cancel_delayed_work(&rcan->tx_err_work);
+		if (rcan->txtorx && rcan->mode <= ROCKCHIP_RK3568_CAN_MODE && dlc & FORMAT_MASK) {
 			rockchip_canfd_write(rcan, CAN_TX_CHECK_FIC, FORMAT_MASK);
-			quota = (rockchip_canfd_read(rcan, CAN_RXFC) &
-				 rcan->rx_fifo_mask) >>
-				rcan->rx_fifo_shift;
+			quota = rockchip_canfd_get_rx_fifo_cnt(ndev);
 			if (quota) {
 				while (work_done < quota)
 					work_done += rockchip_canfd_rx(ndev);
@@ -775,11 +821,15 @@
 	}
 
 	if (isr & RX_FINISH_INT) {
-		quota = (rockchip_canfd_read(rcan, CAN_RXFC) & rcan->rx_fifo_mask) >>
-			rcan->rx_fifo_shift;
-		if (quota) {
-			while (work_done < quota)
-				work_done += rockchip_canfd_rx(ndev);
+		if (rcan->mode == ROCKCHIP_RK3568_CAN_MODE_V2) {
+			rockchip_canfd_write(rcan, CAN_INT_MASK, 0x1);
+			napi_schedule(&rcan->napi);
+		} else {
+			quota = rockchip_canfd_get_rx_fifo_cnt(ndev);
+			if (quota) {
+				while (work_done < quota)
+					work_done += rockchip_canfd_rx(ndev);
+			}
 		}
 	}
 
@@ -817,6 +867,8 @@
 	}
 
 	can_led_event(ndev, CAN_LED_EVENT_OPEN);
+	if (rcan->mode == ROCKCHIP_RK3568_CAN_MODE_V2)
+		napi_enable(&rcan->napi);
 	netif_start_queue(ndev);
 
 	netdev_dbg(ndev, "%s\n", __func__);
@@ -834,6 +886,8 @@
 	struct rockchip_canfd *rcan = netdev_priv(ndev);
 
 	netif_stop_queue(ndev);
+	if (rcan->mode == ROCKCHIP_RK3568_CAN_MODE_V2)
+		napi_disable(&rcan->napi);
 	rockchip_canfd_stop(ndev);
 	close_candev(ndev);
 	can_led_event(ndev, CAN_LED_EVENT_STOP);
@@ -1011,6 +1065,9 @@
 
 	rcan->mode = (unsigned long)of_device_get_match_data(&pdev->dev);
 
+	if ((cpu_is_rk3566() || cpu_is_rk3568()) && (rockchip_get_cpu_version() == 3))
+		rcan->mode = ROCKCHIP_RK3568_CAN_MODE_V2;
+
 	rcan->base = addr;
 	rcan->can.clock.freq = clk_get_rate(rcan->clks[0].clk);
 	rcan->dev = &pdev->dev;
@@ -1032,6 +1089,7 @@
 		break;
 	case ROCKCHIP_CAN_MODE:
 	case ROCKCHIP_RK3568_CAN_MODE:
+	case ROCKCHIP_RK3568_CAN_MODE_V2:
 		rcan->can.bittiming_const = &rockchip_canfd_bittiming_const;
 		rcan->can.do_set_mode = rockchip_canfd_set_mode;
 		rcan->can.do_get_berr_counter = rockchip_canfd_get_berr_counter;
@@ -1056,10 +1114,17 @@
 					   rcan->tx_invalid, 4))
 		rcan->txtorx = 1;
 
+	if (rcan->mode == ROCKCHIP_RK3568_CAN_MODE_V2) {
+		rcan->txtorx = 0;
+		netif_napi_add(ndev, &rcan->napi, rockchip_canfd_rx_poll, 6);
+	}
+
 	ndev->netdev_ops = &rockchip_canfd_netdev_ops;
 	ndev->irq = irq;
 	ndev->flags |= IFF_ECHO;
 	rcan->can.restart_ms = 1;
+
+	irq_set_affinity_hint(irq, get_cpu_mask(num_online_cpus() - 1));
 
 	INIT_DELAYED_WORK(&rcan->tx_err_work, rockchip_canfd_tx_err_delay_work);
 
@@ -1097,9 +1162,12 @@
 static int rockchip_canfd_remove(struct platform_device *pdev)
 {
 	struct net_device *ndev = platform_get_drvdata(pdev);
+	struct rockchip_canfd *rcan = netdev_priv(ndev);
 
 	unregister_netdev(ndev);
 	pm_runtime_disable(&pdev->dev);
+	if (rcan->mode == ROCKCHIP_RK3568_CAN_MODE_V2)
+		netif_napi_del(&rcan->napi);
 	free_candev(ndev);
 
 	return 0;
diff --git a/kernel/drivers/net/ethernet/chelsio/cxgb/common.h b/kernel/drivers/net/ethernet/chelsio/cxgb/common.h
index 0321be7..6475060 100644
--- a/kernel/drivers/net/ethernet/chelsio/cxgb/common.h
+++ b/kernel/drivers/net/ethernet/chelsio/cxgb/common.h
@@ -238,6 +238,7 @@
 	int msg_enable;
 	u32 mmio_len;
 
+	struct work_struct ext_intr_handler_task;
 	struct adapter_params params;
 
 	/* Terminator modules. */
@@ -256,7 +257,6 @@
 
 	/* guards async operations */
 	spinlock_t async_lock ____cacheline_aligned;
-	u32 pending_thread_intr;
 	u32 slow_intr_mask;
 	int t1powersave;
 };
@@ -334,7 +334,8 @@
 void t1_interrupts_disable(adapter_t *adapter);
 void t1_interrupts_clear(adapter_t *adapter);
 int t1_elmer0_ext_intr_handler(adapter_t *adapter);
-irqreturn_t t1_slow_intr_handler(adapter_t *adapter);
+void t1_elmer0_ext_intr(adapter_t *adapter);
+int t1_slow_intr_handler(adapter_t *adapter);
 
 int t1_link_start(struct cphy *phy, struct cmac *mac, struct link_config *lc);
 const struct board_info *t1_get_board_info(unsigned int board_id);
@@ -346,6 +347,7 @@
 int t1_init_hw_modules(adapter_t *adapter);
 int t1_init_sw_modules(adapter_t *adapter, const struct board_info *bi);
 void t1_free_sw_modules(adapter_t *adapter);
+void t1_fatal_err(adapter_t *adapter);
 void t1_link_changed(adapter_t *adapter, int port_id);
 void t1_link_negotiated(adapter_t *adapter, int port_id, int link_stat,
 			    int speed, int duplex, int pause);
diff --git a/kernel/drivers/net/ethernet/chelsio/cxgb/cxgb2.c b/kernel/drivers/net/ethernet/chelsio/cxgb/cxgb2.c
index 2a28a38..c6db85f 100644
--- a/kernel/drivers/net/ethernet/chelsio/cxgb/cxgb2.c
+++ b/kernel/drivers/net/ethernet/chelsio/cxgb/cxgb2.c
@@ -211,10 +211,9 @@
 	t1_interrupts_clear(adapter);
 
 	adapter->params.has_msi = !disable_msi && !pci_enable_msi(adapter->pdev);
-	err = request_threaded_irq(adapter->pdev->irq, t1_interrupt,
-				   t1_interrupt_thread,
-				   adapter->params.has_msi ? 0 : IRQF_SHARED,
-				   adapter->name, adapter);
+	err = request_irq(adapter->pdev->irq, t1_interrupt,
+			  adapter->params.has_msi ? 0 : IRQF_SHARED,
+			  adapter->name, adapter);
 	if (err) {
 		if (adapter->params.has_msi)
 			pci_disable_msi(adapter->pdev);
@@ -917,6 +916,51 @@
 	spin_unlock(&adapter->work_lock);
 }
 
+/*
+ * Processes elmer0 external interrupts in process context.
+ */
+static void ext_intr_task(struct work_struct *work)
+{
+	struct adapter *adapter =
+		container_of(work, struct adapter, ext_intr_handler_task);
+
+	t1_elmer0_ext_intr_handler(adapter);
+
+	/* Now reenable external interrupts */
+	spin_lock_irq(&adapter->async_lock);
+	adapter->slow_intr_mask |= F_PL_INTR_EXT;
+	writel(F_PL_INTR_EXT, adapter->regs + A_PL_CAUSE);
+	writel(adapter->slow_intr_mask | F_PL_INTR_SGE_DATA,
+		   adapter->regs + A_PL_ENABLE);
+	spin_unlock_irq(&adapter->async_lock);
+}
+
+/*
+ * Interrupt-context handler for elmer0 external interrupts.
+ */
+void t1_elmer0_ext_intr(struct adapter *adapter)
+{
+	/*
+	 * Schedule a task to handle external interrupts as we require
+	 * a process context.  We disable EXT interrupts in the interim
+	 * and let the task reenable them when it's done.
+	 */
+	adapter->slow_intr_mask &= ~F_PL_INTR_EXT;
+	writel(adapter->slow_intr_mask | F_PL_INTR_SGE_DATA,
+		   adapter->regs + A_PL_ENABLE);
+	schedule_work(&adapter->ext_intr_handler_task);
+}
+
+void t1_fatal_err(struct adapter *adapter)
+{
+	if (adapter->flags & FULL_INIT_DONE) {
+		t1_sge_stop(adapter->sge);
+		t1_interrupts_disable(adapter);
+	}
+	pr_alert("%s: encountered fatal error, operation suspended\n",
+		 adapter->name);
+}
+
 static const struct net_device_ops cxgb_netdev_ops = {
 	.ndo_open		= cxgb_open,
 	.ndo_stop		= cxgb_close,
@@ -1018,6 +1062,8 @@
 			spin_lock_init(&adapter->async_lock);
 			spin_lock_init(&adapter->mac_lock);
 
+			INIT_WORK(&adapter->ext_intr_handler_task,
+				  ext_intr_task);
 			INIT_DELAYED_WORK(&adapter->stats_update_task,
 					  mac_stats_task);
 
diff --git a/kernel/drivers/net/ethernet/chelsio/cxgb/sge.c b/kernel/drivers/net/ethernet/chelsio/cxgb/sge.c
index cda01f2..2d9c2b5 100644
--- a/kernel/drivers/net/ethernet/chelsio/cxgb/sge.c
+++ b/kernel/drivers/net/ethernet/chelsio/cxgb/sge.c
@@ -940,11 +940,10 @@
 /*
  * SGE 'Error' interrupt handler
  */
-bool t1_sge_intr_error_handler(struct sge *sge)
+int t1_sge_intr_error_handler(struct sge *sge)
 {
 	struct adapter *adapter = sge->adapter;
 	u32 cause = readl(adapter->regs + A_SG_INT_CAUSE);
-	bool wake = false;
 
 	if (adapter->port[0].dev->hw_features & NETIF_F_TSO)
 		cause &= ~F_PACKET_TOO_BIG;
@@ -968,14 +967,11 @@
 		sge->stats.pkt_mismatch++;
 		pr_alert("%s: SGE packet mismatch\n", adapter->name);
 	}
-	if (cause & SGE_INT_FATAL) {
-		t1_interrupts_disable(adapter);
-		adapter->pending_thread_intr |= F_PL_INTR_SGE_ERR;
-		wake = true;
-	}
+	if (cause & SGE_INT_FATAL)
+		t1_fatal_err(adapter);
 
 	writel(cause, adapter->regs + A_SG_INT_CAUSE);
-	return wake;
+	return 0;
 }
 
 const struct sge_intr_counts *t1_sge_get_intr_counts(const struct sge *sge)
@@ -1623,46 +1619,11 @@
 	return work_done;
 }
 
-irqreturn_t t1_interrupt_thread(int irq, void *data)
-{
-	struct adapter *adapter = data;
-	u32 pending_thread_intr;
-
-	spin_lock_irq(&adapter->async_lock);
-	pending_thread_intr = adapter->pending_thread_intr;
-	adapter->pending_thread_intr = 0;
-	spin_unlock_irq(&adapter->async_lock);
-
-	if (!pending_thread_intr)
-		return IRQ_NONE;
-
-	if (pending_thread_intr & F_PL_INTR_EXT)
-		t1_elmer0_ext_intr_handler(adapter);
-
-	/* This error is fatal, interrupts remain off */
-	if (pending_thread_intr & F_PL_INTR_SGE_ERR) {
-		pr_alert("%s: encountered fatal error, operation suspended\n",
-			 adapter->name);
-		t1_sge_stop(adapter->sge);
-		return IRQ_HANDLED;
-	}
-
-	spin_lock_irq(&adapter->async_lock);
-	adapter->slow_intr_mask |= F_PL_INTR_EXT;
-
-	writel(F_PL_INTR_EXT, adapter->regs + A_PL_CAUSE);
-	writel(adapter->slow_intr_mask | F_PL_INTR_SGE_DATA,
-	       adapter->regs + A_PL_ENABLE);
-	spin_unlock_irq(&adapter->async_lock);
-
-	return IRQ_HANDLED;
-}
-
 irqreturn_t t1_interrupt(int irq, void *data)
 {
 	struct adapter *adapter = data;
 	struct sge *sge = adapter->sge;
-	irqreturn_t handled;
+	int handled;
 
 	if (likely(responses_pending(adapter))) {
 		writel(F_PL_INTR_SGE_DATA, adapter->regs + A_PL_CAUSE);
@@ -1684,10 +1645,10 @@
 	handled = t1_slow_intr_handler(adapter);
 	spin_unlock(&adapter->async_lock);
 
-	if (handled == IRQ_NONE)
+	if (!handled)
 		sge->stats.unhandled_irqs++;
 
-	return handled;
+	return IRQ_RETVAL(handled != 0);
 }
 
 /*
diff --git a/kernel/drivers/net/ethernet/chelsio/cxgb/sge.h b/kernel/drivers/net/ethernet/chelsio/cxgb/sge.h
index 716705b..a1ba591 100644
--- a/kernel/drivers/net/ethernet/chelsio/cxgb/sge.h
+++ b/kernel/drivers/net/ethernet/chelsio/cxgb/sge.h
@@ -74,7 +74,6 @@
 int t1_sge_configure(struct sge *, struct sge_params *);
 int t1_sge_set_coalesce_params(struct sge *, struct sge_params *);
 void t1_sge_destroy(struct sge *);
-irqreturn_t t1_interrupt_thread(int irq, void *data);
 irqreturn_t t1_interrupt(int irq, void *cookie);
 int t1_poll(struct napi_struct *, int);
 
@@ -82,7 +81,7 @@
 void t1_vlan_mode(struct adapter *adapter, netdev_features_t features);
 void t1_sge_start(struct sge *);
 void t1_sge_stop(struct sge *);
-bool t1_sge_intr_error_handler(struct sge *sge);
+int t1_sge_intr_error_handler(struct sge *);
 void t1_sge_intr_enable(struct sge *);
 void t1_sge_intr_disable(struct sge *);
 void t1_sge_intr_clear(struct sge *);
diff --git a/kernel/drivers/net/ethernet/chelsio/cxgb/subr.c b/kernel/drivers/net/ethernet/chelsio/cxgb/subr.c
index 310add2..ea0f874 100644
--- a/kernel/drivers/net/ethernet/chelsio/cxgb/subr.c
+++ b/kernel/drivers/net/ethernet/chelsio/cxgb/subr.c
@@ -170,7 +170,7 @@
 	t1_link_negotiated(adapter, port_id, link_ok, speed, duplex, fc);
 }
 
-static bool t1_pci_intr_handler(adapter_t *adapter)
+static int t1_pci_intr_handler(adapter_t *adapter)
 {
 	u32 pcix_cause;
 
@@ -179,13 +179,9 @@
 	if (pcix_cause) {
 		pci_write_config_dword(adapter->pdev, A_PCICFG_INTR_CAUSE,
 				       pcix_cause);
-		/* PCI errors are fatal */
-		t1_interrupts_disable(adapter);
-		adapter->pending_thread_intr |= F_PL_INTR_SGE_ERR;
-		pr_alert("%s: PCI error encountered.\n", adapter->name);
-		return true;
+		t1_fatal_err(adapter);    /* PCI errors are fatal */
 	}
-	return false;
+	return 0;
 }
 
 #ifdef CONFIG_CHELSIO_T1_1G
@@ -214,16 +210,13 @@
 /*
  * Slow path interrupt handler for FPGAs.
  */
-static irqreturn_t fpga_slow_intr(adapter_t *adapter)
+static int fpga_slow_intr(adapter_t *adapter)
 {
 	u32 cause = readl(adapter->regs + A_PL_CAUSE);
-	irqreturn_t ret = IRQ_NONE;
 
 	cause &= ~F_PL_INTR_SGE_DATA;
-	if (cause & F_PL_INTR_SGE_ERR) {
-		if (t1_sge_intr_error_handler(adapter->sge))
-			ret = IRQ_WAKE_THREAD;
-	}
+	if (cause & F_PL_INTR_SGE_ERR)
+		t1_sge_intr_error_handler(adapter->sge);
 
 	if (cause & FPGA_PCIX_INTERRUPT_GMAC)
 		fpga_phy_intr_handler(adapter);
@@ -238,19 +231,14 @@
 		/* Clear TP interrupt */
 		writel(tp_cause, adapter->regs + FPGA_TP_ADDR_INTERRUPT_CAUSE);
 	}
-	if (cause & FPGA_PCIX_INTERRUPT_PCIX) {
-		if (t1_pci_intr_handler(adapter))
-			ret = IRQ_WAKE_THREAD;
-	}
+	if (cause & FPGA_PCIX_INTERRUPT_PCIX)
+		t1_pci_intr_handler(adapter);
 
 	/* Clear the interrupts just processed. */
 	if (cause)
 		writel(cause, adapter->regs + A_PL_CAUSE);
 
-	if (ret != IRQ_NONE)
-		return ret;
-
-	return cause == 0 ? IRQ_NONE : IRQ_HANDLED;
+	return cause != 0;
 }
 #endif
 
@@ -854,45 +842,31 @@
 /*
  * Slow path interrupt handler for ASICs.
  */
-static irqreturn_t asic_slow_intr(adapter_t *adapter)
+static int asic_slow_intr(adapter_t *adapter)
 {
 	u32 cause = readl(adapter->regs + A_PL_CAUSE);
-	irqreturn_t ret = IRQ_HANDLED;
 
 	cause &= adapter->slow_intr_mask;
 	if (!cause)
-		return IRQ_NONE;
-	if (cause & F_PL_INTR_SGE_ERR) {
-		if (t1_sge_intr_error_handler(adapter->sge))
-			ret = IRQ_WAKE_THREAD;
-	}
+		return 0;
+	if (cause & F_PL_INTR_SGE_ERR)
+		t1_sge_intr_error_handler(adapter->sge);
 	if (cause & F_PL_INTR_TP)
 		t1_tp_intr_handler(adapter->tp);
 	if (cause & F_PL_INTR_ESPI)
 		t1_espi_intr_handler(adapter->espi);
-	if (cause & F_PL_INTR_PCIX) {
-		if (t1_pci_intr_handler(adapter))
-			ret = IRQ_WAKE_THREAD;
-	}
-	if (cause & F_PL_INTR_EXT) {
-		/* Wake the threaded interrupt to handle external interrupts as
-		 * we require a process context. We disable EXT interrupts in
-		 * the interim and let the thread reenable them when it's done.
-		 */
-		adapter->pending_thread_intr |= F_PL_INTR_EXT;
-		adapter->slow_intr_mask &= ~F_PL_INTR_EXT;
-		writel(adapter->slow_intr_mask | F_PL_INTR_SGE_DATA,
-		       adapter->regs + A_PL_ENABLE);
-		ret = IRQ_WAKE_THREAD;
-	}
+	if (cause & F_PL_INTR_PCIX)
+		t1_pci_intr_handler(adapter);
+	if (cause & F_PL_INTR_EXT)
+		t1_elmer0_ext_intr(adapter);
 
 	/* Clear the interrupts just processed. */
 	writel(cause, adapter->regs + A_PL_CAUSE);
 	readl(adapter->regs + A_PL_CAUSE); /* flush writes */
-	return ret;
+	return 1;
 }
 
-irqreturn_t t1_slow_intr_handler(adapter_t *adapter)
+int t1_slow_intr_handler(adapter_t *adapter)
 {
 #ifdef CONFIG_CHELSIO_T1_1G
 	if (!t1_is_asic(adapter))
diff --git a/kernel/drivers/net/ethernet/dlink/sundance.c b/kernel/drivers/net/ethernet/dlink/sundance.c
index df0eab4..e3a8858 100644
--- a/kernel/drivers/net/ethernet/dlink/sundance.c
+++ b/kernel/drivers/net/ethernet/dlink/sundance.c
@@ -963,7 +963,7 @@
 	unsigned long flag;
 
 	netif_stop_queue(dev);
-	tasklet_disable_in_atomic(&np->tx_tasklet);
+	tasklet_disable(&np->tx_tasklet);
 	iowrite16(0, ioaddr + IntrEnable);
 	printk(KERN_WARNING "%s: Transmit timed out, TxStatus %2.2x "
 		   "TxFrameId %2.2x,"
diff --git a/kernel/drivers/net/ethernet/jme.c b/kernel/drivers/net/ethernet/jme.c
index f1b9284..e9efe07 100644
--- a/kernel/drivers/net/ethernet/jme.c
+++ b/kernel/drivers/net/ethernet/jme.c
@@ -1265,9 +1265,9 @@
 	jwrite32f(jme, JME_APMC, apmc);
 }
 
-static void jme_link_change_work(struct work_struct *work)
+static void jme_link_change_tasklet(struct tasklet_struct *t)
 {
-	struct jme_adapter *jme = container_of(work, struct jme_adapter, linkch_task);
+	struct jme_adapter *jme = from_tasklet(jme, t, linkch_task);
 	struct net_device *netdev = jme->dev;
 	int rc;
 
@@ -1510,7 +1510,7 @@
 		 * all other events are ignored
 		 */
 		jwrite32(jme, JME_IEVE, intrstat);
-		schedule_work(&jme->linkch_task);
+		tasklet_schedule(&jme->linkch_task);
 		goto out_reenable;
 	}
 
@@ -1832,6 +1832,7 @@
 	jme_clear_pm_disable_wol(jme);
 	JME_NAPI_ENABLE(jme);
 
+	tasklet_setup(&jme->linkch_task, jme_link_change_tasklet);
 	tasklet_setup(&jme->txclean_task, jme_tx_clean_tasklet);
 	tasklet_setup(&jme->rxclean_task, jme_rx_clean_tasklet);
 	tasklet_setup(&jme->rxempty_task, jme_rx_empty_tasklet);
@@ -1919,7 +1920,7 @@
 
 	JME_NAPI_DISABLE(jme);
 
-	cancel_work_sync(&jme->linkch_task);
+	tasklet_kill(&jme->linkch_task);
 	tasklet_kill(&jme->txclean_task);
 	tasklet_kill(&jme->rxclean_task);
 	tasklet_kill(&jme->rxempty_task);
@@ -3034,7 +3035,6 @@
 	atomic_set(&jme->rx_empty, 1);
 
 	tasklet_setup(&jme->pcc_task, jme_pcc_tasklet);
-	INIT_WORK(&jme->linkch_task, jme_link_change_work);
 	jme->dpi.cur = PCC_P1;
 
 	jme->reg_ghc = 0;
diff --git a/kernel/drivers/net/ethernet/jme.h b/kernel/drivers/net/ethernet/jme.h
index 2af7632..a2c3b00 100644
--- a/kernel/drivers/net/ethernet/jme.h
+++ b/kernel/drivers/net/ethernet/jme.h
@@ -411,7 +411,7 @@
 	struct tasklet_struct	rxempty_task;
 	struct tasklet_struct	rxclean_task;
 	struct tasklet_struct	txclean_task;
-	struct work_struct	linkch_task;
+	struct tasklet_struct	linkch_task;
 	struct tasklet_struct	pcc_task;
 	unsigned long		flags;
 	u32			reg_txcs;
diff --git a/kernel/drivers/net/wireless/ath/ath9k/beacon.c b/kernel/drivers/net/wireless/ath/ath9k/beacon.c
index 72e2e71..71e2ada 100644
--- a/kernel/drivers/net/wireless/ath/ath9k/beacon.c
+++ b/kernel/drivers/net/wireless/ath/ath9k/beacon.c
@@ -251,7 +251,7 @@
 	int first_slot = ATH_BCBUF;
 	int slot;
 
-	tasklet_disable_in_atomic(&sc->bcon_tasklet);
+	tasklet_disable(&sc->bcon_tasklet);
 
 	/* Find first taken slot. */
 	for (slot = 0; slot < ATH_BCBUF; slot++) {
diff --git a/kernel/drivers/net/wireless/rockchip_wlan/infineon/bcmdhd/Makefile b/kernel/drivers/net/wireless/rockchip_wlan/infineon/bcmdhd/Makefile
index be6dc59..7179479 100644
--- a/kernel/drivers/net/wireless/rockchip_wlan/infineon/bcmdhd/Makefile
+++ b/kernel/drivers/net/wireless/rockchip_wlan/infineon/bcmdhd/Makefile
@@ -29,6 +29,7 @@
 CONFIG_ANDROID := y
 CONFIG_ANDROID12 := y
 CONFIG_BCMDHD_OOB_HOST_WAKE := y
+CONFIG_MACH_PLATFORM := y
 
 #####################
 # SDIO Basic feature
@@ -525,6 +526,14 @@
     DHDOFILES += wl_linux_mon.o
 endif
 
+ifeq ($(CONFIG_MACH_PLATFORM),y)
+	DHDOFILES += dhd_gpio.o
+	DHDCFLAGS += -DDHD_OF_SUPPORT -DCUSTOMER_OOB -DHW_OOB -DCUSTOMER_HW
+	DHDCFLAGS += -DBCMPCIE_OOB_HOST_WAKE
+	DHDCFLAGS += -DCUSTOMER_HW_ROCKCHIP
+	#DHDCFLAGS += -DENABLE_INSMOD_NO_FW_LOAD
+endif
+
 ifneq ($(CONFIG_DHD_OF_SUPPORT),)
     DHDCFLAGS += -DDHD_OF_SUPPORT
     DHDOFILES += dhd_custom_msm.o
diff --git a/kernel/drivers/net/wireless/rockchip_wlan/infineon/bcmdhd/dhd_custom_msm.c b/kernel/drivers/net/wireless/rockchip_wlan/infineon/bcmdhd/dhd_custom_msm.c
index 453b371..5d796b5 100644
--- a/kernel/drivers/net/wireless/rockchip_wlan/infineon/bcmdhd/dhd_custom_msm.c
+++ b/kernel/drivers/net/wireless/rockchip_wlan/infineon/bcmdhd/dhd_custom_msm.c
@@ -250,7 +250,6 @@
 	IORESOURCE_IRQ_HIGHLEVEL,
 #endif /* CONFIG_BCMDHD_PCIE */
 };
-EXPORT_SYMBOL(dhd_wlan_resources);
 
 struct wifi_platform_data dhd_wlan_control = {
 	.set_power	= dhd_wlan_power,
@@ -260,7 +259,6 @@
 	.mem_prealloc	= dhd_wlan_mem_prealloc,
 #endif /* CONFIG_BROADCOM_WIFI_RESERVED_MEM */
 };
-EXPORT_SYMBOL(dhd_wlan_control);
 
 #ifndef USE_CUSTOM_MSM_PCIE
 int __init
diff --git a/kernel/drivers/net/wireless/rockchip_wlan/infineon/bcmdhd/dhd_gpio.c b/kernel/drivers/net/wireless/rockchip_wlan/infineon/bcmdhd/dhd_gpio.c
new file mode 100644
index 0000000..3a1e508
--- /dev/null
+++ b/kernel/drivers/net/wireless/rockchip_wlan/infineon/bcmdhd/dhd_gpio.c
@@ -0,0 +1,437 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#include <osl.h>
+#include <dhd_linux.h>
+#include <linux/gpio.h>
+#ifdef CUSTOMER_HW_ROCKCHIP
+#include <linux/rfkill-wlan.h>
+#endif
+
+#if defined(BUS_POWER_RESTORE) && defined(BCMSDIO)
+#include <linux/mmc/core.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/sdio_func.h>
+#endif /* defined(BUS_POWER_RESTORE) && defined(BCMSDIO) */
+
+#ifdef CONFIG_DHD_USE_STATIC_BUF
+#ifdef DHD_STATIC_IN_DRIVER
+extern int dhd_static_buf_init(void);
+extern void dhd_static_buf_exit(void);
+#endif /* DHD_STATIC_IN_DRIVER */
+#ifdef BCMDHD_MDRIVER
+extern void *dhd_wlan_mem_prealloc(uint bus_type, int index,
+	int section, unsigned long size);
+#else
+extern void *dhd_wlan_mem_prealloc(int section, unsigned long size);
+#endif
+#endif /* CONFIG_DHD_USE_STATIC_BUF */
+#ifdef CUSTOMER_HW_ROCKCHIP
+#ifdef BCMPCIE
+//extern void rk_pcie_power_on_atu_fixup(void);
+#endif
+#endif
+
+#ifdef BCMDHD_DTS
+/* This is sample code in dts file.
+bcmdhd {
+	compatible = "android,bcmdhd_wlan";
+	gpio_wl_reg_on = <&gpio GPIOH_4 GPIO_ACTIVE_HIGH>;
+	gpio_wl_host_wake = <&gpio GPIOZ_15 GPIO_ACTIVE_HIGH>;
+};
+*/
+#define DHD_DT_COMPAT_ENTRY		"android,bcmdhd_wlan"
+#define GPIO_WL_REG_ON_PROPNAME		"gpio_wl_reg_on"
+#define GPIO_WL_HOST_WAKE_PROPNAME	"gpio_wl_host_wake"
+#endif
+
+static int
+dhd_wlan_set_power(int on, wifi_adapter_info_t *adapter)
+{
+
+	int gpio_wl_reg_on = adapter->gpio_wl_reg_on;
+	int err = 0;
+	printf("xxh debug dhd_wlan_set_power\n");
+
+	if (on) {
+		printf("======== PULL WL_REG_ON(%d) HIGH! ========\n", gpio_wl_reg_on);
+		if (gpio_wl_reg_on >= 0) {
+			err = gpio_direction_output(gpio_wl_reg_on, 1);
+			if (err) {
+				printf("%s: WL_REG_ON didn't output high\n", __FUNCTION__);
+				return -EIO;
+			}
+		}
+#ifdef CUSTOMER_HW_ROCKCHIP
+		rockchip_wifi_power(1);
+#ifdef BCMPCIE
+		//rk_pcie_power_on_atu_fixup();
+#endif
+#endif
+#ifdef BUS_POWER_RESTORE
+#ifdef BCMSDIO
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) && LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 0)
+		if (adapter->sdio_func && adapter->sdio_func->card && adapter->sdio_func->card->host) {
+			mdelay(100);
+			printf("======== mmc_power_restore_host! ========\n");
+			mmc_power_restore_host(adapter->sdio_func->card->host);
+		}
+#endif
+#elif defined(BCMPCIE)
+		if (adapter->pci_dev) {
+			mdelay(100);
+			printf("======== pci_set_power_state PCI_D0! ========\n");
+			pci_set_power_state(adapter->pci_dev, PCI_D0);
+			if (adapter->pci_saved_state)
+				pci_load_and_free_saved_state(adapter->pci_dev, &adapter->pci_saved_state);
+			pci_restore_state(adapter->pci_dev);
+			err = pci_enable_device(adapter->pci_dev);
+			if (err < 0)
+				printf("%s: PCI enable device failed", __FUNCTION__);
+			pci_set_master(adapter->pci_dev);
+		}
+#endif /* BCMPCIE */
+#endif /* BUS_POWER_RESTORE */
+		/* Lets customer power to get stable */
+		mdelay(100);
+	} else {
+#ifdef BUS_POWER_RESTORE
+#ifdef BCMSDIO
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) && LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 0)
+		if (adapter->sdio_func && adapter->sdio_func->card && adapter->sdio_func->card->host) {
+			printf("======== mmc_power_save_host! ========\n");
+			mmc_power_save_host(adapter->sdio_func->card->host);
+		}
+#endif
+#elif defined(BCMPCIE)
+		if (adapter->pci_dev) {
+			printf("======== pci_set_power_state PCI_D3hot! ========\n");
+			pci_save_state(adapter->pci_dev);
+			adapter->pci_saved_state = pci_store_saved_state(adapter->pci_dev);
+			if (pci_is_enabled(adapter->pci_dev))
+				pci_disable_device(adapter->pci_dev);
+			pci_set_power_state(adapter->pci_dev, PCI_D3hot);
+		}
+#endif /* BCMPCIE */
+#endif /* BUS_POWER_RESTORE */
+		printf("======== PULL WL_REG_ON(%d) LOW! ========\n", gpio_wl_reg_on);
+		if (gpio_wl_reg_on >= 0) {
+			err = gpio_direction_output(gpio_wl_reg_on, 0);
+			if (err) {
+				printf("%s: WL_REG_ON didn't output low\n", __FUNCTION__);
+				return -EIO;
+			}
+		}
+#ifdef CUSTOMER_HW_ROCKCHIP
+		rockchip_wifi_power(0);
+#endif
+	}
+
+	return err;
+}
+
+static int dhd_wlan_set_reset(int onoff)
+{
+	return 0;
+}
+
+static int dhd_wlan_set_carddetect(int present)
+{
+	int err = 0;
+
+#if !defined(BUS_POWER_RESTORE)
+	if (present) {
+#if defined(BCMSDIO)
+		printf("======== Card detection to detect SDIO card! ========\n");
+#ifdef CUSTOMER_HW_PLATFORM
+		err = sdhci_force_presence_change(&sdmmc_channel, 1);
+#endif /* CUSTOMER_HW_PLATFORM */
+#ifdef CUSTOMER_HW_ROCKCHIP
+		rockchip_wifi_set_carddetect(1);
+#endif
+#elif defined(BCMPCIE)
+		printf("======== Card detection to detect PCIE card! ========\n");
+#endif
+	} else {
+#if defined(BCMSDIO)
+		printf("======== Card detection to remove SDIO card! ========\n");
+#ifdef CUSTOMER_HW_PLATFORM
+		err = sdhci_force_presence_change(&sdmmc_channel, 0);
+#endif /* CUSTOMER_HW_PLATFORM */
+#ifdef CUSTOMER_HW_ROCKCHIP
+	rockchip_wifi_set_carddetect(0);
+#endif
+#elif defined(BCMPCIE)
+		printf("======== Card detection to remove PCIE card! ========\n");
+#endif
+	}
+#endif /* BUS_POWER_RESTORE */
+
+	return err;
+}
+
+static int dhd_wlan_get_mac_addr(unsigned char *buf, int ifidx)
+{
+	int err = 0;
+
+	if (ifidx == 1) {
+#ifdef EXAMPLE_GET_MAC
+		struct ether_addr ea_example = {{0x00, 0x11, 0x22, 0x33, 0x44, 0xFF}};
+		bcopy((char *)&ea_example, buf, sizeof(struct ether_addr));
+#endif /* EXAMPLE_GET_MAC */
+	} else {
+#ifdef EXAMPLE_GET_MAC
+		struct ether_addr ea_example = {{0x02, 0x11, 0x22, 0x33, 0x44, 0x55}};
+		bcopy((char *)&ea_example, buf, sizeof(struct ether_addr));
+#endif /* EXAMPLE_GET_MAC */
+#ifdef CUSTOMER_HW_ROCKCHIP
+		err = rockchip_wifi_mac_addr(buf);
+#endif
+	}
+
+#ifdef EXAMPLE_GET_MAC_VER2
+	/* EXAMPLE code */
+	{
+		char macpad[56]= {
+		0x00,0xaa,0x9c,0x84,0xc7,0xbc,0x9b,0xf6,
+		0x02,0x33,0xa9,0x4d,0x5c,0xb4,0x0a,0x5d,
+		0xa8,0xef,0xb0,0xcf,0x8e,0xbf,0x24,0x8a,
+		0x87,0x0f,0x6f,0x0d,0xeb,0x83,0x6a,0x70,
+		0x4a,0xeb,0xf6,0xe6,0x3c,0xe7,0x5f,0xfc,
+		0x0e,0xa7,0xb3,0x0f,0x00,0xe4,0x4a,0xaf,
+		0x87,0x08,0x16,0x6d,0x3a,0xe3,0xc7,0x80};
+		bcopy(macpad, buf+6, sizeof(macpad));
+	}
+#endif /* EXAMPLE_GET_MAC_VER2 */
+
+	printf("======== %s err=%d ========\n", __FUNCTION__, err);
+
+	return err;
+}
+
+static struct cntry_locales_custom brcm_wlan_translate_custom_table[] = {
+	/* Table should be filled out based on custom platform regulatory requirement */
+#ifdef EXAMPLE_TABLE
+	{"",   "XT", 49},  /* Universal if Country code is unknown or empty */
+	{"US", "US", 0},
+#endif /* EXMAPLE_TABLE */
+};
+
+#ifdef CUSTOM_FORCE_NODFS_FLAG
+struct cntry_locales_custom brcm_wlan_translate_nodfs_table[] = {
+#ifdef EXAMPLE_TABLE
+	{"",   "XT", 50},  /* Universal if Country code is unknown or empty */
+	{"US", "US", 0},
+#endif /* EXMAPLE_TABLE */
+};
+#endif
+
+static void *dhd_wlan_get_country_code(char *ccode
+#ifdef CUSTOM_FORCE_NODFS_FLAG
+	, u32 flags
+#endif
+)
+{
+	struct cntry_locales_custom *locales;
+	int size;
+	int i;
+
+	if (!ccode)
+		return NULL;
+
+#ifdef CUSTOM_FORCE_NODFS_FLAG
+	if (flags & WLAN_PLAT_NODFS_FLAG) {
+		locales = brcm_wlan_translate_nodfs_table;
+		size = ARRAY_SIZE(brcm_wlan_translate_nodfs_table);
+	} else {
+#endif
+		locales = brcm_wlan_translate_custom_table;
+		size = ARRAY_SIZE(brcm_wlan_translate_custom_table);
+#ifdef CUSTOM_FORCE_NODFS_FLAG
+	}
+#endif
+
+	for (i = 0; i < size; i++)
+		if (strcmp(ccode, locales[i].iso_abbrev) == 0)
+			return &locales[i];
+	return NULL;
+}
+
+struct resource dhd_wlan_resources = {
+	.name	= "bcmdhd_wlan_irq",
+	.start	= 0, /* Dummy */
+	.end	= 0, /* Dummy */
+	.flags	= IORESOURCE_IRQ | IORESOURCE_IRQ_SHAREABLE |
+#ifdef CONFIG_BCMDHD_PCIE
+	IORESOURCE_IRQ_HIGHEDGE,
+#else
+	IORESOURCE_IRQ_HIGHLEVEL,
+#endif /* CONFIG_BCMDHD_PCIE */
+};
+
+struct wifi_platform_data dhd_wlan_control = {
+	.set_power	= dhd_wlan_set_power,
+	.set_reset	= dhd_wlan_set_reset,
+	.set_carddetect	= dhd_wlan_set_carddetect,
+	.get_mac_addr	= dhd_wlan_get_mac_addr,
+#ifdef CONFIG_DHD_USE_STATIC_BUF
+	.mem_prealloc	= dhd_wlan_mem_prealloc,
+#endif /* CONFIG_DHD_USE_STATIC_BUF */
+	.get_country_code = dhd_wlan_get_country_code,
+};
+
+int dhd_wlan_init_gpio(wifi_adapter_info_t *adapter)
+{
+#ifdef BCMDHD_DTS
+	char wlan_node[32];
+	struct device_node *root_node = NULL;
+#endif
+	int err = 0;
+	int gpio_wl_reg_on;
+#ifdef CUSTOMER_OOB
+	int gpio_wl_host_wake;
+	int host_oob_irq = -1;
+	uint host_oob_irq_flags = 0;
+#ifdef CUSTOMER_HW_ROCKCHIP
+#ifdef HW_OOB
+	int irq_flags = -1;
+#endif
+#endif
+#endif
+
+	/* Please check your schematic and fill right GPIO number which connected to
+	* WL_REG_ON and WL_HOST_WAKE.
+	*/
+#ifdef BCMDHD_DTS
+	strcpy(wlan_node, DHD_DT_COMPAT_ENTRY);
+	printf("======== Get GPIO from DTS(%s) ========\n", wlan_node);
+	root_node = of_find_compatible_node(NULL, NULL, wlan_node);
+	if (root_node) {
+		gpio_wl_reg_on = of_get_named_gpio(root_node, GPIO_WL_REG_ON_PROPNAME, 0);
+#ifdef CUSTOMER_OOB
+		gpio_wl_host_wake = of_get_named_gpio(root_node, GPIO_WL_HOST_WAKE_PROPNAME, 0);
+#endif
+	} else
+#endif
+	{
+		gpio_wl_reg_on = -1;
+#ifdef CUSTOMER_OOB
+		gpio_wl_host_wake = -1;
+#endif
+	}
+
+	if (gpio_wl_reg_on >= 0) {
+		err = gpio_request(gpio_wl_reg_on, "WL_REG_ON");
+		if (err < 0) {
+			printf("%s: gpio_request(%d) for WL_REG_ON failed\n",
+				__FUNCTION__, gpio_wl_reg_on);
+			gpio_wl_reg_on = -1;
+		}
+	}
+	adapter->gpio_wl_reg_on = gpio_wl_reg_on;
+
+#ifdef CUSTOMER_OOB
+	if (gpio_wl_host_wake >= 0) {
+		err = gpio_request(gpio_wl_host_wake, "bcmdhd");
+		if (err < 0) {
+			printf("%s: gpio_request(%d) for WL_HOST_WAKE failed\n",
+				__FUNCTION__, gpio_wl_host_wake);
+			return -1;
+		}
+		adapter->gpio_wl_host_wake = gpio_wl_host_wake;
+		err = gpio_direction_input(gpio_wl_host_wake);
+		if (err < 0) {
+			printf("%s: gpio_direction_input(%d) for WL_HOST_WAKE failed\n",
+				__FUNCTION__, gpio_wl_host_wake);
+			gpio_free(gpio_wl_host_wake);
+			return -1;
+		}
+		host_oob_irq = gpio_to_irq(gpio_wl_host_wake);
+		if (host_oob_irq < 0) {
+			printf("%s: gpio_to_irq(%d) for WL_HOST_WAKE failed\n",
+				__FUNCTION__, gpio_wl_host_wake);
+			gpio_free(gpio_wl_host_wake);
+			return -1;
+		}
+	}
+#ifdef CUSTOMER_HW_ROCKCHIP
+	host_oob_irq = rockchip_wifi_get_oob_irq();
+#endif
+
+#ifdef HW_OOB
+#ifdef HW_OOB_LOW_LEVEL
+	host_oob_irq_flags = IORESOURCE_IRQ | IORESOURCE_IRQ_LOWLEVEL | IORESOURCE_IRQ_SHAREABLE;
+#else
+	host_oob_irq_flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL | IORESOURCE_IRQ_SHAREABLE;
+#endif
+#ifdef CUSTOMER_HW_ROCKCHIP
+	host_oob_irq_flags = IORESOURCE_IRQ | IORESOURCE_IRQ_SHAREABLE;
+	irq_flags = rockchip_wifi_get_oob_irq_flag();
+	if (irq_flags == 1)
+		host_oob_irq_flags |= IORESOURCE_IRQ_HIGHLEVEL;
+	else if (irq_flags == 0)
+		host_oob_irq_flags |= IORESOURCE_IRQ_LOWLEVEL;
+	else
+		pr_warn("%s: unknown oob irqflags !\n", __func__);
+#endif
+#else
+	host_oob_irq_flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE | IORESOURCE_IRQ_SHAREABLE;
+#endif
+	host_oob_irq_flags &= IRQF_TRIGGER_MASK;
+
+	adapter->irq_num = host_oob_irq;
+	adapter->intr_flags = host_oob_irq_flags;
+	printf("%s: WL_HOST_WAKE=%d, oob_irq=%d, oob_irq_flags=0x%x\n", __FUNCTION__,
+		gpio_wl_host_wake, host_oob_irq, host_oob_irq_flags);
+#endif /* CUSTOMER_OOB */
+	printf("%s: WL_REG_ON=%d\n", __FUNCTION__, gpio_wl_reg_on);
+	printf("xxh dhd_wlan_init_gpio 1 \n");
+
+	return 0;
+}
+
+static void dhd_wlan_deinit_gpio(wifi_adapter_info_t *adapter)
+{
+	int gpio_wl_reg_on = adapter->gpio_wl_reg_on;
+#ifdef CUSTOMER_OOB
+	int gpio_wl_host_wake = adapter->gpio_wl_host_wake;
+#endif
+
+	if (gpio_wl_reg_on >= 0) {
+		printf("%s: gpio_free(WL_REG_ON %d)\n", __FUNCTION__, gpio_wl_reg_on);
+		gpio_free(gpio_wl_reg_on);
+		gpio_wl_reg_on = -1;
+	}
+#ifdef CUSTOMER_OOB
+	if (gpio_wl_host_wake >= 0) {
+		printf("%s: gpio_free(WL_HOST_WAKE %d)\n", __FUNCTION__, gpio_wl_host_wake);
+		gpio_free(gpio_wl_host_wake);
+		gpio_wl_host_wake = -1;
+	}
+#endif /* CUSTOMER_OOB */
+}
+
+int dhd_wlan_init_plat_data(wifi_adapter_info_t *adapter)
+{
+	int err = 0;
+
+	printf("======== %s ========\n", __FUNCTION__);
+	err = dhd_wlan_init_gpio(adapter);
+	if (err)
+		goto exit;
+
+#ifdef DHD_STATIC_IN_DRIVER
+	err = dhd_static_buf_init();
+#endif
+
+exit:
+	return err;
+}
+
+void dhd_wlan_deinit_plat_data(wifi_adapter_info_t *adapter)
+{
+	printf("======== %s ========\n", __FUNCTION__);
+#ifdef DHD_STATIC_IN_DRIVER
+	dhd_static_buf_exit();
+#endif
+	dhd_wlan_deinit_gpio(adapter);
+}
diff --git a/kernel/drivers/net/wireless/rockchip_wlan/infineon/bcmdhd/dhd_linux.h b/kernel/drivers/net/wireless/rockchip_wlan/infineon/bcmdhd/dhd_linux.h
index 53a727a..b7c68a0 100644
--- a/kernel/drivers/net/wireless/rockchip_wlan/infineon/bcmdhd/dhd_linux.h
+++ b/kernel/drivers/net/wireless/rockchip_wlan/infineon/bcmdhd/dhd_linux.h
@@ -239,11 +239,29 @@
 				} \
 			} while (0)
 
+typedef struct wifi_adapter_info {
+	const char	*name;
+	uint		irq_num;
+	uint		intr_flags;
+	const char	*fw_path;
+	const char	*nv_path;
+	void		*wifi_plat_data;	/* wifi ctrl func, for backward compatibility */
+	uint		bus_type;
+	int index;
+	uint		bus_num;
+	uint		slot_num;
+	int         gpio_wl_reg_on;
+	int         gpio_wl_host_wake;
+#if defined(BT_OVER_SDIO)
+	const char	*btfw_path;
+#endif /* defined (BT_OVER_SDIO) */
+} wifi_adapter_info_t;
+
 #if !defined(CONFIG_WIFI_CONTROL_FUNC)
 #define WLAN_PLAT_NODFS_FLAG	0x01
 #define WLAN_PLAT_AP_FLAG	0x02
 struct wifi_platform_data {
-	int (*set_power)(int val);
+	int (*set_power)(int val,  wifi_adapter_info_t *adapter);
 	int (*set_reset)(int val);
 	int (*set_carddetect)(int val);
 	void *(*mem_prealloc)(int section, unsigned long size);
@@ -261,20 +279,6 @@
 
 #define DHD_REGISTRATION_TIMEOUT  12000  /* msec : allowed time to finished dhd registration */
 
-typedef struct wifi_adapter_info {
-	const char	*name;
-	uint		irq_num;
-	uint		intr_flags;
-	const char	*fw_path;
-	const char	*nv_path;
-	void		*wifi_plat_data;	/* wifi ctrl func, for backward compatibility */
-	uint		bus_type;
-	uint		bus_num;
-	uint		slot_num;
-#if defined(BT_OVER_SDIO)
-	const char	*btfw_path;
-#endif /* defined (BT_OVER_SDIO) */
-} wifi_adapter_info_t;
 
 typedef struct bcmdhd_wifi_platdata {
 	uint				num_adapters;
diff --git a/kernel/drivers/net/wireless/rockchip_wlan/infineon/bcmdhd/dhd_linux_platdev.c b/kernel/drivers/net/wireless/rockchip_wlan/infineon/bcmdhd/dhd_linux_platdev.c
index f91e496..9da8b5a 100644
--- a/kernel/drivers/net/wireless/rockchip_wlan/infineon/bcmdhd/dhd_linux_platdev.c
+++ b/kernel/drivers/net/wireless/rockchip_wlan/infineon/bcmdhd/dhd_linux_platdev.c
@@ -202,7 +202,7 @@
 		}
 #endif /* ENABLE_4335BT_WAR */
 
-		err = plat_data->set_power(on);
+		err = plat_data->set_power(on, adapter);
 	}
 
 	if (msec && !err)
@@ -541,9 +541,15 @@
 	if (dts_enabled) {
 		struct resource *resource;
 		adapter->wifi_plat_data = (void *)&dhd_wlan_control;
+#ifdef CUSTOMER_HW
+		wifi_plat_dev_probe_ret = dhd_wlan_init_plat_data(adapter);
+		if (wifi_plat_dev_probe_ret)
+			return wifi_plat_dev_probe_ret;
+#endif
 		resource = &dhd_wlan_resources;
-		adapter->irq_num = resource->start;
-		adapter->intr_flags = resource->flags & IRQF_TRIGGER_MASK;
+		//adapter->irq_num = resource->start;
+		//adapter->intr_flags = resource->flags & IRQF_TRIGGER_MASK;
+
 #ifdef DHD_ISR_NO_SUSPEND
 		adapter->intr_flags |= IRQF_NO_SUSPEND;
 #endif // endif
diff --git a/kernel/drivers/net/wireless/rockchip_wlan/infineon/bcmdhd/dhd_pcie_linux.c b/kernel/drivers/net/wireless/rockchip_wlan/infineon/bcmdhd/dhd_pcie_linux.c
index 426afe7..49d183f 100644
--- a/kernel/drivers/net/wireless/rockchip_wlan/infineon/bcmdhd/dhd_pcie_linux.c
+++ b/kernel/drivers/net/wireless/rockchip_wlan/infineon/bcmdhd/dhd_pcie_linux.c
@@ -1006,9 +1006,6 @@
 		DHD_ERROR(("%s: pci_set_power_state error %d\n",
 			__FUNCTION__, ret));
 	}
-#ifdef OEM_ANDROID
-	dev->state_saved = FALSE;
-#endif /* OEM_ANDROID */
 	dhdpcie_suspend_dump_cfgregs(bus, "AFTER_EP_SUSPEND");
 	return ret;
 }
@@ -1046,9 +1043,6 @@
 	pci_load_and_free_saved_state(dev, &pch->state);
 #endif /* OEM_ANDROID && LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) */
 	DHD_ERROR(("%s: Enter\n", __FUNCTION__));
-#ifdef OEM_ANDROID
-	dev->state_saved = TRUE;
-#endif /* OEM_ANDROID */
 	pci_restore_state(dev);
 #ifdef FORCE_TPOWERON
 	if (dhdpcie_chip_req_forced_tpoweron(pch->bus)) {
@@ -2801,7 +2795,7 @@
 	}
 
 	if (dhdpcie_osinfo->oob_irq_num > 0) {
-		DHD_INFO_HW4(("%s OOB irq=%d flags=%X \n", __FUNCTION__,
+		DHD_ERROR(("%s OOB irq=%d flags=%X \n", __FUNCTION__,
 			(int)dhdpcie_osinfo->oob_irq_num,
 			(int)dhdpcie_osinfo->oob_irq_flags));
 		err = request_irq(dhdpcie_osinfo->oob_irq_num, wlan_oob_irq,
diff --git a/kernel/drivers/net/wireless/rockchip_wlan/infineon/bcmdhd/wl_cfgvendor.c b/kernel/drivers/net/wireless/rockchip_wlan/infineon/bcmdhd/wl_cfgvendor.c
index 3dd8083..8e88deb 100644
--- a/kernel/drivers/net/wireless/rockchip_wlan/infineon/bcmdhd/wl_cfgvendor.c
+++ b/kernel/drivers/net/wireless/rockchip_wlan/infineon/bcmdhd/wl_cfgvendor.c
@@ -6226,12 +6226,14 @@
 	RETURN_EIO_IF_NOT_UP(cfg);
 
 	/* Get the device rev info */
+#ifdef OEM_ANDROID
 	bzero(&revinfo, sizeof(revinfo));
 	err = wldev_ioctl_get(bcmcfg_to_prmry_ndev(cfg), WLC_GET_REVINFO, &revinfo,
 			sizeof(revinfo));
 	if (err != BCME_OK) {
 		goto exit;
 	}
+#endif
 
 	outdata = (void *)MALLOCZ(cfg->osh, WLC_IOCTL_MAXLEN);
 	if (outdata == NULL) {
diff --git a/kernel/drivers/nvme/host/nvme.h b/kernel/drivers/nvme/host/nvme.h
index 8633649..9324a38 100644
--- a/kernel/drivers/nvme/host/nvme.h
+++ b/kernel/drivers/nvme/host/nvme.h
@@ -155,6 +155,11 @@
 	 * Reports garbage in the namespace identifiers (eui64, nguid, uuid).
 	 */
 	NVME_QUIRK_BOGUS_NID			= (1 << 18),
+
+	/*
+	 * Limit io queue depth to 32
+	 */
+	NVME_QUIRK_LIMIT_IOQD32			= (1 << 31),
 };
 
 /*
diff --git a/kernel/drivers/nvme/host/pci.c b/kernel/drivers/nvme/host/pci.c
index c222d7b..0cefbe9 100644
--- a/kernel/drivers/nvme/host/pci.c
+++ b/kernel/drivers/nvme/host/pci.c
@@ -2394,6 +2394,9 @@
 
 	dev->ctrl.cap = lo_hi_readq(dev->bar + NVME_REG_CAP);
 
+	if (dev->ctrl.quirks & NVME_QUIRK_LIMIT_IOQD32)
+		io_queue_depth = 32;
+
 	dev->q_depth = min_t(u32, NVME_CAP_MQES(dev->ctrl.cap) + 1,
 				io_queue_depth);
 	dev->ctrl.sqsize = dev->q_depth - 1; /* 0's based queue depth */
@@ -3236,6 +3239,8 @@
 		.driver_data = NVME_QUIRK_DELAY_BEFORE_CHK_RDY |
 				NVME_QUIRK_DISABLE_WRITE_ZEROES|
 				NVME_QUIRK_IGNORE_DEV_SUBNQN, },
+	{ PCI_DEVICE(0x1987, 0x5013),	/* Phison E13 */
+		.driver_data = NVME_QUIRK_LIMIT_IOQD32},
 	{ PCI_DEVICE(0x1987, 0x5016),	/* Phison E16 */
 		.driver_data = NVME_QUIRK_IGNORE_DEV_SUBNQN |
 				NVME_QUIRK_BOGUS_NID, },
diff --git a/kernel/drivers/pci/controller/dwc/pcie-dw-ep-rockchip.c b/kernel/drivers/pci/controller/dwc/pcie-dw-ep-rockchip.c
index 6655bed..31e84f7 100644
--- a/kernel/drivers/pci/controller/dwc/pcie-dw-ep-rockchip.c
+++ b/kernel/drivers/pci/controller/dwc/pcie-dw-ep-rockchip.c
@@ -81,6 +81,10 @@
 #define PCIE_CLIENT_HOT_RESET_CTRL      0x180
 #define PCIE_CLIENT_LTSSM_STATUS	0x300
 #define PCIE_CLIENT_INTR_MASK		0x24
+#define PCIE_LTSSM_APP_DLY1_EN		BIT(0)
+#define PCIE_LTSSM_APP_DLY2_EN		BIT(1)
+#define PCIE_LTSSM_APP_DLY1_DONE	BIT(2)
+#define PCIE_LTSSM_APP_DLY2_DONE	BIT(3)
 #define PCIE_LTSSM_ENABLE_ENHANCE       BIT(4)
 #define PCIE_CLIENT_MSI_GEN_CON		0x38
 
@@ -106,6 +110,7 @@
 #define PCIE_EP_OBJ_INFO_DRV_VERSION	0x00000001
 
 #define PCIE_BAR_MAX_NUM		6
+#define PCIE_HOTRESET_TMOUT_US		10000
 
 struct rockchip_pcie {
 	struct dw_pcie			pci;
@@ -130,6 +135,8 @@
 	phys_addr_t			dbi_base_physical;
 	struct pcie_ep_obj_info		*obj_info;
 	enum pcie_ep_mmap_resource	cur_mmap_res;
+	struct workqueue_struct		*hot_rst_wq;
+	struct work_struct		hot_rst_work;
 };
 
 struct rockchip_pcie_misc_dev {
@@ -586,7 +593,8 @@
 
 	/* LTSSM EN ctrl mode */
 	val = rockchip_pcie_readl_apb(rockchip, PCIE_CLIENT_HOT_RESET_CTRL);
-	val |= PCIE_LTSSM_ENABLE_ENHANCE | (PCIE_LTSSM_ENABLE_ENHANCE << 16);
+	val |= (PCIE_LTSSM_ENABLE_ENHANCE | PCIE_LTSSM_APP_DLY2_EN) |
+		((PCIE_LTSSM_ENABLE_ENHANCE | PCIE_LTSSM_APP_DLY2_EN) << 16);
 	rockchip_pcie_writel_apb(rockchip, val, PCIE_CLIENT_HOT_RESET_CTRL);
 }
 
@@ -642,7 +650,7 @@
 	u32 chn;
 	union int_status wr_status, rd_status;
 	union int_clear clears;
-	u32 reg, val, mask;
+	u32 reg, mask;
 	bool sigio = false;
 
 	/* ELBI helper, only check the valid bits, and discard the rest interrupts */
@@ -713,14 +721,8 @@
 	}
 
 	reg = rockchip_pcie_readl_apb(rockchip, PCIE_CLIENT_INTR_STATUS_MISC);
-	if (reg & BIT(2)) {
-		/* Setup command register */
-		val = dw_pcie_readl_dbi(pci, PCI_COMMAND);
-		val &= 0xffff0000;
-		val |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY |
-		       PCI_COMMAND_MASTER | PCI_COMMAND_SERR;
-		dw_pcie_writel_dbi(pci, PCI_COMMAND, val);
-	}
+	if (reg & BIT(2))
+		queue_work(rockchip->hot_rst_wq, &rockchip->hot_rst_work);
 
 	rockchip_pcie_writel_apb(rockchip, reg, PCIE_CLIENT_INTR_STATUS_MISC);
 
@@ -868,6 +870,23 @@
 	table->weilo.weight0 = 0x0;
 	table->start.stop = 0x0;
 	table->start.chnl = table->chn;
+}
+
+static void rockchip_pcie_hot_rst_work(struct work_struct *work)
+{
+	struct rockchip_pcie *rockchip = container_of(work, struct rockchip_pcie, hot_rst_work);
+	u32 status;
+	int ret;
+
+	if (rockchip_pcie_readl_apb(rockchip, PCIE_CLIENT_HOT_RESET_CTRL) & PCIE_LTSSM_APP_DLY2_EN) {
+		ret = readl_poll_timeout(rockchip->apb_base + PCIE_CLIENT_LTSSM_STATUS,
+			 status, ((status & 0x3F) == 0), 100, PCIE_HOTRESET_TMOUT_US);
+		if (ret)
+			dev_err(rockchip->pci.dev, "wait for detect quiet failed!\n");
+
+		rockchip_pcie_writel_apb(rockchip, (PCIE_LTSSM_APP_DLY2_DONE) | ((PCIE_LTSSM_APP_DLY2_DONE) << 16),
+					PCIE_CLIENT_HOT_RESET_CTRL);
+	}
 }
 
 static int rockchip_pcie_get_dma_status(struct dma_trx_obj *obj, u8 chn, enum dma_dir dir)
@@ -1121,6 +1140,7 @@
 	struct rockchip_pcie *rockchip;
 	int ret;
 	int retry, i;
+	u32 reg;
 
 	rockchip = devm_kzalloc(dev, sizeof(*rockchip), GFP_KERNEL);
 	if (!rockchip)
@@ -1182,6 +1202,26 @@
 	rockchip_pcie_start_link(&rockchip->pci);
 	rockchip_pcie_devmode_update(rockchip, RKEP_MODE_KERNEL, RKEP_SMODE_LNKRDY);
 
+	rockchip->hot_rst_wq = create_singlethread_workqueue("rkep_hot_rst_wq");
+	if (!rockchip->hot_rst_wq) {
+		dev_err(dev, "failed to create hot_rst workqueue\n");
+		ret = -ENOMEM;
+		goto deinit_phy;
+	}
+	INIT_WORK(&rockchip->hot_rst_work, rockchip_pcie_hot_rst_work);
+
+	reg = rockchip_pcie_readl_apb(rockchip, PCIE_CLIENT_INTR_STATUS_MISC);
+	if ((reg & BIT(2)) &&
+	    (rockchip_pcie_readl_apb(rockchip, PCIE_CLIENT_HOT_RESET_CTRL) & PCIE_LTSSM_APP_DLY2_EN)) {
+		rockchip_pcie_writel_apb(rockchip, PCIE_LTSSM_APP_DLY2_DONE | (PCIE_LTSSM_APP_DLY2_DONE << 16),
+					 PCIE_CLIENT_HOT_RESET_CTRL);
+		dev_info(dev, "hot reset ever\n");
+	}
+	rockchip_pcie_writel_apb(rockchip, reg, PCIE_CLIENT_INTR_STATUS_MISC);
+
+	/* Enable client reset or link down interrupt */
+	rockchip_pcie_writel_apb(rockchip, 0x40000, PCIE_CLIENT_INTR_MASK);
+
 	for (retry = 0; retry < 10000; retry++) {
 		if (dw_pcie_link_up(&rockchip->pci)) {
 			/*
diff --git a/kernel/drivers/pci/controller/dwc/pcie-dw-rockchip.c b/kernel/drivers/pci/controller/dwc/pcie-dw-rockchip.c
index 9b89741..4e5a225 100644
--- a/kernel/drivers/pci/controller/dwc/pcie-dw-rockchip.c
+++ b/kernel/drivers/pci/controller/dwc/pcie-dw-rockchip.c
@@ -115,6 +115,10 @@
 #define PME_TURN_OFF			(BIT(4) | BIT(20))
 #define PCIE_CLIENT_GENERAL_DEBUG	0x104
 #define PCIE_CLIENT_HOT_RESET_CTRL	0x180
+#define PCIE_LTSSM_APP_DLY1_EN		BIT(0)
+#define PCIE_LTSSM_APP_DLY2_EN		BIT(1)
+#define PCIE_LTSSM_APP_DLY1_DONE	BIT(2)
+#define PCIE_LTSSM_APP_DLY2_DONE	BIT(3)
 #define PCIE_LTSSM_ENABLE_ENHANCE	BIT(4)
 #define PCIE_CLIENT_LTSSM_STATUS	0x300
 #define SMLH_LINKUP			BIT(16)
@@ -137,6 +141,7 @@
 
 #define PCIE_PL_ORDER_RULE_CTRL_OFF	0x8B4
 #define RK_PCIE_L2_TMOUT_US		5000
+#define RK_PCIE_HOTRESET_TMOUT_US	10000
 
 enum rk_pcie_ltssm_code {
 	S_L0 = 0x11,
@@ -185,6 +190,10 @@
 	u32				l1ss_ctl1;
 	struct dentry			*debugfs;
 	u32				msi_vector_num;
+	struct workqueue_struct		*hot_rst_wq;
+	struct work_struct		hot_rst_work;
+	u32				comp_prst[2];
+	u32				intx;
 };
 
 struct rk_pcie_of_data {
@@ -1119,6 +1128,10 @@
 
 	dw_pcie_setup_rc(pp);
 
+	/* Disable BAR0 BAR1 */
+	dw_pcie_writel_dbi(pci, PCIE_TYPE0_HDR_DBI2_OFFSET + 0x10 + BAR_0 * 4, 0);
+	dw_pcie_writel_dbi(pci, PCIE_TYPE0_HDR_DBI2_OFFSET + 0x10 + BAR_1 * 4, 0);
+
 	ret = rk_pcie_establish_link(pci);
 
 	if (pp->msi_irq > 0)
@@ -1434,13 +1447,37 @@
 	table->start.chnl = table->chn;
 }
 
+static void rk_pcie_hot_rst_work(struct work_struct *work)
+{
+	struct rk_pcie *rk_pcie = container_of(work, struct rk_pcie, hot_rst_work);
+	u32 val, status;
+	int ret;
+
+	/* Setup command register */
+	val = dw_pcie_readl_dbi(rk_pcie->pci, PCI_COMMAND);
+	val &= 0xffff0000;
+	val |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY |
+		PCI_COMMAND_MASTER | PCI_COMMAND_SERR;
+	dw_pcie_writel_dbi(rk_pcie->pci, PCI_COMMAND, val);
+
+	if (rk_pcie_readl_apb(rk_pcie, PCIE_CLIENT_HOT_RESET_CTRL) & PCIE_LTSSM_APP_DLY2_EN) {
+		ret = readl_poll_timeout(rk_pcie->apb_base + PCIE_CLIENT_LTSSM_STATUS,
+			 status, ((status & 0x3F) == 0), 100, RK_PCIE_HOTRESET_TMOUT_US);
+		if (ret)
+			dev_err(rk_pcie->pci->dev, "wait for detect quiet failed!\n");
+
+		rk_pcie_writel_apb(rk_pcie, PCIE_CLIENT_HOT_RESET_CTRL,
+			(PCIE_LTSSM_APP_DLY2_DONE) | ((PCIE_LTSSM_APP_DLY2_DONE) << 16));
+	}
+}
+
 static irqreturn_t rk_pcie_sys_irq_handler(int irq, void *arg)
 {
 	struct rk_pcie *rk_pcie = arg;
 	u32 chn;
 	union int_status status;
 	union int_clear clears;
-	u32 reg, val;
+	u32 reg;
 
 	status.asdword = dw_pcie_readl_dbi(rk_pcie->pci, PCIE_DMA_OFFSET +
 					   PCIE_DMA_WR_INT_STATUS);
@@ -1481,14 +1518,8 @@
 	}
 
 	reg = rk_pcie_readl_apb(rk_pcie, PCIE_CLIENT_INTR_STATUS_MISC);
-	if (reg & BIT(2)) {
-		/* Setup command register */
-		val = dw_pcie_readl_dbi(rk_pcie->pci, PCI_COMMAND);
-		val &= 0xffff0000;
-		val |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY |
-		       PCI_COMMAND_MASTER | PCI_COMMAND_SERR;
-		dw_pcie_writel_dbi(rk_pcie->pci, PCI_COMMAND, val);
-	}
+	if (reg & BIT(2))
+		queue_work(rk_pcie->hot_rst_wq, &rk_pcie->hot_rst_work);
 
 	rk_pcie_writel_apb(rk_pcie, PCIE_CLIENT_INTR_STATUS_MISC, reg);
 
@@ -1612,7 +1643,8 @@
 
 	/* LTSSM EN ctrl mode */
 	val = rk_pcie_readl_apb(rk_pcie, PCIE_CLIENT_HOT_RESET_CTRL);
-	val |= PCIE_LTSSM_ENABLE_ENHANCE | (PCIE_LTSSM_ENABLE_ENHANCE << 16);
+	val |= (PCIE_LTSSM_ENABLE_ENHANCE | PCIE_LTSSM_APP_DLY2_EN)
+		| ((PCIE_LTSSM_APP_DLY2_EN | PCIE_LTSSM_ENABLE_ENHANCE) << 16);
 	rk_pcie_writel_apb(rk_pcie, PCIE_CLIENT_HOT_RESET_CTRL, val);
 }
 
@@ -1650,7 +1682,7 @@
 static int rk_pcie_intx_map(struct irq_domain *domain, unsigned int irq,
 			    irq_hw_number_t hwirq)
 {
-	irq_set_chip_and_handler(irq, &rk_pcie_legacy_irq_chip, handle_simple_irq);
+	irq_set_chip_and_handler(irq, &rk_pcie_legacy_irq_chip, handle_level_irq);
 	irq_set_chip_data(irq, domain->host_data);
 
 	return 0;
@@ -2051,17 +2083,39 @@
 		rk_pcie->is_signal_test = true;
 	}
 
-	/* Force into compliance mode */
-	if (device_property_read_bool(dev, "rockchip,compliance-mode")) {
-		val = dw_pcie_readl_dbi(pci, PCIE_CAP_LINK_CONTROL2_LINK_STATUS);
-		val |= BIT(4);
-		dw_pcie_writel_dbi(pci, PCIE_CAP_LINK_CONTROL2_LINK_STATUS, val);
+	/*
+	 * Force into compliance mode
+	 * comp_prst is a two dimensional array of which the first element
+	 * stands for speed mode, and the second one is preset value encoding:
+	 * [0] 0->SMA tool control the signal switch, 1/2/3 is for manual Gen setting
+	 * [1] transmitter setting for manual Gen setting, valid only if [0] isn't zero.
+	 */
+	if (!device_property_read_u32_array(dev, "rockchip,compliance-mode",
+					    rk_pcie->comp_prst, 2)) {
+		BUG_ON(rk_pcie->comp_prst[0] > 3 || rk_pcie->comp_prst[1] > 10);
+		if (!rk_pcie->comp_prst[0]) {
+			dev_info(dev, "Auto compliance mode for SMA tool.\n");
+		} else {
+			dev_info(dev, "compliance mode for soldered board Gen%d, P%d.\n",
+				 rk_pcie->comp_prst[0], rk_pcie->comp_prst[1]);
+			val = dw_pcie_readl_dbi(pci, PCIE_CAP_LINK_CONTROL2_LINK_STATUS);
+			val |= BIT(4) | rk_pcie->comp_prst[0] | (rk_pcie->comp_prst[1] << 12);
+			dw_pcie_writel_dbi(pci, PCIE_CAP_LINK_CONTROL2_LINK_STATUS, val);
+		}
 		rk_pcie->is_signal_test = true;
 	}
 
 	/* Skip waiting for training to pass in system PM routine */
 	if (device_property_read_bool(dev, "rockchip,skip-scan-in-resume"))
 		rk_pcie->skip_scan_in_resume = true;
+
+	rk_pcie->hot_rst_wq = create_singlethread_workqueue("rk_pcie_hot_rst_wq");
+	if (!rk_pcie->hot_rst_wq) {
+		dev_err(dev, "failed to create hot_rst workqueue\n");
+		ret = -ENOMEM;
+		goto remove_irq_domain;
+	}
+	INIT_WORK(&rk_pcie->hot_rst_work, rk_pcie_hot_rst_work);
 
 	switch (rk_pcie->mode) {
 	case RK_PCIE_RC_TYPE:
@@ -2076,12 +2130,12 @@
 		return 0;
 
 	if (ret)
-		goto remove_irq_domain;
+		goto remove_rst_wq;
 
 	ret = rk_pcie_init_dma_trx(rk_pcie);
 	if (ret) {
 		dev_err(dev, "failed to add dma extension\n");
-		goto remove_irq_domain;
+		goto remove_rst_wq;
 	}
 
 	if (rk_pcie->dma_obj) {
@@ -2093,7 +2147,7 @@
 		/* hold link reset grant after link-up */
 		ret = rk_pcie_reset_grant_ctrl(rk_pcie, false);
 		if (ret)
-			goto remove_irq_domain;
+			goto remove_rst_wq;
 	}
 
 	dw_pcie_dbi_ro_wr_dis(pci);
@@ -2121,6 +2175,8 @@
 
 	return 0;
 
+remove_rst_wq:
+	destroy_workqueue(rk_pcie->hot_rst_wq);
 remove_irq_domain:
 	if (rk_pcie->irq_domain)
 		irq_domain_remove(rk_pcie->irq_domain);
@@ -2304,6 +2360,8 @@
 	phy_power_off(rk_pcie->phy);
 	phy_exit(rk_pcie->phy);
 
+	rk_pcie->intx = rk_pcie_readl_apb(rk_pcie, PCIE_CLIENT_INTR_MASK_LEGACY);
+
 	clk_bulk_disable_unprepare(rk_pcie->clk_cnt, rk_pcie->clks);
 
 	rk_pcie->in_suspend = true;
@@ -2368,6 +2426,9 @@
 	if (std_rc)
 		dw_pcie_setup_rc(&rk_pcie->pci->pp);
 
+	rk_pcie_writel_apb(rk_pcie, PCIE_CLIENT_INTR_MASK_LEGACY,
+			   rk_pcie->intx | 0xffff0000);
+
 	ret = rk_pcie_establish_link(rk_pcie->pci);
 	if (ret) {
 		dev_err(dev, "failed to establish pcie link\n");
diff --git a/kernel/drivers/pci/controller/pci-hyperv.c b/kernel/drivers/pci/controller/pci-hyperv.c
index 03e2569..4353443 100644
--- a/kernel/drivers/pci/controller/pci-hyperv.c
+++ b/kernel/drivers/pci/controller/pci-hyperv.c
@@ -1522,7 +1522,7 @@
 	 * Prevents hv_pci_onchannelcallback() from running concurrently
 	 * in the tasklet.
 	 */
-	tasklet_disable_in_atomic(&channel->callback_event);
+	tasklet_disable(&channel->callback_event);
 
 	/*
 	 * Since this function is called with IRQ locks held, can't
diff --git a/kernel/drivers/phy/rockchip/phy-rockchip-csi2-dphy-common.h b/kernel/drivers/phy/rockchip/phy-rockchip-csi2-dphy-common.h
index 0ec812b..3811d6f 100644
--- a/kernel/drivers/phy/rockchip/phy-rockchip-csi2-dphy-common.h
+++ b/kernel/drivers/phy/rockchip/phy-rockchip-csi2-dphy-common.h
@@ -9,9 +9,14 @@
 #define _PHY_ROCKCHIP_CSI2_DPHY_COMMON_H_
 
 #include <linux/rk-camera-module.h>
+#include <linux/rkcif-config.h>
 
 #define PHY_MAX 16
 #define MAX_DEV_NAME_LEN 32
+
+#define MAX_SAMSUNG_PHY_NUM 2
+
+#define MAX_INNO_PHY_NUM 2
 
 /* add new chip id in tail by time order */
 enum csi2_dphy_chip_id {
@@ -59,14 +64,18 @@
 
 struct dphy_drv_data {
 	const char dev_name[MAX_DEV_NAME_LEN];
-	enum csi2_dphy_vendor vendor;
+	enum csi2_dphy_chip_id chip_id;
+	char num_inno_phy;
+	char num_samsung_phy;
 };
 
 struct csi2_dphy {
 	struct device *dev;
 	struct list_head list;
 	struct csi2_dphy_hw *dphy_hw;
+	struct csi2_dphy_hw *dphy_hw_group[MAX_INNO_PHY_NUM];
 	struct samsung_mipi_dcphy *samsung_phy;
+	struct samsung_mipi_dcphy *samsung_phy_group[MAX_SAMSUNG_PHY_NUM];
 	struct v4l2_async_notifier notifier;
 	struct v4l2_subdev sd;
 	struct mutex mutex; /* lock for updating protection */
@@ -75,8 +84,10 @@
 	u64 data_rate_mbps;
 	int num_sensors;
 	int phy_index;
+	struct rkcif_csi_info csi_info;
+	void *phy_hw[RKMODULE_MULTI_DEV_NUM];
 	bool is_streaming;
-	enum csi2_dphy_lane_mode lane_mode;
+	int lane_mode;
 	const struct dphy_drv_data *drv_data;
 	struct rkmodule_csi_dphy_param dphy_param;
 };
diff --git a/kernel/drivers/phy/rockchip/phy-rockchip-csi2-dphy.c b/kernel/drivers/phy/rockchip/phy-rockchip-csi2-dphy.c
index 81827de..373818c 100644
--- a/kernel/drivers/phy/rockchip/phy-rockchip-csi2-dphy.c
+++ b/kernel/drivers/phy/rockchip/phy-rockchip-csi2-dphy.c
@@ -25,6 +25,17 @@
 #include "phy-rockchip-csi2-dphy-common.h"
 #include "phy-rockchip-samsung-dcphy.h"
 
+static struct rkmodule_csi_dphy_param rk3588_dcphy_param = {
+	.vendor = PHY_VENDOR_SAMSUNG,
+	.lp_vol_ref = 3,
+	.lp_hys_sw = {3, 0, 0, 0},
+	.lp_escclk_pol_sel = {1, 0, 0, 0},
+	.skew_data_cal_clk = {0, 3, 3, 3},
+	.clk_hs_term_sel = 2,
+	.data_hs_term_sel = {2, 2, 2, 2},
+	.reserved = {0},
+};
+
 struct sensor_async_subdev {
 	struct v4l2_async_subdev asd;
 	struct v4l2_mbus_config mbus;
@@ -42,7 +53,10 @@
 {
 	struct media_pad *local, *remote;
 	struct media_entity *sensor_me;
+	struct csi2_dphy *dphy = to_csi2_dphy(sd);
 
+	if (dphy->num_sensors == 0)
+		return NULL;
 	local = &sd->entity.pads[CSI2_DPHY_RX_PAD_SINK];
 	remote = media_entity_remote_pad(local);
 	if (!remote) {
@@ -72,7 +86,7 @@
 	struct v4l2_subdev *sensor_sd = get_remote_sensor(sd);
 	struct v4l2_ctrl *link_freq;
 	struct v4l2_querymenu qm = { .id = V4L2_CID_LINK_FREQ, };
-	int ret;
+	int ret = 0;
 
 	if (!sensor_sd)
 		return -ENODEV;
@@ -101,14 +115,214 @@
 	return 0;
 }
 
+static int rockchip_csi2_dphy_attach_hw(struct csi2_dphy *dphy, int csi_idx, int index)
+{
+	struct csi2_dphy_hw *dphy_hw;
+	struct samsung_mipi_dcphy *dcphy_hw;
+	struct v4l2_subdev *sensor_sd = get_remote_sensor(&dphy->sd);
+	struct csi2_sensor *sensor = NULL;
+	int lanes = 2;
+
+	if (sensor_sd) {
+		sensor = sd_to_sensor(dphy, sensor_sd);
+		lanes = sensor->lanes;
+	}
+
+	if (dphy->drv_data->chip_id == CHIP_ID_RK3568 ||
+	    dphy->drv_data->chip_id == CHIP_ID_RV1106) {
+		dphy_hw = dphy->dphy_hw_group[0];
+		mutex_lock(&dphy_hw->mutex);
+		dphy_hw->dphy_dev[dphy_hw->dphy_dev_num] = dphy;
+		dphy_hw->dphy_dev_num++;
+		switch (dphy->phy_index) {
+		case 0:
+			dphy->lane_mode = PHY_FULL_MODE;
+			dphy_hw->lane_mode = LANE_MODE_FULL;
+			break;
+		case 1:
+			dphy->lane_mode = PHY_SPLIT_01;
+			dphy_hw->lane_mode = LANE_MODE_SPLIT;
+			break;
+		case 2:
+			dphy->lane_mode = PHY_SPLIT_23;
+			dphy_hw->lane_mode = LANE_MODE_SPLIT;
+			break;
+		default:
+			dphy->lane_mode = PHY_FULL_MODE;
+			dphy_hw->lane_mode = LANE_MODE_FULL;
+			break;
+		}
+		dphy->dphy_hw = dphy_hw;
+		dphy->phy_hw[index] = (void *)dphy_hw;
+		dphy->csi_info.dphy_vendor[index] = PHY_VENDOR_INNO;
+		mutex_unlock(&dphy_hw->mutex);
+	} else if (dphy->drv_data->chip_id == CHIP_ID_RK3588) {
+		if (csi_idx < 2) {
+			dcphy_hw = dphy->samsung_phy_group[csi_idx];
+			mutex_lock(&dcphy_hw->mutex);
+			dcphy_hw->dphy_dev_num++;
+			mutex_unlock(&dcphy_hw->mutex);
+			dphy->samsung_phy = dcphy_hw;
+			dphy->phy_hw[index] = (void *)dcphy_hw;
+			dphy->dphy_param = rk3588_dcphy_param;
+			dphy->csi_info.dphy_vendor[index] = PHY_VENDOR_SAMSUNG;
+		} else {
+			dphy_hw = dphy->dphy_hw_group[(csi_idx - 2) / 2];
+			mutex_lock(&dphy_hw->mutex);
+			if (csi_idx == 2 || csi_idx == 4) {
+				if (lanes == 4) {
+					dphy->lane_mode = PHY_FULL_MODE;
+					dphy_hw->lane_mode = LANE_MODE_FULL;
+					if (csi_idx == 2)
+						dphy->phy_index = 0;
+					else
+						dphy->phy_index = 3;
+				} else {
+					dphy->lane_mode = PHY_SPLIT_01;
+					dphy_hw->lane_mode = LANE_MODE_SPLIT;
+					if (csi_idx == 2)
+						dphy->phy_index = 1;
+					else
+						dphy->phy_index = 4;
+				}
+			} else if (csi_idx == 3 || csi_idx == 5) {
+				if (lanes == 4) {
+					dev_info(dphy->dev, "%s csi host%d only support PHY_SPLIT_23\n",
+						 __func__, csi_idx);
+					mutex_unlock(&dphy_hw->mutex);
+					return -EINVAL;
+				}
+				dphy->lane_mode = PHY_SPLIT_23;
+				dphy_hw->lane_mode = LANE_MODE_SPLIT;
+				if (csi_idx == 3)
+					dphy->phy_index = 2;
+				else
+					dphy->phy_index = 5;
+			}
+			dphy_hw->dphy_dev_num++;
+			dphy->dphy_hw = dphy_hw;
+			dphy->phy_hw[index] = (void *)dphy_hw;
+			dphy->csi_info.dphy_vendor[index] = PHY_VENDOR_INNO;
+			mutex_unlock(&dphy_hw->mutex);
+		}
+	} else {
+		dphy_hw = dphy->dphy_hw_group[csi_idx / 2];
+		mutex_lock(&dphy_hw->mutex);
+		if (csi_idx == 0 || csi_idx == 2) {
+			if (lanes == 4) {
+				dphy->lane_mode = PHY_FULL_MODE;
+				dphy_hw->lane_mode = LANE_MODE_FULL;
+				if (csi_idx == 0)
+					dphy->phy_index = 0;
+				else
+					dphy->phy_index = 3;
+			} else {
+				dphy->lane_mode = PHY_SPLIT_01;
+				dphy_hw->lane_mode = LANE_MODE_SPLIT;
+				if (csi_idx == 0)
+					dphy->phy_index = 1;
+				else
+					dphy->phy_index = 4;
+			}
+		} else if (csi_idx == 1 || csi_idx == 3) {
+			if (lanes == 4) {
+				dev_info(dphy->dev, "%s csi host%d only support PHY_SPLIT_23\n",
+					 __func__, csi_idx);
+				mutex_unlock(&dphy_hw->mutex);
+				return -EINVAL;
+			}
+			dphy->lane_mode = PHY_SPLIT_23;
+			dphy_hw->lane_mode = LANE_MODE_SPLIT;
+			if (csi_idx == 1)
+				dphy->phy_index = 2;
+			else
+				dphy->phy_index = 5;
+		} else {
+			dev_info(dphy->dev, "%s error csi host%d\n",
+				 __func__, csi_idx);
+			mutex_unlock(&dphy_hw->mutex);
+			return -EINVAL;
+		}
+		dphy_hw->dphy_dev[dphy_hw->dphy_dev_num] = dphy;
+		dphy_hw->dphy_dev_num++;
+		dphy->phy_hw[index] = (void *)dphy_hw;
+		dphy->csi_info.dphy_vendor[index] = PHY_VENDOR_INNO;
+		mutex_unlock(&dphy_hw->mutex);
+	}
+
+	return 0;
+}
+
+static int rockchip_csi2_dphy_detach_hw(struct csi2_dphy *dphy, int csi_idx, int index)
+{
+	struct csi2_dphy_hw *dphy_hw = NULL;
+	struct samsung_mipi_dcphy *dcphy_hw = NULL;
+	struct csi2_dphy *csi2_dphy = NULL;
+	int i = 0;
+
+	if (dphy->drv_data->chip_id == CHIP_ID_RK3568 ||
+	    dphy->drv_data->chip_id == CHIP_ID_RV1106) {
+		dphy_hw = (struct csi2_dphy_hw *)dphy->phy_hw[index];
+		if (!dphy_hw) {
+			dev_err(dphy->dev, "%s csi_idx %d detach hw failed\n",
+				__func__, csi_idx);
+			return -EINVAL;
+		}
+		mutex_lock(&dphy_hw->mutex);
+		for (i = 0; i < dphy_hw->dphy_dev_num; i++) {
+			csi2_dphy = dphy_hw->dphy_dev[i];
+			if (csi2_dphy &&
+			    csi2_dphy->phy_index == dphy->phy_index) {
+				dphy_hw->dphy_dev[i] = NULL;
+				dphy_hw->dphy_dev_num--;
+				break;
+			}
+		}
+		mutex_unlock(&dphy_hw->mutex);
+	} else if (dphy->drv_data->chip_id == CHIP_ID_RK3588) {
+		if (csi_idx < 2) {
+			dcphy_hw = (struct samsung_mipi_dcphy *)dphy->phy_hw[index];
+			if (!dcphy_hw) {
+				dev_err(dphy->dev, "%s csi_idx %d detach hw failed\n",
+					__func__, csi_idx);
+				return -EINVAL;
+			}
+			mutex_lock(&dcphy_hw->mutex);
+			dcphy_hw->dphy_dev_num--;
+			mutex_unlock(&dcphy_hw->mutex);
+		} else {
+			dphy_hw = (struct csi2_dphy_hw *)dphy->phy_hw[index];
+			if (!dphy_hw) {
+				dev_err(dphy->dev, "%s csi_idx %d detach hw failed\n",
+					__func__, csi_idx);
+				return -EINVAL;
+			}
+			mutex_lock(&dphy_hw->mutex);
+			dphy_hw->dphy_dev_num--;
+			mutex_unlock(&dphy_hw->mutex);
+		}
+	} else {
+		dphy_hw = (struct csi2_dphy_hw *)dphy->phy_hw[index];
+		if (!dphy_hw) {
+			dev_err(dphy->dev, "%s csi_idx %d detach hw failed\n",
+				__func__, csi_idx);
+			return -EINVAL;
+		}
+		mutex_lock(&dphy_hw->mutex);
+		dphy_hw->dphy_dev_num--;
+		mutex_unlock(&dphy_hw->mutex);
+	}
+
+	return 0;
+}
+
 static int csi2_dphy_update_sensor_mbus(struct v4l2_subdev *sd)
 {
 	struct csi2_dphy *dphy = to_csi2_dphy(sd);
 	struct v4l2_subdev *sensor_sd = get_remote_sensor(sd);
 	struct csi2_sensor *sensor;
 	struct v4l2_mbus_config mbus;
-	struct rkmodule_bus_config bus_config;
-	int ret;
+	int ret = 0;
 
 	if (!sensor_sd)
 		return -ENODEV;
@@ -137,86 +351,83 @@
 	default:
 		return -EINVAL;
 	}
-	if (dphy->drv_data->vendor == PHY_VENDOR_INNO) {
-		ret = v4l2_subdev_call(sensor_sd, core, ioctl,
-				       RKMODULE_GET_BUS_CONFIG, &bus_config);
-		if (!ret) {
-			dev_info(dphy->dev, "phy_mode %d,lane %d\n",
-				bus_config.bus.phy_mode, bus_config.bus.lanes);
-			if (bus_config.bus.phy_mode == PHY_FULL_MODE) {
-				if (dphy->dphy_hw->drv_data->chip_id == CHIP_ID_RK3588 &&
-				    dphy->phy_index % 3 == 2) {
-					dev_err(dphy->dev, "%s dphy%d only use for PHY_SPLIT_23\n",
-						__func__, dphy->phy_index);
-					ret = -EINVAL;
-				}
-				dphy->lane_mode = LANE_MODE_FULL;
-			} else if (bus_config.bus.phy_mode == PHY_SPLIT_01) {
-				if (dphy->dphy_hw->drv_data->chip_id == CHIP_ID_RK3588_DCPHY) {
-					dev_err(dphy->dev, "%s The chip not support split mode\n",
-						__func__);
-					ret = -EINVAL;
-				} else if (dphy->phy_index % 3 == 2) {
-					dev_err(dphy->dev, "%s dphy%d only use for PHY_SPLIT_23\n",
-						__func__, dphy->phy_index);
-					ret = -EINVAL;
-				} else {
-					dphy->lane_mode = LANE_MODE_SPLIT;
-				}
-			} else if (bus_config.bus.phy_mode == PHY_SPLIT_23) {
-				if (dphy->dphy_hw->drv_data->chip_id == CHIP_ID_RK3588_DCPHY) {
-					dev_err(dphy->dev, "%s The chip not support split mode\n",
-						__func__);
-					ret = -EINVAL;
-				} else if (dphy->phy_index % 3 != 2) {
-					dev_err(dphy->dev, "%s dphy%d not support PHY_SPLIT_23\n",
-						__func__, dphy->phy_index);
-					ret = -EINVAL;
-				} else {
-					dphy->lane_mode = LANE_MODE_SPLIT;
+
+	return 0;
+}
+
+static int csi2_dphy_update_config(struct v4l2_subdev *sd)
+{
+	struct csi2_dphy *dphy = to_csi2_dphy(sd);
+	struct v4l2_subdev *sensor_sd = get_remote_sensor(sd);
+	struct rkmodule_csi_dphy_param dphy_param;
+	struct rkmodule_bus_config bus_config;
+	int csi_idx = 0;
+	int ret = 0;
+	int i = 0;
+
+	for (i = 0; i < dphy->csi_info.csi_num; i++) {
+		if (dphy->drv_data->chip_id != CHIP_ID_RK3568 &&
+		    dphy->drv_data->chip_id != CHIP_ID_RV1106) {
+			csi_idx = dphy->csi_info.csi_idx[i];
+			rockchip_csi2_dphy_attach_hw(dphy, csi_idx, i);
+		}
+		if (dphy->csi_info.dphy_vendor[i] == PHY_VENDOR_INNO) {
+			ret = v4l2_subdev_call(sensor_sd, core, ioctl,
+					       RKMODULE_GET_BUS_CONFIG, &bus_config);
+			if (!ret) {
+				dev_info(dphy->dev, "phy_mode %d,lane %d\n",
+					bus_config.bus.phy_mode, bus_config.bus.lanes);
+				if (bus_config.bus.phy_mode == PHY_FULL_MODE) {
+					if (dphy->phy_index % 3 == 2) {
+						dev_err(dphy->dev, "%s dphy%d only use for PHY_SPLIT_23\n",
+							__func__, dphy->phy_index);
+						return -EINVAL;
+					}
+					dphy->lane_mode = PHY_FULL_MODE;
+					dphy->dphy_hw->lane_mode = LANE_MODE_FULL;
+				} else if (bus_config.bus.phy_mode == PHY_SPLIT_01) {
+					if (dphy->phy_index % 3 == 2) {
+						dev_err(dphy->dev, "%s dphy%d only use for PHY_SPLIT_23\n",
+							__func__, dphy->phy_index);
+						return -EINVAL;
+					}
+					dphy->lane_mode = PHY_SPLIT_01;
+					dphy->dphy_hw->lane_mode = LANE_MODE_SPLIT;
+				} else if (bus_config.bus.phy_mode == PHY_SPLIT_23) {
+					if (dphy->phy_index % 3 != 2) {
+						dev_err(dphy->dev, "%s dphy%d not support PHY_SPLIT_23\n",
+							__func__, dphy->phy_index);
+						return -EINVAL;
+					}
+					dphy->lane_mode = PHY_SPLIT_23;
+					dphy->dphy_hw->lane_mode = LANE_MODE_SPLIT;
 				}
 			}
-			if (!ret)
-				dphy->dphy_hw->lane_mode = dphy->lane_mode;
-		} else {
-			ret = 0;
 		}
 	}
-	if (dphy->drv_data->vendor == PHY_VENDOR_SAMSUNG) {
-		ret = v4l2_subdev_call(sensor_sd, core, ioctl,
-				       RKMODULE_GET_CSI_DPHY_PARAM,
-				       &dphy->dphy_param);
-		if (ret) {
-			dev_dbg(dphy->dev, "%s fail to get dphy param, used default value\n",
-				__func__);
-			ret = 0;
-		}
-	}
-	return ret;
+	ret = v4l2_subdev_call(sensor_sd, core, ioctl,
+			       RKMODULE_GET_CSI_DPHY_PARAM,
+			       &dphy_param);
+	if (!ret)
+		dphy->dphy_param = dphy_param;
+	return 0;
 }
 
 static int csi2_dphy_s_stream_start(struct v4l2_subdev *sd)
 {
 	struct csi2_dphy *dphy = to_csi2_dphy(sd);
-	struct csi2_dphy_hw *hw = dphy->dphy_hw;
-	struct samsung_mipi_dcphy *samsung_phy = dphy->samsung_phy;
-	int  ret = 0;
+	int i = 0;
 
-	if (dphy->is_streaming)
-		return 0;
-
-	ret = csi2_dphy_get_sensor_data_rate(sd);
-	if (ret < 0)
-		return ret;
-
-	csi2_dphy_update_sensor_mbus(sd);
-
-	if (dphy->drv_data->vendor == PHY_VENDOR_SAMSUNG) {
-		if (samsung_phy && samsung_phy->stream_on)
-			samsung_phy->stream_on(dphy, sd);
-	} else {
-		if (hw->stream_on)
-			hw->stream_on(dphy, sd);
+	for (i = 0; i < dphy->csi_info.csi_num; i++) {
+		if (dphy->csi_info.dphy_vendor[i] == PHY_VENDOR_SAMSUNG) {
+			dphy->samsung_phy = (struct samsung_mipi_dcphy *)dphy->phy_hw[i];
+			if (dphy->samsung_phy && dphy->samsung_phy->stream_on)
+				dphy->samsung_phy->stream_on(dphy, sd);
+		} else {
+			dphy->dphy_hw = (struct csi2_dphy_hw *)dphy->phy_hw[i];
+			if (dphy->dphy_hw && dphy->dphy_hw->stream_on)
+				dphy->dphy_hw->stream_on(dphy, sd);
+		}
 	}
 
 	dphy->is_streaming = true;
@@ -227,18 +438,21 @@
 static int csi2_dphy_s_stream_stop(struct v4l2_subdev *sd)
 {
 	struct csi2_dphy *dphy = to_csi2_dphy(sd);
-	struct csi2_dphy_hw *hw = dphy->dphy_hw;
-	struct samsung_mipi_dcphy *samsung_phy = dphy->samsung_phy;
+	int i = 0;
 
-	if (!dphy->is_streaming)
-		return 0;
-
-	if (dphy->drv_data->vendor == PHY_VENDOR_SAMSUNG) {
-		if (samsung_phy && samsung_phy->stream_off)
-			samsung_phy->stream_off(dphy, sd);
-	} else {
-		if (hw->stream_off)
-			hw->stream_off(dphy, sd);
+	for (i = 0; i < dphy->csi_info.csi_num; i++) {
+		if (dphy->csi_info.dphy_vendor[i] == PHY_VENDOR_SAMSUNG) {
+			dphy->samsung_phy = (struct samsung_mipi_dcphy *)dphy->phy_hw[i];
+			if (dphy->samsung_phy && dphy->samsung_phy->stream_off)
+				dphy->samsung_phy->stream_off(dphy, sd);
+		} else {
+			dphy->dphy_hw = (struct csi2_dphy_hw *)dphy->phy_hw[i];
+			if (dphy->dphy_hw && dphy->dphy_hw->stream_off)
+				dphy->dphy_hw->stream_off(dphy, sd);
+		}
+		if (dphy->drv_data->chip_id != CHIP_ID_RK3568 &&
+		    dphy->drv_data->chip_id != CHIP_ID_RV1106)
+			rockchip_csi2_dphy_detach_hw(dphy, dphy->csi_info.csi_idx[i], i);
 	}
 
 	dphy->is_streaming = false;
@@ -249,20 +463,94 @@
 	return 0;
 }
 
+static int csi2_dphy_enable_clk(struct csi2_dphy *dphy)
+{
+	struct csi2_dphy_hw *hw = NULL;
+	struct samsung_mipi_dcphy *samsung_phy = NULL;
+	int ret;
+	int i = 0;
+
+	for (i = 0; i < dphy->csi_info.csi_num; i++) {
+		if (dphy->csi_info.dphy_vendor[i] == PHY_VENDOR_SAMSUNG) {
+			samsung_phy = (struct samsung_mipi_dcphy *)dphy->phy_hw[i];
+			if (samsung_phy)
+				clk_prepare_enable(samsung_phy->pclk);
+		} else {
+			hw = (struct csi2_dphy_hw *)dphy->phy_hw[i];
+			if (hw) {
+				ret = clk_bulk_prepare_enable(hw->num_clks, hw->clks_bulk);
+				if (ret) {
+					dev_err(hw->dev, "failed to enable clks\n");
+					return ret;
+				}
+			}
+		}
+	}
+	return 0;
+}
+
+static void csi2_dphy_disable_clk(struct csi2_dphy *dphy)
+{
+	struct csi2_dphy_hw *hw = NULL;
+	struct samsung_mipi_dcphy *samsung_phy = NULL;
+	int i = 0;
+
+	for (i = 0; i < dphy->csi_info.csi_num; i++) {
+		if (dphy->csi_info.dphy_vendor[i] == PHY_VENDOR_SAMSUNG) {
+			samsung_phy = (struct samsung_mipi_dcphy *)dphy->phy_hw[i];
+			if (samsung_phy)
+				clk_disable_unprepare(samsung_phy->pclk);
+		} else {
+			hw = (struct csi2_dphy_hw *)dphy->phy_hw[i];
+			if (hw)
+				clk_bulk_disable_unprepare(hw->num_clks, hw->clks_bulk);
+		}
+	}
+}
+
 static int csi2_dphy_s_stream(struct v4l2_subdev *sd, int on)
 {
 	struct csi2_dphy *dphy = to_csi2_dphy(sd);
 	int ret = 0;
 
 	mutex_lock(&dphy->mutex);
-	if (on)
+	if (on) {
+		if (dphy->is_streaming) {
+			mutex_unlock(&dphy->mutex);
+			return 0;
+		}
+
+		ret = csi2_dphy_get_sensor_data_rate(sd);
+		if (ret < 0) {
+			mutex_unlock(&dphy->mutex);
+			return ret;
+		}
+
+		csi2_dphy_update_sensor_mbus(sd);
+		ret = csi2_dphy_update_config(sd);
+		if (ret < 0) {
+			mutex_unlock(&dphy->mutex);
+			return ret;
+		}
+
+		ret = csi2_dphy_enable_clk(dphy);
+		if (ret) {
+			mutex_unlock(&dphy->mutex);
+			return ret;
+		}
 		ret = csi2_dphy_s_stream_start(sd);
-	else
+	} else {
+		if (!dphy->is_streaming) {
+			mutex_unlock(&dphy->mutex);
+			return 0;
+		}
 		ret = csi2_dphy_s_stream_stop(sd);
+		csi2_dphy_disable_clk(dphy);
+	}
 	mutex_unlock(&dphy->mutex);
 
-	dev_info(dphy->dev, "%s stream on:%d, dphy%d\n",
-		 __func__, on, dphy->phy_index);
+	dev_info(dphy->dev, "%s stream on:%d, dphy%d, ret %d\n",
+		 __func__, on, dphy->phy_index, ret);
 
 	return ret;
 }
@@ -312,15 +600,10 @@
 	struct media_entity *me = dev_get_drvdata(dev);
 	struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(me);
 	struct csi2_dphy *dphy = to_csi2_dphy(sd);
-	struct csi2_dphy_hw *hw = dphy->dphy_hw;
-	struct samsung_mipi_dcphy *samsung_phy = dphy->samsung_phy;
 
-	if (dphy->drv_data->vendor == PHY_VENDOR_SAMSUNG) {
-		if (samsung_phy)
-			clk_disable_unprepare(samsung_phy->pclk);
-	} else {
-		if (hw)
-			clk_bulk_disable_unprepare(hw->num_clks, hw->clks_bulk);
+	if (dphy->is_streaming) {
+		csi2_dphy_s_stream(sd, 0);
+		dphy->is_streaming = false;
 	}
 
 	return 0;
@@ -328,26 +611,6 @@
 
 static __maybe_unused int csi2_dphy_runtime_resume(struct device *dev)
 {
-	struct media_entity *me = dev_get_drvdata(dev);
-	struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(me);
-	struct csi2_dphy *dphy = to_csi2_dphy(sd);
-	struct csi2_dphy_hw *hw = dphy->dphy_hw;
-	struct samsung_mipi_dcphy *samsung_phy = dphy->samsung_phy;
-	int ret;
-
-	if (dphy->drv_data->vendor == PHY_VENDOR_SAMSUNG) {
-		if (samsung_phy)
-			clk_prepare_enable(samsung_phy->pclk);
-	} else {
-		if (hw) {
-			ret = clk_bulk_prepare_enable(hw->num_clks, hw->clks_bulk);
-			if (ret) {
-				dev_err(hw->dev, "failed to enable clks\n");
-				return ret;
-			}
-		}
-	}
-
 	return 0;
 }
 
@@ -384,8 +647,55 @@
 	return v4l2_subdev_call(sensor, pad, get_selection, NULL, sel);
 }
 
+static long rkcif_csi2_dphy_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
+{
+	struct csi2_dphy *dphy = to_csi2_dphy(sd);
+	long ret = 0;
+
+	switch (cmd) {
+	case RKCIF_CMD_SET_CSI_IDX:
+		if (dphy->drv_data->chip_id != CHIP_ID_RK3568 &&
+		    dphy->drv_data->chip_id != CHIP_ID_RV1106)
+			dphy->csi_info = *((struct rkcif_csi_info *)arg);
+		break;
+	default:
+		ret = -ENOIOCTLCMD;
+		break;
+	}
+
+	return ret;
+}
+
+#ifdef CONFIG_COMPAT
+static long rkcif_csi2_dphy_compat_ioctl32(struct v4l2_subdev *sd,
+				      unsigned int cmd, unsigned long arg)
+{
+	void __user *up = compat_ptr(arg);
+	struct rkcif_csi_info csi_info = {0};
+	long ret;
+
+	switch (cmd) {
+	case RKCIF_CMD_SET_CSI_IDX:
+		if (copy_from_user(&csi_info, up, sizeof(struct rkcif_csi_info)))
+			return -EFAULT;
+
+		ret = rkcif_csi2_dphy_ioctl(sd, cmd, &csi_info);
+		break;
+	default:
+		ret = -ENOIOCTLCMD;
+		break;
+	}
+
+	return ret;
+}
+#endif
+
 static const struct v4l2_subdev_core_ops csi2_dphy_core_ops = {
 	.s_power = csi2_dphy_s_power,
+	.ioctl = rkcif_csi2_dphy_ioctl,
+#ifdef CONFIG_COMPAT
+	.compat_ioctl32 = rkcif_csi2_dphy_compat_ioctl32,
+#endif
 };
 
 static const struct v4l2_subdev_video_ops csi2_dphy_video_ops = {
@@ -491,8 +801,9 @@
 		return -EINVAL;
 	}
 
-	if (vep->bus_type == V4L2_MBUS_CSI2_DPHY) {
-		config->type = V4L2_MBUS_CSI2_DPHY;
+	if (vep->bus_type == V4L2_MBUS_CSI2_DPHY ||
+	    vep->bus_type == V4L2_MBUS_CSI2_CPHY) {
+		config->type = vep->bus_type;
 		config->flags = vep->bus.mipi_csi2.flags;
 		s_asd->lanes = vep->bus.mipi_csi2.num_data_lanes;
 	} else if (vep->bus_type == V4L2_MBUS_CCP2) {
@@ -559,162 +870,32 @@
 	return v4l2_async_register_subdev(&dphy->sd);
 }
 
-static int rockchip_csi2_dphy_attach_samsung_phy(struct csi2_dphy *dphy)
-{
-	struct device *dev = dphy->dev;
-	struct phy *dcphy;
-	struct samsung_mipi_dcphy *dphy_hw;
-	int ret = 0;
-
-	dcphy = devm_phy_optional_get(dev, "dcphy");
-	if (IS_ERR(dcphy)) {
-		ret = PTR_ERR(dcphy);
-		dev_err(dphy->dev, "failed to get mipi dcphy: %d\n", ret);
-		return ret;
-	}
-
-	dphy_hw = phy_get_drvdata(dcphy);
-	dphy_hw->dphy_dev[dphy_hw->dphy_dev_num] = dphy;
-	dphy_hw->dphy_dev_num++;
-	dphy->samsung_phy = dphy_hw;
-
-	return 0;
-}
-
-static int rockchip_csi2_dphy_detach_samsung_phy(struct csi2_dphy *dphy)
-{
-	struct samsung_mipi_dcphy *dphy_hw = dphy->samsung_phy;
-	struct csi2_dphy *csi2_dphy = NULL;
-	int i;
-
-	for (i = 0; i < dphy_hw->dphy_dev_num; i++) {
-		csi2_dphy = dphy_hw->dphy_dev[i];
-		if (csi2_dphy &&
-		    csi2_dphy->phy_index == dphy->phy_index) {
-			dphy_hw->dphy_dev[i] = NULL;
-			dphy_hw->dphy_dev_num--;
-			break;
-		}
-	}
-
-	return 0;
-}
-
-static int rockchip_csi2_dphy_attach_hw(struct csi2_dphy *dphy)
-{
-	struct platform_device *plat_dev;
-	struct device *dev = dphy->dev;
-	struct csi2_dphy_hw *dphy_hw;
-	struct device_node *np;
-	enum csi2_dphy_lane_mode target_mode;
-	int i;
-
-	if (dphy->phy_index % 3 == 0)
-		target_mode = LANE_MODE_FULL;
-	else
-		target_mode = LANE_MODE_SPLIT;
-
-	np = of_parse_phandle(dev->of_node, "rockchip,hw", 0);
-	if (!np || !of_device_is_available(np)) {
-		dev_err(dphy->dev,
-			"failed to get dphy%d hw node\n", dphy->phy_index);
-		return -ENODEV;
-	}
-
-	plat_dev = of_find_device_by_node(np);
-	of_node_put(np);
-	if (!plat_dev) {
-		dev_err(dphy->dev,
-			"failed to get dphy%d hw from node\n",
-			dphy->phy_index);
-		return -ENODEV;
-	}
-
-	dphy_hw = platform_get_drvdata(plat_dev);
-	if (!dphy_hw) {
-		dev_err(dphy->dev,
-			"failed attach dphy%d hw\n",
-			dphy->phy_index);
-		return -EINVAL;
-	}
-
-	if (dphy_hw->lane_mode == LANE_MODE_UNDEF) {
-		dphy_hw->lane_mode = target_mode;
-	} else {
-		struct csi2_dphy *phy = dphy_hw->dphy_dev[0];
-
-		for (i = 0; i < dphy_hw->dphy_dev_num; i++) {
-			if (dphy_hw->dphy_dev[i]->lane_mode == dphy_hw->lane_mode) {
-				phy = dphy_hw->dphy_dev[i];
-				break;
-			}
-		}
-
-		if (target_mode != dphy_hw->lane_mode) {
-			dev_err(dphy->dev,
-				"Err:csi2 dphy hw has been set as %s mode by phy%d, target mode is:%s\n",
-				dphy_hw->lane_mode == LANE_MODE_FULL ? "full" : "split",
-				phy->phy_index,
-				target_mode == LANE_MODE_FULL ? "full" : "split");
-			return -ENODEV;
-		}
-	}
-
-	dphy_hw->dphy_dev[dphy_hw->dphy_dev_num] = dphy;
-	dphy_hw->dphy_dev_num++;
-	dphy->dphy_hw = dphy_hw;
-
-	return 0;
-}
-
-static int rockchip_csi2_dphy_detach_hw(struct csi2_dphy *dphy)
-{
-	struct csi2_dphy_hw *dphy_hw = dphy->dphy_hw;
-	struct csi2_dphy *csi2_dphy = NULL;
-	int i;
-
-	for (i = 0; i < dphy_hw->dphy_dev_num; i++) {
-		csi2_dphy = dphy_hw->dphy_dev[i];
-		if (csi2_dphy &&
-		    csi2_dphy->phy_index == dphy->phy_index) {
-			dphy_hw->dphy_dev[i] = NULL;
-			dphy_hw->dphy_dev_num--;
-			break;
-		}
-	}
-
-	return 0;
-}
-
 static struct dphy_drv_data rk3568_dphy_drv_data = {
 	.dev_name = "csi2dphy",
-	.vendor = PHY_VENDOR_INNO,
+	.chip_id = CHIP_ID_RK3568,
+	.num_inno_phy = 1,
+	.num_samsung_phy = 0,
 };
 
-static struct dphy_drv_data rk3588_dcphy_drv_data = {
-	.dev_name = "csi2dcphy",
-	.vendor = PHY_VENDOR_SAMSUNG,
-};
-
-static struct rkmodule_csi_dphy_param rk3588_dcphy_param = {
-	.vendor = PHY_VENDOR_SAMSUNG,
-	.lp_vol_ref = 3,
-	.lp_hys_sw = {3, 0, 0, 0},
-	.lp_escclk_pol_sel = {1, 0, 0, 0},
-	.skew_data_cal_clk = {0, 3, 3, 3},
-	.clk_hs_term_sel = 2,
-	.data_hs_term_sel = {2, 2, 2, 2},
-	.reserved = {0},
+static struct dphy_drv_data rk3588_dphy_drv_data = {
+	.dev_name = "csi2dphy",
+	.chip_id = CHIP_ID_RK3588,
+	.num_inno_phy = 2,
+	.num_samsung_phy = 2,
 };
 
 static struct dphy_drv_data rv1106_dphy_drv_data = {
 	.dev_name = "csi2dphy",
-	.vendor = PHY_VENDOR_INNO,
+	.chip_id = CHIP_ID_RV1106,
+	.num_inno_phy = 1,
+	.num_samsung_phy = 0,
 };
 
 static struct dphy_drv_data rk3562_dphy_drv_data = {
 	.dev_name = "csi2dphy",
-	.vendor = PHY_VENDOR_INNO,
+	.chip_id = CHIP_ID_RK3562,
+	.num_inno_phy = 2,
+	.num_samsung_phy = 0,
 };
 
 static const struct of_device_id rockchip_csi2_dphy_match_id[] = {
@@ -723,8 +904,8 @@
 		.data = &rk3568_dphy_drv_data,
 	},
 	{
-		.compatible = "rockchip,rk3588-csi2-dcphy",
-		.data = &rk3588_dcphy_drv_data,
+		.compatible = "rockchip,rk3588-csi2-dphy",
+		.data = &rk3588_dphy_drv_data,
 	},
 	{
 		.compatible = "rockchip,rv1106-csi2-dphy",
@@ -737,6 +918,79 @@
 	{}
 };
 MODULE_DEVICE_TABLE(of, rockchip_csi2_dphy_match_id);
+
+static int rockchip_csi2_dphy_get_samsung_phy_hw(struct csi2_dphy *dphy)
+{
+	struct phy *dcphy;
+	struct device *dev = dphy->dev;
+	struct samsung_mipi_dcphy *dcphy_hw;
+	char phy_name[32];
+	int i = 0;
+	int ret = 0;
+
+	for (i = 0; i < dphy->drv_data->num_samsung_phy; i++) {
+		sprintf(phy_name, "dcphy%d", i);
+		dcphy = devm_phy_optional_get(dev, phy_name);
+		if (IS_ERR(dcphy)) {
+			ret = PTR_ERR(dcphy);
+			dev_err(dphy->dev, "failed to get mipi dcphy: %d\n", ret);
+			return ret;
+		}
+		dcphy_hw = phy_get_drvdata(dcphy);
+		dphy->samsung_phy_group[i] = dcphy_hw;
+	}
+	return 0;
+}
+
+static int rockchip_csi2_dphy_get_inno_phy_hw(struct csi2_dphy *dphy)
+{
+	struct platform_device *plat_dev;
+	struct device *dev = dphy->dev;
+	struct csi2_dphy_hw *dphy_hw;
+	struct device_node *np;
+	int i = 0;
+
+	for (i = 0; i < dphy->drv_data->num_inno_phy; i++) {
+		np = of_parse_phandle(dev->of_node, "rockchip,hw", i);
+		if (!np || !of_device_is_available(np)) {
+			dev_err(dphy->dev,
+				"failed to get dphy%d hw node\n", dphy->phy_index);
+			return -ENODEV;
+		}
+		plat_dev = of_find_device_by_node(np);
+		of_node_put(np);
+		if (!plat_dev) {
+			dev_err(dphy->dev,
+				"failed to get dphy%d hw from node\n",
+				dphy->phy_index);
+			return -ENODEV;
+		}
+		dphy_hw = platform_get_drvdata(plat_dev);
+		if (!dphy_hw) {
+			dev_err(dphy->dev,
+				"failed attach dphy%d hw\n",
+				dphy->phy_index);
+			return -EINVAL;
+		}
+		dphy->dphy_hw_group[i] = dphy_hw;
+	}
+	return 0;
+}
+
+static int rockchip_csi2_dphy_get_hw(struct csi2_dphy *dphy)
+{
+	int ret = 0;
+
+	if (dphy->drv_data->chip_id == CHIP_ID_RK3588) {
+		ret = rockchip_csi2_dphy_get_samsung_phy_hw(dphy);
+		if (ret)
+			return ret;
+		ret = rockchip_csi2_dphy_get_inno_phy_hw(dphy);
+	} else {
+		ret = rockchip_csi2_dphy_get_inno_phy_hw(dphy);
+	}
+	return ret;
+}
 
 static int rockchip_csi2_dphy_probe(struct platform_device *pdev)
 {
@@ -757,22 +1011,22 @@
 		return -EINVAL;
 	drv_data = of_id->data;
 	csi2dphy->drv_data = drv_data;
+
 	csi2dphy->phy_index = of_alias_get_id(dev->of_node, drv_data->dev_name);
 	if (csi2dphy->phy_index < 0 || csi2dphy->phy_index >= PHY_MAX)
 		csi2dphy->phy_index = 0;
-	if (csi2dphy->drv_data->vendor == PHY_VENDOR_SAMSUNG) {
-		ret = rockchip_csi2_dphy_attach_samsung_phy(csi2dphy);
-		csi2dphy->dphy_param = rk3588_dcphy_param;
-	} else {
-		ret = rockchip_csi2_dphy_attach_hw(csi2dphy);
-	}
-	if (ret) {
-		dev_err(dev,
-			"csi2 dphy hw can't be attached, register dphy%d failed!\n",
-			csi2dphy->phy_index);
-		return -ENODEV;
-	}
 
+	ret = rockchip_csi2_dphy_get_hw(csi2dphy);
+	if (ret)
+		return -EINVAL;
+	if (csi2dphy->drv_data->chip_id == CHIP_ID_RK3568 ||
+	    csi2dphy->drv_data->chip_id == CHIP_ID_RV1106) {
+		csi2dphy->csi_info.csi_num = 1;
+		csi2dphy->csi_info.dphy_vendor[0] = PHY_VENDOR_INNO;
+		rockchip_csi2_dphy_attach_hw(csi2dphy, 0, 0);
+	} else {
+		csi2dphy->csi_info.csi_num = 0;
+	}
 	sd = &csi2dphy->sd;
 	mutex_init(&csi2dphy->mutex);
 	v4l2_subdev_init(sd, &csi2_dphy_subdev_ops);
@@ -795,12 +1049,7 @@
 
 detach_hw:
 	mutex_destroy(&csi2dphy->mutex);
-	if (csi2dphy->drv_data->vendor == PHY_VENDOR_SAMSUNG)
-		rockchip_csi2_dphy_detach_samsung_phy(csi2dphy);
-	else
-		rockchip_csi2_dphy_detach_hw(csi2dphy);
-
-	return 0;
+	return -EINVAL;
 }
 
 static int rockchip_csi2_dphy_remove(struct platform_device *pdev)
@@ -808,7 +1057,10 @@
 	struct media_entity *me = platform_get_drvdata(pdev);
 	struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(me);
 	struct csi2_dphy *dphy = to_csi2_dphy(sd);
+	int i = 0;
 
+	for (i = 0; i < dphy->csi_info.csi_num; i++)
+		rockchip_csi2_dphy_detach_hw(dphy, dphy->csi_info.csi_idx[i], i);
 	media_entity_cleanup(&sd->entity);
 
 	pm_runtime_disable(&pdev->dev);
diff --git a/kernel/drivers/phy/rockchip/phy-rockchip-inno-dsidphy.c b/kernel/drivers/phy/rockchip/phy-rockchip-inno-dsidphy.c
index 04007a0..365d562 100644
--- a/kernel/drivers/phy/rockchip/phy-rockchip-inno-dsidphy.c
+++ b/kernel/drivers/phy/rockchip/phy-rockchip-inno-dsidphy.c
@@ -626,7 +626,6 @@
 	inno_mipi_dphy_reset(inno);
 	inno_mipi_dphy_timing_init(inno);
 	inno_mipi_dphy_lane_enable(inno);
-	inno_mipi_dphy_lane_enable(inno);
 }
 
 static void inno_dsidphy_lvds_mode_enable(struct inno_dsidphy *inno)
diff --git a/kernel/drivers/phy/rockchip/phy-rockchip-inno-usb2.c b/kernel/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
index fe928d9..20cbf30 100644
--- a/kernel/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
+++ b/kernel/drivers/phy/rockchip/phy-rockchip-inno-usb2.c
@@ -1767,8 +1767,12 @@
 	if (property_enabled(rphy->grf, &rport->port_cfg->idfall_det_st)) {
 		property_enable(rphy->grf, &rport->port_cfg->idfall_det_clr,
 				true);
-		/* switch to host if id fall det and iddig status is low */
-		if (!property_enabled(rphy->grf, &rport->port_cfg->utmi_iddig))
+		/*
+		 * if id fall det, switch to host if ID Detector pin is floating
+		 * or iddig status is low.
+		 */
+		if (!rport->port_cfg->utmi_iddig.enable ||
+		    !property_enabled(rphy->grf, &rport->port_cfg->utmi_iddig))
 			cable_vbus_state = true;
 	} else if (property_enabled(rphy->grf, &rport->port_cfg->idrise_det_st)) {
 		property_enable(rphy->grf, &rport->port_cfg->idrise_det_clr,
@@ -3123,6 +3127,26 @@
 			}
 		}
 
+		/* Enable bvalid detect irq */
+		if (rport->port_id == USB2PHY_PORT_OTG &&
+		    (rport->mode == USB_DR_MODE_PERIPHERAL ||
+		     rport->mode == USB_DR_MODE_OTG) &&
+		    (rport->bvalid_irq > 0 || rport->otg_mux_irq > 0 || rphy->irq > 0) &&
+		    !rport->vbus_always_on) {
+			ret = rockchip_usb2phy_enable_vbus_irq(rphy, rport,
+							       true);
+			if (ret) {
+				dev_err(rphy->dev,
+					"failed to enable bvalid irq\n");
+				return ret;
+			}
+
+			if (property_enabled(rphy->grf, &rport->port_cfg->utmi_bvalid))
+				schedule_delayed_work(&rport->otg_sm_work,
+						      OTG_SCHEDULE_DELAY);
+
+		}
+
 		if (rport->port_id == USB2PHY_PORT_OTG && wakeup_enable &&
 		    rport->bvalid_irq > 0)
 			disable_irq_wake(rport->bvalid_irq);
@@ -3408,7 +3432,6 @@
 				.ls_det_clr	= { 0x0118, 0, 0, 0, 1 },
 				.utmi_avalid	= { 0x0120, 10, 10, 0, 1 },
 				.utmi_bvalid	= { 0x0120, 9, 9, 0, 1 },
-				.utmi_iddig	= { 0x0120, 6, 6, 0, 1 },
 				.utmi_ls	= { 0x0120, 5, 4, 0, 1 },
 				.vbus_det_en	= { 0x001c, 15, 15, 1, 0 },
 			},
diff --git a/kernel/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c b/kernel/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c
index 36b8420..3e8e436 100644
--- a/kernel/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c
+++ b/kernel/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c
@@ -483,6 +483,18 @@
 		val |= 0x01 << 17;
 		writel(val, priv->mmio + 0x200);
 
+		/* Set slow slew rate control for PI */
+		val = readl(priv->mmio + 0x204);
+		val &= ~GENMASK(2, 0);
+		val |= 0x07;
+		writel(val, priv->mmio + 0x204);
+
+		/* Set CDR phase path with 2x gain */
+		val = readl(priv->mmio + 0x204);
+		val &= ~GENMASK(5, 5);
+		val |= 0x01 << 5;
+		writel(val, priv->mmio + 0x204);
+
 		/* Set Rx squelch input filler bandwidth */
 		val = readl(priv->mmio + 0x20c);
 		val &= ~GENMASK(2, 0);
@@ -697,7 +709,7 @@
 			/* CKDRV output swing adjust to 650mv */
 			val = readl(priv->mmio + (0xd << 2));
 			val &= ~(0xf << 1);
-			val |= 0xb;
+			val |= (0xb << 1);
 			writel(val, priv->mmio + (0xd << 2));
 		}
 		break;
diff --git a/kernel/drivers/phy/rockchip/phy-rockchip-samsung-dcphy.c b/kernel/drivers/phy/rockchip/phy-rockchip-samsung-dcphy.c
index 8e9e719..125e58d 100644
--- a/kernel/drivers/phy/rockchip/phy-rockchip-samsung-dcphy.c
+++ b/kernel/drivers/phy/rockchip/phy-rockchip-samsung-dcphy.c
@@ -1269,9 +1269,9 @@
 	{ 500,  0x102}, { 990, 0x002}, { 2500, 0x001},
 };
 
-static void samsung_mipi_dcphy_bias_block_enable(struct samsung_mipi_dcphy *samsung)
+static void samsung_mipi_dcphy_bias_block_enable(struct samsung_mipi_dcphy *samsung,
+						 struct csi2_dphy *csi_dphy)
 {
-	struct csi2_dphy *csi_dphy = samsung->dphy_dev[0];
 	u32 bias_con2 = 0x3223;
 
 	if (csi_dphy &&
@@ -1466,9 +1466,9 @@
 
 	/*
 	 * Divide-by-2 Clock from Serial Clock. Use this when data rate is under
-	 * 1500Mbps, otherwise divide-by-16 Clock from Serial Clock
+	 * 500Msps, otherwise divide-by-16 Clock from Serial Clock
 	 */
-	if (lane_hs_rate < 1500)
+	if (lane_hs_rate < 500)
 		val = HSTX_CLK_SEL;
 
 	val |= T_LPX(timing->lpx);
@@ -1701,7 +1701,7 @@
 {
 	reset_control_assert(samsung->m_phy_rst);
 
-	samsung_mipi_dcphy_bias_block_enable(samsung);
+	samsung_mipi_dcphy_bias_block_enable(samsung, NULL);
 	samsung_mipi_dcphy_pll_configure(samsung);
 	samsung_mipi_dphy_clk_lane_timing_init(samsung);
 	samsung_mipi_dphy_data_lane_timing_init(samsung);
@@ -1721,7 +1721,7 @@
 	regmap_write(samsung->grf_regmap, MIPI_DCPHY_GRF_CON0, M_CPHY_MODE);
 	reset_control_assert(samsung->m_phy_rst);
 
-	samsung_mipi_dcphy_bias_block_enable(samsung);
+	samsung_mipi_dcphy_bias_block_enable(samsung, NULL);
 	samsung_mipi_dcphy_hs_vreg_amp_configure(samsung);
 	samsung_mipi_dcphy_pll_configure(samsung);
 	samsung_mipi_cphy_timing_init(samsung);
@@ -2207,7 +2207,7 @@
 	if (samsung->s_phy_rst)
 		reset_control_assert(samsung->s_phy_rst);
 
-	samsung_mipi_dcphy_bias_block_enable(samsung);
+	samsung_mipi_dcphy_bias_block_enable(samsung, dphy);
 	ret = samsung_dcphy_rx_config_common(dphy, sensor);
 	if (ret)
 		goto out_streamon;
diff --git a/kernel/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c b/kernel/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
index 93c1f1e..2d142cf 100644
--- a/kernel/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
+++ b/kernel/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
@@ -318,6 +318,7 @@
 
 /* lane_reg031E */
 #define LN_POLARITY_INV				BIT(2)
+#define LN_LANE_MODE				BIT(1)
 
 #define LANE_REG(lane, offset)			(0x400 * (lane) + (offset))
 
@@ -1059,8 +1060,9 @@
 		u32 invert = hdptx->lane_polarity_invert[lane];
 
 		regmap_update_bits(hdptx->regmap, LANE_REG(lane, 0x0c78),
-				   LN_POLARITY_INV,
-				   FIELD_PREP(LN_POLARITY_INV, invert));
+				   LN_POLARITY_INV | LN_LANE_MODE,
+				   FIELD_PREP(LN_POLARITY_INV, invert) |
+				   FIELD_PREP(LN_LANE_MODE, 1));
 	}
 
 	if (mode == PHY_MODE_DP) {
diff --git a/kernel/drivers/phy/rockchip/phy-rockchip-typec.c b/kernel/drivers/phy/rockchip/phy-rockchip-typec.c
index 13d849d..f4ffb14 100644
--- a/kernel/drivers/phy/rockchip/phy-rockchip-typec.c
+++ b/kernel/drivers/phy/rockchip/phy-rockchip-typec.c
@@ -56,7 +56,8 @@
 
 #include <linux/mfd/syscon.h>
 #include <linux/phy/phy.h>
-#include <linux/phy/phy-rockchip-typec.h>
+#include <linux/usb/typec_mux.h>
+#include <linux/usb/typec_dp.h>
 
 #define CMN_SSM_BANDGAP			(0x21 << 2)
 #define CMN_SSM_BIAS			(0x22 << 2)
@@ -844,78 +845,62 @@
 	writel(0xfb, tcphy->base + XCVR_DIAG_BIDI_CTRL(lane));
 }
 
-static void tcphy_dp_cfg_lane(struct rockchip_typec_phy *tcphy, int link_rate,
-			      u8 swing, u8 pre_emp, u32 lane)
+static void tcphy_dp_cfg_lane(struct rockchip_typec_phy *tcphy, u32 lane)
 {
-	u16 val;
-
 	writel(0xbefc, tcphy->base + XCVR_PSM_RCTRL(lane));
 	writel(0x6799, tcphy->base + TX_PSC_A0(lane));
 	writel(0x6798, tcphy->base + TX_PSC_A1(lane));
 	writel(0x98, tcphy->base + TX_PSC_A2(lane));
 	writel(0x98, tcphy->base + TX_PSC_A3(lane));
-
-	writel(tcphy->config[swing][pre_emp].swing,
-	       tcphy->base + TX_TXCC_MGNFS_MULT_000(lane));
-	writel(tcphy->config[swing][pre_emp].pe,
-	       tcphy->base + TX_TXCC_CPOST_MULT_00(lane));
-
-	if (swing == 2 && pre_emp == 0 && link_rate != 540000) {
-		writel(0x700, tcphy->base + TX_DIAG_TX_DRV(lane));
-		writel(0x13c, tcphy->base + TX_TXCC_CAL_SCLR_MULT(lane));
-	} else {
-		writel(0x128, tcphy->base + TX_TXCC_CAL_SCLR_MULT(lane));
-		writel(0x0400, tcphy->base + TX_DIAG_TX_DRV(lane));
-	}
-
-	val = readl(tcphy->base + XCVR_DIAG_PLLDRC_CTRL(lane));
-	val = val & 0x8fff;
-	switch (link_rate) {
-	case 540000:
-		val |= (5 << 12);
-		break;
-	case 162000:
-	case 270000:
-	default:
-		val |= (6 << 12);
-		break;
-	}
-	writel(val, tcphy->base + XCVR_DIAG_PLLDRC_CTRL(lane));
 }
 
-int tcphy_dp_set_phy_config(struct phy *phy, int link_rate,
-			    int lane_count, u8 swing, u8 pre_emp)
+static int rockchip_dp_phy_set_voltages(struct rockchip_typec_phy *tcphy,
+					struct phy_configure_opts_dp *dp)
 {
-	struct rockchip_typec_phy *tcphy = phy_get_drvdata(phy);
-	u8 i;
+	u8 i, j, lane;
+	u32 val;
 
-	if (!phy->power_count)
-		return -EPERM;
 
-	if (tcphy->mode == MODE_DFP_DP) {
-		for (i = 0; i < 4; i++)
-			tcphy_dp_cfg_lane(tcphy, link_rate, swing, pre_emp, i);
+	if (dp->lanes == 4) {
+		i = 0;
+		j = 3;
 	} else {
 		if (tcphy->flip) {
-			tcphy_dp_cfg_lane(tcphy, link_rate, swing, pre_emp, 0);
-			tcphy_dp_cfg_lane(tcphy, link_rate, swing, pre_emp, 1);
+			i = 0;
+			j = 1;
 		} else {
-			tcphy_dp_cfg_lane(tcphy, link_rate, swing, pre_emp, 2);
-			tcphy_dp_cfg_lane(tcphy, link_rate, swing, pre_emp, 3);
+			i = 2;
+			j = 3;
 		}
+	}
+
+	for (lane = i; lane <= j; lane++) {
+		writel(tcphy->config[dp->voltage[lane]][dp->pre[lane]].swing,
+		       tcphy->base + TX_TXCC_MGNFS_MULT_000(lane));
+		writel(tcphy->config[dp->voltage[lane]][dp->pre[lane]].pe,
+		       tcphy->base + TX_TXCC_CPOST_MULT_00(lane));
+
+		if (dp->voltage[lane] == 2 && dp->pre[lane] == 0 && dp->link_rate != 540000) {
+			writel(0x700, tcphy->base + TX_DIAG_TX_DRV(lane));
+			writel(0x13c, tcphy->base + TX_TXCC_CAL_SCLR_MULT(lane));
+		} else {
+			writel(0x128, tcphy->base + TX_TXCC_CAL_SCLR_MULT(lane));
+			writel(0x0400, tcphy->base + TX_DIAG_TX_DRV(lane));
+		}
+
+		val = readl(tcphy->base + XCVR_DIAG_PLLDRC_CTRL(lane));
+		val &= ~GENMASK(14, 12);
+		val |= ((dp->link_rate == 540000) ? 0x5 : 0x6) << 12;
+		writel(val, tcphy->base + XCVR_DIAG_PLLDRC_CTRL(lane));
 	}
 
 	return 0;
 }
-EXPORT_SYMBOL(tcphy_dp_set_phy_config);
 
-int tcphy_dp_set_lane_count(struct phy *phy, u8 lane_count)
+static int rockchip_dp_phy_set_lanes(struct rockchip_typec_phy *tcphy,
+				     struct phy_configure_opts_dp *dp)
 {
-	struct rockchip_typec_phy *tcphy = phy_get_drvdata(phy);
 	u32 reg;
-
-	if (!phy->power_count)
-		return -EPERM;
 
 	/*
 	 * In cases where fewer than the configured number of DP lanes are
@@ -927,7 +912,7 @@
 	reg = readl(tcphy->base + PHY_DP_MODE_CTL);
 	reg |= PHY_DP_LANE_DISABLE;
 
-	switch (lane_count) {
+	switch (dp->lanes) {
 	case 4:
 		reg &= ~(PHY_DP_LANE_3_DISABLE | PHY_DP_LANE_2_DISABLE |
 			 PHY_DP_LANE_1_DISABLE | PHY_DP_LANE_0_DISABLE);
@@ -946,18 +931,14 @@
 
 	return 0;
 }
-EXPORT_SYMBOL(tcphy_dp_set_lane_count);
 
-int tcphy_dp_set_link_rate(struct phy *phy, int link_rate, bool ssc_on)
+static int rockchip_dp_phy_set_rate(struct rockchip_typec_phy *tcphy,
+				    struct phy_configure_opts_dp *dp)
 {
-	struct rockchip_typec_phy *tcphy = phy_get_drvdata(phy);
 	const struct phy_reg *phy_cfg;
 	u32 cmn_diag_hsclk_sel, phy_dp_clk_ctl, reg;
 	u32 i, cfg_size;
 	int ret;
-
-	if (!phy->power_count)
-		return -EPERM;
 
 	/* Place the PHY lanes in the A3 power state. */
 	ret = tcphy_dp_set_power_state(tcphy, PHY_DP_POWER_STATE_A3);
@@ -1001,29 +982,29 @@
 	phy_dp_clk_ctl = readl(tcphy->base + PHY_DP_CLK_CTL);
 	phy_dp_clk_ctl &= ~(GENMASK(15, 12) | GENMASK(11, 8));
 
-	switch (link_rate) {
-	case 162000:
+	switch (dp->link_rate) {
+	case 1620:
 		cmn_diag_hsclk_sel |= (3 << 4) | (0 << 0);
 		phy_dp_clk_ctl |= (2 << 12) | (4 << 8);
 
-		phy_cfg = ssc_on ? dp_pll_rbr_ssc_cfg : dp_pll_rbr_cfg;
-		cfg_size = ssc_on ? ARRAY_SIZE(dp_pll_rbr_ssc_cfg) :
+		phy_cfg = dp->ssc ? dp_pll_rbr_ssc_cfg : dp_pll_rbr_cfg;
+		cfg_size = dp->ssc ? ARRAY_SIZE(dp_pll_rbr_ssc_cfg) :
 				    ARRAY_SIZE(dp_pll_rbr_cfg);
 		break;
-	case 270000:
+	case 2700:
 		cmn_diag_hsclk_sel |= (3 << 4) | (0 << 0);
 		phy_dp_clk_ctl |= (2 << 12) | (4 << 8);
 
-		phy_cfg = ssc_on ? dp_pll_hbr_ssc_cfg : dp_pll_hbr_cfg;
-		cfg_size = ssc_on ? ARRAY_SIZE(dp_pll_hbr_ssc_cfg) :
+		phy_cfg = dp->ssc ? dp_pll_hbr_ssc_cfg : dp_pll_hbr_cfg;
+		cfg_size = dp->ssc ? ARRAY_SIZE(dp_pll_hbr_ssc_cfg) :
 				    ARRAY_SIZE(dp_pll_hbr_cfg);
 		break;
-	case 540000:
+	case 5400:
 		cmn_diag_hsclk_sel |= (2 << 4) | (0 << 0);
 		phy_dp_clk_ctl |= (1 << 12) | (2 << 8);
 
-		phy_cfg = ssc_on ? dp_pll_hbr2_ssc_cfg : dp_pll_hbr2_cfg;
-		cfg_size = ssc_on ? ARRAY_SIZE(dp_pll_hbr2_ssc_cfg) :
+		phy_cfg = dp->ssc ? dp_pll_hbr2_ssc_cfg : dp_pll_hbr2_cfg;
+		cfg_size = dp->ssc ? ARRAY_SIZE(dp_pll_hbr2_ssc_cfg) :
 				    ARRAY_SIZE(dp_pll_hbr2_cfg);
 		break;
 	default:
@@ -1081,7 +1062,6 @@
 
 	return 0;
 }
-EXPORT_SYMBOL(tcphy_dp_set_link_rate);
 
 static inline int property_enable(struct rockchip_typec_phy *tcphy,
 				  const struct usb3phy_reg *reg, bool en)
@@ -1287,20 +1267,20 @@
 		tcphy_cfg_usb3_to_usb2_only(tcphy, true);
 		tcphy_cfg_dp_pll(tcphy, DP_DEFAULT_RATE);
 		for (i = 0; i < 4; i++)
-			tcphy_dp_cfg_lane(tcphy, DP_DEFAULT_RATE, 0, 0, i);
+			tcphy_dp_cfg_lane(tcphy, i);
 	} else {
 		tcphy_cfg_usb3_pll(tcphy);
 		tcphy_cfg_dp_pll(tcphy, DP_DEFAULT_RATE);
 		if (tcphy->flip) {
 			tcphy_tx_usb3_cfg_lane(tcphy, 3);
 			tcphy_rx_usb3_cfg_lane(tcphy, 2);
-			tcphy_dp_cfg_lane(tcphy, DP_DEFAULT_RATE, 0, 0, 0);
-			tcphy_dp_cfg_lane(tcphy, DP_DEFAULT_RATE, 0, 0, 1);
+			tcphy_dp_cfg_lane(tcphy, 0);
+			tcphy_dp_cfg_lane(tcphy, 1);
 		} else {
 			tcphy_tx_usb3_cfg_lane(tcphy, 0);
 			tcphy_rx_usb3_cfg_lane(tcphy, 1);
-			tcphy_dp_cfg_lane(tcphy, DP_DEFAULT_RATE, 0, 0, 2);
-			tcphy_dp_cfg_lane(tcphy, DP_DEFAULT_RATE, 0, 0, 3);
+			tcphy_dp_cfg_lane(tcphy, 2);
+			tcphy_dp_cfg_lane(tcphy, 3);
 		}
 	}
 
@@ -1578,9 +1558,102 @@
 	return 0;
 }
 
+static int rockchip_dp_phy_verify_config(struct rockchip_typec_phy *tcphy,
+					 struct phy_configure_opts_dp *dp)
+{
+	u8 i;
+
+	/* If changing link rate was required, verify it's supported. */
+	if (dp->set_rate) {
+		switch (dp->link_rate) {
+		case 1620:
+		case 2700:
+		case 5400:
+			/* valid bit rate */
+			break;
+		default:
+			return -EINVAL;
+		}
+	}
+
+	/* Verify lane count. */
+	switch (dp->lanes) {
+	case 1:
+	case 2:
+	case 4:
+		/* valid lane count. */
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/*
+	 * If changing voltages is required, check swing and pre-emphasis
+	 * levels, per-lane.
+	 */
+	if (dp->set_voltages) {
+		/* Lane count verified previously. */
+		for (i = 0; i < dp->lanes; i++) {
+			if (dp->voltage[i] > 3 || dp->pre[i] > 3)
+				return -EINVAL;
+
+			/* Sum of voltage swing and pre-emphasis levels cannot
+			 * exceed 3.
+			 */
+			if (dp->voltage[i] + dp->pre[i] > 3)
+				return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+static int rockchip_dp_phy_configure(struct phy *phy,
+					union phy_configure_opts *opts)
+{
+	struct rockchip_typec_phy *tcphy = phy_get_drvdata(phy);
+	int ret;
+
+	if (!phy->power_count)
+		return -EPERM;
+
+	ret = rockchip_dp_phy_verify_config(tcphy, &opts->dp);
+	if (ret) {
+		dev_err(&phy->dev, "invalid params for phy configure\n");
+		return ret;
+	}
+
+	if (opts->dp.set_lanes) {
+		ret = rockchip_dp_phy_set_lanes(tcphy, &opts->dp);
+		if (ret) {
+			dev_err(&phy->dev, "rockchip_dp_phy_set_lanes failed\n");
+			return ret;
+		}
+	}
+
+	if (opts->dp.set_rate) {
+		ret = rockchip_dp_phy_set_rate(tcphy, &opts->dp);
+		if (ret) {
+			dev_err(&phy->dev, "rockchip_dp_phy_set_rate failed\n");
+			return ret;
+		}
+	}
+
+	if (opts->dp.set_voltages) {
+		ret = rockchip_dp_phy_set_voltages(tcphy, &opts->dp);
+		if (ret) {
+			dev_err(&phy->dev, "rockchip_dp_phy_set_voltages failed\n");
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
 static const struct phy_ops rockchip_dp_phy_ops = {
 	.power_on	= rockchip_dp_phy_power_on,
 	.power_off	= rockchip_dp_phy_power_off,
+	.configure	= rockchip_dp_phy_configure,
 	.owner		= THIS_MODULE,
 };
 
diff --git a/kernel/drivers/phy/rockchip/phy-rockchip-usbdp.c b/kernel/drivers/phy/rockchip/phy-rockchip-usbdp.c
index f1a5169..678825f 100644
--- a/kernel/drivers/phy/rockchip/phy-rockchip-usbdp.c
+++ b/kernel/drivers/phy/rockchip/phy-rockchip-usbdp.c
@@ -341,11 +341,11 @@
 	{0x0D2C, 0xFF}, {0x1D2C, 0xFF},
 	{0x0D34, 0x0F}, {0x1D34, 0x0F},
 	{0x08FC, 0x2A}, {0x0914, 0x28},
-	{0x0A30, 0x03}, {0x0E38, 0x05},
+	{0x0A30, 0x03}, {0x0E38, 0x03},
 	{0x0ECC, 0x27}, {0x0ED0, 0x22},
 	{0x0ED4, 0x26}, {0x18FC, 0x2A},
 	{0x1914, 0x28}, {0x1A30, 0x03},
-	{0x1E38, 0x05}, {0x1ECC, 0x27},
+	{0x1E38, 0x03}, {0x1ECC, 0x27},
 	{0x1ED0, 0x22}, {0x1ED4, 0x26},
 	{0x0048, 0x0F}, {0x0060, 0x3C},
 	{0x0064, 0xF7}, {0x006C, 0x20},
diff --git a/kernel/drivers/pinctrl/pinctrl-rockchip.c b/kernel/drivers/pinctrl/pinctrl-rockchip.c
index 81191ae..01a5c1a 100644
--- a/kernel/drivers/pinctrl/pinctrl-rockchip.c
+++ b/kernel/drivers/pinctrl/pinctrl-rockchip.c
@@ -702,14 +702,23 @@
 
 static struct rockchip_mux_route_data rv1126_mux_route_data[] = {
 	RK_MUXROUTE_GRF(3, RK_PD2, 1, 0x10260, WRITE_MASK_VAL(0, 0, 0)), /* I2S0_MCLK_M0 */
+	RK_MUXROUTE_GRF(3, RK_PD1, 1, 0x10260, WRITE_MASK_VAL(0, 0, 0)), /* I2S0_SCLK_RX_M0 */
+	RK_MUXROUTE_GRF(3, RK_PD0, 1, 0x10260, WRITE_MASK_VAL(0, 0, 0)), /* I2S0_SCLK_TX_M0 */
 	RK_MUXROUTE_GRF(3, RK_PB0, 3, 0x10260, WRITE_MASK_VAL(0, 0, 1)), /* I2S0_MCLK_M1 */
+	RK_MUXROUTE_GRF(3, RK_PB1, 3, 0x10260, WRITE_MASK_VAL(0, 0, 1)), /* I2S0_SCLK_RX_M1 */
+	RK_MUXROUTE_GRF(3, RK_PA4, 3, 0x10260, WRITE_MASK_VAL(0, 0, 1)), /* I2S0_SCLK_TX_M1 */
 
 	RK_MUXROUTE_GRF(0, RK_PD4, 4, 0x10260, WRITE_MASK_VAL(3, 2, 0)), /* I2S1_MCLK_M0 */
+	RK_MUXROUTE_GRF(1, RK_PA1, 4, 0x10260, WRITE_MASK_VAL(3, 2, 0)), /* I2S1_SCLK_M0 */
 	RK_MUXROUTE_GRF(1, RK_PD5, 2, 0x10260, WRITE_MASK_VAL(3, 2, 1)), /* I2S1_MCLK_M1 */
+	RK_MUXROUTE_GRF(1, RK_PD6, 2, 0x10260, WRITE_MASK_VAL(3, 2, 1)), /* I2S1_SCLK_M1 */
 	RK_MUXROUTE_GRF(2, RK_PC7, 6, 0x10260, WRITE_MASK_VAL(3, 2, 2)), /* I2S1_MCLK_M2 */
+	RK_MUXROUTE_GRF(2, RK_PD1, 6, 0x10260, WRITE_MASK_VAL(3, 2, 2)), /* I2S1_SCLK_M2 */
 
 	RK_MUXROUTE_GRF(1, RK_PD0, 1, 0x10260, WRITE_MASK_VAL(4, 4, 0)), /* I2S2_MCLK_M0 */
+	RK_MUXROUTE_GRF(1, RK_PC6, 1, 0x10260, WRITE_MASK_VAL(4, 4, 0)), /* I2S2_SCLK_M0 */
 	RK_MUXROUTE_GRF(2, RK_PB3, 2, 0x10260, WRITE_MASK_VAL(4, 4, 1)), /* I2S2_MCLK_M1 */
+	RK_MUXROUTE_GRF(2, RK_PB1, 2, 0x10260, WRITE_MASK_VAL(4, 4, 1)), /* I2S2_SCLK_M1 */
 
 	RK_MUXROUTE_GRF(3, RK_PD4, 2, 0x10260, WRITE_MASK_VAL(12, 12, 0)), /* PDM_CLK0_M0 */
 	RK_MUXROUTE_GRF(3, RK_PC0, 3, 0x10260, WRITE_MASK_VAL(12, 12, 1)), /* PDM_CLK0_M1 */
@@ -923,7 +932,9 @@
 	RK_MUXROUTE_SAME(2, RK_PC3, 2, 0x50, BIT(16 + 3)), /* pdm_sdi0m0 */
 	RK_MUXROUTE_SAME(1, RK_PC7, 3, 0x50, BIT(16 + 3) | BIT(3)), /* pdm_sdi0m1 */
 	RK_MUXROUTE_SAME(3, RK_PA2, 4, 0x50, BIT(16 + 4) | BIT(16 + 5) | BIT(5)), /* spi_rxdm2 */
+	RK_MUXROUTE_SAME(1, RK_PC6, 1, 0x50, BIT(16 + 6)), /* i2s2_sclkm0 */
 	RK_MUXROUTE_SAME(1, RK_PD0, 1, 0x50, BIT(16 + 6)), /* i2s2_sdim0 */
+	RK_MUXROUTE_SAME(3, RK_PA0, 6, 0x50, BIT(16 + 6) | BIT(6)), /* i2s2_sclkm1 */
 	RK_MUXROUTE_SAME(3, RK_PA2, 6, 0x50, BIT(16 + 6) | BIT(6)), /* i2s2_sdim1 */
 	RK_MUXROUTE_SAME(2, RK_PC6, 3, 0x50, BIT(16 + 7) | BIT(7)), /* card_iom1 */
 	RK_MUXROUTE_SAME(2, RK_PC0, 3, 0x50, BIT(16 + 8) | BIT(8)), /* tsp_d5m1 */
@@ -1012,13 +1023,25 @@
 	RK_MUXROUTE_GRF(2, RK_PB0, 3, 0x0310, WRITE_MASK_VAL(9, 8, 0)), /* UART9 IO mux M0 */
 	RK_MUXROUTE_GRF(4, RK_PC5, 4, 0x0310, WRITE_MASK_VAL(9, 8, 1)), /* UART9 IO mux M1 */
 	RK_MUXROUTE_GRF(4, RK_PA4, 4, 0x0310, WRITE_MASK_VAL(9, 8, 2)), /* UART9 IO mux M2 */
-	RK_MUXROUTE_GRF(1, RK_PA2, 1, 0x0310, WRITE_MASK_VAL(11, 10, 0)), /* I2S1 IO mux M0 */
-	RK_MUXROUTE_GRF(3, RK_PC6, 4, 0x0310, WRITE_MASK_VAL(11, 10, 1)), /* I2S1 IO mux M1 */
-	RK_MUXROUTE_GRF(2, RK_PD0, 5, 0x0310, WRITE_MASK_VAL(11, 10, 2)), /* I2S1 IO mux M2 */
-	RK_MUXROUTE_GRF(2, RK_PC1, 1, 0x0310, WRITE_MASK_VAL(12, 12, 0)), /* I2S2 IO mux M0 */
-	RK_MUXROUTE_GRF(4, RK_PB6, 5, 0x0310, WRITE_MASK_VAL(12, 12, 1)), /* I2S2 IO mux M1 */
-	RK_MUXROUTE_GRF(3, RK_PA2, 4, 0x0310, WRITE_MASK_VAL(14, 14, 0)), /* I2S3 IO mux M0 */
-	RK_MUXROUTE_GRF(4, RK_PC2, 5, 0x0310, WRITE_MASK_VAL(14, 14, 1)), /* I2S3 IO mux M1 */
+	RK_MUXROUTE_GRF(1, RK_PA2, 1, 0x0310, WRITE_MASK_VAL(11, 10, 0)), /* I2S1 MCLK   mux M0 */
+	RK_MUXROUTE_GRF(1, RK_PA4, 1, 0x0310, WRITE_MASK_VAL(11, 10, 0)), /* I2S1 SCLKRX mux M0 */
+	RK_MUXROUTE_GRF(1, RK_PA3, 1, 0x0310, WRITE_MASK_VAL(11, 10, 0)), /* I2S1 SCLKTX mux M0 */
+	RK_MUXROUTE_GRF(3, RK_PC6, 4, 0x0310, WRITE_MASK_VAL(11, 10, 1)), /* I2S1 MCLK   mux M1 */
+	RK_MUXROUTE_GRF(4, RK_PA6, 5, 0x0310, WRITE_MASK_VAL(11, 10, 1)), /* I2S1 SCLKRX mux M1 */
+	RK_MUXROUTE_GRF(3, RK_PC7, 4, 0x0310, WRITE_MASK_VAL(11, 10, 1)), /* I2S1 SCLKTX mux M1 */
+	RK_MUXROUTE_GRF(2, RK_PD0, 5, 0x0310, WRITE_MASK_VAL(11, 10, 2)), /* I2S1 MCLK   mux M2 */
+	RK_MUXROUTE_GRF(3, RK_PC3, 5, 0x0310, WRITE_MASK_VAL(11, 10, 2)), /* I2S1 SCLKRX mux M2 */
+	RK_MUXROUTE_GRF(2, RK_PD1, 5, 0x0310, WRITE_MASK_VAL(11, 10, 2)), /* I2S1 SCLKTX mux M2 */
+	RK_MUXROUTE_GRF(2, RK_PC1, 1, 0x0310, WRITE_MASK_VAL(12, 12, 0)), /* I2S2 MCLK   mux M0 */
+	RK_MUXROUTE_GRF(2, RK_PB7, 1, 0x0310, WRITE_MASK_VAL(12, 12, 0)), /* I2S2 SCLKRX mux M0 */
+	RK_MUXROUTE_GRF(2, RK_PC2, 1, 0x0310, WRITE_MASK_VAL(12, 12, 0)), /* I2S2 SCLKTX mux M0 */
+	RK_MUXROUTE_GRF(4, RK_PB6, 5, 0x0310, WRITE_MASK_VAL(12, 12, 1)), /* I2S2 MCLK   mux M1 */
+	RK_MUXROUTE_GRF(4, RK_PC1, 5, 0x0310, WRITE_MASK_VAL(12, 12, 1)), /* I2S2 SCLKRX mux M1 */
+	RK_MUXROUTE_GRF(4, RK_PB7, 4, 0x0310, WRITE_MASK_VAL(12, 12, 1)), /* I2S2 SCLKTX mux M1 */
+	RK_MUXROUTE_GRF(3, RK_PA2, 4, 0x0310, WRITE_MASK_VAL(14, 14, 0)), /* I2S3 MCLK   mux M0 */
+	RK_MUXROUTE_GRF(3, RK_PA3, 4, 0x0310, WRITE_MASK_VAL(14, 14, 0)), /* I2S3 SCLK   mux M0 */
+	RK_MUXROUTE_GRF(4, RK_PC2, 5, 0x0310, WRITE_MASK_VAL(14, 14, 1)), /* I2S3 MCLK   mux M1 */
+	RK_MUXROUTE_GRF(4, RK_PC3, 5, 0x0310, WRITE_MASK_VAL(14, 14, 1)), /* I2S3 SCLK   mux M1 */
 	RK_MUXROUTE_GRF(1, RK_PA4, 3, 0x0314, WRITE_MASK_VAL(1, 0, 0)), /* PDM IO mux M0 */
 	RK_MUXROUTE_GRF(1, RK_PA6, 3, 0x0314, WRITE_MASK_VAL(1, 0, 0)), /* PDM IO mux M0 */
 	RK_MUXROUTE_GRF(3, RK_PD6, 5, 0x0314, WRITE_MASK_VAL(1, 0, 1)), /* PDM IO mux M1 */
diff --git a/kernel/drivers/pwm/pwm-rockchip.c b/kernel/drivers/pwm/pwm-rockchip.c
index 50e9195..fa455fb 100644
--- a/kernel/drivers/pwm/pwm-rockchip.c
+++ b/kernel/drivers/pwm/pwm-rockchip.c
@@ -169,7 +169,7 @@
 			       const struct pwm_state *state)
 {
 	struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip);
-	unsigned long period, duty;
+	unsigned long period, duty, delay_ns;
 	unsigned long flags;
 	u64 div;
 	u32 ctrl;
@@ -191,11 +191,13 @@
 	div = (u64)pc->clk_rate * state->duty_cycle;
 	duty = DIV_ROUND_CLOSEST_ULL(div, dclk_div * pc->data->prescaler * NSEC_PER_SEC);
 
+	if (pc->data->supports_lock) {
+		div = (u64)10 * NSEC_PER_SEC * dclk_div * pc->data->prescaler;
+		delay_ns = DIV_ROUND_UP_ULL(div, pc->clk_rate);
+	}
+
 	local_irq_save(flags);
-	/*
-	 * Lock the period and duty of previous configuration, then
-	 * change the duty and period, that would not be effective.
-	 */
+
 	ctrl = readl_relaxed(pc->base + pc->data->regs.ctrl);
 	if (pc->data->vop_pwm) {
 		if (pc->vop_pwm_en)
@@ -253,6 +255,10 @@
 	}
 #endif
 
+	/*
+	 * Lock the period and duty of previous configuration, then
+	 * change the duty and period, that would not be effective.
+	 */
 	if (pc->data->supports_lock) {
 		ctrl |= PWM_LOCK_EN;
 		writel_relaxed(ctrl, pc->base + pc->data->regs.ctrl);
@@ -270,12 +276,14 @@
 	}
 
 	/*
-	 * Unlock and set polarity at the same time,
-	 * the configuration of duty, period and polarity
-	 * would be effective together at next period.
+	 * Unlock and set polarity at the same time, the configuration of duty,
+	 * period and polarity would be effective together at next period. It
+	 * takes 10 dclk cycles to make sure lock works before unlocking.
 	 */
-	if (pc->data->supports_lock)
+	if (pc->data->supports_lock) {
 		ctrl &= ~PWM_LOCK_EN;
+		ndelay(delay_ns);
+	}
 
 	writel(ctrl, pc->base + pc->data->regs.ctrl);
 	local_irq_restore(flags);
diff --git a/kernel/drivers/rkflash/sfc.c b/kernel/drivers/rkflash/sfc.c
index 44a1479..19773a8 100644
--- a/kernel/drivers/rkflash/sfc.c
+++ b/kernel/drivers/rkflash/sfc.c
@@ -12,6 +12,7 @@
 #define SFC_MAX_IOSIZE_VER4		(0xFFFFFFFF)
 
 static void __iomem *g_sfc_reg;
+static u32 sfc_version;
 
 static void sfc_reset(void)
 {
@@ -75,6 +76,7 @@
 
 	if (sfc_get_version() >= SFC_VER_4)
 		writel(1, g_sfc_reg + SFC_LEN_CTRL);
+	sfc_version = sfc_get_version();
 
 	return SFC_OK;
 }
@@ -116,7 +118,7 @@
 	op->sfctrl.d32 |= 0x2;
 	cmd.b.datasize = size;
 
-	if (sfc_get_version() >= SFC_VER_4)
+	if (sfc_version >= SFC_VER_4)
 		writel(size, g_sfc_reg + SFC_LEN_EXT);
 	else
 		cmd.b.datasize = size;
@@ -238,6 +240,8 @@
 					break;
 				}
 
+				if (!bytes)
+					break;
 				sfc_delay(1);
 
 				if (timeout++ > 10000) {
diff --git a/kernel/drivers/rkflash/sfc_nand.c b/kernel/drivers/rkflash/sfc_nand.c
index f0eeec9..4accf3d 100644
--- a/kernel/drivers/rkflash/sfc_nand.c
+++ b/kernel/drivers/rkflash/sfc_nand.c
@@ -311,7 +311,7 @@
 	return ret;
 }
 
-static int sfc_nand_wait_busy(u8 *data, int timeout)
+static int sfc_nand_wait_busy_sleep(u8 *data, int timeout, int sleep_us)
 {
 	int ret;
 	int i;
@@ -319,7 +319,9 @@
 
 	*data = 0;
 
-	for (i = 0; i < timeout; i++) {
+	for (i = 0; i < timeout; i += sleep_us) {
+		usleep_range(sleep_us, sleep_us + 50);
+
 		ret = sfc_nand_read_feature(0xC0, &status);
 
 		if (ret != SFC_OK)
@@ -329,8 +331,6 @@
 
 		if (!(status & (1 << 0)))
 			return SFC_OK;
-
-		sfc_delay(1);
 	}
 
 	return SFC_NAND_WAIT_TIME_OUT;
@@ -794,7 +794,7 @@
 	if (ret != SFC_OK)
 		return ret;
 
-	ret = sfc_nand_wait_busy(&status, 1000 * 1000);
+	ret = sfc_nand_wait_busy_sleep(&status, 1000 * 1000, 1000);
 
 	if (status & (1 << 2))
 		return SFC_NAND_PROG_ERASE_ERROR;
@@ -851,6 +851,7 @@
 	op.sfctrl.d32 = 0;
 	op.sfctrl.b.datalines = sfc_nand_dev.prog_lines;
 	op.sfctrl.b.addrbits = 16;
+	op.sfctrl.b.enbledma = 0;
 	plane = p_nand_info->plane_per_die == 2 ? ((addr >> 6) & 0x1) << 12 : 0;
 	sfc_request(&op, plane, p_page_buf, page_size);
 
@@ -880,7 +881,8 @@
 	if (ret != SFC_OK)
 		return ret;
 
-	ret = sfc_nand_wait_busy(&status, 1000 * 1000);
+	ret = sfc_nand_wait_busy_sleep(&status, 1000 * 1000, 200);
+
 	if (status & (1 << 3))
 		return SFC_NAND_PROG_ERASE_ERROR;
 
@@ -931,7 +933,10 @@
 	    sfc_get_version() < SFC_VER_3)
 		sfc_nand_rw_preset();
 
-	sfc_nand_wait_busy(&status, 1000 * 1000);
+	sfc_nand_wait_busy_sleep(&status, 1000 * 1000, 50);
+	if (sfc_nand_dev.manufacturer == 0x01 && status)
+		sfc_nand_wait_busy_sleep(&status, 1000 * 1000, 50);
+
 	ecc_result = p_nand_info->ecc_status();
 
 	op.sfcmd.d32 = 0;
@@ -942,6 +947,7 @@
 	op.sfctrl.d32 = 0;
 	op.sfctrl.b.datalines = sfc_nand_dev.read_lines;
 	op.sfctrl.b.addrbits = 16;
+	op.sfctrl.b.enbledma = 0;
 
 	plane = p_nand_info->plane_per_die == 2 ? ((row >> 6) & 0x1) << 12 : 0;
 	ret = sfc_request(&op, plane | column, p_page_buf, len);
diff --git a/kernel/drivers/rknpu/include/rknpu_drv.h b/kernel/drivers/rknpu/include/rknpu_drv.h
index 13280c1..2f7de54 100644
--- a/kernel/drivers/rknpu/include/rknpu_drv.h
+++ b/kernel/drivers/rknpu/include/rknpu_drv.h
@@ -30,10 +30,10 @@
 
 #define DRIVER_NAME "rknpu"
 #define DRIVER_DESC "RKNPU driver"
-#define DRIVER_DATE "20230428"
+#define DRIVER_DATE "20230825"
 #define DRIVER_MAJOR 0
-#define DRIVER_MINOR 8
-#define DRIVER_PATCHLEVEL 8
+#define DRIVER_MINOR 9
+#define DRIVER_PATCHLEVEL 2
 
 #define LOG_TAG "RKNPU"
 
@@ -75,6 +75,7 @@
 	int num_resets;
 	__u64 nbuf_phyaddr;
 	__u64 nbuf_size;
+	__u64 max_submit_number;
 };
 
 struct rknpu_timer {
@@ -101,6 +102,7 @@
 	void __iomem *base[RKNPU_MAX_CORES];
 	struct device *dev;
 #ifdef CONFIG_ROCKCHIP_RKNPU_DRM_GEM
+	struct device *fake_dev;
 	struct drm_device *drm_dev;
 #endif
 #ifdef CONFIG_ROCKCHIP_RKNPU_DMA_HEAP
diff --git a/kernel/drivers/rknpu/include/rknpu_gem.h b/kernel/drivers/rknpu/include/rknpu_gem.h
index 0afc87b..aedcab8 100644
--- a/kernel/drivers/rknpu/include/rknpu_gem.h
+++ b/kernel/drivers/rknpu/include/rknpu_gem.h
@@ -163,6 +163,8 @@
 int rknpu_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf);
 #endif
 
+int rknpu_gem_mmap_obj(struct drm_gem_object *obj, struct vm_area_struct *vma);
+
 /* set vm_flags and we can change the vm attribute to other one at here. */
 int rknpu_gem_mmap(struct file *filp, struct vm_area_struct *vma);
 
@@ -176,8 +178,13 @@
 rknpu_gem_prime_import_sg_table(struct drm_device *dev,
 				struct dma_buf_attachment *attach,
 				struct sg_table *sgt);
+#if KERNEL_VERSION(6, 1, 0) > LINUX_VERSION_CODE
 void *rknpu_gem_prime_vmap(struct drm_gem_object *obj);
 void rknpu_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr);
+#else
+int rknpu_gem_prime_vmap(struct drm_gem_object *obj, struct iosys_map *map);
+void rknpu_gem_prime_vunmap(struct drm_gem_object *obj, struct iosys_map *map);
+#endif
 int rknpu_gem_prime_mmap(struct drm_gem_object *obj,
 			 struct vm_area_struct *vma);
 
diff --git a/kernel/drivers/rknpu/include/rknpu_ioctl.h b/kernel/drivers/rknpu/include/rknpu_ioctl.h
index 6294ac5..3b0b857 100644
--- a/kernel/drivers/rknpu/include/rknpu_ioctl.h
+++ b/kernel/drivers/rknpu/include/rknpu_ioctl.h
@@ -73,8 +73,8 @@
 	RKNPU_MEM_ZEROING = 1 << 5,
 	/* allocate secure buffer */
 	RKNPU_MEM_SECURE = 1 << 6,
-	/* allocate from non-dma32 zone */
-	RKNPU_MEM_NON_DMA32 = 1 << 7,
+	/* allocate from dma32 zone */
+	RKNPU_MEM_DMA32 = 1 << 7,
 	/* request SRAM */
 	RKNPU_MEM_TRY_ALLOC_SRAM = 1 << 8,
 	/* request NBUF */
@@ -82,7 +82,7 @@
 	RKNPU_MEM_MASK = RKNPU_MEM_NON_CONTIGUOUS | RKNPU_MEM_CACHEABLE |
 			 RKNPU_MEM_WRITE_COMBINE | RKNPU_MEM_KERNEL_MAPPING |
 			 RKNPU_MEM_IOMMU | RKNPU_MEM_ZEROING |
-			 RKNPU_MEM_SECURE | RKNPU_MEM_NON_DMA32 |
+			 RKNPU_MEM_SECURE | RKNPU_MEM_DMA32 |
 			 RKNPU_MEM_TRY_ALLOC_SRAM | RKNPU_MEM_TRY_ALLOC_NBUF
 };
 
diff --git a/kernel/drivers/rknpu/include/rknpu_iommu.h b/kernel/drivers/rknpu/include/rknpu_iommu.h
index 3951764..aa680c9 100644
--- a/kernel/drivers/rknpu/include/rknpu_iommu.h
+++ b/kernel/drivers/rknpu/include/rknpu_iommu.h
@@ -10,8 +10,12 @@
 #include <linux/mutex.h>
 #include <linux/seq_file.h>
 #include <linux/iommu.h>
-#include <linux/dma-iommu.h>
 #include <linux/iova.h>
+#include <linux/version.h>
+
+#if KERNEL_VERSION(6, 1, 0) > LINUX_VERSION_CODE
+#include <linux/dma-iommu.h>
+#endif
 
 #include "rknpu_drv.h"
 
diff --git a/kernel/drivers/rknpu/include/rknpu_job.h b/kernel/drivers/rknpu/include/rknpu_job.h
index 73f2719..c62b1bf 100644
--- a/kernel/drivers/rknpu/include/rknpu_job.h
+++ b/kernel/drivers/rknpu/include/rknpu_job.h
@@ -30,7 +30,6 @@
 	struct rknpu_device *rknpu_dev;
 	struct list_head head[RKNPU_MAX_CORES];
 	struct work_struct cleanup_work;
-	bool in_queue[RKNPU_MAX_CORES];
 	bool irq_entry[RKNPU_MAX_CORES];
 	unsigned int flags;
 	int ret;
@@ -46,6 +45,8 @@
 	atomic_t run_count;
 	atomic_t interrupt_count;
 	ktime_t hw_recoder_time;
+	ktime_t commit_pc_time;
+	atomic_t submit_count[RKNPU_MAX_CORES];
 };
 
 irqreturn_t rknpu_core0_irq_handler(int irq, void *data);
diff --git a/kernel/drivers/rknpu/include/rknpu_mm.h b/kernel/drivers/rknpu/include/rknpu_mm.h
index 52fa044..73ae6d7 100644
--- a/kernel/drivers/rknpu/include/rknpu_mm.h
+++ b/kernel/drivers/rknpu/include/rknpu_mm.h
@@ -10,7 +10,6 @@
 #include <linux/mutex.h>
 #include <linux/seq_file.h>
 #include <linux/iommu.h>
-#include <linux/dma-iommu.h>
 #include <linux/iova.h>
 
 #include "rknpu_drv.h"
diff --git a/kernel/drivers/rknpu/rknpu_debugger.c b/kernel/drivers/rknpu/rknpu_debugger.c
index 146aa7d..97c8752 100644
--- a/kernel/drivers/rknpu/rknpu_debugger.c
+++ b/kernel/drivers/rknpu/rknpu_debugger.c
@@ -63,7 +63,7 @@
 
 		div_value = (RKNPU_LOAD_INTERVAL / 100000);
 		do_div(busy_time_total, div_value);
-		load = busy_time_total;
+		load = busy_time_total > 100 ? 100 : busy_time_total;
 
 		if (rknpu_dev->config->num_irqs > 1)
 			seq_printf(m, "%2.d%%,", load);
@@ -457,7 +457,11 @@
 #ifdef CONFIG_ROCKCHIP_RKNPU_PROC_FS
 static int rknpu_procfs_open(struct inode *inode, struct file *file)
 {
+#if KERNEL_VERSION(6, 1, 0) > LINUX_VERSION_CODE
 	struct rknpu_debugger_node *node = PDE_DATA(inode);
+#else
+	struct rknpu_debugger_node *node = pde_data(inode);
+#endif
 
 	return single_open(file, node->info_ent->show, node);
 }
diff --git a/kernel/drivers/rknpu/rknpu_drv.c b/kernel/drivers/rknpu/rknpu_drv.c
index eb31273..867212c 100644
--- a/kernel/drivers/rknpu/rknpu_drv.c
+++ b/kernel/drivers/rknpu/rknpu_drv.c
@@ -33,7 +33,6 @@
 #include <linux/pm_runtime.h>
 #include <linux/devfreq_cooling.h>
 #include <linux/regmap.h>
-#include <linux/dma-iommu.h>
 #include <linux/of_address.h>
 
 #ifndef FPGA_PLATFORM
@@ -118,7 +117,8 @@
 	.num_irqs = ARRAY_SIZE(rknpu_irqs),
 	.num_resets = ARRAY_SIZE(rknpu_resets),
 	.nbuf_phyaddr = 0,
-	.nbuf_size = 0
+	.nbuf_size = 0,
+	.max_submit_number = (1 << 12) - 1
 };
 
 static const struct rknpu_config rk3588_rknpu_config = {
@@ -136,7 +136,8 @@
 	.num_irqs = ARRAY_SIZE(rk3588_npu_irqs),
 	.num_resets = ARRAY_SIZE(rk3588_npu_resets),
 	.nbuf_phyaddr = 0,
-	.nbuf_size = 0
+	.nbuf_size = 0,
+	.max_submit_number = (1 << 12) - 1
 };
 
 static const struct rknpu_config rv1106_rknpu_config = {
@@ -154,7 +155,8 @@
 	.num_irqs = ARRAY_SIZE(rknpu_irqs),
 	.num_resets = ARRAY_SIZE(rknpu_resets),
 	.nbuf_phyaddr = 0,
-	.nbuf_size = 0
+	.nbuf_size = 0,
+	.max_submit_number = (1 << 16) - 1
 };
 
 static const struct rknpu_config rk3562_rknpu_config = {
@@ -172,7 +174,8 @@
 	.num_irqs = ARRAY_SIZE(rknpu_irqs),
 	.num_resets = ARRAY_SIZE(rknpu_resets),
 	.nbuf_phyaddr = 0xfe400000,
-	.nbuf_size = 256 * 1024
+	.nbuf_size = 256 * 1024,
+	.max_submit_number = (1 << 16) - 1
 };
 
 /* driver probe and init */
@@ -225,7 +228,6 @@
 {
 	int ret = 0;
 
-	cancel_delayed_work(&rknpu_dev->power_off_work);
 	mutex_lock(&rknpu_dev->power_lock);
 	if (atomic_inc_return(&rknpu_dev->power_refcount) == 1)
 		ret = rknpu_power_on(rknpu_dev);
@@ -248,6 +250,9 @@
 
 static int rknpu_power_put_delay(struct rknpu_device *rknpu_dev)
 {
+	if (rknpu_dev->power_put_delay == 0)
+		return rknpu_power_put(rknpu_dev);
+
 	mutex_lock(&rknpu_dev->power_lock);
 	if (atomic_read(&rknpu_dev->power_refcount) == 1)
 		queue_delayed_work(
@@ -256,6 +261,7 @@
 	else
 		atomic_dec_if_positive(&rknpu_dev->power_refcount);
 	mutex_unlock(&rknpu_dev->power_lock);
+
 	return 0;
 }
 
@@ -489,11 +495,13 @@
 #endif
 
 #ifdef CONFIG_ROCKCHIP_RKNPU_DRM_GEM
+#if KERNEL_VERSION(6, 1, 0) > LINUX_VERSION_CODE
 static const struct vm_operations_struct rknpu_gem_vm_ops = {
 	.fault = rknpu_gem_fault,
 	.open = drm_gem_vm_open,
 	.close = drm_gem_vm_close,
 };
+#endif
 
 static int rknpu_action_ioctl(struct drm_device *dev, void *data,
 			      struct drm_file *file_priv)
@@ -535,6 +543,9 @@
 			  DRM_RENDER_ALLOW),
 };
 
+#if KERNEL_VERSION(6, 1, 0) <= LINUX_VERSION_CODE
+DEFINE_DRM_GEM_FOPS(rknpu_drm_driver_fops);
+#else
 static const struct file_operations rknpu_drm_driver_fops = {
 	.owner = THIS_MODULE,
 	.open = drm_open,
@@ -548,6 +559,7 @@
 	.release = drm_release,
 	.llseek = noop_llseek,
 };
+#endif
 
 static struct drm_driver rknpu_drm_driver = {
 #if KERNEL_VERSION(5, 4, 0) <= LINUX_VERSION_CODE
@@ -555,28 +567,34 @@
 #else
 	.driver_features = DRIVER_GEM | DRIVER_PRIME | DRIVER_RENDER,
 #endif
+#if KERNEL_VERSION(6, 1, 0) > LINUX_VERSION_CODE
 	.gem_free_object_unlocked = rknpu_gem_free_object,
 	.gem_vm_ops = &rknpu_gem_vm_ops,
+	.dumb_destroy = drm_gem_dumb_destroy,
+	.gem_prime_export = drm_gem_prime_export,
+	.gem_prime_get_sg_table = rknpu_gem_prime_get_sg_table,
+	.gem_prime_vmap = rknpu_gem_prime_vmap,
+	.gem_prime_vunmap = rknpu_gem_prime_vunmap,
+#endif
 	.dumb_create = rknpu_gem_dumb_create,
 #if KERNEL_VERSION(4, 19, 0) > LINUX_VERSION_CODE
 	.dumb_map_offset = rknpu_gem_dumb_map_offset,
 #else
 	.dumb_map_offset = drm_gem_dumb_map_offset,
 #endif
-	.dumb_destroy = drm_gem_dumb_destroy,
 	.prime_handle_to_fd = drm_gem_prime_handle_to_fd,
 	.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
-	.gem_prime_export = drm_gem_prime_export,
 #if KERNEL_VERSION(4, 13, 0) <= LINUX_VERSION_CODE
 	.gem_prime_import = rknpu_gem_prime_import,
 #else
 	.gem_prime_import = drm_gem_prime_import,
 #endif
-	.gem_prime_get_sg_table = rknpu_gem_prime_get_sg_table,
 	.gem_prime_import_sg_table = rknpu_gem_prime_import_sg_table,
-	.gem_prime_vmap = rknpu_gem_prime_vmap,
-	.gem_prime_vunmap = rknpu_gem_prime_vunmap,
+#if KERNEL_VERSION(6, 1, 0) <= LINUX_VERSION_CODE
+	.gem_prime_mmap = drm_gem_prime_mmap,
+#else
 	.gem_prime_mmap = rknpu_gem_prime_mmap,
+#endif
 	.ioctls = rknpu_ioctls,
 	.num_ioctls = ARRAY_SIZE(rknpu_ioctls),
 	.fops = &rknpu_drm_driver_fops,
@@ -596,7 +614,7 @@
 		container_of(timer, struct rknpu_device, timer);
 	struct rknpu_subcore_data *subcore_data = NULL;
 	struct rknpu_job *job = NULL;
-	ktime_t now = ktime_get();
+	ktime_t now;
 	unsigned long flags;
 	int i;
 
@@ -607,9 +625,10 @@
 
 		job = subcore_data->job;
 		if (job) {
+			now = ktime_get();
 			subcore_data->timer.busy_time +=
 				ktime_us_delta(now, job->hw_recoder_time);
-			job->hw_recoder_time = ktime_get();
+			job->hw_recoder_time = now;
 		}
 
 		subcore_data->timer.busy_time_record =
@@ -661,6 +680,42 @@
 }
 
 #ifdef CONFIG_ROCKCHIP_RKNPU_DRM_GEM
+static int drm_fake_dev_register(struct rknpu_device *rknpu_dev)
+{
+	const struct platform_device_info rknpu_dev_info = {
+		.name = "rknpu_dev",
+		.id = PLATFORM_DEVID_AUTO,
+		.dma_mask = rknpu_dev->config->dma_mask,
+	};
+	struct platform_device *pdev = NULL;
+	int ret = -EINVAL;
+
+	pdev = platform_device_register_full(&rknpu_dev_info);
+	if (pdev) {
+		ret = of_dma_configure(&pdev->dev, NULL, true);
+		if (ret) {
+			platform_device_unregister(pdev);
+			pdev = NULL;
+		}
+	}
+
+	rknpu_dev->fake_dev = pdev ? &pdev->dev : NULL;
+
+	return ret;
+}
+
+static void drm_fake_dev_unregister(struct rknpu_device *rknpu_dev)
+{
+	struct platform_device *pdev = NULL;
+
+	if (!rknpu_dev->fake_dev)
+		return;
+
+	pdev = to_platform_device(rknpu_dev->fake_dev);
+
+	platform_device_unregister(pdev);
+}
+
 static int rknpu_drm_probe(struct rknpu_device *rknpu_dev)
 {
 	struct device *dev = rknpu_dev->dev;
@@ -679,6 +734,8 @@
 	drm_dev->dev_private = rknpu_dev;
 	rknpu_dev->drm_dev = drm_dev;
 
+	drm_fake_dev_register(rknpu_dev);
+
 	return 0;
 
 err_free_drm:
@@ -694,6 +751,8 @@
 static void rknpu_drm_remove(struct rknpu_device *rknpu_dev)
 {
 	struct drm_device *drm_dev = rknpu_dev->drm_dev;
+
+	drm_fake_dev_unregister(rknpu_dev);
 
 	drm_dev_unregister(drm_dev);
 
@@ -742,7 +801,8 @@
 	}
 
 #ifndef FPGA_PLATFORM
-#if KERNEL_VERSION(5, 10, 0) <= LINUX_VERSION_CODE
+#if KERNEL_VERSION(5, 10, 0) <= LINUX_VERSION_CODE &&                          \
+	KERNEL_VERSION(6, 1, 0) > LINUX_VERSION_CODE
 	rockchip_monitor_volt_adjust_lock(rknpu_dev->mdev_info);
 #endif
 #endif
@@ -803,7 +863,8 @@
 
 out:
 #ifndef FPGA_PLATFORM
-#if KERNEL_VERSION(5, 10, 0) <= LINUX_VERSION_CODE
+#if KERNEL_VERSION(5, 10, 0) <= LINUX_VERSION_CODE &&                          \
+	KERNEL_VERSION(6, 1, 0) > LINUX_VERSION_CODE
 	rockchip_monitor_volt_adjust_unlock(rknpu_dev->mdev_info);
 #endif
 #endif
@@ -819,7 +880,8 @@
 	int ret;
 	bool val;
 
-#if KERNEL_VERSION(5, 10, 0) <= LINUX_VERSION_CODE
+#if KERNEL_VERSION(5, 10, 0) <= LINUX_VERSION_CODE &&                          \
+	KERNEL_VERSION(6, 1, 0) > LINUX_VERSION_CODE
 	rockchip_monitor_volt_adjust_lock(rknpu_dev->mdev_info);
 #endif
 #endif
@@ -843,7 +905,8 @@
 		if (ret) {
 			LOG_DEV_ERROR(dev, "iommu still enabled\n");
 			pm_runtime_get_sync(dev);
-#if KERNEL_VERSION(5, 10, 0) <= LINUX_VERSION_CODE
+#if KERNEL_VERSION(5, 10, 0) <= LINUX_VERSION_CODE &&                          \
+	KERNEL_VERSION(6, 1, 0) > LINUX_VERSION_CODE
 			rockchip_monitor_volt_adjust_unlock(
 				rknpu_dev->mdev_info);
 #endif
@@ -862,7 +925,8 @@
 	}
 
 #ifndef FPGA_PLATFORM
-#if KERNEL_VERSION(5, 10, 0) <= LINUX_VERSION_CODE
+#if KERNEL_VERSION(5, 10, 0) <= LINUX_VERSION_CODE &&                          \
+	KERNEL_VERSION(6, 1, 0) > LINUX_VERSION_CODE
 	rockchip_monitor_volt_adjust_unlock(rknpu_dev->mdev_info);
 #endif
 #endif
@@ -881,6 +945,7 @@
 }
 
 #ifndef FPGA_PLATFORM
+#if KERNEL_VERSION(6, 1, 0) > LINUX_VERSION_CODE
 static struct monitor_dev_profile npu_mdevp = {
 	.type = MONITOR_TYPE_DEV,
 	.low_temp_adjust = rockchip_monitor_dev_low_temp_adjust,
@@ -1143,6 +1208,7 @@
 	.get_dev_status = npu_devfreq_get_dev_status,
 	.get_cur_freq = npu_devfreq_get_cur_freq,
 };
+#endif
 
 #ifdef CONFIG_PM_DEVFREQ
 static int devfreq_rknpu_ondemand_func(struct devfreq *df, unsigned long *freq)
@@ -1170,6 +1236,7 @@
 };
 #endif
 
+#if KERNEL_VERSION(6, 1, 0) > LINUX_VERSION_CODE
 static unsigned long npu_get_static_power(struct devfreq *devfreq,
 					  unsigned long voltage)
 {
@@ -1543,6 +1610,7 @@
 	return ret;
 }
 #endif
+#endif
 
 static int rknpu_devfreq_remove(struct rknpu_device *rknpu_dev)
 {
@@ -1565,9 +1633,12 @@
 {
 	const struct rknpu_config *config = rknpu_dev->config;
 	struct device *dev = &pdev->dev;
+#if KERNEL_VERSION(6, 1, 0) > LINUX_VERSION_CODE
 	struct resource *res;
+#endif
 	int i, ret, irq;
 
+#if KERNEL_VERSION(6, 1, 0) > LINUX_VERSION_CODE
 	res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
 					   config->irqs[0].name);
 	if (res) {
@@ -1606,6 +1677,25 @@
 			return ret;
 		}
 	}
+#else
+	/* there are irq names in dts */
+	for (i = 0; i < config->num_irqs; i++) {
+		irq = platform_get_irq_byname(pdev, config->irqs[i].name);
+		if (irq < 0) {
+			LOG_DEV_ERROR(dev, "no npu %s in dts\n",
+				      config->irqs[i].name);
+			return irq;
+		}
+
+		ret = devm_request_irq(dev, irq, config->irqs[i].irq_hdl,
+				       IRQF_SHARED, dev_name(dev), rknpu_dev);
+		if (ret < 0) {
+			LOG_DEV_ERROR(dev, "request %s failed: %d\n",
+				      config->irqs[i].name, ret);
+			return ret;
+		}
+	}
+#endif
 
 	return 0;
 }
@@ -1744,7 +1834,8 @@
 	}
 
 #ifndef FPGA_PLATFORM
-#if KERNEL_VERSION(5, 10, 0) <= LINUX_VERSION_CODE
+#if KERNEL_VERSION(5, 10, 0) <= LINUX_VERSION_CODE &&                          \
+	KERNEL_VERSION(6, 1, 0) > LINUX_VERSION_CODE
 	if (strstr(__clk_get_name(rknpu_dev->clks[0].clk), "scmi"))
 		rknpu_dev->opp_info.scmi_clk = rknpu_dev->clks[0].clk;
 #endif
@@ -1886,7 +1977,9 @@
 		goto err_remove_drv;
 
 #ifndef FPGA_PLATFORM
+#if KERNEL_VERSION(6, 1, 0) > LINUX_VERSION_CODE
 	rknpu_devfreq_init(rknpu_dev);
+#endif
 #endif
 
 	// set default power put delay to 3s
@@ -1995,7 +2088,8 @@
 }
 
 #ifndef FPGA_PLATFORM
-#if KERNEL_VERSION(5, 10, 0) <= LINUX_VERSION_CODE
+#if KERNEL_VERSION(5, 10, 0) <= LINUX_VERSION_CODE &&                          \
+	KERNEL_VERSION(6, 1, 0) > LINUX_VERSION_CODE
 static int rknpu_runtime_suspend(struct device *dev)
 {
 	struct rknpu_device *rknpu_dev = dev_get_drvdata(dev);
@@ -2054,7 +2148,8 @@
 		.owner = THIS_MODULE,
 		.name = "RKNPU",
 #ifndef FPGA_PLATFORM
-#if KERNEL_VERSION(5, 5, 0) < LINUX_VERSION_CODE
+#if KERNEL_VERSION(5, 5, 0) < LINUX_VERSION_CODE &&                            \
+	KERNEL_VERSION(6, 1, 0) > LINUX_VERSION_CODE
 		.pm = &rknpu_pm_ops,
 #endif
 #endif
@@ -2081,3 +2176,6 @@
 MODULE_LICENSE("GPL v2");
 MODULE_VERSION(RKNPU_GET_DRV_VERSION_STRING(DRIVER_MAJOR, DRIVER_MINOR,
 					    DRIVER_PATCHLEVEL));
+#if KERNEL_VERSION(5, 16, 0) < LINUX_VERSION_CODE
+MODULE_IMPORT_NS(DMA_BUF);
+#endif
diff --git a/kernel/drivers/rknpu/rknpu_gem.c b/kernel/drivers/rknpu/rknpu_gem.c
index f97be2b..415d3a4 100644
--- a/kernel/drivers/rknpu/rknpu_gem.c
+++ b/kernel/drivers/rknpu/rknpu_gem.c
@@ -13,7 +13,6 @@
 #include <linux/shmem_fs.h>
 #include <linux/dma-buf.h>
 #include <linux/iommu.h>
-#include <linux/dma-iommu.h>
 #include <linux/pfn_t.h>
 #include <linux/version.h>
 #include <asm/cacheflush.h>
@@ -68,6 +67,7 @@
 			      rknpu_obj->size);
 		goto free_sgt;
 	}
+	iommu_flush_iotlb_all(iommu_get_domain_for_dev(drm->dev));
 
 	if (rknpu_obj->flags & RKNPU_MEM_KERNEL_MAPPING) {
 		rknpu_obj->cookie = vmap(rknpu_obj->pages, rknpu_obj->num_pages,
@@ -182,7 +182,9 @@
 	if (rknpu_obj->flags & RKNPU_MEM_ZEROING)
 		gfp_mask |= __GFP_ZERO;
 
-	if (!(rknpu_obj->flags & RKNPU_MEM_NON_DMA32)) {
+	if (!rknpu_dev->iommu_en ||
+	    rknpu_dev->config->dma_mask <= DMA_BIT_MASK(32) ||
+	    (rknpu_obj->flags & RKNPU_MEM_DMA32)) {
 		gfp_mask &= ~__GFP_HIGHMEM;
 		gfp_mask |= __GFP_DMA32;
 	}
@@ -253,10 +255,15 @@
 			  i, &s->dma_address, s->length);
 	}
 
-	if (drm_prime_sg_to_page_addr_arrays(sgt, rknpu_obj->pages, NULL,
-					     nr_pages)) {
-		LOG_DEV_ERROR(drm->dev, "invalid sgtable.\n");
-		ret = -EINVAL;
+#if KERNEL_VERSION(6, 1, 0) > LINUX_VERSION_CODE
+	ret = drm_prime_sg_to_page_addr_arrays(sgt, rknpu_obj->pages, NULL,
+					       nr_pages);
+#else
+	ret = drm_prime_sg_to_page_array(sgt, rknpu_obj->pages, nr_pages);
+#endif
+
+	if (ret < 0) {
+		LOG_DEV_ERROR(drm->dev, "invalid sgtable, ret: %d\n", ret);
 		goto err_free_sg_table;
 	}
 
@@ -335,9 +342,28 @@
 	return drm_gem_handle_delete(file_priv, handle);
 }
 
+#if KERNEL_VERSION(6, 1, 0) <= LINUX_VERSION_CODE
+static const struct vm_operations_struct vm_ops = {
+	.fault = rknpu_gem_fault,
+	.open = drm_gem_vm_open,
+	.close = drm_gem_vm_close,
+};
+
+static const struct drm_gem_object_funcs rknpu_gem_object_funcs = {
+	.free = rknpu_gem_free_object,
+	.export = drm_gem_prime_export,
+	.get_sg_table = rknpu_gem_prime_get_sg_table,
+	.vmap = rknpu_gem_prime_vmap,
+	.vunmap = rknpu_gem_prime_vunmap,
+	.mmap = rknpu_gem_mmap_obj,
+	.vm_ops = &vm_ops,
+};
+#endif
+
 static struct rknpu_gem_object *rknpu_gem_init(struct drm_device *drm,
 					       unsigned long size)
 {
+	struct rknpu_device *rknpu_dev = drm->dev_private;
 	struct rknpu_gem_object *rknpu_obj = NULL;
 	struct drm_gem_object *obj = NULL;
 	gfp_t gfp_mask;
@@ -348,6 +374,9 @@
 		return ERR_PTR(-ENOMEM);
 
 	obj = &rknpu_obj->base;
+#if KERNEL_VERSION(6, 1, 0) <= LINUX_VERSION_CODE
+	obj->funcs = &rknpu_gem_object_funcs;
+#endif
 
 	ret = drm_gem_object_init(drm, obj, size);
 	if (ret < 0) {
@@ -363,7 +392,9 @@
 	if (rknpu_obj->flags & RKNPU_MEM_ZEROING)
 		gfp_mask |= __GFP_ZERO;
 
-	if (!(rknpu_obj->flags & RKNPU_MEM_NON_DMA32)) {
+	if (!rknpu_dev->iommu_en ||
+	    rknpu_dev->config->dma_mask <= DMA_BIT_MASK(32) ||
+	    (rknpu_obj->flags & RKNPU_MEM_DMA32)) {
 		gfp_mask &= ~__GFP_HIGHMEM;
 		gfp_mask |= __GFP_DMA32;
 	}
@@ -422,7 +453,7 @@
 		return -EINVAL;
 	}
 
-	cookie = domain->iova_cookie;
+	cookie = (void *)domain->iova_cookie;
 	iovad = &cookie->iovad;
 	rknpu_obj->iova_size = iova_align(iovad, cache_size + rknpu_obj->size);
 	rknpu_obj->iova_start = rknpu_iommu_dma_alloc_iova(
@@ -534,8 +565,8 @@
 	iommu_unmap(domain, rknpu_obj->iova_start, cache_size);
 
 free_iova:
-	rknpu_iommu_dma_free_iova(domain->iova_cookie, rknpu_obj->iova_start,
-				  rknpu_obj->iova_size);
+	rknpu_iommu_dma_free_iova((void *)domain->iova_cookie,
+				  rknpu_obj->iova_start, rknpu_obj->iova_size);
 
 	return ret;
 }
@@ -566,7 +597,7 @@
 		if (rknpu_obj->size > 0)
 			iommu_unmap(domain, rknpu_obj->iova_start + cache_size,
 				    rknpu_obj->size);
-		rknpu_iommu_dma_free_iova(domain->iova_cookie,
+		rknpu_iommu_dma_free_iova((void *)domain->iova_cookie,
 					  rknpu_obj->iova_start,
 					  rknpu_obj->iova_size);
 	}
@@ -954,6 +985,7 @@
 	 * vm_pgoff (used as a fake buffer offset by DRM) to 0 as we want to map
 	 * the whole buffer.
 	 */
+	vma->vm_flags |= VM_DONTCOPY | VM_DONTEXPAND | VM_DONTDUMP | VM_IO;
 	vma->vm_flags &= ~VM_PFNMAP;
 	vma->vm_pgoff = 0;
 
@@ -1148,8 +1180,7 @@
 }
 #endif
 
-static int rknpu_gem_mmap_obj(struct drm_gem_object *obj,
-			      struct vm_area_struct *vma)
+int rknpu_gem_mmap_obj(struct drm_gem_object *obj, struct vm_area_struct *vma)
 {
 	struct rknpu_gem_object *rknpu_obj = to_rknpu_obj(obj);
 	int ret = -EINVAL;
@@ -1246,8 +1277,12 @@
 		goto err;
 	}
 
+#if KERNEL_VERSION(6, 1, 0) > LINUX_VERSION_CODE
 	ret = drm_prime_sg_to_page_addr_arrays(sgt, rknpu_obj->pages, NULL,
 					       npages);
+#else
+	ret = drm_prime_sg_to_page_array(sgt, rknpu_obj->pages, npages);
+#endif
 	if (ret < 0)
 		goto err_free_large;
 
@@ -1275,6 +1310,7 @@
 	return ERR_PTR(ret);
 }
 
+#if KERNEL_VERSION(6, 1, 0) > LINUX_VERSION_CODE
 void *rknpu_gem_prime_vmap(struct drm_gem_object *obj)
 {
 	struct rknpu_gem_object *rknpu_obj = to_rknpu_obj(obj);
@@ -1290,6 +1326,35 @@
 {
 	vunmap(vaddr);
 }
+#else
+int rknpu_gem_prime_vmap(struct drm_gem_object *obj, struct iosys_map *map)
+{
+	struct rknpu_gem_object *rknpu_obj = to_rknpu_obj(obj);
+	void *vaddr = NULL;
+
+	if (!rknpu_obj->pages)
+		return -EINVAL;
+
+	vaddr = vmap(rknpu_obj->pages, rknpu_obj->num_pages, VM_MAP,
+			  PAGE_KERNEL);
+	if (!vaddr)
+		return -ENOMEM;
+
+	iosys_map_set_vaddr(map, vaddr);
+
+	return 0;
+}
+
+void rknpu_gem_prime_vunmap(struct drm_gem_object *obj, struct iosys_map *map)
+{
+	struct rknpu_gem_object *rknpu_obj = to_rknpu_obj(obj);
+
+	if (rknpu_obj->pages) {
+		vunmap(map->vaddr);
+		map->vaddr = NULL;
+	}
+}
+#endif
 
 int rknpu_gem_prime_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma)
 {
@@ -1306,6 +1371,7 @@
 			    unsigned long *length, unsigned long *offset,
 			    enum rknpu_cache_type cache_type)
 {
+#if KERNEL_VERSION(6, 1, 0) > LINUX_VERSION_CODE
 	struct drm_gem_object *obj = &rknpu_obj->base;
 	struct rknpu_device *rknpu_dev = obj->dev->dev_private;
 	void __iomem *cache_base_io = NULL;
@@ -1348,6 +1414,8 @@
 		*length -= cache_length;
 		*offset = 0;
 	}
+#endif
+
 	return 0;
 }
 
@@ -1355,10 +1423,12 @@
 			 struct drm_file *file_priv)
 {
 	struct rknpu_gem_object *rknpu_obj = NULL;
+	struct rknpu_device *rknpu_dev = dev->dev_private;
 	struct rknpu_mem_sync *args = data;
 	struct scatterlist *sg;
+	dma_addr_t sg_phys_addr;
 	unsigned long length, offset = 0;
-	unsigned long sg_left, size = 0;
+	unsigned long sg_offset, sg_left, size = 0;
 	unsigned long len = 0;
 	int i;
 
@@ -1382,6 +1452,8 @@
 						      DMA_FROM_DEVICE);
 		}
 	} else {
+		WARN_ON(!rknpu_dev->fake_dev);
+
 		length = args->size;
 		offset = args->offset;
 
@@ -1405,17 +1477,23 @@
 			if (len <= offset)
 				continue;
 
+			sg_phys_addr = sg_phys(sg);
+
 			sg_left = len - offset;
+			sg_offset = sg->length - sg_left;
+
 			size = (length < sg_left) ? length : sg_left;
 
 			if (args->flags & RKNPU_MEM_SYNC_TO_DEVICE) {
-				dma_sync_sg_for_device(dev->dev, sg, 1,
-						       DMA_TO_DEVICE);
+				dma_sync_single_range_for_device(
+					rknpu_dev->fake_dev, sg_phys_addr,
+					sg_offset, size, DMA_TO_DEVICE);
 			}
 
 			if (args->flags & RKNPU_MEM_SYNC_FROM_DEVICE) {
-				dma_sync_sg_for_cpu(dev->dev, sg, 1,
-						    DMA_FROM_DEVICE);
+				dma_sync_single_range_for_cpu(
+					rknpu_dev->fake_dev, sg_phys_addr,
+					sg_offset, size, DMA_FROM_DEVICE);
 			}
 
 			offset += size;
diff --git a/kernel/drivers/rknpu/rknpu_iommu.c b/kernel/drivers/rknpu/rknpu_iommu.c
index 39cc8f8..01620d9 100644
--- a/kernel/drivers/rknpu/rknpu_iommu.c
+++ b/kernel/drivers/rknpu/rknpu_iommu.c
@@ -18,6 +18,8 @@
 
 	shift = iova_shift(iovad);
 	iova_len = size >> shift;
+
+#if KERNEL_VERSION(6, 1, 0) > LINUX_VERSION_CODE
 	/*
 	 * Freeing non-power-of-two-sized allocations back into the IOVA caches
 	 * will come back to bite us badly, so we have to waste a bit of space
@@ -26,6 +28,7 @@
 	 */
 	if (iova_len < (1 << (IOVA_RANGE_CACHE_MAX_SIZE - 1)))
 		iova_len = roundup_pow_of_two(iova_len);
+#endif
 
 #if (KERNEL_VERSION(5, 10, 0) <= LINUX_VERSION_CODE)
 	dma_limit = min_not_zero(dma_limit, dev->bus_dma_limit);
diff --git a/kernel/drivers/rknpu/rknpu_job.c b/kernel/drivers/rknpu/rknpu_job.c
index 6a167c4..f0f1dd7 100644
--- a/kernel/drivers/rknpu/rknpu_job.c
+++ b/kernel/drivers/rknpu/rknpu_job.c
@@ -23,16 +23,25 @@
 #define REG_READ(offset) _REG_READ(rknpu_core_base, offset)
 #define REG_WRITE(value, offset) _REG_WRITE(rknpu_core_base, value, offset)
 
-static int rknpu_core_index(int core_mask)
+static int rknpu_wait_core_index(int core_mask)
 {
 	int index = 0;
 
-	if (core_mask & RKNPU_CORE0_MASK)
+	switch (core_mask & ((1 << RKNPU_MAX_CORES) - 1)) {
+	case RKNPU_CORE0_MASK:
+	case RKNPU_CORE0_MASK | RKNPU_CORE1_MASK:
+	case RKNPU_CORE0_MASK | RKNPU_CORE1_MASK | RKNPU_CORE2_MASK:
 		index = 0;
-	else if (core_mask & RKNPU_CORE1_MASK)
+		break;
+	case RKNPU_CORE1_MASK:
 		index = 1;
-	else if (core_mask & RKNPU_CORE2_MASK)
+		break;
+	case RKNPU_CORE2_MASK:
 		index = 2;
+		break;
+	default:
+		break;
+	}
 
 	return index;
 }
@@ -58,14 +67,24 @@
 	return core_mask;
 }
 
-static int rknn_get_task_number(struct rknpu_job *job, int core_index)
+static int rknpu_get_task_number(struct rknpu_job *job, int core_index)
 {
+	struct rknpu_device *rknpu_dev = job->rknpu_dev;
 	int task_num = job->args->task_number;
 
-	if (job->use_core_num == 2)
-		task_num = job->args->subcore_task[core_index].task_number;
-	else if (job->use_core_num == 3)
-		task_num = job->args->subcore_task[core_index + 2].task_number;
+	if (core_index >= RKNPU_MAX_CORES || core_index < 0) {
+		LOG_ERROR("core_index: %d set error!", core_index);
+		return 0;
+	}
+
+	if (rknpu_dev->config->num_irqs > 1) {
+		if (job->use_core_num == 1 || job->use_core_num == 2)
+			task_num =
+				job->args->subcore_task[core_index].task_number;
+		else if (job->use_core_num == 3)
+			task_num = job->args->subcore_task[core_index + 2]
+					   .task_number;
+	}
 
 	return task_num;
 }
@@ -157,10 +176,12 @@
 	struct rknpu_submit *args = job->args;
 	struct rknpu_task *last_task = NULL;
 	struct rknpu_subcore_data *subcore_data = NULL;
+	struct rknpu_job *entry, *q;
 	void __iomem *rknpu_core_base = NULL;
-	int core_index = rknpu_core_index(job->args->core_mask);
+	int core_index = rknpu_wait_core_index(job->args->core_mask);
 	unsigned long flags;
 	int wait_count = 0;
+	bool continue_wait = false;
 	int ret = -EINVAL;
 	int i = 0;
 
@@ -171,31 +192,47 @@
 					 job->flags & RKNPU_JOB_DONE ||
 						 rknpu_dev->soft_reseting,
 					 msecs_to_jiffies(args->timeout));
+
 		if (++wait_count >= 3)
 			break;
-	} while (ret == 0 && job->in_queue[core_index]);
 
-	if (job->in_queue[core_index]) {
+		if (ret == 0) {
+			int64_t commit_time = 0;
+			spin_lock_irqsave(&rknpu_dev->irq_lock, flags);
+			commit_time = ktime_us_delta(ktime_get(),
+						     job->commit_pc_time);
+			continue_wait =
+				job->commit_pc_time == 0 ?
+					true :
+					(commit_time < args->timeout * 1000);
+			spin_unlock_irqrestore(&rknpu_dev->irq_lock, flags);
+			LOG_ERROR(
+				"job: %p, wait_count: %d, continue_wait: %d, commit time: %lldus, wait time: %lldus, timeout time: %uus\n",
+				job, wait_count, continue_wait,
+				(job->commit_pc_time == 0 ? 0 : commit_time),
+				ktime_us_delta(ktime_get(), job->timestamp),
+				args->timeout * 1000);
+		}
+	} while (ret == 0 && continue_wait);
+
+	last_task = job->last_task;
+	if (!last_task) {
 		spin_lock_irqsave(&rknpu_dev->lock, flags);
-		subcore_data->task_num -= rknn_get_task_number(job, core_index);
-		if (job->use_core_num == 1) {
-			list_del_init(&job->head[core_index]);
-			job->in_queue[core_index] = false;
-		} else if (job->use_core_num > 1) {
-			for (i = 0; i < job->use_core_num; i++) {
-				if (job->in_queue[i]) {
-					list_del_init(&job->head[i]);
-					job->in_queue[i] = false;
+		for (i = 0; i < job->use_core_num; i++) {
+			subcore_data = &rknpu_dev->subcore_datas[i];
+			list_for_each_entry_safe(
+				entry, q, &subcore_data->todo_list, head[i]) {
+				if (entry == job) {
+					list_del(&job->head[i]);
+					break;
 				}
 			}
 		}
-		spin_unlock_irqrestore(&rknpu_dev->lock, flags);
+		spin_unlock_irqrestore(&rknpu_dev->irq_lock, flags);
+
+		LOG_ERROR("job commit failed\n");
 		return ret < 0 ? ret : -EINVAL;
 	}
-
-	last_task = job->last_task;
-	if (!last_task)
-		return ret < 0 ? ret : -EINVAL;
 
 	last_task->int_status = job->int_status[core_index];
 
@@ -213,7 +250,7 @@
 		LOG_ERROR(
 			"failed to wait job, task counter: %d, flags: %#x, ret = %d, elapsed time: %lldus\n",
 			args->task_counter, args->flags, ret,
-			ktime_to_us(ktime_sub(ktime_get(), job->timestamp)));
+			ktime_us_delta(ktime_get(), job->timestamp));
 
 		return ret < 0 ? ret : -ETIMEDOUT;
 	}
@@ -226,7 +263,8 @@
 	return 0;
 }
 
-static inline int rknpu_job_commit_pc(struct rknpu_job *job, int core_index)
+static inline int rknpu_job_subcore_commit_pc(struct rknpu_job *job,
+					      int core_index)
 {
 	struct rknpu_device *rknpu_dev = job->rknpu_dev;
 	struct rknpu_submit *args = job->args;
@@ -243,15 +281,19 @@
 	struct rknpu_task *last_task = NULL;
 	void __iomem *rknpu_core_base = rknpu_dev->base[core_index];
 	int task_start = args->task_start;
-	int task_end = args->task_start + args->task_number - 1;
+	int task_end;
 	int task_number = args->task_number;
 	int task_pp_en = args->flags & RKNPU_JOB_PINGPONG ? 1 : 0;
 	int pc_data_amount_scale = rknpu_dev->config->pc_data_amount_scale;
 	int pc_task_number_bits = rknpu_dev->config->pc_task_number_bits;
 	int i = 0;
+	int submit_index = atomic_read(&job->submit_count[core_index]);
+	int max_submit_number = rknpu_dev->config->max_submit_number;
 
-	if (!task_obj)
-		return -EINVAL;
+	if (!task_obj) {
+		job->ret = -EINVAL;
+		return job->ret;
+	}
 
 	if (rknpu_dev->config->num_irqs > 1) {
 		for (i = 0; i < rknpu_dev->config->num_irqs; i++) {
@@ -261,38 +303,40 @@
 			}
 		}
 
-		if (job->use_core_num == 1) {
+		switch (job->use_core_num) {
+		case 1:
+		case 2:
 			task_start = args->subcore_task[core_index].task_start;
-			task_end = args->subcore_task[core_index].task_start +
-				   args->subcore_task[core_index].task_number -
-				   1;
 			task_number =
 				args->subcore_task[core_index].task_number;
-		} else if (job->use_core_num == 2) {
-			task_start = args->subcore_task[core_index].task_start;
-			task_end = args->subcore_task[core_index].task_start +
-				   args->subcore_task[core_index].task_number -
-				   1;
-			task_number =
-				args->subcore_task[core_index].task_number;
-		} else if (job->use_core_num == 3) {
+			break;
+		case 3:
 			task_start =
 				args->subcore_task[core_index + 2].task_start;
-			task_end =
-				args->subcore_task[core_index + 2].task_start +
-				args->subcore_task[core_index + 2].task_number -
-				1;
 			task_number =
 				args->subcore_task[core_index + 2].task_number;
+			break;
+		default:
+			LOG_ERROR("Unknown use core num %d\n",
+				  job->use_core_num);
+			break;
 		}
 	}
+
+	task_start = task_start + submit_index * max_submit_number;
+	task_number = task_number - submit_index * max_submit_number;
+	task_number = task_number > max_submit_number ? max_submit_number :
+							task_number;
+	task_end = task_start + task_number - 1;
 
 	task_base = task_obj->kv_addr;
 
 	first_task = &task_base[task_start];
 	last_task = &task_base[task_end];
 
+	spin_lock(&rknpu_dev->lock);
 	REG_WRITE(first_task->regcmd_addr, RKNPU_OFFSET_PC_DATA_ADDR);
+	spin_unlock(&rknpu_dev->lock);
 
 	REG_WRITE((first_task->regcfg_amount + RKNPU_PC_DATA_EXTRA_AMOUNT +
 		   pc_data_amount_scale - 1) /
@@ -319,19 +363,50 @@
 	return 0;
 }
 
-static int rknpu_job_commit(struct rknpu_job *job, int core_index)
+static inline int rknpu_job_subcore_commit(struct rknpu_job *job, int core_index)
 {
 	struct rknpu_device *rknpu_dev = job->rknpu_dev;
 	struct rknpu_submit *args = job->args;
 	void __iomem *rknpu_core_base = rknpu_dev->base[core_index];
 
 	// switch to slave mode
+	spin_lock(&rknpu_dev->lock);
 	REG_WRITE(0x1, RKNPU_OFFSET_PC_DATA_ADDR);
+	spin_unlock(&rknpu_dev->lock);
 
-	if (!(args->flags & RKNPU_JOB_PC))
-		return -EINVAL;
+	if (!(args->flags & RKNPU_JOB_PC)) {
+		job->ret = -EINVAL;
+		return job->ret;
+	}
 
-	return rknpu_job_commit_pc(job, core_index);
+	return rknpu_job_subcore_commit_pc(job, core_index);
+}
+
+static void rknpu_job_commit(struct rknpu_job *job)
+{
+	switch (job->args->core_mask & ((1 << RKNPU_MAX_CORES) - 1)) {
+	case RKNPU_CORE0_MASK:
+		rknpu_job_subcore_commit(job, 0);
+		break;
+	case RKNPU_CORE1_MASK:
+		rknpu_job_subcore_commit(job, 1);
+		break;
+	case RKNPU_CORE2_MASK:
+		rknpu_job_subcore_commit(job, 2);
+		break;
+	case RKNPU_CORE0_MASK | RKNPU_CORE1_MASK:
+		rknpu_job_subcore_commit(job, 0);
+		rknpu_job_subcore_commit(job, 1);
+		break;
+	case RKNPU_CORE0_MASK | RKNPU_CORE1_MASK | RKNPU_CORE2_MASK:
+		rknpu_job_subcore_commit(job, 0);
+		rknpu_job_subcore_commit(job, 1);
+		rknpu_job_subcore_commit(job, 2);
+		break;
+	default:
+		LOG_ERROR("Unknown core mask: %d\n", job->args->core_mask);
+		break;
+	}
 }
 
 static void rknpu_job_next(struct rknpu_device *rknpu_dev, int core_index)
@@ -356,18 +431,13 @@
 			       head[core_index]);
 
 	list_del_init(&job->head[core_index]);
-	job->in_queue[core_index] = false;
 	subcore_data->job = job;
 	job->hw_recoder_time = ktime_get();
+	job->commit_pc_time = job->hw_recoder_time;
 	spin_unlock_irqrestore(&rknpu_dev->irq_lock, flags);
 
 	if (atomic_dec_and_test(&job->run_count)) {
-		if (job->args->core_mask & RKNPU_CORE0_MASK)
-			job->ret = rknpu_job_commit(job, 0);
-		if (job->args->core_mask & RKNPU_CORE1_MASK)
-			job->ret = rknpu_job_commit(job, 1);
-		if (job->args->core_mask & RKNPU_CORE2_MASK)
-			job->ret = rknpu_job_commit(job, 2);
+		rknpu_job_commit(job);
 	}
 }
 
@@ -376,15 +446,22 @@
 	struct rknpu_device *rknpu_dev = job->rknpu_dev;
 	struct rknpu_subcore_data *subcore_data = NULL;
 	unsigned long flags;
-	ktime_t now = ktime_get();
+	int max_submit_number = rknpu_dev->config->max_submit_number;
+
+	if (atomic_inc_return(&job->submit_count[core_index]) <
+	    (rknpu_get_task_number(job, core_index) + max_submit_number - 1) /
+		    max_submit_number) {
+		rknpu_job_commit(job);
+		return;
+	}
 
 	subcore_data = &rknpu_dev->subcore_datas[core_index];
 
 	spin_lock_irqsave(&rknpu_dev->irq_lock, flags);
 	subcore_data->job = NULL;
-	subcore_data->task_num -= rknn_get_task_number(job, core_index);
+	subcore_data->task_num -= rknpu_get_task_number(job, core_index);
 	subcore_data->timer.busy_time +=
-		ktime_us_delta(now, job->hw_recoder_time);
+		ktime_us_delta(ktime_get(), job->hw_recoder_time);
 	spin_unlock_irqrestore(&rknpu_dev->irq_lock, flags);
 
 	if (atomic_dec_and_test(&job->interrupt_count)) {
@@ -417,7 +494,8 @@
 	int task_num_list[3] = { 0, 1, 2 };
 	int tmp = 0;
 
-	if ((job->args->core_mask & 0x07) == RKNPU_CORE_AUTO_MASK) {
+	if ((job->args->core_mask & ((1 << RKNPU_MAX_CORES) - 1)) ==
+	    RKNPU_CORE_AUTO_MASK) {
 		if (rknpu_dev->subcore_datas[0].task_num >
 		    rknpu_dev->subcore_datas[1].task_num) {
 			tmp = task_num_list[1];
@@ -456,8 +534,7 @@
 		if (job->args->core_mask & rknpu_core_mask(i)) {
 			subcore_data = &rknpu_dev->subcore_datas[i];
 			list_add_tail(&job->head[i], &subcore_data->todo_list);
-			subcore_data->task_num += rknn_get_task_number(job, i);
-			job->in_queue[i] = true;
+			subcore_data->task_num += rknpu_get_task_number(job, i);
 		}
 	}
 	spin_unlock_irqrestore(&rknpu_dev->irq_lock, flags);
@@ -477,18 +554,18 @@
 
 	msleep(100);
 
+	spin_lock_irqsave(&rknpu_dev->irq_lock, flags);
 	for (i = 0; i < rknpu_dev->config->num_irqs; i++) {
 		if (job->args->core_mask & rknpu_core_mask(i)) {
 			subcore_data = &rknpu_dev->subcore_datas[i];
-			spin_lock_irqsave(&rknpu_dev->irq_lock, flags);
 			if (job == subcore_data->job && !job->irq_entry[i]) {
 				subcore_data->job = NULL;
 				subcore_data->task_num -=
-					rknn_get_task_number(job, i);
+					rknpu_get_task_number(job, i);
 			}
-			spin_unlock_irqrestore(&rknpu_dev->irq_lock, flags);
 		}
 	}
+	spin_unlock_irqrestore(&rknpu_dev->irq_lock, flags);
 
 	if (job->ret == -ETIMEDOUT) {
 		LOG_ERROR("job timeout, flags: %#x:\n", job->flags);
@@ -505,8 +582,8 @@
 						 rknpu_dev->config
 							 ->pc_task_status_offset) &
 					 rknpu_dev->config->pc_task_number_mask),
-					ktime_to_us(ktime_sub(ktime_get(),
-							      job->timestamp)));
+					ktime_us_delta(ktime_get(),
+						       job->timestamp));
 			}
 		}
 		rknpu_soft_reset(rknpu_dev);
@@ -514,7 +591,7 @@
 		LOG_ERROR(
 			"job abort, flags: %#x, ret: %d, elapsed time: %lldus\n",
 			job->flags, job->ret,
-			ktime_to_us(ktime_sub(ktime_get(), job->timestamp)));
+			ktime_us_delta(ktime_get(), job->timestamp));
 	}
 
 	rknpu_job_cleanup(job);
@@ -609,7 +686,6 @@
 {
 	struct rknpu_job *job = NULL;
 	unsigned long flags;
-	ktime_t now = ktime_get();
 	struct rknpu_subcore_data *subcore_data = NULL;
 	int i = 0;
 
@@ -618,7 +694,7 @@
 			subcore_data = &rknpu_dev->subcore_datas[i];
 			job = subcore_data->job;
 			if (job &&
-			    ktime_to_ms(ktime_sub(now, job->timestamp)) >=
+			    ktime_us_delta(ktime_get(), job->timestamp) >=
 				    job->args->timeout) {
 				rknpu_soft_reset(rknpu_dev);
 
@@ -640,7 +716,6 @@
 							struct rknpu_job,
 							head[i]);
 						list_del_init(&job->head[i]);
-						job->in_queue[i] = false;
 					} else {
 						job = NULL;
 					}
diff --git a/kernel/drivers/rknpu/rknpu_mem.c b/kernel/drivers/rknpu/rknpu_mem.c
index ff7e92d..5242f15 100644
--- a/kernel/drivers/rknpu/rknpu_mem.c
+++ b/kernel/drivers/rknpu/rknpu_mem.c
@@ -15,6 +15,8 @@
 #include "rknpu_ioctl.h"
 #include "rknpu_mem.h"
 
+#ifdef CONFIG_ROCKCHIP_RKNPU_DMA_HEAP
+
 int rknpu_mem_create_ioctl(struct rknpu_device *rknpu_dev, unsigned long data,
 			   struct file *file)
 {
@@ -108,7 +110,7 @@
 	}
 
 	page_count = length >> PAGE_SHIFT;
-	pages = kmalloc_array(page_count, sizeof(struct page), GFP_KERNEL);
+	pages = vmalloc(page_count * sizeof(struct page));
 	if (!pages) {
 		LOG_ERROR("alloc pages failed\n");
 		ret = -ENOMEM;
@@ -146,7 +148,8 @@
 		goto err_unmap_kv_addr;
 	}
 
-	kfree(pages);
+	vfree(pages);
+	pages = NULL;
 	dma_buf_unmap_attachment(attachment, table, DMA_BIDIRECTIONAL);
 	dma_buf_detach(dmabuf, attachment);
 
@@ -169,7 +172,8 @@
 	rknpu_obj->kv_addr = NULL;
 
 err_free_pages:
-	kfree(pages);
+	vfree(pages);
+	pages = NULL;
 
 err_detach_dma_buf:
 	dma_buf_unmap_attachment(attachment, table, DMA_BIDIRECTIONAL);
@@ -292,7 +296,9 @@
 {
 	struct rknpu_mem_object *rknpu_obj = NULL;
 	struct rknpu_mem_sync args;
+#ifdef CONFIG_DMABUF_PARTIAL
 	struct dma_buf *dmabuf;
+#endif
 	int ret = -EFAULT;
 
 	if (unlikely(copy_from_user(&args, (struct rknpu_mem_sync *)data,
@@ -310,7 +316,6 @@
 	}
 
 	rknpu_obj = (struct rknpu_mem_object *)(uintptr_t)args.obj_addr;
-	dmabuf = rknpu_obj->dmabuf;
 
 #ifndef CONFIG_DMABUF_PARTIAL
 	if (args.flags & RKNPU_MEM_SYNC_TO_DEVICE) {
@@ -322,6 +327,7 @@
 				   DMA_FROM_DEVICE, true);
 	}
 #else
+	dmabuf = rknpu_obj->dmabuf;
 	if (args.flags & RKNPU_MEM_SYNC_TO_DEVICE) {
 		dmabuf->ops->end_cpu_access_partial(dmabuf, DMA_TO_DEVICE,
 						    args.offset, args.size);
@@ -334,3 +340,5 @@
 
 	return 0;
 }
+
+#endif
diff --git a/kernel/drivers/rpmsg/rockchip_rpmsg.c b/kernel/drivers/rpmsg/rockchip_rpmsg.c
index be1b202..8977125 100644
--- a/kernel/drivers/rpmsg/rockchip_rpmsg.c
+++ b/kernel/drivers/rpmsg/rockchip_rpmsg.c
@@ -26,11 +26,6 @@
 
 #include "rpmsg_internal.h"
 
-enum rk_rpmsg_chip {
-	RK3562,
-	RK3568,
-};
-
 struct rk_virtio_dev {
 	struct virtio_device vdev;
 	unsigned int vring[2];
@@ -44,7 +39,6 @@
 
 struct rk_rpmsg_dev {
 	struct platform_device *pdev;
-	enum rk_rpmsg_chip chip;
 	int vdev_nums;
 	unsigned int link_id;
 	int first_notify;
@@ -114,7 +108,6 @@
 		dev_err(dev, "mbox send failed!\n");
 		return false;
 	}
-	mbox_chan_txdone(rpdev->mbox_tx_chan, 0);
 
 	return true;
 }
@@ -308,7 +301,6 @@
 
 	dev_info(dev, "rockchip rpmsg platform probe.\n");
 	rpdev->pdev = pdev;
-	rpdev->chip = (enum rk_rpmsg_chip)device_get_match_data(dev);
 	rpdev->first_notify = 0;
 
 	cl = &rpdev->mbox_cl;
@@ -400,8 +392,7 @@
 }
 
 static const struct of_device_id rockchip_rpmsg_match[] = {
-	{ .compatible = "rockchip,rk3562-rpmsg", .data = (void *)RK3562, },
-	{ .compatible = "rockchip,rk3568-rpmsg", .data = (void *)RK3568, },
+	{ .compatible = "rockchip,rpmsg", },
 	{ /* sentinel */ },
 };
 
diff --git a/kernel/drivers/rpmsg/rockchip_rpmsg_test.c b/kernel/drivers/rpmsg/rockchip_rpmsg_test.c
index 08677d6..7f607bd 100644
--- a/kernel/drivers/rpmsg/rockchip_rpmsg_test.c
+++ b/kernel/drivers/rpmsg/rockchip_rpmsg_test.c
@@ -6,17 +6,22 @@
  * Author: Hongming Zou <hongming.zou@rock-chips.com>
  */
 
+#include <linux/delay.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/rpmsg.h>
 #include <linux/rpmsg/rockchip_rpmsg.h>
+#include <linux/time.h>
 #include <linux/virtio.h>
 
 #define LINUX_TEST_MSG_1 "Announce master ept id!"
 #define LINUX_TEST_MSG_2 "Rockchip rpmsg linux test pingpong!"
-#define MSG_LIMIT       100
+#define MSG_LIMIT       10000
 
-struct instance_data {
+/* different processor cores may need to adjust the value of this definition */
+#define LINUX_RPMSG_COMPENSATION	(1)	//ms
+
+struct rpmsg_info_t {
 	int rx_count;
 };
 
@@ -25,18 +30,19 @@
 {
 	int ret;
 	uint32_t remote_ept_id;
-	struct instance_data *idata = dev_get_drvdata(&rp->dev);
+	struct rpmsg_info_t *info = dev_get_drvdata(&rp->dev);
 
 	remote_ept_id = src;
 	dev_info(&rp->dev, "rx msg %s rx_count %d(remote_ept_id: 0x%x)\n",
-			(char *)payload, ++idata->rx_count, remote_ept_id);
+			(char *)payload, ++info->rx_count, remote_ept_id);
 
 	/* test should not live forever */
-	if (idata->rx_count >= MSG_LIMIT) {
+	if (info->rx_count >= MSG_LIMIT) {
 		dev_info(&rp->dev, "Rockchip rpmsg test exit!\n");
 		return 0;
 	}
 
+	mdelay(LINUX_RPMSG_COMPENSATION);
 	/* send a new message now */
 	ret = rpmsg_sendto(rp->ept, LINUX_TEST_MSG_2, strlen(LINUX_TEST_MSG_2), remote_ept_id);
 	if (ret)
@@ -48,17 +54,17 @@
 {
 	int ret;
 	uint32_t master_ept_id, remote_ept_id;
-	struct instance_data *idata;
+	struct rpmsg_info_t *info;
 
 	master_ept_id = rp->src;
 	remote_ept_id = rp->dst;
 	dev_info(&rp->dev, "new channel: 0x%x -> 0x%x!\n", master_ept_id, remote_ept_id);
 
-	idata = devm_kzalloc(&rp->dev, sizeof(*idata), GFP_KERNEL);
-	if (!idata)
+	info = devm_kzalloc(&rp->dev, sizeof(*info), GFP_KERNEL);
+	if (!info)
 		return -ENOMEM;
 
-	dev_set_drvdata(&rp->dev, idata);
+	dev_set_drvdata(&rp->dev, info);
 
 	/*
 	 * send a message to our remote processor, and tell remote
@@ -69,7 +75,7 @@
 		dev_err(&rp->dev, "rpmsg_send failed: %d\n", ret);
 		return ret;
 	}
-
+	mdelay(LINUX_RPMSG_COMPENSATION);
 	ret = rpmsg_sendto(rp->ept, LINUX_TEST_MSG_2, strlen(LINUX_TEST_MSG_2), remote_ept_id);
 	if (ret) {
 		dev_err(&rp->dev, "rpmsg_send failed: %d\n", ret);
@@ -86,6 +92,7 @@
 
 static struct rpmsg_device_id rockchip_rpmsg_test_id_table[] = {
 	{ .name = "rpmsg-ap3-ch0" },
+	{ .name = "rpmsg-mcu0-test" },
 	{ /* sentinel */ },
 };
 
diff --git a/kernel/drivers/rtc/rtc-rk808.c b/kernel/drivers/rtc/rtc-rk808.c
index c65286b..c5e19fb 100644
--- a/kernel/drivers/rtc/rtc-rk808.c
+++ b/kernel/drivers/rtc/rtc-rk808.c
@@ -28,6 +28,7 @@
 #define BIT_RTC_CTRL_REG_RTC_READSEL_M		BIT(7)
 #define BIT_RTC_INTERRUPTS_REG_IT_ALARM_M	BIT(3)
 #define RTC_STATUS_MASK		0xFE
+#define RTC_ALARM_STATUS			BIT(6)
 
 #define SECONDS_REG_MSK		0x7F
 #define MINUTES_REG_MAK		0x7F
@@ -248,6 +249,12 @@
 	ret = regmap_update_bits(rk808->regmap, rk808_rtc->creg->int_reg,
 				 BIT_RTC_INTERRUPTS_REG_IT_ALARM_M, 0);
 
+	/*
+	 * The rtc alarm status(BIT(6)) must be cleared after alarm 1s or
+	 * after the alarm is disabled.
+	 */
+	ret = regmap_write(rk808->regmap, rk808_rtc->creg->status_reg,
+			   RTC_ALARM_STATUS);
 	return ret;
 }
 
diff --git a/kernel/drivers/rtc/rtc-rockchip.c b/kernel/drivers/rtc/rtc-rockchip.c
index 5fa4f1a..8dd82ef 100644
--- a/kernel/drivers/rtc/rtc-rockchip.c
+++ b/kernel/drivers/rtc/rtc-rockchip.c
@@ -508,7 +508,7 @@
 	c_mon = DIV_ROUND_CLOSEST(30 * 24 * tcamp, 32768);
 
 	if (c_hour > 1)
-		rockchip_rtc_write(rtc->regmap, RTC_COMP_H, (c_hour - 1) | trim_dir);
+		rockchip_rtc_write(rtc->regmap, RTC_COMP_H, bin2bcd((c_hour - 1)) | trim_dir);
 	else
 		rockchip_rtc_write(rtc->regmap, RTC_COMP_H, CLK32K_NO_COMP);
 
@@ -522,7 +522,7 @@
 
 	if (c_det_day > 1)
 		rockchip_rtc_write(rtc->regmap, RTC_COMP_D,
-				   (c_det_day - 1) | trim_dir);
+				   bin2bcd((c_det_day - 1)) | trim_dir);
 	else
 		rockchip_rtc_write(rtc->regmap, RTC_COMP_D, CLK32K_NO_COMP);
 
@@ -536,7 +536,7 @@
 
 	if (c_det_mon)
 		rockchip_rtc_write(rtc->regmap, RTC_COMP_M,
-				   (c_det_mon - 1) | trim_dir);
+				   bin2bcd((c_det_mon - 1)) | trim_dir);
 	else
 		rockchip_rtc_write(rtc->regmap, RTC_COMP_M, CLK32K_NO_COMP);
 
diff --git a/kernel/drivers/scsi/fcoe/fcoe.c b/kernel/drivers/scsi/fcoe/fcoe.c
index dc97e4f..0f92749 100644
--- a/kernel/drivers/scsi/fcoe/fcoe.c
+++ b/kernel/drivers/scsi/fcoe/fcoe.c
@@ -1452,11 +1452,11 @@
 static int fcoe_alloc_paged_crc_eof(struct sk_buff *skb, int tlen)
 {
 	struct fcoe_percpu_s *fps;
-	int rc, cpu = get_cpu_light();
+	int rc;
 
-	fps = &per_cpu(fcoe_percpu, cpu);
+	fps = &get_cpu_var(fcoe_percpu);
 	rc = fcoe_get_paged_crc_eof(skb, tlen, fps);
-	put_cpu_light();
+	put_cpu_var(fcoe_percpu);
 
 	return rc;
 }
@@ -1641,11 +1641,11 @@
 		return 0;
 	}
 
-	stats = per_cpu_ptr(lport->stats, get_cpu_light());
+	stats = per_cpu_ptr(lport->stats, get_cpu());
 	stats->InvalidCRCCount++;
 	if (stats->InvalidCRCCount < 5)
 		printk(KERN_WARNING "fcoe: dropping frame with CRC error\n");
-	put_cpu_light();
+	put_cpu();
 	return -EINVAL;
 }
 
@@ -1686,7 +1686,7 @@
 	 */
 	hp = (struct fcoe_hdr *) skb_network_header(skb);
 
-	stats = per_cpu_ptr(lport->stats, get_cpu_light());
+	stats = per_cpu_ptr(lport->stats, get_cpu());
 	if (unlikely(FC_FCOE_DECAPS_VER(hp) != FC_FCOE_VER)) {
 		if (stats->ErrorFrames < 5)
 			printk(KERN_WARNING "fcoe: FCoE version "
@@ -1718,13 +1718,13 @@
 		goto drop;
 
 	if (!fcoe_filter_frames(lport, fp)) {
-		put_cpu_light();
+		put_cpu();
 		fc_exch_recv(lport, fp);
 		return;
 	}
 drop:
 	stats->ErrorFrames++;
-	put_cpu_light();
+	put_cpu();
 	kfree_skb(skb);
 }
 
diff --git a/kernel/drivers/scsi/fcoe/fcoe_ctlr.c b/kernel/drivers/scsi/fcoe/fcoe_ctlr.c
index 8f9a029..bbc5d6b 100644
--- a/kernel/drivers/scsi/fcoe/fcoe_ctlr.c
+++ b/kernel/drivers/scsi/fcoe/fcoe_ctlr.c
@@ -828,7 +828,7 @@
 
 	INIT_LIST_HEAD(&del_list);
 
-	stats = per_cpu_ptr(fip->lp->stats, get_cpu_light());
+	stats = per_cpu_ptr(fip->lp->stats, get_cpu());
 
 	list_for_each_entry_safe(fcf, next, &fip->fcfs, list) {
 		deadline = fcf->time + fcf->fka_period + fcf->fka_period / 2;
@@ -864,7 +864,7 @@
 				sel_time = fcf->time;
 		}
 	}
-	put_cpu_light();
+	put_cpu();
 
 	list_for_each_entry_safe(fcf, next, &del_list, list) {
 		/* Removes fcf from current list */
diff --git a/kernel/drivers/scsi/libfc/fc_exch.c b/kernel/drivers/scsi/libfc/fc_exch.c
index 65160ea..4261380 100644
--- a/kernel/drivers/scsi/libfc/fc_exch.c
+++ b/kernel/drivers/scsi/libfc/fc_exch.c
@@ -826,10 +826,10 @@
 	}
 	memset(ep, 0, sizeof(*ep));
 
-	cpu = get_cpu_light();
+	cpu = get_cpu();
 	pool = per_cpu_ptr(mp->pool, cpu);
 	spin_lock_bh(&pool->lock);
-	put_cpu_light();
+	put_cpu();
 
 	/* peek cache of free slot */
 	if (pool->left != FC_XID_UNKNOWN) {
diff --git a/kernel/drivers/soc/rockchip/fiq_debugger/fiq_debugger.c b/kernel/drivers/soc/rockchip/fiq_debugger/fiq_debugger.c
index e36d211..1f8e88e 100644
--- a/kernel/drivers/soc/rockchip/fiq_debugger/fiq_debugger.c
+++ b/kernel/drivers/soc/rockchip/fiq_debugger/fiq_debugger.c
@@ -263,10 +263,11 @@
 {
 	char buf[512];
 	size_t len;
-	struct kmsg_dumper_iter dumper = { .active = true };
+	struct kmsg_dumper dumper = { .active = true };
 
-	kmsg_dump_rewind(&dumper);
-	while (kmsg_dump_get_line(&dumper, true, buf,
+
+	kmsg_dump_rewind_nolock(&dumper);
+	while (kmsg_dump_get_line_nolock(&dumper, true, buf,
 					 sizeof(buf) - 1, &len)) {
 		buf[len] = 0;
 		fiq_debugger_puts(state, buf);
diff --git a/kernel/drivers/soc/rockchip/fiq_debugger/rk_fiq_debugger.c b/kernel/drivers/soc/rockchip/fiq_debugger/rk_fiq_debugger.c
index 3c85c83..1fb72a5 100644
--- a/kernel/drivers/soc/rockchip/fiq_debugger/rk_fiq_debugger.c
+++ b/kernel/drivers/soc/rockchip/fiq_debugger/rk_fiq_debugger.c
@@ -179,11 +179,13 @@
 
 	if (lsr & UART_LSR_DR) {
 		temp = rk_fiq_read(t, UART_RX);
-		buf[n & 0x1f] = temp;
-		n++;
-		if (temp == 'q' && n > 2) {
-			if ((buf[(n - 2) & 0x1f] == 'i') &&
-			    (buf[(n - 3) & 0x1f] == 'f'))
+		buf[++n & 0x1f] = temp;
+
+		if (temp == 'q') {
+			if ((buf[(n - 1) & 0x1f] == 'i') &&
+			    (buf[(n - 2) & 0x1f] == 'f') &&
+			    (buf[(n - 3) & 0x1f] != '_') &&
+			    (buf[(n - 3) & 0x1f] != ' '))
 				return FIQ_DEBUGGER_BREAK;
 			else
 				return temp;
@@ -334,7 +336,7 @@
 		unsigned int dropped;
 
 		set_current_state(TASK_INTERRUPTIBLE);
-		if (kfifo_is_empty(&fifo) && kfifo_is_empty(&tty_fifo)) {
+		if (console_thread_stop || (kfifo_is_empty(&fifo) && kfifo_is_empty(&tty_fifo))) {
 			smp_store_mb(console_thread_running, false);
 			schedule();
 			smp_store_mb(console_thread_running, true);
@@ -344,13 +346,13 @@
 		set_current_state(TASK_RUNNING);
 
 		while (!console_thread_stop && (!kfifo_is_empty(&fifo) || !kfifo_is_empty(&tty_fifo))) {
-			while (kfifo_get(&fifo, &c)) {
+			while (!console_thread_stop && kfifo_get(&fifo, &c)) {
 				console_put(pdev, &c, 1);
 				if (c == '\n')
 					break;
 			}
 
-			while (kfifo_get(&tty_fifo, &c)) {
+			while (!console_thread_stop && kfifo_get(&tty_fifo, &c)) {
 				console_putc(pdev, c);
 				len_tty++;
 				if (c == '\n')
@@ -418,6 +420,8 @@
 	unsigned int ret = 0;
 	struct rk_fiq_debugger *t;
 
+	if (console_thread_stop)
+		return count;
 	t = container_of(dev_get_platdata(&pdev->dev), typeof(*t), pdata);
 
 	if (count > 0) {
diff --git a/kernel/drivers/soc/rockchip/minidump/minidump_private.h b/kernel/drivers/soc/rockchip/minidump/minidump_private.h
index c7c4699..12cf812 100644
--- a/kernel/drivers/soc/rockchip/minidump/minidump_private.h
+++ b/kernel/drivers/soc/rockchip/minidump/minidump_private.h
@@ -54,6 +54,7 @@
  * @ss_region_count : Number of regions added in this SS toc
  * @md_ss_smem_regions_baseptr : regions base pointer of the Subsystem
  * @elf_header : base pointer of the minidump elf header
+ * @minidump_table : base pointer of the minidump_table
  */
 struct md_ss_toc {
 	u32			md_ss_toc_init;
@@ -63,6 +64,8 @@
 	u32			ss_region_count;
 	u64			md_ss_smem_regions_baseptr;
 	u64			elf_header;
+	u64			elf_size;
+	u64			minidump_table;
 };
 
 /**
diff --git a/kernel/drivers/soc/rockchip/minidump/rk_minidump.c b/kernel/drivers/soc/rockchip/minidump/rk_minidump.c
index 33bc638..908bb5c 100644
--- a/kernel/drivers/soc/rockchip/minidump/rk_minidump.c
+++ b/kernel/drivers/soc/rockchip/minidump/rk_minidump.c
@@ -673,6 +673,7 @@
 	md_ss_toc->encryption_status = MD_SS_ENCR_NONE;
 	md_ss_toc->encryption_required = MD_SS_ENCR_REQ;
 	md_ss_toc->elf_header = (u64)r.start;
+	md_ss_toc->minidump_table = (u64)virt_to_phys(&minidump_table);
 
 	minidump_table.md_ss_toc = md_ss_toc;
 	minidump_table.md_regions = devm_kzalloc(&pdev->dev, (MAX_NUM_ENTRIES *
diff --git a/kernel/drivers/soc/rockchip/mtd_vendor_storage.c b/kernel/drivers/soc/rockchip/mtd_vendor_storage.c
index 2f4e4f8..1f32930 100644
--- a/kernel/drivers/soc/rockchip/mtd_vendor_storage.c
+++ b/kernel/drivers/soc/rockchip/mtd_vendor_storage.c
@@ -20,6 +20,7 @@
 
 #define MTD_VENDOR_PART_START		0
 #define MTD_VENDOR_PART_SIZE		FLASH_VENDOR_PART_SIZE
+#define MTD_VENDOR_NOR_BLOCK_SIZE	128
 #define MTD_VENDOR_PART_NUM		1
 #define MTD_VENDOR_TAG			VENDOR_HEAD_TAG
 
@@ -43,6 +44,7 @@
 static struct flash_vendor_info *g_vendor;
 static DEFINE_MUTEX(vendor_ops_mutex);
 static struct mtd_info *mtd;
+static u32 mtd_erase_size;
 static const char *vendor_mtd_name = "vnvm";
 static struct mtd_nand_info nand_info;
 static struct platform_device *g_pdev;
@@ -54,8 +56,8 @@
 	struct erase_info ei;
 
 re_write:
-	if (nand_info.page_offset >= mtd->erasesize) {
-		nand_info.blk_offset += mtd->erasesize;
+	if (nand_info.page_offset >= mtd_erase_size) {
+		nand_info.blk_offset += mtd_erase_size;
 		if (nand_info.blk_offset >= mtd->size)
 			nand_info.blk_offset = 0;
 		if (mtd_block_isbad(mtd, nand_info.blk_offset))
@@ -63,7 +65,7 @@
 
 		memset(&ei, 0, sizeof(struct erase_info));
 		ei.addr = nand_info.blk_offset;
-		ei.len	= mtd->erasesize;
+		ei.len	= mtd_erase_size;
 		if (mtd_erase(mtd, &ei))
 			goto re_write;
 
@@ -100,7 +102,15 @@
 	nand_info.ops_size = (sizeof(*g_vendor) + mtd->writesize - 1) / mtd->writesize;
 	nand_info.ops_size *= mtd->writesize;
 
-	for (offset = 0; offset < mtd->size; offset += mtd->erasesize) {
+	/*
+	 * The NOR FLASH erase size maybe config as 4KB, need to re-define
+	 * and maintain consistency with uboot.
+	 */
+	mtd_erase_size = mtd->erasesize;
+	if (mtd_erase_size <= MTD_VENDOR_NOR_BLOCK_SIZE * 512)
+		mtd_erase_size = MTD_VENDOR_NOR_BLOCK_SIZE * 512;
+
+	for (offset = 0; offset < mtd->size; offset += mtd_erase_size) {
 		if (!mtd_block_isbad(mtd, offset)) {
 			err = mtd_read(mtd, offset, sizeof(*g_vendor),
 				       &bytes_read, (u8 *)g_vendor);
@@ -115,11 +125,11 @@
 				}
 			}
 		} else if (nand_info.blk_offset == offset)
-			nand_info.blk_offset += mtd->erasesize;
+			nand_info.blk_offset += mtd_erase_size;
 	}
 
 	if (nand_info.version) {
-		for (offset = mtd->erasesize - nand_info.ops_size;
+		for (offset = mtd_erase_size - nand_info.ops_size;
 		     offset >= 0;
 		     offset -= nand_info.ops_size) {
 			err = mtd_read(mtd, nand_info.blk_offset + offset,
@@ -145,7 +155,10 @@
 			if (bytes_read == sizeof(*g_vendor) &&
 			    g_vendor->tag == MTD_VENDOR_TAG &&
 			    g_vendor->version == g_vendor->version2) {
-				nand_info.version = g_vendor->version;
+				if (nand_info.version > g_vendor->version)
+					g_vendor->version = nand_info.version;
+				else
+					nand_info.version = g_vendor->version;
 				break;
 			}
 		}
@@ -155,11 +168,11 @@
 		g_vendor->tag = MTD_VENDOR_TAG;
 		g_vendor->free_size = sizeof(g_vendor->data);
 		g_vendor->version2 = g_vendor->version;
-		for (offset = 0; offset < mtd->size; offset += mtd->erasesize) {
+		for (offset = 0; offset < mtd->size; offset += mtd_erase_size) {
 			if (!mtd_block_isbad(mtd, offset)) {
 				memset(&ei, 0, sizeof(struct erase_info));
 				ei.addr = nand_info.blk_offset + offset;
-				ei.len  = mtd->erasesize;
+				ei.len  = mtd_erase_size;
 				mtd_erase(mtd, &ei);
 			}
 		}
diff --git a/kernel/drivers/soc/rockchip/rockchip-cpuinfo.c b/kernel/drivers/soc/rockchip/rockchip-cpuinfo.c
index 9eea6f3..5785d42 100644
--- a/kernel/drivers/soc/rockchip/rockchip-cpuinfo.c
+++ b/kernel/drivers/soc/rockchip/rockchip-cpuinfo.c
@@ -214,7 +214,10 @@
 
 static void rk3528_init(void)
 {
-	rockchip_soc_id = ROCKCHIP_SOC_RK3528;
+	if (of_machine_is_compatible("rockchip,rk3528"))
+		rockchip_soc_id = ROCKCHIP_SOC_RK3528;
+	else if (of_machine_is_compatible("rockchip,rk3528a"))
+		rockchip_soc_id = ROCKCHIP_SOC_RK3528A;
 }
 
 #define RK356X_PMU_GRF_PHYS		0xfdc20000
@@ -235,6 +238,12 @@
 static void rk3566_init(void)
 {
 	rockchip_soc_id = ROCKCHIP_SOC_RK3566;
+	rk356x_set_cpu_version();
+}
+
+static void rk3567_init(void)
+{
+	rockchip_soc_id = ROCKCHIP_SOC_RK3567;
 	rk356x_set_cpu_version();
 }
 
@@ -270,6 +279,8 @@
 		rk3528_init();
 	}  else if (cpu_is_rk3566()) {
 		rk3566_init();
+	}  else if (cpu_is_rk3567()) {
+		rk3567_init();
 	} else if (cpu_is_rk3568()) {
 		rk3568_init();
 	} else if (cpu_is_px30()) {
diff --git a/kernel/drivers/soc/rockchip/rockchip_amp.c b/kernel/drivers/soc/rockchip/rockchip_amp.c
index 6c66362..db52d09 100644
--- a/kernel/drivers/soc/rockchip/rockchip_amp.c
+++ b/kernel/drivers/soc/rockchip/rockchip_amp.c
@@ -6,6 +6,7 @@
  * Author: Tony Xie <tony.xie@rock-chips.com>
  */
 
+#include <asm/cputype.h>
 #include <linux/clk.h>
 #include <linux/module.h>
 #include <linux/of.h>
@@ -13,10 +14,17 @@
 #include <linux/pm_domain.h>
 #include <linux/pm_runtime.h>
 #include <linux/rockchip/rockchip_sip.h>
+#include <soc/rockchip/rockchip_amp.h>
 
 #define RK_CPU_STATUS_OFF		0
 #define RK_CPU_STATUS_ON		1
 #define RK_CPU_STATUS_BUSY		-1
+#define AMP_AFF_MAX_CLUSTER		4
+#define AMP_AFF_MAX_CPU			8
+#define GPIO_BANK_NUM			16
+#define GPIO_GROUP_PRIO_MAX		3
+
+#define AMP_GIC_DBG(fmt, arg...)	do { if (0) { pr_warn(fmt, ##arg); } } while (0)
 
 enum amp_cpu_ctrl_status {
 	AMP_CPU_STATUS_AMP_DIS = 0,
@@ -43,6 +51,33 @@
 	u64 entry;
 	u64 cpu_id;
 } cpu_boot_info[CONFIG_NR_CPUS];
+
+struct amp_gpio_group_s {
+	u32 bank_id;
+	u32 prio;
+	u32 irq_aff[AMP_AFF_MAX_CPU];
+	u32 irq_id[AMP_AFF_MAX_CPU];
+	u32 en[AMP_AFF_MAX_CPU];
+};
+
+struct amp_irq_cfg_s {
+	u32 prio;
+	u32 cpumask;
+	u32 aff;
+	int amp_flag;
+} irqs_cfg[1024];
+
+static struct amp_gic_ctrl_s {
+	struct {
+		u32 aff;
+		u32 cpumask;
+		u32 flag;
+	} aff_to_cpumask[AMP_AFF_MAX_CLUSTER][AMP_AFF_MAX_CPU];
+	struct amp_irq_cfg_s irqs_cfg[1024];
+	u32 validmask[1020 / 32 + 1];
+	struct amp_gpio_group_s gpio_grp[GPIO_BANK_NUM][GPIO_GROUP_PRIO_MAX];
+	u32 gpio_banks;
+} amp_ctrl;
 
 static int get_cpu_boot_info_idx(unsigned long cpu_id)
 {
@@ -77,29 +112,29 @@
 static void cpu_status_print(unsigned long cpu_id, struct arm_smccc_res *res)
 {
 	if (res->a0) {
-		pr_info("get cpu-0x%lx status(%lx) error!\n", cpu_id, res->a0);
+		pr_info("failed to get cpu[%lx] status, ret=%lx!\n", cpu_id, res->a0);
 		return;
 	}
 
 	if (res->a1 == AMP_CPU_STATUS_AMP_DIS)
-		pr_info("cpu-0x%lx amp is disable (%ld)\n", cpu_id, res->a1);
+		pr_info("cpu[%lx] amp is disabled (%ld)\n", cpu_id, res->a1);
 	else if (res->a1 == AMP_CPU_STATUS_EN)
-		pr_info("cpu-0x%lx amp is enable (%ld)\n", cpu_id, res->a1);
+		pr_info("cpu[%lx] amp is enabled (%ld)\n", cpu_id, res->a1);
 	else if (res->a1 == AMP_CPU_STATUS_ON)
-		pr_info("cpu-0x%lx amp: cpu is on (%ld)\n", cpu_id, res->a1);
+		pr_info("cpu[%lx] amp: cpu is on (%ld)\n", cpu_id, res->a1);
 	else if (res->a1 == AMP_CPU_STATUS_OFF)
-		pr_info("cpu-0x%lx amp: cpu is off(%ld)\n", cpu_id, res->a1);
+		pr_info("cpu[%lx] amp: cpu is off(%ld)\n", cpu_id, res->a1);
 	else
-		pr_info("cpu-0x%lx status(%ld) is error\n", cpu_id, res->a1);
+		pr_info("cpu[%lx] amp status(%ld) is error\n", cpu_id, res->a1);
 
 	if (res->a2 == RK_CPU_STATUS_OFF)
-		pr_info("cpu-0x%lx status(%ld) is off\n", cpu_id, res->a2);
+		pr_info("cpu[%lx] status(%ld) is off\n", cpu_id, res->a2);
 	else if (res->a2 == RK_CPU_STATUS_ON)
-		pr_info("cpu-0x%lx status(%ld) is on\n", cpu_id, res->a2);
+		pr_info("cpu[%lx] status(%ld) is on\n", cpu_id, res->a2);
 	else if (res->a2 == RK_CPU_STATUS_BUSY)
-		pr_info("cpu-0x%lx status(%ld) is busy\n", cpu_id, res->a2);
+		pr_info("cpu[%lx] status(%ld) is busy\n", cpu_id, res->a2);
 	else
-		pr_info("cpu-0x%lx status(%ld) is error\n", cpu_id, res->a2);
+		pr_info("cpu[%lx] status(%ld) is error\n", cpu_id, res->a2);
 }
 
 static ssize_t boot_cpu_store(struct device *dev,
@@ -107,9 +142,9 @@
 			      const char *buf,
 			      size_t count)
 {
-	char cmd[10];
-	unsigned long cpu_id;
 	struct arm_smccc_res res = {0};
+	unsigned long cpu_id;
+	char cmd[10];
 	int ret, idx;
 
 	ret = sscanf(buf, "%s", cmd);
@@ -120,12 +155,10 @@
 
 	if (!strncmp(cmd, "status", strlen("status"))) {
 		ret = sscanf(buf, "%s %lx", cmd, &cpu_id);
-
 		if (ret != 2)
 			return -EINVAL;
 
-		res = sip_smc_get_amp_info(RK_AMP_SUB_FUNC_GET_CPU_STATUS,
-					   cpu_id);
+		res = sip_smc_get_amp_info(RK_AMP_SUB_FUNC_GET_CPU_STATUS, cpu_id);
 		cpu_status_print(cpu_id, &res);
 	} else if (!strncmp(cmd, "off", strlen("off"))) {
 		ret = sscanf(buf, "%s %lx", cmd, &cpu_id);
@@ -133,17 +166,14 @@
 			return -EINVAL;
 
 		idx = get_cpu_boot_info_idx(cpu_id);
-
 		if (idx >= 0 && cpu_boot_info[idx].en) {
 			ret = sip_smc_amp_config(RK_AMP_SUB_FUNC_REQ_CPU_OFF,
 						 cpu_id, 0, 0);
 			if (ret)
-				pr_info("requesting a cpu off is error(%d)!\n",
-					ret);
+				dev_warn(dev, "failed to request cpu[%lx] off, ret=%d!\n", cpu_id, ret);
 		}
 	} else if (!strncmp(cmd, "on", strlen("on"))) {
 		ret = sscanf(buf, "%s %lx", cmd, &cpu_id);
-
 		if (ret != 2)
 			return -EINVAL;
 
@@ -154,11 +184,14 @@
 						 cpu_boot_info[idx].entry,
 						 0);
 			if (ret)
-				pr_info("booting up a cpu is error(%d)!\n",
-					ret);
+				dev_warn(dev, "Brought up cpu[%lx] failed, ret=%d\n", cpu_id, ret);
+			else
+				pr_info("Brought up cpu[%lx] ok.\n", cpu_id);
+		} else {
+			dev_warn(dev, "cpu[%lx] is unavailable\n", cpu_id);
 		}
 	} else {
-		pr_info("unsupported cmd(%s)\n", cmd);
+		dev_warn(dev, "unsupported cmd(%s)\n", cmd);
 	}
 
 	return count;
@@ -173,31 +206,34 @@
 				  struct device_node *cpu_node, int idx)
 {
 	u64 cpu_entry, cpu_id;
-	u32 cpu_mode;
+	u32 cpu_mode, boot_on;
 	int ret;
 
 	if (idx >= CONFIG_NR_CPUS)
 		return -1;
 
+	if (of_property_read_u64_array(cpu_node, "id", &cpu_id, 1)) {
+		dev_warn(dev, "failed to get 'id'\n");
+		return -1;
+	}
+
 	if (of_property_read_u64_array(cpu_node, "entry", &cpu_entry, 1)) {
-		dev_warn(dev, "can not get the entry\n");
+		dev_warn(dev, "failed to get cpu[%llx] 'entry'\n", cpu_id);
 		return -1;
 	}
 
 	if (!cpu_entry) {
-		dev_warn(dev, "cpu-entry is 0\n");
-		return -1;
-	}
-
-	if (of_property_read_u64_array(cpu_node, "id", &cpu_id, 1)) {
-		dev_warn(dev, "can not get the cpu id\n");
+		dev_warn(dev, "invalid cpu[%llx] 'entry': 0\n", cpu_id);
 		return -1;
 	}
 
 	if (of_property_read_u32_array(cpu_node, "mode", &cpu_mode, 1)) {
-		dev_warn(dev, "can not get the cpu mode\n");
+		dev_warn(dev, "failed to get cpu[%llx] 'mode'\n", cpu_id);
 		return -1;
 	}
+
+	if (of_property_read_u32_array(cpu_node, "boot-on", &boot_on, 1))
+		boot_on = 1; /* compatible old action */
 
 	cpu_boot_info[idx].entry = cpu_entry;
 	cpu_boot_info[idx].mode = cpu_mode;
@@ -205,14 +241,18 @@
 
 	ret = sip_smc_amp_config(RK_AMP_SUB_FUNC_CFG_MODE, cpu_id, cpu_mode, 0);
 	if (ret) {
-		dev_warn(dev, "setting cpu mode is error(%d)!\n", ret);
+		dev_warn(dev, "failed to set cpu mode, ret=%d\n", ret);
 		return ret;
 	}
 
-	ret = sip_smc_amp_config(RK_AMP_SUB_FUNC_CPU_ON, cpu_id, cpu_entry, 0);
-	if (ret) {
-		dev_warn(dev, "booting up a cpu is error(%d)!\n", ret);
-		return ret;
+	if (boot_on) {
+		ret = sip_smc_amp_config(RK_AMP_SUB_FUNC_CPU_ON, cpu_id, cpu_entry, 0);
+		if (ret) {
+			dev_warn(dev, "Brought up cpu[%llx] failed, ret=%d\n", cpu_id, ret);
+			return ret;
+		} else {
+			pr_info("Brought up cpu[%llx] ok.\n", cpu_id);
+		}
 	}
 
 	cpu_boot_info[idx].en = 1;
@@ -220,11 +260,262 @@
 	return 0;
 }
 
+int rockchip_amp_check_amp_irq(u32 irq)
+{
+	return amp_ctrl.irqs_cfg[irq].amp_flag;
+}
+
+u32 rockchip_amp_get_irq_prio(u32 irq)
+{
+	return amp_ctrl.irqs_cfg[irq].prio;
+}
+
+u32 rockchip_amp_get_irq_cpumask(u32 irq)
+{
+	return amp_ctrl.irqs_cfg[irq].cpumask;
+}
+
+static u32 amp_get_cpumask_bit(u32 aff)
+{
+	u32 aff_cluster, aff_cpu;
+
+	aff_cluster = MPIDR_AFFINITY_LEVEL(aff, 1);
+	aff_cpu = MPIDR_AFFINITY_LEVEL(aff, 0);
+
+	if (aff_cpu >= AMP_AFF_MAX_CPU || aff_cluster >= AMP_AFF_MAX_CLUSTER)
+		return 0;
+
+	AMP_GIC_DBG("%s: aff:%d-%d: %x\n", __func__, aff_cluster, aff_cpu,
+		    amp_ctrl.aff_to_cpumask[aff_cluster][aff_cpu].cpumask);
+
+	return amp_ctrl.aff_to_cpumask[aff_cluster][aff_cpu].cpumask;
+}
+
+static int gic_amp_get_gpio_prio_group_info(struct device_node *np,
+					    struct amp_gic_ctrl_s *amp_ctrl,
+					    int prio_id)
+{
+	u32 gpio_bank, count0, count1, prio, irq_id, irq_aff;
+	int i;
+	struct amp_gpio_group_s *gpio_grp;
+	struct amp_irq_cfg_s *irqs_cfg;
+
+	if (prio_id >= GPIO_GROUP_PRIO_MAX)
+		return -EINVAL;
+
+	if (of_property_read_u32_array(np, "gpio-bank", &gpio_bank, 1))
+		return -EINVAL;
+	if (gpio_bank >= amp_ctrl->gpio_banks)
+		return -EINVAL;
+
+	gpio_grp = &amp_ctrl->gpio_grp[gpio_bank][prio_id];
+
+	if (of_property_read_u32_array(np, "prio", &prio, 1))
+		return -EINVAL;
+
+	if (gpio_bank >= GPIO_BANK_NUM)
+		return -EINVAL;
+
+	AMP_GIC_DBG("%s: gpio-%d, group prio:%d-%x\n",
+		    __func__, gpio_bank, prio_id, prio);
+
+	count0 = of_property_count_u32_elems(np, "girq-id");
+	count1 = of_property_count_u32_elems(np, "girq-aff");
+
+	if (count0 != count1)
+		return -EINVAL;
+
+	gpio_grp->prio = prio;
+
+	for (i = 0; i < count0; i++) {
+		of_property_read_u32_index(np, "girq-id", i, &irq_id);
+		gpio_grp->irq_id[i] = irq_id;
+		of_property_read_u32_index(np, "girq-aff", i, &irq_aff);
+
+		gpio_grp->irq_aff[i] = irq_aff;
+
+		of_property_read_u32_index(np, "girq-en", i, &gpio_grp->en[i]);
+
+		irqs_cfg = &amp_ctrl->irqs_cfg[irq_id];
+
+		AMP_GIC_DBG(" %s: group cpu-%d, irq-%d: prio-%x, aff-%x en-%d\n",
+			    __func__, i, gpio_grp->irq_id[i], gpio_grp->prio,
+			    gpio_grp->irq_aff[i], gpio_grp->en[i]);
+
+		if (gpio_grp->en[i]) {
+			irqs_cfg->prio = gpio_grp->prio;
+			irqs_cfg->aff = irq_aff;
+			irqs_cfg->cpumask = amp_get_cpumask_bit(irq_aff);
+			irqs_cfg->amp_flag = 1;
+		}
+
+		AMP_GIC_DBG("  %s: irqs_cfg prio-%x aff-%x cpumaks-%x en-%d\n",
+			    __func__, irqs_cfg->prio, irqs_cfg->aff,
+			    irqs_cfg->cpumask, irqs_cfg->amp_flag);
+	}
+
+	return 0;
+}
+
+static int gic_amp_gpio_group_get_info(struct device_node *group_node,
+				       struct amp_gic_ctrl_s *amp_ctrl,
+				       int idx)
+{
+	int i = 0;
+	struct device_node *node;
+
+	if (group_node) {
+		for_each_available_child_of_node(group_node, node) {
+			if (i >= GPIO_GROUP_PRIO_MAX)
+				break;
+			if (!gic_amp_get_gpio_prio_group_info(node, amp_ctrl,
+							      i)) {
+				i++;
+			}
+		}
+	}
+	return 0;
+}
+
+static void gic_of_get_gpio_group(struct device_node *np,
+				  struct amp_gic_ctrl_s *amp_ctrl)
+{
+	struct device_node *gpio_group_node, *node;
+	int i = 0;
+
+	if (of_property_read_u32_array(np, "gpio-group-banks",
+				       &amp_ctrl->gpio_banks, 1))
+		return;
+
+	gpio_group_node = of_get_child_by_name(np, "gpio-group");
+	if (gpio_group_node) {
+		for_each_available_child_of_node(gpio_group_node, node) {
+			if (i >= amp_ctrl->gpio_banks)
+				break;
+			if (!gic_amp_gpio_group_get_info(node, amp_ctrl, i))
+				i++;
+		}
+	}
+
+	of_node_put(gpio_group_node);
+}
+
+static int amp_gic_get_cpumask(struct device_node *np, struct amp_gic_ctrl_s *amp_ctrl)
+{
+	const struct property *prop;
+	int count, i;
+	u32 cluster, aff_cpu, aff, cpumask;
+
+	prop = of_find_property(np, "amp-cpu-aff-maskbits", NULL);
+	if (!prop)
+		return -1;
+
+	if (!prop->value)
+		return -1;
+
+	count = of_property_count_u32_elems(np, "amp-cpu-aff-maskbits");
+	if (count % 2)
+		return -1;
+
+	for (i = 0; i < count / 2; i++) {
+		of_property_read_u32_index(np, "amp-cpu-aff-maskbits",
+					   2 * i, &aff);
+		cluster = MPIDR_AFFINITY_LEVEL(aff, 1);
+		aff_cpu = MPIDR_AFFINITY_LEVEL(aff, 0);
+		amp_ctrl->aff_to_cpumask[cluster][aff_cpu].aff = aff;
+
+		of_property_read_u32_index(np, "amp-cpu-aff-maskbits",
+					   2 * i + 1, &cpumask);
+
+		amp_ctrl->aff_to_cpumask[cluster][aff_cpu].cpumask = cpumask;
+
+		AMP_GIC_DBG("cpumask: %d-%d: aff-%d cpumask-%d\n",
+			    cluster, aff_cpu, aff, cpumask);
+
+		if (!cpumask)
+			return -1;
+	}
+
+	return 0;
+}
+
+static void amp_gic_get_irqs_config(struct device_node *np,
+				    struct amp_gic_ctrl_s *amp_ctrl)
+{
+	const struct property *prop;
+	int count, i;
+	u32 irq, prio, aff;
+
+	prop = of_find_property(np, "amp-irqs", NULL);
+	if (!prop)
+		return;
+
+	if (!prop->value)
+		return;
+
+	count = of_property_count_u32_elems(np, "amp-irqs");
+
+	if (count < 0 || count % 3)
+		return;
+
+	for (i = 0; i < count / 3; i++) {
+		of_property_read_u32_index(np, "amp-irqs", 3 * i, &irq);
+
+		if (irq > 1020)
+			break;
+
+		of_property_read_u32_index(np, "amp-irqs", 3 * i + 1, &prio);
+		of_property_read_u32_index(np, "amp-irqs", 3 * i + 2, &aff);
+
+		AMP_GIC_DBG("%s: irq-%d aff-%d prio-%x\n",
+			    __func__, irq, aff, prio);
+
+		amp_ctrl->irqs_cfg[irq].prio = prio;
+		amp_ctrl->irqs_cfg[irq].aff = aff;
+		amp_ctrl->irqs_cfg[irq].cpumask = amp_get_cpumask_bit(aff);
+
+		if (!amp_ctrl->irqs_cfg[irq].cpumask) {
+			AMP_GIC_DBG("%s: get cpumask error\n", __func__);
+			break;
+		}
+
+		if (!amp_ctrl->irqs_cfg[irq].aff &&
+		    !amp_ctrl->irqs_cfg[irq].prio)
+			break;
+
+		amp_ctrl->irqs_cfg[irq].amp_flag = 1;
+
+		AMP_GIC_DBG("%s: irq-%d aff-%d cpumask-%d pri-%x\n",
+			    __func__, irq, amp_ctrl->irqs_cfg[irq].aff,
+			    amp_ctrl->irqs_cfg[irq].cpumask,
+			    amp_ctrl->irqs_cfg[irq].prio);
+	}
+}
+
+void rockchip_amp_get_gic_info(void)
+{
+	struct device_node *np;
+
+	np = of_find_node_by_name(NULL, "rockchip-amp");
+	if (!np)
+		return;
+
+	if (amp_gic_get_cpumask(np, &amp_ctrl)) {
+		pr_err("%s: get amp gic cpu mask error\n", __func__);
+		goto exit;
+	}
+	gic_of_get_gpio_group(np, &amp_ctrl);
+	amp_gic_get_irqs_config(np, &amp_ctrl);
+
+exit:
+	of_node_put(np);
+}
+
 static int rockchip_amp_probe(struct platform_device *pdev)
 {
-	struct rkamp_device *rkamp_dev = NULL;
-	int ret, i, idx = 0;
 	struct device_node *cpus_node, *cpu_node;
+	struct rkamp_device *rkamp_dev;
+	int ret, i, idx = 0;
 
 	rkamp_dev = devm_kzalloc(&pdev->dev, sizeof(*rkamp_dev), GFP_KERNEL);
 	if (!rkamp_dev)
@@ -233,18 +524,20 @@
 	rkamp_dev->num_clks = devm_clk_bulk_get_all(&pdev->dev, &rkamp_dev->clks);
 	if (rkamp_dev->num_clks < 0)
 		return -ENODEV;
+
 	ret = clk_bulk_prepare_enable(rkamp_dev->num_clks, rkamp_dev->clks);
 	if (ret)
 		return dev_err_probe(&pdev->dev, ret, "failed to prepare enable clks: %d\n", ret);
 
 	pm_runtime_enable(&pdev->dev);
 
-	rkamp_dev->num_pds = of_count_phandle_with_args(pdev->dev.of_node, "power-domains",
-							"#power-domain-cells");
-
+	rkamp_dev->num_pds =
+		of_count_phandle_with_args(pdev->dev.of_node, "power-domains",
+					   "#power-domain-cells");
 	if (rkamp_dev->num_pds > 0) {
-		rkamp_dev->pd_dev = devm_kmalloc_array(&pdev->dev, rkamp_dev->num_pds,
-						       sizeof(*rkamp_dev->pd_dev), GFP_KERNEL);
+		rkamp_dev->pd_dev =
+			devm_kmalloc_array(&pdev->dev, rkamp_dev->num_pds,
+					   sizeof(*rkamp_dev->pd_dev), GFP_KERNEL);
 		if (!rkamp_dev->pd_dev)
 			return -ENOMEM;
 
@@ -265,14 +558,12 @@
 	}
 
 	cpus_node = of_get_child_by_name(pdev->dev.of_node, "amp-cpus");
-
 	if (cpus_node) {
 		for_each_available_child_of_node(cpus_node, cpu_node) {
-			if (!rockchip_amp_boot_cpus(&pdev->dev, cpu_node,
-						    idx)) {
+			if (!rockchip_amp_boot_cpus(&pdev->dev, cpu_node, idx))
 				idx++;
-			}
 		}
+		of_node_put(cpus_node);
 	}
 
 	rk_amp_kobj = kobject_create_and_add("rk_amp", NULL);
@@ -290,8 +581,8 @@
 
 static int rockchip_amp_remove(struct platform_device *pdev)
 {
-	int i;
 	struct rkamp_device *rkamp_dev = platform_get_drvdata(pdev);
+	int i;
 
 	clk_bulk_disable_unprepare(rkamp_dev->num_clks, rkamp_dev->clks);
 
diff --git a/kernel/drivers/soc/rockchip/rockchip_opp_select.c b/kernel/drivers/soc/rockchip/rockchip_opp_select.c
index 82559da..8caae5c 100644
--- a/kernel/drivers/soc/rockchip/rockchip_opp_select.c
+++ b/kernel/drivers/soc/rockchip/rockchip_opp_select.c
@@ -1165,12 +1165,13 @@
 }
 
 void rockchip_of_get_pvtm_sel(struct device *dev, struct device_node *np,
-			      char *reg_name, int process,
+			      char *reg_name, int bin, int process,
 			      int *volt_sel, int *scale_sel)
 {
 	struct property *prop = NULL;
 	char name[NAME_MAX];
 	int pvtm, ret;
+	u32 hw = 0;
 
 	if (of_property_read_bool(np, "rockchip,pvtm-pvtpll"))
 		pvtm = rockchip_get_pvtm_pvtpll(dev, np, reg_name);
@@ -1185,6 +1186,12 @@
 		snprintf(name, sizeof(name),
 			 "rockchip,p%d-pvtm-voltage-sel", process);
 		prop = of_find_property(np, name, NULL);
+	} else if (bin >= 0) {
+		of_property_read_u32(np, "rockchip,pvtm-hw", &hw);
+		if (hw && (hw & BIT(bin))) {
+			sprintf(name, "rockchip,pvtm-voltage-sel-hw");
+			prop = of_find_property(np, name, NULL);
+		}
 	}
 	if (!prop)
 		sprintf(name, "rockchip,pvtm-voltage-sel");
@@ -1195,6 +1202,7 @@
 next:
 	if (!scale_sel)
 		return;
+	prop = NULL;
 	if (process >= 0) {
 		snprintf(name, sizeof(name),
 			 "rockchip,p%d-pvtm-scaling-sel", process);
@@ -1294,6 +1302,46 @@
 }
 EXPORT_SYMBOL(rockchip_get_volt_rm_table);
 
+int rockchip_get_soc_info(struct device *dev, struct device_node *np, int *bin,
+			  int *process)
+{
+	u8 value = 0;
+	int ret = 0;
+
+	if (*bin >= 0 || *process >= 0)
+		return 0;
+
+	if (of_property_match_string(np, "nvmem-cell-names",
+				     "remark_spec_serial_number") >= 0)
+		rockchip_nvmem_cell_read_u8(np, "remark_spec_serial_number", &value);
+
+	if (!value && of_property_match_string(np, "nvmem-cell-names",
+					       "specification_serial_number") >= 0) {
+		ret = rockchip_nvmem_cell_read_u8(np,
+						  "specification_serial_number",
+						  &value);
+		if (ret) {
+			dev_err(dev,
+				"Failed to get specification_serial_number\n");
+			return ret;
+		}
+	}
+
+	/* M */
+	if (value == 0xd)
+		*bin = 1;
+	/* J */
+	else if (value == 0xa)
+		*bin = 2;
+
+	if (*bin < 0)
+		*bin = 0;
+	dev_info(dev, "bin=%d\n", *bin);
+
+	return 0;
+}
+EXPORT_SYMBOL(rockchip_get_soc_info);
+
 void rockchip_get_scale_volt_sel(struct device *dev, char *lkg_name,
 				 char *reg_name, int bin, int process,
 				 int *scale, int *volt_sel)
@@ -1311,7 +1359,7 @@
 
 	rockchip_of_get_lkg_sel(dev, np, lkg_name, process,
 				&lkg_volt_sel, &lkg_scale);
-	rockchip_of_get_pvtm_sel(dev, np, reg_name, process,
+	rockchip_of_get_pvtm_sel(dev, np, reg_name, bin, process,
 				 &pvtm_volt_sel, &pvtm_scale);
 	rockchip_of_get_bin_sel(dev, np, bin, &bin_scale);
 	rockchip_of_get_bin_volt_sel(dev, np, bin, &bin_volt_sel);
@@ -1348,6 +1396,42 @@
 	return dev_pm_opp_set_prop_name(dev, name);
 }
 EXPORT_SYMBOL(rockchip_set_opp_prop_name);
+
+struct opp_table *rockchip_set_opp_supported_hw(struct device *dev,
+						struct device_node *np,
+						int bin, int volt_sel)
+{
+	struct opp_table *opp_table;
+	u32 supported_hw[2];
+	u32 version = 0, speed = 0;
+
+	if (!of_property_read_bool(np, "rockchip,supported-hw"))
+		return NULL;
+
+	opp_table = dev_pm_opp_get_opp_table(dev);
+	if (!opp_table)
+		return NULL;
+	if (opp_table->supported_hw) {
+		dev_pm_opp_put_opp_table(opp_table);
+		return NULL;
+	}
+	dev_pm_opp_put_opp_table(opp_table);
+
+	if (bin >= 0)
+		version = bin;
+	if (volt_sel >= 0)
+		speed = volt_sel;
+
+	/* SoC Version */
+	supported_hw[0] = BIT(version);
+	/* Speed Grade */
+	supported_hw[1] = BIT(speed);
+
+	dev_info(dev, "soc version=%d, speed=%d\n", version, speed);
+
+	return dev_pm_opp_set_supported_hw(dev, supported_hw, 2);
+}
+EXPORT_SYMBOL(rockchip_set_opp_supported_hw);
 
 static int rockchip_adjust_opp_by_irdrop(struct device *dev,
 					 struct device_node *np,
@@ -1484,9 +1568,14 @@
 		if (opp->rate > opp_info.max_freq * 1000000)
 			continue;
 
-		opp->supplies->u_volt += opp_info.volt * 1000;
-		if (opp->supplies->u_volt > opp->supplies->u_volt_max)
-			opp->supplies->u_volt = opp->supplies->u_volt_max;
+		opp->supplies[0].u_volt += opp_info.volt * 1000;
+		if (opp->supplies[0].u_volt > opp->supplies[0].u_volt_max)
+			opp->supplies[0].u_volt = opp->supplies[0].u_volt_max;
+		if (opp_table->regulator_count > 1) {
+			opp->supplies[1].u_volt += opp_info.volt * 1000;
+			if (opp->supplies[1].u_volt > opp->supplies[1].u_volt_max)
+				opp->supplies[1].u_volt = opp->supplies[1].u_volt_max;
+		}
 	}
 	mutex_unlock(&opp_table->lock);
 
@@ -1810,11 +1899,13 @@
 		info->data->get_soc_info(dev, np, &bin, &process);
 
 next:
+	rockchip_get_soc_info(dev, np, &bin, &process);
 	rockchip_get_scale_volt_sel(dev, lkg_name, reg_name, bin, process,
 				    &scale, &volt_sel);
 	if (info && info->data && info->data->set_soc_info)
 		info->data->set_soc_info(dev, np, bin, process, volt_sel);
 	rockchip_set_opp_prop_name(dev, process, volt_sel);
+	rockchip_set_opp_supported_hw(dev, np, bin, volt_sel);
 	ret = dev_pm_opp_of_add_table(dev);
 	if (ret) {
 		dev_err(dev, "Invalid operating-points in device tree.\n");
diff --git a/kernel/drivers/soc/rockchip/rockchip_pm_config.c b/kernel/drivers/soc/rockchip/rockchip_pm_config.c
index 639a004..13427dd 100644
--- a/kernel/drivers/soc/rockchip/rockchip_pm_config.c
+++ b/kernel/drivers/soc/rockchip/rockchip_pm_config.c
@@ -27,6 +27,10 @@
 #define MAX_ON_OFF_REG_PROP_NAME_LEN	60
 #define MAX_CONFIG_PROP_NAME_LEN	60
 
+#define RK_ATAG_MCU_SLP_CORE		0x526b0001
+#define RK_ATAG_MCU_SLP_MAX		0x526b00ff
+#define RK_ATAG_NONE			0x00000000
+
 enum rk_pm_state {
 	RK_PM_MEM = 0,
 	RK_PM_MEM_LITE,
@@ -52,6 +56,31 @@
 	u32 wakeup_config;
 } sleep_config[RK_PM_STATE_MAX];
 
+/* rk_tag related defines */
+#define sleep_tag_next(t)	\
+	((struct rk_sleep_tag *)((__u32 *)(t) + (t)->hdr.size))
+
+struct rk_tag_header {
+	u32 size;
+	u32 tag;
+};
+
+struct rk_sleep_tag {
+	struct rk_tag_header hdr;
+	u32 params[];
+};
+
+struct rk_mcu_sleep_core_tag {
+	struct rk_tag_header hdr;
+	u32 total_size;
+	u32 reserve[13];
+};
+
+struct rk_mcu_sleep_tags {
+	struct rk_mcu_sleep_core_tag core;
+	struct rk_sleep_tag slp_tags;
+};
+
 static const struct of_device_id pm_match_table[] = {
 	{ .compatible = "rockchip,pm-px30",},
 	{ .compatible = "rockchip,pm-rk1808",},
@@ -70,9 +99,19 @@
 };
 
 #ifndef MODULE
+enum {
+	RK_PM_VIRT_PWROFF_EN = 0,
+	RK_PM_VIRT_PWROFF_IRQ_CFG = 1,
+	RK_PM_VIRT_PWROFF_MAX,
+};
+
+static u32 *virtual_pwroff_irqs;
+
 static void rockchip_pm_virt_pwroff_prepare(void)
 {
-	int error;
+	int error, i;
+
+	pm_wakeup_clear(0);
 
 	regulator_suspend_prepare(PM_SUSPEND_MEM);
 
@@ -82,8 +121,58 @@
 		return;
 	}
 
-	sip_smc_set_suspend_mode(VIRTUAL_POWEROFF, 0, 1);
+	sip_smc_set_suspend_mode(VIRTUAL_POWEROFF, RK_PM_VIRT_PWROFF_EN, 1);
+
+	if (virtual_pwroff_irqs) {
+		for (i = 0; virtual_pwroff_irqs[i]; i++) {
+			error = sip_smc_set_suspend_mode(VIRTUAL_POWEROFF,
+							 RK_PM_VIRT_PWROFF_IRQ_CFG,
+							 virtual_pwroff_irqs[i]);
+			if (error) {
+				pr_err("%s: config virtual_pwroff_irqs[%d] error, overflow or update trust!\n",
+				       __func__, i);
+				break;
+			}
+		}
+	}
+
 	sip_smc_virtual_poweroff();
+}
+
+static int parse_virtual_pwroff_config(struct device_node *node)
+{
+	int ret = 0, cnt;
+	u32 virtual_poweroff_en = 0;
+
+	if (!of_property_read_u32_array(node,
+					"rockchip,virtual-poweroff",
+					&virtual_poweroff_en, 1) &&
+	    virtual_poweroff_en)
+		pm_power_off_prepare = rockchip_pm_virt_pwroff_prepare;
+
+	if (!virtual_poweroff_en)
+		return 0;
+
+	cnt = of_property_count_u32_elems(node, "rockchip,virtual-poweroff-irqs");
+	if (cnt > 0) {
+		/* 0 as the last element of virtual_pwroff_irqs */
+		virtual_pwroff_irqs = kzalloc((cnt + 1) * sizeof(u32), GFP_KERNEL);
+		if (!virtual_pwroff_irqs) {
+			ret = -ENOMEM;
+			goto out;
+		}
+
+		ret = of_property_read_u32_array(node, "rockchip,virtual-poweroff-irqs",
+						 virtual_pwroff_irqs, cnt);
+		if (ret) {
+			pr_err("%s: get rockchip,virtual-poweroff-irqs error\n",
+			       __func__);
+			goto out;
+		}
+	}
+
+out:
+	return ret;
 }
 
 static int parse_sleep_config(struct device_node *node, enum rk_pm_state state)
@@ -166,6 +255,115 @@
 }
 #endif
 
+static int parse_mcu_sleep_config(struct device_node *node)
+{
+	int ret, cnt;
+	struct arm_smccc_res res;
+	struct device_node *mcu_sleep_node;
+	struct device_node *child;
+	struct rk_mcu_sleep_tags *config;
+	struct rk_sleep_tag *slp_tag;
+	char *end;
+
+	mcu_sleep_node = of_find_node_by_name(node, "rockchip-mcu-sleep-cfg");
+	if (IS_ERR_OR_NULL(mcu_sleep_node)) {
+		ret = -ENODEV;
+		goto out;
+	}
+
+	cnt = of_get_child_count(mcu_sleep_node);
+	if (!cnt) {
+		ret = -EINVAL;
+		goto free_mcu_mode;
+	}
+
+	/*
+	 * 4kb for sleep parameters
+	 */
+	res = sip_smc_request_share_mem(1, SHARE_PAGE_TYPE_SLEEP);
+	if (res.a0 != 0) {
+		pr_err("%s: no trust memory for mcu_sleep\n", __func__);
+		ret = -ENOMEM;
+		goto free_mcu_mode;
+	}
+
+	/* Initialize core tag */
+	memset((void *)res.a1, 0, sizeof(struct rk_mcu_sleep_tags));
+	config = (struct rk_mcu_sleep_tags *)res.a1;
+	config->core.hdr.tag = RK_ATAG_MCU_SLP_CORE;
+	config->core.hdr.size = sizeof(struct rk_mcu_sleep_core_tag) / sizeof(u32);
+	config->core.total_size = sizeof(struct rk_mcu_sleep_tags) -
+				  sizeof(struct rk_sleep_tag);
+
+	slp_tag = &config->slp_tags;
+
+	/* End point of sleep data  */
+	end = (char *)config + PAGE_SIZE - sizeof(struct rk_sleep_tag);
+
+	for_each_available_child_of_node(mcu_sleep_node, child) {
+		/* Is overflow? */
+		if ((char *)slp_tag->params >= end)
+			break;
+
+		ret = of_property_read_u32_array(child, "rockchip,tag",
+						 &slp_tag->hdr.tag, 1);
+		if (ret ||
+		    slp_tag->hdr.tag <= RK_ATAG_MCU_SLP_CORE ||
+		    slp_tag->hdr.tag >= RK_ATAG_MCU_SLP_MAX) {
+			pr_info("%s: no or invalid rockchip,tag in %s\n",
+				__func__, child->name);
+
+			continue;
+		}
+
+		cnt = of_property_count_u32_elems(child, "rockchip,params");
+		if (cnt > 0) {
+			/* Is overflow? */
+			if ((char *)(slp_tag->params + cnt) >= end) {
+				pr_warn("%s: no more space for rockchip,tag in %s\n",
+					__func__, child->name);
+				break;
+			}
+
+			ret = of_property_read_u32_array(child, "rockchip,params",
+							 slp_tag->params, cnt);
+			if (ret) {
+				pr_err("%s: rockchip,params error in %s\n",
+				       __func__, child->name);
+				break;
+			}
+
+			slp_tag->hdr.size =
+				cnt + sizeof(struct rk_tag_header) / sizeof(u32);
+		} else if (cnt == 0) {
+			slp_tag->hdr.size = 0;
+		} else {
+			continue;
+		}
+
+		config->core.total_size += slp_tag->hdr.size * sizeof(u32);
+
+		slp_tag = sleep_tag_next(slp_tag);
+	}
+
+	/* Add none tag.
+	 * Compiler will combine the follow code as "str xzr, [x28]", but
+	 * "slp->hdr" may not be 8-byte alignment. So we use memset_io instead:
+	 * slp_tag->hdr.size = 0;
+	 * slp_tag->hdr.tag = RK_ATAG_NONE;
+	 */
+	memset_io(&slp_tag->hdr, 0, sizeof(slp_tag->hdr));
+
+	config->core.total_size += sizeof(struct rk_sleep_tag);
+
+	ret = 0;
+
+free_mcu_mode:
+	of_node_put(mcu_sleep_node);
+out:
+	return ret;
+}
+
 static int pm_config_probe(struct platform_device *pdev)
 {
 	const struct of_device_id *match_id;
@@ -177,9 +375,7 @@
 	u32 apios_suspend = 0;
 	u32 io_ret_config = 0;
 	u32 sleep_pin_config[2] = {0};
-#ifndef MODULE
-	u32 virtual_poweroff_en = 0;
-#endif
+
 	enum of_gpio_flags flags;
 	int i = 0;
 	int length;
@@ -270,12 +466,10 @@
 				 ret);
 	}
 
+	parse_mcu_sleep_config(node);
+
 #ifndef MODULE
-	if (!of_property_read_u32_array(node,
-					"rockchip,virtual-poweroff",
-					&virtual_poweroff_en, 1) &&
-	    virtual_poweroff_en)
-		pm_power_off_prepare = rockchip_pm_virt_pwroff_prepare;
+	parse_virtual_pwroff_config(node);
 
 	for (i = RK_PM_MEM; i < RK_PM_STATE_MAX; i++) {
 		parse_sleep_config(node, i);
diff --git a/kernel/drivers/soc/rockchip/rockchip_system_monitor.c b/kernel/drivers/soc/rockchip/rockchip_system_monitor.c
index 0b1f48d..4082565 100644
--- a/kernel/drivers/soc/rockchip/rockchip_system_monitor.c
+++ b/kernel/drivers/soc/rockchip/rockchip_system_monitor.c
@@ -1029,6 +1029,8 @@
 
 	if (!info->opp_table)
 		return;
+	if (!system_monitor->tz)
+		return;
 
 	/*
 	 * set the init state to low temperature that the voltage will be enough
diff --git a/kernel/drivers/soc/rockchip/rockchip_thunderboot_service.c b/kernel/drivers/soc/rockchip/rockchip_thunderboot_service.c
index 805042d..d1420d1 100644
--- a/kernel/drivers/soc/rockchip/rockchip_thunderboot_service.c
+++ b/kernel/drivers/soc/rockchip/rockchip_thunderboot_service.c
@@ -76,7 +76,7 @@
 
 static void do_mcu_done(struct rk_tb_serv *serv)
 {
-	struct rk_tb_client *client, *client_s;
+	struct rk_tb_client *client;
 	struct rockchip_mbox_msg msg;
 
 	rockchip_mbox_read_msg(serv->mbox_rx_chan, &msg);
@@ -97,12 +97,13 @@
 			return;
 		}
 
-		list_for_each_entry_safe(client, client_s, &clients_list, node) {
+		while (!list_empty(&clients_list)) {
+			client = list_first_entry(&clients_list, struct rk_tb_client, node);
+			list_del(&client->node);
 			spin_unlock(&lock);
 			if (client->cb)
 				client->cb(client->data);
 			spin_lock(&lock);
-			list_del(&client->node);
 		}
 		atomic_set(&mcu_done, 1);
 		spin_unlock(&lock);
diff --git a/kernel/drivers/soc/rockchip/sdmmc_vendor_storage.c b/kernel/drivers/soc/rockchip/sdmmc_vendor_storage.c
index d8681b9..ea1d941 100644
--- a/kernel/drivers/soc/rockchip/sdmmc_vendor_storage.c
+++ b/kernel/drivers/soc/rockchip/sdmmc_vendor_storage.c
@@ -49,12 +49,7 @@
 
 static int emmc_vendor_ops(u8 *buffer, u32 addr, u32 n_sec, int write)
 {
-	u32 i, ret = 0;
-
-	for (i = 0; i < n_sec; i++)
-		ret = rk_emmc_transfer(buffer + i * 512, addr + i, 512, write);
-
-	return ret;
+	return rk_emmc_transfer(buffer, addr, n_sec << 9, write);
 }
 
 static int emmc_vendor_storage_init(void)
@@ -210,34 +205,20 @@
 #ifdef CONFIG_ROCKCHIP_VENDOR_STORAGE_UPDATE_LOADER
 static int id_blk_read_data(u32 index, u32 n_sec, u8 *buf)
 {
-	u32 i;
-	u32 ret = 0;
-
 	if (index + n_sec >= 1024 * 5)
 		return 0;
 	index = index + EMMC_IDB_PART_OFFSET;
-	for (i = 0; i < n_sec; i++) {
-		ret = rk_emmc_transfer(buf + i * 512, index + i, 512, 0);
-		if (ret)
-			return ret;
-	}
-	return ret;
+
+	return rk_emmc_transfer(buf, index, n_sec << 9, 0);
 }
 
 static int id_blk_write_data(u32 index, u32 n_sec, u8 *buf)
 {
-	u32 i;
-	u32 ret = 0;
-
 	if (index + n_sec >= 1024 * 5)
 		return 0;
 	index = index + EMMC_IDB_PART_OFFSET;
-	for (i = 0; i < n_sec; i++) {
-		ret = rk_emmc_transfer(buf + i * 512, index + i, 512, 1);
-		if (ret)
-			return ret;
-	}
-	return ret;
+
+	return rk_emmc_transfer(buf, index, n_sec << 9, 1);
 }
 
 static int emmc_write_idblock(u32 size, u8 *buf, u32 *id_blk_tbl)
diff --git a/kernel/drivers/spi/Kconfig b/kernel/drivers/spi/Kconfig
index 0bdd5fe..7a2cb27 100644
--- a/kernel/drivers/spi/Kconfig
+++ b/kernel/drivers/spi/Kconfig
@@ -1067,6 +1067,12 @@
 	  SPI slave handler to allow remote control of system reboot, power
 	  off, halt, and suspend.
 
+config SPI_SLAVE_ROCKCHIP_OBJ
+	tristate "Rockchip SPI slave inter transmission protocol demo"
+	help
+	  SPI slave with a rockchip protocol specification for SPI slave
+	  transmission, work with the corresponding master driver spidev_rkmst.
+
 endif # SPI_SLAVE
 
 config SPI_DYNAMIC
diff --git a/kernel/drivers/spi/Makefile b/kernel/drivers/spi/Makefile
index 6816188..d3f1082 100644
--- a/kernel/drivers/spi/Makefile
+++ b/kernel/drivers/spi/Makefile
@@ -136,3 +136,5 @@
 # SPI slave protocol handlers
 obj-$(CONFIG_SPI_SLAVE_TIME)		+= spi-slave-time.o
 obj-$(CONFIG_SPI_SLAVE_SYSTEM_CONTROL)	+= spi-slave-system-control.o
+obj-$(CONFIG_SPI_SLAVE_ROCKCHIP_OBJ)	+= spidev-rkslv.o
+obj-$(CONFIG_SPI_SLAVE_ROCKCHIP_OBJ)    += spidev-rkmst.o
diff --git a/kernel/drivers/spi/spi-rockchip.c b/kernel/drivers/spi/spi-rockchip.c
index d6cc6de..b627663 100644
--- a/kernel/drivers/spi/spi-rockchip.c
+++ b/kernel/drivers/spi/spi-rockchip.c
@@ -221,6 +221,7 @@
 	bool slave_aborted;
 	bool cs_inactive; /* spi slave tansmition stop when cs inactive */
 	bool cs_high_supported; /* native CS supports active-high polarity */
+	struct gpio_desc *ready; /* spi slave transmission ready */
 
 	struct spi_transfer *xfer; /* Store xfer temporarily */
 	phys_addr_t base_addr_phy;
@@ -859,8 +860,17 @@
 		ret = rockchip_spi_prepare_irq(rs, ctlr, xfer);
 	}
 
+	if (rs->ready) {
+		gpiod_set_value(rs->ready, 0);
+		udelay(1);
+		gpiod_set_value(rs->ready, 1);
+	}
+
 	if (ret > 0)
 		ret = rockchip_spi_transfer_wait(ctlr, xfer);
+
+	if (rs->ready)
+		gpiod_set_value(rs->ready, 0);
 
 	return ret;
 }
@@ -969,6 +979,7 @@
 	bool slave_mode;
 	struct pinctrl *pinctrl = NULL;
 	const struct rockchip_spi_quirks *quirks_cfg;
+	u32 val;
 
 	slave_mode = of_property_read_bool(np, "spi-slave");
 
@@ -982,6 +993,7 @@
 	if (!ctlr)
 		return -ENOMEM;
 
+	ctlr->rt = device_property_read_bool(&pdev->dev, "rockchip,rt");
 	platform_set_drvdata(pdev, ctlr);
 
 	rs = spi_controller_get_devdata(ctlr);
@@ -1095,6 +1107,13 @@
 	if (quirks_cfg)
 		rs->max_baud_div_in_cpha = quirks_cfg->max_baud_div_in_cpha;
 
+	if (!device_property_read_u32(&pdev->dev, "rockchip,autosuspend-delay-ms", &val)) {
+		if (val > 0) {
+			pm_runtime_set_autosuspend_delay(&pdev->dev, val);
+			pm_runtime_use_autosuspend(&pdev->dev);
+		}
+	}
+
 	pm_runtime_set_active(&pdev->dev);
 	pm_runtime_enable(&pdev->dev);
 
@@ -1175,6 +1194,8 @@
 		rs->cs_inactive = false;
 		break;
 	}
+	if (device_property_read_bool(&pdev->dev, "rockchip,cs-inactive-disable"))
+		rs->cs_inactive = false;
 
 	pinctrl = devm_pinctrl_get(&pdev->dev);
 	if (!IS_ERR(pinctrl)) {
@@ -1183,6 +1204,13 @@
 			dev_warn(&pdev->dev, "no high_speed pinctrl state\n");
 			rs->high_speed_state = NULL;
 		}
+	}
+
+	rs->ready = devm_gpiod_get_optional(&pdev->dev, "ready", GPIOD_OUT_HIGH);
+	if (IS_ERR(rs->ready)) {
+		ret = dev_err_probe(&pdev->dev, PTR_ERR(rs->ready),
+				    "invalid ready-gpios property in node\n");
+		goto err_free_dma_rx;
 	}
 
 	ret = devm_spi_register_controller(&pdev->dev, ctlr);
@@ -1207,7 +1235,8 @@
 			dev_info(&pdev->dev, "register misc device %s\n", misc_name);
 	}
 
-	dev_info(rs->dev, "probed, poll=%d, rsd=%d\n", rs->poll, rs->rsd);
+	dev_info(rs->dev, "probed, poll=%d, rsd=%d, cs-inactive=%d, ready=%d\n",
+		 rs->poll, rs->rsd, rs->cs_inactive, rs->ready ? 1 : 0);
 
 	return 0;
 
diff --git a/kernel/drivers/spi/spidev-rkmst.c b/kernel/drivers/spi/spidev-rkmst.c
new file mode 100644
index 0000000..a969adf
--- /dev/null
+++ b/kernel/drivers/spi/spidev-rkmst.c
@@ -0,0 +1,636 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Copyright (c) 2023 Rockchip Electronics Co., Ltd.
+ */
+
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/iopoll.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <linux/random.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+
+#include <linux/platform_data/spi-rockchip.h>
+
+#define SPI_OBJ_MAX_XFER_SIZE	0x1040
+#define SPI_OBJ_APP_RAM_SIZE	0x10000
+
+#define SPI_OBJ_CTRL_MSG_SIZE	0x8
+#define SPI_OBJ_CTRL_CMD_INIT	0x99
+#define SPI_OBJ_CTRL_CMD_READ	0x3A
+#define SPI_OBJ_CTRL_CMD_WRITE	0x4B
+#define SPI_OBJ_CTRL_CMD_DUPLEX	0x5C
+
+#define SPI_OBJ_DEFAULT_TIMEOUT_US	100000
+
+struct spi_obj_ctrl {
+	u16 cmd;
+	u16 addr;
+	u32 data;
+};
+
+struct spidev_rkmst_data {
+	struct device	*dev;
+	struct spi_device *spi;
+	char *ctrlbuf;
+	char *rxbuf;
+	char *txbuf;
+	struct gpio_desc *ready;
+	int ready_irqnum;
+	bool ready_status;
+	bool verbose;
+	struct miscdevice misc_dev;
+};
+
+static u32 bit_per_word = 8;
+
+static inline void spidev_mst_slave_ready_status(struct spidev_rkmst_data *spidev, bool status)
+{
+	spidev->ready_status = status;
+}
+
+static irqreturn_t spidev_mst_slave_ready_interrupt(int irq, void *arg)
+{
+	struct spidev_rkmst_data *spidev = (struct spidev_rkmst_data *)arg;
+
+	spidev_mst_slave_ready_status(spidev, true);
+
+	return IRQ_HANDLED;
+}
+
+static bool spidev_mst_check_slave_ready(struct spidev_rkmst_data *spidev)
+{
+	return spidev->ready_status;
+}
+
+static int spidev_mst_wait_for_slave_ready(struct spidev_rkmst_data *spidev,
+					   u32 timeout_us)
+{
+	bool ready;
+	int ret;
+
+	ret = read_poll_timeout(spidev_mst_check_slave_ready, ready,
+				ready, 100, timeout_us + 100, false, spidev);
+	if (ret) {
+		dev_err(&spidev->spi->dev, "timeout and reset slave\n");
+
+		return -ETIMEDOUT;
+	}
+
+	return true;
+}
+
+static int spidev_mst_write(struct spidev_rkmst_data *spidev, const void *txbuf, size_t n)
+{
+	struct spi_device *spi = spidev->spi;
+	struct spi_transfer t = {
+			.tx_buf = txbuf,
+			.len = n,
+			.bits_per_word = bit_per_word,
+		};
+	struct spi_message m;
+	int ret;
+
+	spi_message_init(&m);
+	spi_message_add_tail(&t, &m);
+
+	ret = spidev_mst_wait_for_slave_ready(spidev, SPI_OBJ_DEFAULT_TIMEOUT_US);
+	if (ret < 0)
+		return ret;
+
+	spidev_mst_slave_ready_status(spidev, false);
+	ret = spi_sync(spi, &m);
+
+	return ret;
+}
+
+static int spidev_mst_write_bypass(struct spidev_rkmst_data *spidev, const void *txbuf, size_t n)
+{
+	struct spi_device *spi = spidev->spi;
+	struct spi_transfer t = {
+			.tx_buf = txbuf,
+			.len = n,
+			.bits_per_word = bit_per_word,
+		};
+	struct spi_message m;
+	int ret;
+
+	spi_message_init(&m);
+	spi_message_add_tail(&t, &m);
+
+	ret = spi_sync(spi, &m);
+
+	return ret;
+}
+
+static int spidev_mst_read(struct spidev_rkmst_data *spidev, void *rxbuf, size_t n)
+{
+	struct spi_device *spi = spidev->spi;
+	struct spi_transfer t = {
+			.rx_buf = rxbuf,
+			.len = n,
+			.bits_per_word = bit_per_word,
+		};
+	struct spi_message m;
+	int ret;
+
+	spi_message_init(&m);
+	spi_message_add_tail(&t, &m);
+
+	ret = spidev_mst_wait_for_slave_ready(spidev, SPI_OBJ_MAX_XFER_SIZE);
+	if (ret < 0)
+		return ret;
+
+	spidev_mst_slave_ready_status(spidev, false);
+	ret = spi_sync(spi, &m);
+
+	return ret;
+}
+
+static int spidev_slv_write_and_read(struct spidev_rkmst_data *spidev,
+				     const void *tx_buf, void *rx_buf,
+				     size_t len)
+{
+	struct spi_device *spi = spidev->spi;
+	struct spi_transfer t = {
+			.tx_buf = tx_buf,
+			.rx_buf = rx_buf,
+			.len = len,
+		};
+	struct spi_message m;
+	int ret;
+
+	spi_message_init(&m);
+	spi_message_add_tail(&t, &m);
+
+	ret = spidev_mst_wait_for_slave_ready(spidev, SPI_OBJ_MAX_XFER_SIZE);
+	if (ret < 0)
+		return ret;
+
+	spidev_mst_slave_ready_status(spidev, false);
+	ret = spi_sync(spi, &m);
+
+	return ret;
+}
+
+static void spidev_rkmst_reset_slave(struct spidev_rkmst_data *spidev)
+{
+	struct spi_obj_ctrl *ctrl = (struct spi_obj_ctrl *)spidev->ctrlbuf;
+
+	ctrl->cmd = SPI_OBJ_CTRL_CMD_INIT;
+
+	spidev_mst_write_bypass(spidev, ctrl, SPI_OBJ_MAX_XFER_SIZE);
+	msleep(100);
+	spidev_mst_write_bypass(spidev, ctrl, SPI_OBJ_MAX_XFER_SIZE);
+}
+
+
+static int spidev_rkmst_ctrl(struct spidev_rkmst_data *spidev, u32 cmd, u16 addr, u32 data)
+{
+	struct spi_obj_ctrl *ctrl = (struct spi_obj_ctrl *)spidev->ctrlbuf;
+	struct spi_device *spi = spidev->spi;
+	int ret;
+
+	if (spidev->verbose)
+		dev_err(&spi->dev, "ctrl cmd=%x addr=0x%x data=0x%x\n", cmd, addr, data);
+
+	/* ctrl_xfer */
+	ctrl->cmd = cmd;
+	ctrl->addr = addr;
+	ctrl->data = data;
+	ret = spidev_mst_write(spidev, ctrl, SPI_OBJ_CTRL_MSG_SIZE);
+	if (ret) {
+		dev_err(&spi->dev, "ctrl cmd=%x addr=0x%x data=0x%x, ret=%d\n", cmd, addr, data, ret);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int spidev_rkmst_xfer(struct spidev_rkmst_data *spidev, void *tx,
+			     void *rx, u16 addr, u32 len)
+{
+	struct spi_device *spi = spidev->spi;
+	int ret;
+	u32 cmd;
+
+	if (tx && rx)
+		cmd = SPI_OBJ_CTRL_CMD_DUPLEX;
+	else if (rx)
+		cmd = SPI_OBJ_CTRL_CMD_READ;
+	else if (tx)
+		cmd = SPI_OBJ_CTRL_CMD_WRITE;
+	else
+		return -EINVAL;
+
+	/* ctrl_xfer */
+	ret = spidev_rkmst_ctrl(spidev, cmd, addr, len);
+	if (ret) {
+		spidev_rkmst_reset_slave(spidev);
+		return -EIO;
+	}
+
+	if (spidev->verbose)
+		dev_err(&spi->dev, "xfer len=0x%x\n", len);
+	/* data_xfer */
+	switch (cmd) {
+	case SPI_OBJ_CTRL_CMD_READ:
+		ret = spidev_mst_read(spidev, rx, len);
+		if (ret)
+			goto err_out;
+		break;
+	case SPI_OBJ_CTRL_CMD_WRITE:
+		ret = spidev_mst_write(spidev, tx, len);
+		if (ret)
+			goto err_out;
+		break;
+	case SPI_OBJ_CTRL_CMD_DUPLEX:
+		ret = spidev_slv_write_and_read(spidev, tx, rx, len);
+		if (ret)
+			goto err_out;
+		break;
+	default:
+		dev_err(&spi->dev, "%s unknown\n", __func__);
+	}
+
+	return 0;
+err_out:
+	dev_err(&spi->dev, "xfer cmd=%x addr=0x%x len=0x%x, ret=%d\n",
+		cmd, addr, len, ret);
+
+	return ret;
+}
+
+static ssize_t spidev_rkmst_misc_write(struct file *filp, const char __user *buf,
+				       size_t n, loff_t *offset)
+{
+	struct spidev_rkmst_data *spidev;
+	struct spi_device *spi;
+	int argc = 0, i;
+	char tmp[64];
+	char *argv[16];
+	char *cmd, *data;
+
+	if (n >= 64)
+		return -EINVAL;
+
+	spidev = filp->private_data;
+
+	if (!spidev)
+		return -ESHUTDOWN;
+
+	spi = spidev->spi;
+	memset(tmp, 0, sizeof(tmp));
+	if (copy_from_user(tmp, buf, n))
+		return -EFAULT;
+	cmd = tmp;
+	data = tmp;
+
+	while (data < (tmp + n)) {
+		data = strstr(data, " ");
+		if (!data)
+			break;
+		*data = 0;
+		argv[argc] = ++data;
+		argc++;
+		if (argc >= 16)
+			break;
+	}
+
+	tmp[n - 1] = 0;
+
+	if (!strcmp(cmd, "verbose")) {
+		int val;
+
+		if (argc < 1)
+			return -EINVAL;
+
+		if (kstrtoint(argv[0], 0, &val))
+			return -EINVAL;
+
+		if (val == 1)
+			spidev->verbose = true;
+		else
+			spidev->verbose = false;
+	} else if (!strcmp(cmd, "init")) {
+		spidev_rkmst_ctrl(spidev, SPI_OBJ_CTRL_CMD_INIT, 0x55AA, 0x1234567);
+	} else if (!strcmp(cmd, "read")) {
+		int addr, len;
+
+		if (argc < 2)
+			return -EINVAL;
+
+		if (kstrtoint(argv[0], 0, &addr))
+			return -EINVAL;
+		if (kstrtoint(argv[1], 0, &len))
+			return -EINVAL;
+
+		if (!len) {
+			dev_err(&spi->dev, "param invalid,%s %s\n", argv[0], argv[1]);
+			return -EINVAL;
+		}
+
+		if (addr + len > SPI_OBJ_APP_RAM_SIZE) {
+			dev_err(&spi->dev, "rxbuf print out of size\n");
+			return -EINVAL;
+		}
+
+		spidev_rkmst_xfer(spidev, NULL, spidev->rxbuf, addr, len);
+
+		print_hex_dump(KERN_ERR, "m-r: ",
+			       DUMP_PREFIX_OFFSET,
+			       16,
+			       1,
+			       spidev->rxbuf,
+			       len,
+			       1);
+	} else if (!strcmp(cmd, "write")) {
+		int addr, len;
+
+		if (argc < 2)
+			return -EINVAL;
+
+		if (kstrtoint(argv[0], 0, &addr))
+			return -EINVAL;
+		if (kstrtoint(argv[1], 0, &len))
+			return -EINVAL;
+
+		if (!len) {
+			dev_err(&spi->dev, "param invalid,%s %s\n", argv[0], argv[1]);
+			return -EINVAL;
+		}
+
+		if (addr + len > SPI_OBJ_APP_RAM_SIZE) {
+			dev_err(&spi->dev, "rxbuf print out of size\n");
+			return -EINVAL;
+		}
+
+		for (i = 0; i < len; i++)
+			spidev->txbuf[i] = i & 0xFF;
+		((u32 *)spidev->txbuf)[0] = addr;
+
+		spidev_rkmst_xfer(spidev, spidev->txbuf, NULL, addr, len);
+	} else if (!strcmp(cmd, "duplex")) {
+		int addr, len;
+
+		if (argc < 2)
+			return -EINVAL;
+
+		if (kstrtoint(argv[0], 0, &addr))
+			return -EINVAL;
+		if (kstrtoint(argv[1], 0, &len))
+			return -EINVAL;
+
+		if (!len) {
+			dev_err(&spi->dev, "param invalid,%s %s\n", argv[0], argv[1]);
+			return -EINVAL;
+		}
+
+		if (addr + len > SPI_OBJ_APP_RAM_SIZE) {
+			dev_err(&spi->dev, "rxbuf print out of size\n");
+			return -EINVAL;
+		}
+
+		for (i = 0; i < len; i++)
+			spidev->txbuf[i] = i & 0xFF;
+		((u32 *)spidev->txbuf)[0] = addr;
+
+		spidev_rkmst_xfer(spidev, spidev->txbuf, spidev->rxbuf, addr, len);
+
+		print_hex_dump(KERN_ERR, "m-d: ",
+			       DUMP_PREFIX_OFFSET,
+			       16,
+			       1,
+			       spidev->rxbuf,
+			       len,
+			       1);
+	} else if (!strcmp(cmd, "autotest")) {
+		int addr = 0, len, loops, i;
+		unsigned long long bytes = 0;
+		unsigned long us = 0;
+		ktime_t start_time;
+		ktime_t end_time;
+		ktime_t cost_time;
+		char *tempbuf;
+
+		if (argc < 2)
+			return -EINVAL;
+
+		if (kstrtoint(argv[0], 0, &len))
+			return -EINVAL;
+
+		if (kstrtoint(argv[1], 0, &loops))
+			return -EINVAL;
+
+		if (!len) {
+			dev_err(&spi->dev, "param invalid,%s %s\n", argv[0], argv[1]);
+			return -EINVAL;
+		}
+
+		if (len > SPI_OBJ_APP_RAM_SIZE) {
+			dev_err(&spi->dev, "rxbuf print out of size\n");
+			return -EINVAL;
+		}
+
+		tempbuf = kzalloc(len, GFP_KERNEL);
+		if (!tempbuf)
+			return -ENOMEM;
+
+		prandom_bytes(tempbuf, len);
+		spidev_rkmst_xfer(spidev, tempbuf, NULL, addr, len);
+		start_time = ktime_get();
+		for (i = 0; i < loops; i++) {
+			prandom_bytes(spidev->txbuf, len);
+			spidev_rkmst_xfer(spidev, spidev->txbuf, spidev->rxbuf, addr, len);
+			if (memcmp(spidev->rxbuf, tempbuf, len)) {
+				dev_err(&spi->dev, "dulplex autotest failed, loops=%d\n", i);
+				print_hex_dump(KERN_ERR, "m-d-t: ",
+					       DUMP_PREFIX_OFFSET,
+					       16,
+					       1,
+					       spidev->txbuf,
+					       len,
+					       1);
+				print_hex_dump(KERN_ERR, "m-d-r: ",
+					       DUMP_PREFIX_OFFSET,
+					       16,
+					       1,
+					       spidev->rxbuf,
+					       len,
+					       1);
+				print_hex_dump(KERN_ERR, "m-d-c: ",
+					       DUMP_PREFIX_OFFSET,
+					       16,
+					       1,
+					       tempbuf,
+					       len,
+					       1);
+				break;
+			}
+			memcpy(tempbuf, spidev->txbuf, len);
+		}
+		end_time = ktime_get();
+		cost_time = ktime_sub(end_time, start_time);
+		us = ktime_to_us(cost_time);
+
+		bytes = (u64)len * (u64)loops * 1000;
+		do_div(bytes, us);
+		if (i >= loops)
+			dev_err(&spi->dev, "dulplex test pass, cost=%ldus, speed=%lldKB/S, %ldus/loops\n",
+				us, bytes, us / loops);
+
+		start_time = ktime_get();
+		for (i = 0; i < loops; i++) {
+			prandom_bytes(spidev->txbuf, len);
+			spidev_rkmst_xfer(spidev, spidev->txbuf, NULL, addr, len);
+			spidev_rkmst_xfer(spidev, NULL, spidev->rxbuf, addr, len);
+			if (memcmp(spidev->rxbuf, spidev->txbuf, len)) {
+				dev_err(&spi->dev, "read/write autotest failed, loops=%d\n", i);
+				print_hex_dump(KERN_ERR, "m-d-t: ",
+					       DUMP_PREFIX_OFFSET,
+					       16,
+					       1,
+					       spidev->txbuf,
+					       len,
+					       1);
+				print_hex_dump(KERN_ERR, "m-d-r: ",
+					       DUMP_PREFIX_OFFSET,
+					       16,
+					       1,
+					       spidev->rxbuf,
+					       len,
+					       1);
+				break;
+			}
+		}
+		end_time = ktime_get();
+		cost_time = ktime_sub(end_time, start_time);
+		us = ktime_to_us(cost_time);
+
+		bytes = (u64)len * (u64)loops * 2 * 1000; /* multi 2 for both write and read */
+		do_div(bytes, us);
+		if (i >= loops)
+			dev_err(&spi->dev, "read/write test pass, cost=%ldus, speed=%lldKB/S, %ldus/loops\n",
+				us, bytes, us / loops);
+		kfree(tempbuf);
+	} else {
+		dev_err(&spi->dev, "unknown command\n");
+	}
+
+	return n;
+}
+
+static int spidev_rkmst_misc_open(struct inode *inode, struct file *filp)
+{
+	struct miscdevice *miscdev = filp->private_data;
+	struct spidev_rkmst_data *spidev;
+
+	spidev = container_of(miscdev, struct spidev_rkmst_data, misc_dev);
+	filp->private_data = spidev;
+
+	return 0;
+}
+
+static const struct file_operations spidev_rkmst_misc_fops = {
+	.write = spidev_rkmst_misc_write,
+	.open = spidev_rkmst_misc_open,
+};
+
+static int spidev_rkmst_probe(struct spi_device *spi)
+{
+	struct spidev_rkmst_data *spidev = NULL;
+	int ret;
+
+	if (!spi)
+		return -ENOMEM;
+
+	spidev = devm_kzalloc(&spi->dev, sizeof(struct spidev_rkmst_data), GFP_KERNEL);
+	if (!spidev)
+		return -ENOMEM;
+
+	spidev->ctrlbuf = devm_kzalloc(&spi->dev, SPI_OBJ_MAX_XFER_SIZE, GFP_KERNEL);
+	if (!spidev->ctrlbuf)
+		return -ENOMEM;
+
+	spidev->rxbuf = devm_kzalloc(&spi->dev, SPI_OBJ_APP_RAM_SIZE, GFP_KERNEL | GFP_DMA);
+	if (!spidev->rxbuf)
+		return -ENOMEM;
+
+	spidev->txbuf = devm_kzalloc(&spi->dev, SPI_OBJ_MAX_XFER_SIZE, GFP_KERNEL);
+	if (!spidev->txbuf)
+		return -ENOMEM;
+
+	spidev->spi = spi;
+	spidev->dev = &spi->dev;
+
+	spidev_mst_slave_ready_status(spidev, false);
+	spidev->ready = devm_gpiod_get_optional(&spi->dev, "ready", GPIOD_IN);
+	if (IS_ERR(spidev->ready))
+		return dev_err_probe(&spi->dev, PTR_ERR(spidev->ready),
+				     "invalid ready-gpios property in node\n");
+
+	spidev->ready_irqnum = gpiod_to_irq(spidev->ready);
+	ret = devm_request_irq(&spi->dev, spidev->ready_irqnum, spidev_mst_slave_ready_interrupt,
+		IRQF_TRIGGER_FALLING, "spidev-mst-ready-in", spidev);
+	if (ret < 0) {
+		dev_err(&spi->dev, "request ready irq failed\n");
+		return ret;
+	}
+	dev_set_drvdata(&spi->dev, spidev);
+
+	dev_err(&spi->dev, "mode=%d, max_speed_hz=%d\n", spi->mode, spi->max_speed_hz);
+
+	spidev->misc_dev.minor = MISC_DYNAMIC_MINOR;
+	spidev->misc_dev.name = "spidev_rkmst_misc";
+	spidev->misc_dev.fops = &spidev_rkmst_misc_fops;
+	spidev->misc_dev.parent = &spi->dev;
+	ret = misc_register(&spidev->misc_dev);
+	if (ret) {
+		dev_err(&spi->dev, "fail to register misc device\n");
+		return ret;
+	}
+
+	spidev_rkmst_reset_slave(spidev);
+
+	return 0;
+}
+
+static int spidev_rkmst_remove(struct spi_device *spi)
+{
+	struct spidev_rkmst_data *spidev = dev_get_drvdata(&spi->dev);
+
+	misc_deregister(&spidev->misc_dev);
+
+	return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id spidev_rkmst_dt_match[] = {
+	{ .compatible = "rockchip,spi-obj-master", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, spidev_rkmst_dt_match);
+
+#endif /* CONFIG_OF */
+
+static struct spi_driver spidev_rkmst_driver = {
+	.driver = {
+		.name	= "spidev_rkmst",
+		.owner = THIS_MODULE,
+		.of_match_table = of_match_ptr(spidev_rkmst_dt_match),
+	},
+	.probe		= spidev_rkmst_probe,
+	.remove		= spidev_rkmst_remove,
+};
+module_spi_driver(spidev_rkmst_driver);
+
+MODULE_AUTHOR("Jon Lin <jon.lin@rock-chips.com>");
+MODULE_DESCRIPTION("ROCKCHIP SPI Object Master Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("spi:spidev_rkmst");
diff --git a/kernel/drivers/spi/spidev-rkslv.c b/kernel/drivers/spi/spidev-rkslv.c
new file mode 100644
index 0000000..0ff4415
--- /dev/null
+++ b/kernel/drivers/spi/spidev-rkslv.c
@@ -0,0 +1,382 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Copyright (c) 2023 Rockchip Electronics Co., Ltd.
+ */
+
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+
+#define SPI_OBJ_MAX_XFER_SIZE	0x1040
+#define SPI_OBJ_APP_RAM_SIZE	0x10000
+
+#define SPI_OBJ_CTRL_MSG_SIZE	0x8
+#define SPI_OBJ_CTRL_CMD_INIT	0x99
+#define SPI_OBJ_CTRL_CMD_READ	0x3A
+#define SPI_OBJ_CTRL_CMD_WRITE	0x4B
+#define SPI_OBJ_CTRL_CMD_DUPLEX	0x5C
+
+struct spi_obj_ctrl {
+	u16 cmd;
+	u16 addr;
+	u32 data;
+};
+
+struct spidev_rkslv_data {
+	struct device	*dev;
+	struct spi_device *spi;
+	char *ctrlbuf;
+	char *appmem;
+	char *tempbuf;
+	bool verbose;
+	struct task_struct *tsk;
+	bool tsk_run;
+	struct miscdevice misc_dev;
+};
+
+static u32 bit_per_word = 8;
+
+static int spidev_slv_write(struct spidev_rkslv_data *spidev, const void *txbuf, size_t n)
+{
+	int ret = -1;
+	struct spi_device *spi = spidev->spi;
+	struct spi_transfer t = {
+			.tx_buf = txbuf,
+			.len = n,
+			.bits_per_word = bit_per_word,
+		};
+	struct spi_message m;
+
+	spi_message_init(&m);
+	spi_message_add_tail(&t, &m);
+	ret = spi_sync(spi, &m);
+
+	return ret;
+}
+
+static int spidev_slv_read(struct spidev_rkslv_data *spidev, void *rxbuf, size_t n)
+{
+	int ret = -1;
+	struct spi_device *spi = spidev->spi;
+	struct spi_transfer t = {
+			.rx_buf = rxbuf,
+			.len = n,
+			.bits_per_word = bit_per_word,
+		};
+	struct spi_message m;
+
+	spi_message_init(&m);
+	spi_message_add_tail(&t, &m);
+	ret = spi_sync(spi, &m);
+
+	return ret;
+}
+
+static int spidev_slv_write_and_read(struct spidev_rkslv_data *spidev, const void *tx_buf,
+				     void *rx_buf, size_t len)
+{
+	struct spi_device *spi = spidev->spi;
+	struct spi_transfer t = {
+			.tx_buf = tx_buf,
+			.rx_buf = rx_buf,
+			.len = len,
+		};
+	struct spi_message m;
+
+	spi_message_init(&m);
+	spi_message_add_tail(&t, &m);
+	return spi_sync(spi, &m);
+}
+
+static ssize_t spidev_rkslv_misc_write(struct file *filp, const char __user *buf,
+				       size_t n, loff_t *offset)
+{
+	struct spidev_rkslv_data *spidev;
+	struct spi_device *spi;
+	int argc = 0;
+	char tmp[64];
+	char *argv[16];
+	char *cmd, *data;
+
+	if (n >= 64)
+		return -EINVAL;
+
+	spidev = filp->private_data;
+
+	if (!spidev)
+		return -ESHUTDOWN;
+
+	spi = spidev->spi;
+	memset(tmp, 0, sizeof(tmp));
+	if (copy_from_user(tmp, buf, n))
+		return -EFAULT;
+	cmd = tmp;
+	data = tmp;
+
+	while (data < (tmp + n)) {
+		data = strstr(data, " ");
+		if (!data)
+			break;
+		*data = 0;
+		argv[argc] = ++data;
+		argc++;
+		if (argc >= 16)
+			break;
+	}
+
+	tmp[n - 1] = 0;
+
+	if (!strcmp(cmd, "verbose")) {
+		int val;
+
+		if (argc < 1)
+			return -EINVAL;
+
+		if (kstrtoint(argv[0], 0, &val))
+			return -EINVAL;
+
+		if (val == 1)
+			spidev->verbose = true;
+		else
+			spidev->verbose = false;
+	} else if (!strcmp(cmd, "appmem")) {
+		int addr, len;
+
+		if (argc < 2)
+			return -EINVAL;
+
+		if (kstrtoint(argv[0], 0, &addr))
+			return -EINVAL;
+		if (kstrtoint(argv[1], 0, &len))
+			return -EINVAL;
+
+		if (!len) {
+			dev_err(&spi->dev, "param invalid,%s %s\n", argv[0], argv[1]);
+			return -EINVAL;
+		}
+
+		if (addr + len > SPI_OBJ_APP_RAM_SIZE) {
+			dev_err(&spi->dev, "appmem print out of size\n");
+			return -EINVAL;
+		}
+
+		print_hex_dump(KERN_ERR, "APPMEM: ",
+			       DUMP_PREFIX_OFFSET,
+			       16,
+			       1,
+			       spidev->appmem + addr,
+			       len,
+			       1);
+	} else {
+		dev_err(&spi->dev, "unknown command\n");
+	}
+
+	return n;
+}
+
+static int spidev_rkslv_misc_open(struct inode *inode, struct file *filp)
+{
+	struct miscdevice *miscdev = filp->private_data;
+	struct spidev_rkslv_data *spidev;
+
+	spidev = container_of(miscdev, struct spidev_rkslv_data, misc_dev);
+	filp->private_data = spidev;
+
+	return 0;
+}
+
+static const struct file_operations spidev_rkslv_misc_fops = {
+	.write = spidev_rkslv_misc_write,
+	.open = spidev_rkslv_misc_open,
+};
+
+static int spidev_rkslv_xfer(struct spidev_rkslv_data *spidev)
+{
+	char *ctrlbuf = spidev->ctrlbuf, *appmem = spidev->appmem, *tempbuf = spidev->tempbuf;
+	struct spi_obj_ctrl *ctrl;
+	struct spi_device *spi = spidev->spi;
+	u32 len;
+	int ret;
+
+	memset(spidev->ctrlbuf, 0, SPI_OBJ_CTRL_MSG_SIZE);
+	ret = spidev_slv_read(spidev, spidev->ctrlbuf, SPI_OBJ_CTRL_MSG_SIZE);
+	if (ret) {
+		dev_err(&spi->dev, "%s ctrl\n", __func__);
+		return -EIO;
+	}
+
+	ctrl = (struct spi_obj_ctrl *)ctrlbuf;
+	if (spidev->verbose)
+		dev_err(&spi->dev, "ctrl cmd=%x addr=0x%x data=0x%x\n",
+			ctrl->cmd, ctrl->addr, ctrl->data);
+
+	switch (ctrl->cmd) {
+	case SPI_OBJ_CTRL_CMD_INIT:
+		return 0;
+	case SPI_OBJ_CTRL_CMD_READ:
+		len = ctrl->data;
+		ret = spidev_slv_write(spidev, appmem + ctrl->addr, len);
+		if (ret) {
+			dev_err(&spi->dev, "%s cmd=%x addr=0x%x data=0x%x\n",
+				__func__, ctrl->cmd, ctrl->addr, ctrl->data);
+			return -EIO;
+		}
+		break;
+	case SPI_OBJ_CTRL_CMD_WRITE:
+		len = ctrl->data;
+		ret = spidev_slv_read(spidev, appmem + ctrl->addr, len);
+		if (ret) {
+			dev_err(&spi->dev, "%s cmd=%x addr=0x%x data=0x%x\n",
+				__func__, ctrl->cmd, ctrl->addr, ctrl->data);
+			return -EIO;
+		}
+		if (spidev->verbose) {
+			print_hex_dump(KERN_ERR, "s-r: ",
+					DUMP_PREFIX_OFFSET,
+					16,
+					1,
+					appmem + ctrl->addr,
+					len,
+					1);
+		}
+		break;
+	case SPI_OBJ_CTRL_CMD_DUPLEX:
+		len = ctrl->data;
+		ret = spidev_slv_write_and_read(spidev, appmem + ctrl->addr, tempbuf, len);
+		if (ret) {
+			dev_err(&spi->dev, "%s cmd=%x addr=0x%x data=0x%x\n",
+				__func__, ctrl->cmd, ctrl->addr, ctrl->data);
+			return -EIO;
+		}
+		if (spidev->verbose) {
+			print_hex_dump(KERN_ERR, "s-d-t: ",
+				DUMP_PREFIX_OFFSET,
+				16,
+				1,
+				appmem + ctrl->addr,
+				len,
+				1);
+			print_hex_dump(KERN_ERR, "s-d-r: ",
+					DUMP_PREFIX_OFFSET,
+					16,
+					1,
+					tempbuf,
+					len,
+					1);
+		}
+		memcpy(appmem + ctrl->addr, tempbuf, len);
+		break;
+	default:
+		if (spidev->verbose)
+			dev_err(&spi->dev, "%s unknown\n", __func__);
+		return 0;
+	}
+
+	if (spidev->verbose)
+		dev_err(&spi->dev, "xfer len=0x%x\n", ctrl->data);
+
+	return 0;
+}
+
+static int spidev_rkslv_ctrl_receiver_thread(void *p)
+{
+	struct spidev_rkslv_data *spidev = (struct spidev_rkslv_data *)p;
+
+	while (spidev->tsk_run)
+		spidev_rkslv_xfer(spidev);
+
+	return 0;
+}
+
+static int spidev_rkslv_probe(struct spi_device *spi)
+{
+	struct spidev_rkslv_data *spidev = NULL;
+	int ret;
+
+	if (!spi)
+		return -ENOMEM;
+
+	spidev = devm_kzalloc(&spi->dev, sizeof(struct spidev_rkslv_data), GFP_KERNEL);
+	if (!spidev)
+		return -ENOMEM;
+
+	spidev->ctrlbuf = devm_kzalloc(&spi->dev, SPI_OBJ_MAX_XFER_SIZE, GFP_KERNEL);
+	if (!spidev->ctrlbuf)
+		return -ENOMEM;
+
+	spidev->appmem = devm_kzalloc(&spi->dev, SPI_OBJ_APP_RAM_SIZE, GFP_KERNEL | GFP_DMA);
+	if (!spidev->appmem)
+		return -ENOMEM;
+
+	spidev->tempbuf = devm_kzalloc(&spi->dev, SPI_OBJ_MAX_XFER_SIZE, GFP_KERNEL);
+	if (!spidev->tempbuf)
+		return -ENOMEM;
+
+	spidev->spi = spi;
+	spidev->dev = &spi->dev;
+	dev_set_drvdata(&spi->dev, spidev);
+
+	dev_err(&spi->dev, "mode=%d, max_speed_hz=%d\n", spi->mode, spi->max_speed_hz);
+
+	spidev->misc_dev.minor = MISC_DYNAMIC_MINOR;
+	spidev->misc_dev.name = "spidev_rkslv_misc";
+	spidev->misc_dev.fops = &spidev_rkslv_misc_fops;
+	spidev->misc_dev.parent = &spi->dev;
+	ret = misc_register(&spidev->misc_dev);
+	if (ret) {
+		dev_err(&spi->dev, "fail to register misc device\n");
+		return ret;
+	}
+
+	spidev->tsk_run = true;
+	spidev->tsk = kthread_run(spidev_rkslv_ctrl_receiver_thread, spidev, "spidev-rkslv");
+	if (IS_ERR(spidev->tsk)) {
+		dev_err(&spi->dev, "start spidev-rkslv thread failed\n");
+		return PTR_ERR(spidev->tsk);
+	}
+
+	return 0;
+}
+
+static int spidev_rkslv_remove(struct spi_device *spi)
+{
+	struct spidev_rkslv_data *spidev = dev_get_drvdata(&spi->dev);
+
+	spidev->tsk_run = false;
+	spi_slave_abort(spi);
+	kthread_stop(spidev->tsk);
+	misc_deregister(&spidev->misc_dev);
+
+	return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id spidev_rkslv_dt_match[] = {
+	{ .compatible = "rockchip,spi-obj-slave", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, spidev_rkslv_dt_match);
+
+#endif /* CONFIG_OF */
+
+static struct spi_driver spidev_rkmst_driver = {
+	.driver = {
+		.name	= "spidev_rkslv",
+		.owner = THIS_MODULE,
+		.of_match_table = of_match_ptr(spidev_rkslv_dt_match),
+	},
+	.probe		= spidev_rkslv_probe,
+	.remove		= spidev_rkslv_remove,
+};
+module_spi_driver(spidev_rkmst_driver);
+
+MODULE_AUTHOR("Jon Lin <jon.lin@rock-chips.com>");
+MODULE_DESCRIPTION("ROCKCHIP SPI Object Slave Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("spi:spidev_rkslv");
diff --git a/kernel/drivers/thermal/rockchip_thermal.c b/kernel/drivers/thermal/rockchip_thermal.c
index 855ac23..c116277 100644
--- a/kernel/drivers/thermal/rockchip_thermal.c
+++ b/kernel/drivers/thermal/rockchip_thermal.c
@@ -646,40 +646,40 @@
 static const struct tsadc_table rk3528_code_table[] = {
 	{0, MIN_TEMP},
 	{1386, MIN_TEMP},
-	{1419, -40000},
-	{1427, -35000},
-	{1435, -30000},
-	{1443, -25000},
-	{1452, -20000},
-	{1460, -15000},
-	{1468, -10000},
-	{1477, -5000},
-	{1486, 0},
-	{1494, 5000},
-	{1502, 10000},
-	{1510, 15000},
-	{1519, 20000},
-	{1527, 25000},
-	{1535, 30000},
-	{1544, 35000},
-	{1552, 40000},
-	{1561, 45000},
-	{1569, 50000},
-	{1578, 55000},
-	{1586, 60000},
-	{1594, 65000},
-	{1603, 70000},
-	{1612, 75000},
-	{1620, 80000},
+	{1410, -40000},
+	{1419, -35000},
+	{1428, -30000},
+	{1436, -25000},
+	{1445, -20000},
+	{1454, -15000},
+	{1463, -10000},
+	{1471, -5000},
+	{1480, 0},
+	{1489, 5000},
+	{1498, 10000},
+	{1506, 15000},
+	{1515, 20000},
+	{1524, 25000},
+	{1533, 30000},
+	{1541, 35000},
+	{1550, 40000},
+	{1558, 45000},
+	{1567, 50000},
+	{1575, 55000},
+	{1584, 60000},
+	{1593, 65000},
+	{1602, 70000},
+	{1610, 75000},
+	{1619, 80000},
 	{1628, 85000},
 	{1637, 90000},
 	{1646, 95000},
 	{1654, 100000},
-	{1662, 105000},
-	{1671, 110000},
-	{1679, 115000},
-	{1688, 120000},
-	{1696, 125000},
+	{1663, 105000},
+	{1672, 110000},
+	{1680, 115000},
+	{1689, 120000},
+	{1697, 125000},
 	{1790, MAX_TEMP},
 	{TSADCV5_DATA_MASK, MAX_TEMP},
 };
@@ -1482,6 +1482,15 @@
 	return code - base_code;
 }
 
+static int rk_tsadcv3_get_trim_code(const struct chip_tsadc_table *table,
+				    int code, int trim_base, int trim_base_frac)
+{
+	int temp = trim_base * 1000 + trim_base_frac * 100;
+	u32 base_code = rk_tsadcv2_temp_to_code(table, temp);
+
+	return (TSADCV3_Q_MAX_VAL - code) - base_code;
+}
+
 static int rk_tsadcv1_set_clk_rate(struct platform_device *pdev)
 {
 	struct clk *clk;
@@ -1860,6 +1869,8 @@
 	.set_alarm_temp = rk_tsadcv3_alarm_temp,
 	.set_tshut_temp = rk_tsadcv3_tshut_temp,
 	.set_tshut_mode = rk_tsadcv4_tshut_mode,
+	.get_trim_code = rk_tsadcv3_get_trim_code,
+	.trim_slope = 574,
 
 	.table = {
 		.id = rk3528_code_table,
@@ -1884,6 +1895,8 @@
 	.set_alarm_temp = rk_tsadcv3_alarm_temp,
 	.set_tshut_temp = rk_tsadcv3_tshut_temp,
 	.set_tshut_mode = rk_tsadcv4_tshut_mode,
+	.get_trim_code = rk_tsadcv3_get_trim_code,
+	.trim_slope = 588,
 
 	.table = {
 		.id = rk3562_code_table,
@@ -2167,12 +2180,9 @@
 	 * The tsadc won't to handle the error in here
 	 * since some SoCs didn't need this property.
 	 */
-	if (rockchip_get_efuse_value(np, "trim_base", &trim_base)) {
-		dev_info(dev, "Missing trim_base property\n");
-		return 0;
-	}
+	rockchip_get_efuse_value(np, "trim_base", &trim_base);
 	if (!trim_base)
-		return 0;
+		trim_base = 30;
 	rockchip_get_efuse_value(np, "trim_base_frac", &trim_base_frac);
 	/*
 	 * If the tsadc node contains trim_h and trim_l property,
diff --git a/kernel/drivers/tty/serial/8250/8250.h b/kernel/drivers/tty/serial/8250/8250.h
index 59ce698..2e001e2 100644
--- a/kernel/drivers/tty/serial/8250/8250.h
+++ b/kernel/drivers/tty/serial/8250/8250.h
@@ -156,49 +156,6 @@
 	up->dl_write(up, value);
 }
 
-static inline void serial8250_set_IER(struct uart_8250_port *up,
-				      unsigned char ier)
-{
-	struct uart_port *port = &up->port;
-	unsigned int flags;
-	bool is_console;
-
-	is_console = uart_console(port);
-
-	if (is_console)
-		console_atomic_lock(&flags);
-
-	serial_out(up, UART_IER, ier);
-
-	if (is_console)
-		console_atomic_unlock(flags);
-}
-
-static inline unsigned char serial8250_clear_IER(struct uart_8250_port *up)
-{
-	struct uart_port *port = &up->port;
-	unsigned int clearval = 0;
-	unsigned int prior;
-	unsigned int flags;
-	bool is_console;
-
-	is_console = uart_console(port);
-
-	if (up->capabilities & UART_CAP_UUE)
-		clearval = UART_IER_UUE;
-
-	if (is_console)
-		console_atomic_lock(&flags);
-
-	prior = serial_port_in(port, UART_IER);
-	serial_port_out(port, UART_IER, clearval);
-
-	if (is_console)
-		console_atomic_unlock(flags);
-
-	return prior;
-}
-
 static inline bool serial8250_set_THRI(struct uart_8250_port *up)
 {
 	if (up->ier & UART_IER_THRI)
@@ -207,7 +164,7 @@
 #if defined(CONFIG_ARCH_ROCKCHIP) && defined(CONFIG_NO_GKI)
 	up->ier |= UART_IER_PTIME;
 #endif
-	serial8250_set_IER(up, up->ier);
+	serial_out(up, UART_IER, up->ier);
 	return true;
 }
 
@@ -219,7 +176,7 @@
 #if defined(CONFIG_ARCH_ROCKCHIP) && defined(CONFIG_NO_GKI)
 	up->ier &= ~UART_IER_PTIME;
 #endif
-	serial8250_set_IER(up, up->ier);
+	serial_out(up, UART_IER, up->ier);
 	return true;
 }
 
diff --git a/kernel/drivers/tty/serial/8250/8250_core.c b/kernel/drivers/tty/serial/8250/8250_core.c
index bd52f7e..00f6dc7 100644
--- a/kernel/drivers/tty/serial/8250/8250_core.c
+++ b/kernel/drivers/tty/serial/8250/8250_core.c
@@ -275,8 +275,10 @@
 	 * Must disable interrupts or else we risk racing with the interrupt
 	 * based handler.
 	 */
-	if (up->port.irq)
-		ier = serial8250_clear_IER(up);
+	if (up->port.irq) {
+		ier = serial_in(up, UART_IER);
+		serial_out(up, UART_IER, 0);
+	}
 
 	iir = serial_in(up, UART_IIR);
 
@@ -299,7 +301,7 @@
 		serial8250_tx_chars(up);
 
 	if (up->port.irq)
-		serial8250_set_IER(up, ier);
+		serial_out(up, UART_IER, ier);
 
 	spin_unlock_irqrestore(&up->port.lock, flags);
 
@@ -582,14 +584,6 @@
 
 #ifdef CONFIG_SERIAL_8250_CONSOLE
 
-static void univ8250_console_write_atomic(struct console *co, const char *s,
-					  unsigned int count)
-{
-	struct uart_8250_port *up = &serial8250_ports[co->index];
-
-	serial8250_console_write_atomic(up, s, count);
-}
-
 static void univ8250_console_write(struct console *co, const char *s,
 				   unsigned int count)
 {
@@ -683,7 +677,6 @@
 
 static struct console univ8250_console = {
 	.name		= "ttyS",
-	.write_atomic	= univ8250_console_write_atomic,
 	.write		= univ8250_console_write,
 	.device		= uart_console_device,
 	.setup		= univ8250_console_setup,
diff --git a/kernel/drivers/tty/serial/8250/8250_fsl.c b/kernel/drivers/tty/serial/8250/8250_fsl.c
index b33cb45..fbcc90c 100644
--- a/kernel/drivers/tty/serial/8250/8250_fsl.c
+++ b/kernel/drivers/tty/serial/8250/8250_fsl.c
@@ -60,18 +60,9 @@
 
 	/* Stop processing interrupts on input overrun */
 	if ((orig_lsr & UART_LSR_OE) && (up->overrun_backoff_time_ms > 0)) {
-		unsigned int ca_flags;
 		unsigned long delay;
-		bool is_console;
 
-		is_console = uart_console(port);
-
-		if (is_console)
-			console_atomic_lock(&ca_flags);
 		up->ier = port->serial_in(port, UART_IER);
-		if (is_console)
-			console_atomic_unlock(ca_flags);
-
 		if (up->ier & (UART_IER_RLSI | UART_IER_RDI)) {
 			port->ops->stop_rx(port);
 		} else {
diff --git a/kernel/drivers/tty/serial/8250/8250_ingenic.c b/kernel/drivers/tty/serial/8250/8250_ingenic.c
index bcd26d6..988bf6b 100644
--- a/kernel/drivers/tty/serial/8250/8250_ingenic.c
+++ b/kernel/drivers/tty/serial/8250/8250_ingenic.c
@@ -146,8 +146,6 @@
 
 static void ingenic_uart_serial_out(struct uart_port *p, int offset, int value)
 {
-	unsigned int flags;
-	bool is_console;
 	int ier;
 
 	switch (offset) {
@@ -169,12 +167,7 @@
 		 * If we have enabled modem status IRQs we should enable
 		 * modem mode.
 		 */
-		is_console = uart_console(p);
-		if (is_console)
-			console_atomic_lock(&flags);
 		ier = p->serial_in(p, UART_IER);
-		if (is_console)
-			console_atomic_unlock(flags);
 
 		if (ier & UART_IER_MSI)
 			value |= UART_MCR_MDCE | UART_MCR_FCM;
diff --git a/kernel/drivers/tty/serial/8250/8250_mtk.c b/kernel/drivers/tty/serial/8250/8250_mtk.c
index d246f27..de48a58 100644
--- a/kernel/drivers/tty/serial/8250/8250_mtk.c
+++ b/kernel/drivers/tty/serial/8250/8250_mtk.c
@@ -222,37 +222,12 @@
 
 static void mtk8250_disable_intrs(struct uart_8250_port *up, int mask)
 {
-	struct uart_port *port = &up->port;
-	unsigned int flags;
-	unsigned int ier;
-	bool is_console;
-
-	is_console = uart_console(port);
-
-	if (is_console)
-		console_atomic_lock(&flags);
-
-	ier = serial_in(up, UART_IER);
-	serial_out(up, UART_IER, ier & (~mask));
-
-	if (is_console)
-		console_atomic_unlock(flags);
+	serial_out(up, UART_IER, serial_in(up, UART_IER) & (~mask));
 }
 
 static void mtk8250_enable_intrs(struct uart_8250_port *up, int mask)
 {
-	struct uart_port *port = &up->port;
-	unsigned int flags;
-	unsigned int ier;
-
-	if (uart_console(port))
-		console_atomic_lock(&flags);
-
-	ier = serial_in(up, UART_IER);
-	serial_out(up, UART_IER, ier | mask);
-
-	if (uart_console(port))
-		console_atomic_unlock(flags);
+	serial_out(up, UART_IER, serial_in(up, UART_IER) | mask);
 }
 
 static void mtk8250_set_flow_ctrl(struct uart_8250_port *up, int mode)
diff --git a/kernel/drivers/tty/serial/8250/8250_port.c b/kernel/drivers/tty/serial/8250/8250_port.c
index e619eaf..61f225a 100644
--- a/kernel/drivers/tty/serial/8250/8250_port.c
+++ b/kernel/drivers/tty/serial/8250/8250_port.c
@@ -737,7 +737,7 @@
 			serial_out(p, UART_EFR, UART_EFR_ECB);
 			serial_out(p, UART_LCR, 0);
 		}
-		serial8250_set_IER(p, sleep ? UART_IERX_SLEEP : 0);
+		serial_out(p, UART_IER, sleep ? UART_IERX_SLEEP : 0);
 		if (p->capabilities & UART_CAP_EFR) {
 			serial_out(p, UART_LCR, UART_LCR_CONF_MODE_B);
 			serial_out(p, UART_EFR, efr);
@@ -1411,7 +1411,7 @@
 
 	up->ier &= ~(UART_IER_RLSI | UART_IER_RDI);
 	up->port.read_status_mask &= ~UART_LSR_DR;
-	serial8250_set_IER(up, up->ier);
+	serial_port_out(port, UART_IER, up->ier);
 
 	serial8250_rpm_put(up);
 }
@@ -1441,7 +1441,7 @@
 		serial8250_clear_and_reinit_fifos(p);
 
 		p->ier |= UART_IER_RLSI | UART_IER_RDI;
-		serial8250_set_IER(p, p->ier);
+		serial_port_out(&p->port, UART_IER, p->ier);
 	}
 }
 EXPORT_SYMBOL_GPL(serial8250_em485_stop_tx);
@@ -1688,7 +1688,7 @@
 	mctrl_gpio_disable_ms(up->gpios);
 
 	up->ier &= ~UART_IER_MSI;
-	serial8250_set_IER(up, up->ier);
+	serial_port_out(port, UART_IER, up->ier);
 }
 
 static void serial8250_enable_ms(struct uart_port *port)
@@ -1704,7 +1704,7 @@
 	up->ier |= UART_IER_MSI;
 
 	serial8250_rpm_get(up);
-	serial8250_set_IER(up, up->ier);
+	serial_port_out(port, UART_IER, up->ier);
 	serial8250_rpm_put(up);
 }
 
@@ -2171,7 +2171,14 @@
 	struct uart_8250_port *up = up_to_u8250p(port);
 
 	serial8250_rpm_get(up);
-	ier = serial8250_clear_IER(up);
+	/*
+	 *	First save the IER then disable the interrupts
+	 */
+	ier = serial_port_in(port, UART_IER);
+	if (up->capabilities & UART_CAP_UUE)
+		serial_port_out(port, UART_IER, UART_IER_UUE);
+	else
+		serial_port_out(port, UART_IER, 0);
 
 	wait_for_xmitr(up, BOTH_EMPTY);
 	/*
@@ -2184,7 +2191,7 @@
 	 *	and restore the IER
 	 */
 	wait_for_xmitr(up, BOTH_EMPTY);
-	serial8250_set_IER(up, ier);
+	serial_port_out(port, UART_IER, ier);
 	serial8250_rpm_put(up);
 }
 
@@ -2491,7 +2498,7 @@
 	 */
 	spin_lock_irqsave(&port->lock, flags);
 	up->ier = 0;
-	serial8250_set_IER(up, 0);
+	serial_port_out(port, UART_IER, 0);
 	spin_unlock_irqrestore(&port->lock, flags);
 
 	synchronize_irq(port->irq);
@@ -2863,7 +2870,7 @@
 	if (up->capabilities & UART_CAP_RTOIE)
 		up->ier |= UART_IER_RTOIE;
 
-	serial8250_set_IER(up, up->ier);
+	serial_port_out(port, UART_IER, up->ier);
 #endif
 
 	if (up->capabilities & UART_CAP_EFR) {
@@ -2923,7 +2930,7 @@
 	if (up->capabilities & UART_CAP_RTOIE)
 		up->ier |= UART_IER_RTOIE;
 
-	serial8250_set_IER(up, up->ier);
+	serial_port_out(port, UART_IER, up->ier);
 #endif
 
 	spin_unlock_irqrestore(&port->lock, flags);
@@ -3355,24 +3362,12 @@
 
 #ifdef CONFIG_SERIAL_8250_CONSOLE
 
-static void serial8250_console_putchar_locked(struct uart_port *port, int ch)
+static void serial8250_console_putchar(struct uart_port *port, int ch)
 {
 	struct uart_8250_port *up = up_to_u8250p(port);
 
 	wait_for_xmitr(up, UART_LSR_THRE);
 	serial_port_out(port, UART_TX, ch);
-}
-
-static void serial8250_console_putchar(struct uart_port *port, int ch)
-{
-	struct uart_8250_port *up = up_to_u8250p(port);
-	unsigned int flags;
-
-	wait_for_xmitr(up, UART_LSR_THRE);
-
-	console_atomic_lock(&flags);
-	serial8250_console_putchar_locked(port, ch);
-	console_atomic_unlock(flags);
 }
 
 /*
@@ -3396,32 +3391,6 @@
 	serial8250_out_MCR(up, up->mcr | UART_MCR_DTR | UART_MCR_RTS);
 }
 
-void serial8250_console_write_atomic(struct uart_8250_port *up,
-				     const char *s, unsigned int count)
-{
-	struct uart_port *port = &up->port;
-	unsigned int flags;
-	unsigned int ier;
-
-	console_atomic_lock(&flags);
-
-	touch_nmi_watchdog();
-
-	ier = serial8250_clear_IER(up);
-
-	if (atomic_fetch_inc(&up->console_printing)) {
-		uart_console_write(port, "\n", 1,
-				   serial8250_console_putchar_locked);
-	}
-	uart_console_write(port, s, count, serial8250_console_putchar_locked);
-	atomic_dec(&up->console_printing);
-
-	wait_for_xmitr(up, BOTH_EMPTY);
-	serial8250_set_IER(up, ier);
-
-	console_atomic_unlock(flags);
-}
-
 /*
  *	Print a string to the serial port trying not to disturb
  *	any possible real use of the port...
@@ -3438,12 +3407,24 @@
 	struct uart_port *port = &up->port;
 	unsigned long flags;
 	unsigned int ier;
+	int locked = 1;
 
 	touch_nmi_watchdog();
 
-	spin_lock_irqsave(&port->lock, flags);
+	if (oops_in_progress)
+		locked = spin_trylock_irqsave(&port->lock, flags);
+	else
+		spin_lock_irqsave(&port->lock, flags);
 
-	ier = serial8250_clear_IER(up);
+	/*
+	 *	First save the IER then disable the interrupts
+	 */
+	ier = serial_port_in(port, UART_IER);
+
+	if (up->capabilities & UART_CAP_UUE)
+		serial_port_out(port, UART_IER, UART_IER_UUE);
+	else
+		serial_port_out(port, UART_IER, 0);
 
 	/* check scratch reg to see if port powered off during system sleep */
 	if (up->canary && (up->canary != serial_port_in(port, UART_SCR))) {
@@ -3457,9 +3438,7 @@
 		mdelay(port->rs485.delay_rts_before_send);
 	}
 
-	atomic_inc(&up->console_printing);
 	uart_console_write(port, s, count, serial8250_console_putchar);
-	atomic_dec(&up->console_printing);
 
 	/*
 	 *	Finally, wait for transmitter to become empty
@@ -3472,7 +3451,8 @@
 		if (em485->tx_stopped)
 			up->rs485_stop_tx(up);
 	}
-	serial8250_set_IER(up, ier);
+
+	serial_port_out(port, UART_IER, ier);
 
 	/*
 	 *	The receive handling will happen properly because the
@@ -3484,7 +3464,8 @@
 	if (up->msr_saved_flags)
 		serial8250_modem_status(up);
 
-	spin_unlock_irqrestore(&port->lock, flags);
+	if (locked)
+		spin_unlock_irqrestore(&port->lock, flags);
 }
 
 static unsigned int probe_baud(struct uart_port *port)
@@ -3504,7 +3485,6 @@
 
 int serial8250_console_setup(struct uart_port *port, char *options, bool probe)
 {
-	struct uart_8250_port *up = up_to_u8250p(port);
 	int baud = 9600;
 	int bits = 8;
 	int parity = 'n';
@@ -3513,8 +3493,6 @@
 
 	if (!port->iobase && !port->membase)
 		return -ENODEV;
-
-	atomic_set(&up->console_printing, 0);
 
 	if (options)
 		uart_parse_options(options, &baud, &parity, &bits, &flow);
diff --git a/kernel/drivers/tty/serial/amba-pl011.c b/kernel/drivers/tty/serial/amba-pl011.c
index 8b4d408..9900ee3 100644
--- a/kernel/drivers/tty/serial/amba-pl011.c
+++ b/kernel/drivers/tty/serial/amba-pl011.c
@@ -2199,24 +2199,18 @@
 {
 	struct uart_amba_port *uap = amba_ports[co->index];
 	unsigned int old_cr = 0, new_cr;
-	unsigned long flags = 0;
+	unsigned long flags;
 	int locked = 1;
 
 	clk_enable(uap->clk);
 
-	/*
-	 * local_irq_save(flags);
-	 *
-	 * This local_irq_save() is nonsense. If we come in via sysrq
-	 * handling then interrupts are already disabled. Aside of
-	 * that the port.sysrq check is racy on SMP regardless.
-	*/
+	local_irq_save(flags);
 	if (uap->port.sysrq)
 		locked = 0;
 	else if (oops_in_progress)
-		locked = spin_trylock_irqsave(&uap->port.lock, flags);
+		locked = spin_trylock(&uap->port.lock);
 	else
-		spin_lock_irqsave(&uap->port.lock, flags);
+		spin_lock(&uap->port.lock);
 
 	/*
 	 *	First save the CR then disable the interrupts
@@ -2242,7 +2236,8 @@
 		pl011_write(old_cr, uap, REG_CR);
 
 	if (locked)
-		spin_unlock_irqrestore(&uap->port.lock, flags);
+		spin_unlock(&uap->port.lock);
+	local_irq_restore(flags);
 
 	clk_disable(uap->clk);
 }
diff --git a/kernel/drivers/tty/serial/omap-serial.c b/kernel/drivers/tty/serial/omap-serial.c
index 342005e..84e8158 100644
--- a/kernel/drivers/tty/serial/omap-serial.c
+++ b/kernel/drivers/tty/serial/omap-serial.c
@@ -1311,10 +1311,13 @@
 
 	pm_runtime_get_sync(up->dev);
 
-	if (up->port.sysrq || oops_in_progress)
-		locked = spin_trylock_irqsave(&up->port.lock, flags);
+	local_irq_save(flags);
+	if (up->port.sysrq)
+		locked = 0;
+	else if (oops_in_progress)
+		locked = spin_trylock(&up->port.lock);
 	else
-		spin_lock_irqsave(&up->port.lock, flags);
+		spin_lock(&up->port.lock);
 
 	/*
 	 * First save the IER then disable the interrupts
@@ -1343,7 +1346,8 @@
 	pm_runtime_mark_last_busy(up->dev);
 	pm_runtime_put_autosuspend(up->dev);
 	if (locked)
-		spin_unlock_irqrestore(&up->port.lock, flags);
+		spin_unlock(&up->port.lock);
+	local_irq_restore(flags);
 }
 
 static int __init
diff --git a/kernel/drivers/usb/dwc3/Kconfig b/kernel/drivers/usb/dwc3/Kconfig
index 2133acf..e0e9645 100644
--- a/kernel/drivers/usb/dwc3/Kconfig
+++ b/kernel/drivers/usb/dwc3/Kconfig
@@ -117,6 +117,15 @@
 	  Currently supports Xilinx and Qualcomm DWC USB3 IP.
 	  Say 'Y' or 'M' if you have one such device.
 
+config USB_DWC3_ROCKCHIP_INNO
+	bool "Rockchip Platforms with INNO PHY"
+	depends on OF && COMMON_CLK && (ARCH_ROCKCHIP || COMPILE_TEST)
+	default USB_DWC3 && PHY_ROCKCHIP_INNO_USB3 && NO_GKI
+	help
+	  Support of USB2/3 functionality in Rockchip platforms
+	  with INNO USB 3.0 PHY IP inside.
+	  say 'Y' or 'M' if you have one such device.
+
 config USB_DWC3_ST
 	tristate "STMicroelectronics Platforms"
 	depends on (ARCH_STI || COMPILE_TEST) && OF
diff --git a/kernel/drivers/usb/dwc3/Makefile b/kernel/drivers/usb/dwc3/Makefile
index 2259f88..a45c235 100644
--- a/kernel/drivers/usb/dwc3/Makefile
+++ b/kernel/drivers/usb/dwc3/Makefile
@@ -49,6 +49,7 @@
 obj-$(CONFIG_USB_DWC3_KEYSTONE)		+= dwc3-keystone.o
 obj-$(CONFIG_USB_DWC3_MESON_G12A)	+= dwc3-meson-g12a.o
 obj-$(CONFIG_USB_DWC3_OF_SIMPLE)	+= dwc3-of-simple.o
+obj-$(CONFIG_USB_DWC3_ROCKCHIP_INNO)	+= dwc3-rockchip-inno.o
 obj-$(CONFIG_USB_DWC3_ST)		+= dwc3-st.o
 obj-$(CONFIG_USB_DWC3_QCOM)		+= dwc3-qcom.o
 obj-$(CONFIG_USB_DWC3_IMX8MP)		+= dwc3-imx8mp.o
diff --git a/kernel/drivers/usb/dwc3/core.c b/kernel/drivers/usb/dwc3/core.c
index 8384e53..20a7bc7 100644
--- a/kernel/drivers/usb/dwc3/core.c
+++ b/kernel/drivers/usb/dwc3/core.c
@@ -1099,6 +1099,11 @@
 		if (dwc->parkmode_disable_ss_quirk)
 			reg |= DWC3_GUCTL1_PARKMODE_DISABLE_SS;
 
+#ifdef CONFIG_NO_GKI
+		if (dwc->parkmode_disable_hs_quirk)
+			reg |= DWC3_GUCTL1_PARKMODE_DISABLE_HS;
+#endif
+
 		if (dwc->maximum_speed == USB_SPEED_HIGH ||
 		    dwc->maximum_speed == USB_SPEED_FULL)
 			reg |= DWC3_GUCTL1_DEV_FORCE_20_CLK_FOR_30_CLK;
@@ -1418,6 +1423,10 @@
 				"snps,dis-tx-ipgap-linecheck-quirk");
 	dwc->parkmode_disable_ss_quirk = device_property_read_bool(dev,
 				"snps,parkmode-disable-ss-quirk");
+#ifdef CONFIG_NO_GKI
+	dwc->parkmode_disable_hs_quirk = device_property_read_bool(dev,
+				"snps,parkmode-disable-hs-quirk");
+#endif
 
 	dwc->tx_de_emphasis_quirk = device_property_read_bool(dev,
 				"snps,tx_de_emphasis_quirk");
diff --git a/kernel/drivers/usb/dwc3/core.h b/kernel/drivers/usb/dwc3/core.h
index 8930145..11ac1e0 100644
--- a/kernel/drivers/usb/dwc3/core.h
+++ b/kernel/drivers/usb/dwc3/core.h
@@ -262,6 +262,7 @@
 #define DWC3_GUCTL1_DEV_FORCE_20_CLK_FOR_30_CLK	BIT(26)
 #define DWC3_GUCTL1_DEV_L1_EXIT_BY_HW		BIT(24)
 #define DWC3_GUCTL1_PARKMODE_DISABLE_SS		BIT(17)
+#define DWC3_GUCTL1_PARKMODE_DISABLE_HS		BIT(16)
 
 /* Global Status Register */
 #define DWC3_GSTS_OTG_IP	BIT(10)
@@ -1095,6 +1096,8 @@
  *			check during HS transmit.
  * @parkmode_disable_ss_quirk: set if we need to disable all SuperSpeed
  *			instances in park mode.
+ * @parkmode_disable_hs_quirk: set if we need to disable all HishSpeed
+ *			instances in park mode.
  * @tx_de_emphasis_quirk: set if we enable Tx de-emphasis quirk
  * @tx_de_emphasis: Tx de-emphasis value
  *	0	- -6dB de-emphasis
@@ -1310,6 +1313,9 @@
 	unsigned		dis_del_phy_power_chg_quirk:1;
 	unsigned		dis_tx_ipgap_linecheck_quirk:1;
 	unsigned		parkmode_disable_ss_quirk:1;
+#ifdef CONFIG_NO_GKI
+	unsigned		parkmode_disable_hs_quirk:1;
+#endif
 
 	unsigned		tx_de_emphasis_quirk:1;
 	unsigned		tx_de_emphasis:2;
diff --git a/kernel/drivers/usb/gadget/function/uvc_v4l2.c b/kernel/drivers/usb/gadget/function/uvc_v4l2.c
index 591411b..3105d5a 100644
--- a/kernel/drivers/usb/gadget/function/uvc_v4l2.c
+++ b/kernel/drivers/usb/gadget/function/uvc_v4l2.c
@@ -200,7 +200,7 @@
 	if (type != video->queue.queue.type)
 		return -EINVAL;
 
-	if (uvc->state != UVC_STATE_CONNECTED)
+	if (uvc->state == UVC_STATE_DISCONNECTED)
 		return -ENODEV;
 
 	/* Enable UVC video. */
diff --git a/kernel/drivers/usb/typec/tcpm/tcpm.c b/kernel/drivers/usb/typec/tcpm/tcpm.c
index a088c6e..141a143 100644
--- a/kernel/drivers/usb/typec/tcpm/tcpm.c
+++ b/kernel/drivers/usb/typec/tcpm/tcpm.c
@@ -1724,6 +1724,14 @@
 				rlen = 1;
 			} else if (port->data_role == TYPEC_HOST) {
 				tcpm_register_partner_altmodes(port);
+			} else {
+				/* Do dr_swap for ufp if the port supports drd */
+				if (port->typec_caps.data == TYPEC_PORT_DRD &&
+				    !IS_ERR_OR_NULL(port->port_altmode[0])) {
+					port->vdm_sm_running = false;
+					port->upcoming_state = DR_SWAP_SEND;
+					tcpm_ams_start(port, DATA_ROLE_SWAP);
+				}
 			}
 			break;
 		case CMD_ENTER_MODE:
@@ -1755,6 +1763,16 @@
 		tcpm_ams_finish(port);
 		switch (cmd) {
 		case CMD_DISCOVER_IDENT:
+			/* Do dr_swap for ufp if the port supports drd */
+			if (port->typec_caps.data == TYPEC_PORT_DRD &&
+			    port->data_role == TYPEC_DEVICE &&
+			    !IS_ERR_OR_NULL(port->port_altmode[0])) {
+				port->vdm_sm_running = false;
+				port->upcoming_state = DR_SWAP_SEND;
+				tcpm_ams_start(port, DATA_ROLE_SWAP);
+				break;
+			}
+			fallthrough;
 		case CMD_DISCOVER_SVID:
 		case CMD_DISCOVER_MODES:
 		case VDO_CMD_VENDOR(0) ... VDO_CMD_VENDOR(15):
diff --git a/kernel/drivers/video/rockchip/mpp/mpp_common.c b/kernel/drivers/video/rockchip/mpp/mpp_common.c
index dbc625c..0038df8 100644
--- a/kernel/drivers/video/rockchip/mpp/mpp_common.c
+++ b/kernel/drivers/video/rockchip/mpp/mpp_common.c
@@ -36,8 +36,6 @@
 #include "mpp_common.h"
 #include "mpp_iommu.h"
 
-#define MPP_WAIT_TIMEOUT_DELAY		(2000)
-
 /* Use 'v' as magic number */
 #define MPP_IOC_MAGIC		'v'
 
@@ -355,9 +353,9 @@
 		mutex_unlock(&queue->session_lock);
 
 		if (task_count) {
-			mpp_dbg_session("session %d:%d task not finished %d\n",
-					session->pid, session->index,
-					atomic_read(&queue->detach_count));
+			mpp_dbg_session("session %d:%d not finished %d task cnt %d\n",
+					session->device_type, session->index,
+					atomic_read(&queue->detach_count), task_count);
 
 			mpp_session_clear_pending(session);
 		} else {
@@ -588,6 +586,7 @@
 	mpp_dev_reset(mpp);
 	mpp_power_off(mpp);
 
+	mpp_iommu_dev_deactivate(mpp->iommu_info, mpp);
 	set_bit(TASK_STATE_TIMEOUT, &task->state);
 	set_bit(TASK_STATE_DONE, &task->state);
 	/* Wake up the GET thread */
@@ -717,10 +716,6 @@
 		group->resets[type] = rst;
 		group->queue = mpp->queue;
 	}
-	/* if reset not in the same queue, it means different device
-	 * may reset in the same time, then rw_sem_on should set true.
-	 */
-	group->rw_sem_on |= (group->queue != mpp->queue) ? true : false;
 	dev_info(mpp->dev, "reset_group->rw_sem_on=%d\n", group->rw_sem_on);
 	up_write(&group->rw_sem);
 
@@ -821,12 +816,19 @@
 		mpp_set_grf(mpp->grf_info);
 	}
 	/*
+	 * Lock the reader locker of the device resource lock here,
+	 * release at the finish operation
+	 */
+	mpp_reset_down_read(mpp->reset_group);
+
+	/*
 	 * for iommu share hardware, should attach to ensure
 	 * working in current device
 	 */
 	ret = mpp_iommu_attach(mpp->iommu_info);
 	if (ret) {
 		dev_err(mpp->dev, "mpp_iommu_attach failed\n");
+		mpp_reset_up_read(mpp->reset_group);
 		return -ENODATA;
 	}
 
@@ -836,11 +838,6 @@
 
 	if (mpp->auto_freq_en && mpp->hw_ops->set_freq)
 		mpp->hw_ops->set_freq(mpp, task);
-	/*
-	 * TODO: Lock the reader locker of the device resource lock here,
-	 * release at the finish operation
-	 */
-	mpp_reset_down_read(mpp->reset_group);
 
 	mpp_iommu_dev_activate(mpp->iommu_info, mpp);
 	if (mpp->dev_ops->run)
@@ -922,23 +919,15 @@
 	}
 	mpp = mpp_get_task_used_device(task, session);
 
-	ret = wait_event_timeout(task->wait,
-				 test_bit(TASK_STATE_DONE, &task->state),
-				 msecs_to_jiffies(MPP_WAIT_TIMEOUT_DELAY));
-	if (ret > 0) {
-		if (mpp->dev_ops->result)
-			ret = mpp->dev_ops->result(mpp, task, msgs);
-	} else {
-		atomic_inc(&task->abort_request);
-		set_bit(TASK_STATE_ABORT, &task->state);
-		mpp_err("timeout, pid %d session %d:%d count %d cur_task %p id %d\n",
-			session->pid, session->pid, session->index,
-			atomic_read(&session->task_count), task,
-			task->task_id);
-	}
+	ret = wait_event_interruptible(task->wait, test_bit(TASK_STATE_DONE, &task->state));
+	if (ret == -ERESTARTSYS)
+		mpp_err("wait task break by signal\n");
 
-	mpp_debug_func(DEBUG_TASK_INFO, "task %d kref_%d\n",
-		       task->task_id, kref_read(&task->ref));
+	if (mpp->dev_ops->result)
+		ret = mpp->dev_ops->result(mpp, task, msgs);
+	mpp_debug_func(DEBUG_TASK_INFO, "wait done session %d:%d count %d task %d state %lx\n",
+		       session->device_type, session->index, atomic_read(&session->task_count),
+		       task->task_index, task->state);
 
 	mpp_session_pop_pending(session, task);
 
@@ -1013,6 +1002,10 @@
 			return -ENODEV;
 		} else {
 			mpp->reset_group = mpp->srv->reset_groups[reset_group_node];
+			if (!mpp->reset_group->queue)
+				mpp->reset_group->queue = queue;
+			if (mpp->reset_group->queue != mpp->queue)
+				mpp->reset_group->rw_sem_on = true;
 		}
 	}
 
@@ -2266,7 +2259,7 @@
 		irq_ret = mpp->dev_ops->irq(mpp);
 
 	if (task) {
-		if (irq_ret != IRQ_NONE) {
+		if (irq_ret == IRQ_WAKE_THREAD) {
 			/* if wait or delayed work timeout, abort request will turn on,
 			 * isr should not to response, and handle it in delayed work
 			 */
diff --git a/kernel/drivers/video/rockchip/mpp/mpp_iommu.c b/kernel/drivers/video/rockchip/mpp/mpp_iommu.c
index 29685df..116ec66 100644
--- a/kernel/drivers/video/rockchip/mpp/mpp_iommu.c
+++ b/kernel/drivers/video/rockchip/mpp/mpp_iommu.c
@@ -455,6 +455,9 @@
 		return 0;
 	}
 
+	if (mpp->cur_task)
+		mpp_task_dump_mem_region(mpp, mpp->cur_task);
+
 	if (mpp->dev_ops && mpp->dev_ops->dump_dev)
 		mpp->dev_ops->dump_dev(mpp);
 	else
diff --git a/kernel/drivers/video/rockchip/mpp/mpp_rkvdec.c b/kernel/drivers/video/rockchip/mpp/mpp_rkvdec.c
index cad51dd..4310a09 100644
--- a/kernel/drivers/video/rockchip/mpp/mpp_rkvdec.c
+++ b/kernel/drivers/video/rockchip/mpp/mpp_rkvdec.c
@@ -953,11 +953,11 @@
 	task = to_rkvdec_task(mpp_task);
 
 	/*
-	 * HW defeat workaround: VP9 power save optimization cause decoding
+	 * HW defeat workaround: VP9 and H.265 power save optimization cause decoding
 	 * corruption, disable optimization here.
 	 */
 	fmt = RKVDEC_GET_FORMAT(task->reg[RKVDEC_REG_SYS_CTRL_INDEX]);
-	if (fmt == RKVDEC_FMT_VP9D) {
+	if (fmt == RKVDEC_FMT_VP9D || fmt == RKVDEC_FMT_H265D) {
 		cfg = task->reg[RKVDEC_POWER_CTL_INDEX] | 0xFFFF;
 		task->reg[RKVDEC_POWER_CTL_INDEX] = cfg & (~(1 << 12));
 		mpp_write_relaxed(mpp, RKVDEC_POWER_CTL_BASE,
@@ -1429,7 +1429,7 @@
 		goto done;
 	}
 	dec->aux_iova = -1;
-	mpp->iommu_info->hdl = rkvdec_3328_iommu_hdl;
+	mpp->fault_handler = rkvdec_3328_iommu_hdl;
 
 	ret = rkvdec_devfreq_init(mpp);
 done:
diff --git a/kernel/drivers/video/rockchip/mpp/mpp_rkvdec2.c b/kernel/drivers/video/rockchip/mpp/mpp_rkvdec2.c
index 70aa130..9d146c1 100644
--- a/kernel/drivers/video/rockchip/mpp/mpp_rkvdec2.c
+++ b/kernel/drivers/video/rockchip/mpp/mpp_rkvdec2.c
@@ -1581,7 +1581,7 @@
 		mpp->dev_ops->task_worker = rkvdec2_hard_ccu_worker;
 		irq_proc = rkvdec2_hard_ccu_irq;
 	}
-	mpp->iommu_info->hdl = rkvdec2_ccu_iommu_fault_handle;
+	mpp->fault_handler = rkvdec2_ccu_iommu_fault_handle;
 	kthread_init_work(&mpp->work, mpp->dev_ops->task_worker);
 
 	/* get irq request */
diff --git a/kernel/drivers/video/rockchip/mpp/mpp_rkvdec2_link.c b/kernel/drivers/video/rockchip/mpp/mpp_rkvdec2_link.c
index eb0e620..0d87eb4 100644
--- a/kernel/drivers/video/rockchip/mpp/mpp_rkvdec2_link.c
+++ b/kernel/drivers/video/rockchip/mpp/mpp_rkvdec2_link.c
@@ -18,8 +18,6 @@
 
 #include "hack/mpp_rkvdec2_link_hack_rk3568.c"
 
-#define WORK_TIMEOUT_MS		(500)
-#define WAIT_TIMEOUT_MS		(2000)
 #define RKVDEC2_LINK_HACK_TASK_FLAG	(0xff)
 
 /* vdpu381 link hw info for rk3588 */
@@ -519,11 +517,6 @@
 	struct rkvdec_link_dev *link_dec = dec->link_dec;
 	u32 irq_status = 0;
 
-	if (!atomic_read(&link_dec->power_enabled)) {
-		dev_info(link_dec->dev, "irq on power off\n");
-		return -1;
-	}
-
 	irq_status = readl(link_dec->reg_base + RKVDEC_LINK_IRQ_BASE);
 
 	if (irq_status & RKVDEC_LINK_BIT_IRQ_RAW) {
@@ -980,6 +973,7 @@
 
 		list_move_tail(&task->table->link, &link_dec->unused_list);
 		list_del_init(&mpp_task->queue_link);
+		link_dec->task_running--;
 
 		set_bit(TASK_STATE_HANDLE, &mpp_task->state);
 		set_bit(TASK_STATE_PROC_DONE, &mpp_task->state);
@@ -988,13 +982,10 @@
 		if (test_bit(TASK_STATE_ABORT, &mpp_task->state))
 			set_bit(TASK_STATE_ABORT_READY, &mpp_task->state);
 
-		wake_up(&mpp_task->wait);
-		kref_put(&mpp_task->ref, rkvdec2_link_free_task);
-		link_dec->task_running--;
-
 		mpp_dbg_link("session %d task %d irq_status %#08x timeout %d abort %d\n",
 			     mpp_task->session->index, mpp_task->task_index,
 			     irq_status, timeout_flag, abort_flag);
+
 		if (irq_status & RKVDEC_INT_ERROR_MASK) {
 			dev_err(mpp->dev,
 				"session %d task %d irq_status %#08x timeout %u abort %u\n",
@@ -1003,6 +994,9 @@
 			if (!reset_flag)
 				atomic_inc(&mpp->reset_request);
 		}
+
+		wake_up(&mpp_task->wait);
+		kref_put(&mpp_task->ref, rkvdec2_link_free_task);
 	}
 
 	/* resend running task after reset */
@@ -1192,19 +1186,16 @@
 		return -EIO;
 	}
 
-	ret = wait_event_timeout(mpp_task->wait, task_is_done(mpp_task),
-				 msecs_to_jiffies(WAIT_TIMEOUT_MS));
-	if (ret) {
-		ret = rkvdec2_result(mpp, mpp_task, msgs);
+	ret = wait_event_interruptible(mpp_task->wait, task_is_done(mpp_task));
+	if (ret == -ERESTARTSYS)
+		mpp_err("wait task break by signal\n");
 
-		mpp_session_pop_done(session, mpp_task);
-	} else {
-		mpp_err("task %d:%d state %lx timeout -> abort\n",
-			session->index, mpp_task->task_id, mpp_task->state);
+	ret = rkvdec2_result(mpp, mpp_task, msgs);
 
-		atomic_inc(&mpp_task->abort_request);
-		set_bit(TASK_STATE_ABORT, &mpp_task->state);
-	}
+	mpp_session_pop_done(session, mpp_task);
+	mpp_debug_func(DEBUG_TASK_INFO, "wait done session %d:%d count %d task %d state %lx\n",
+		       session->device_type, session->index, atomic_read(&session->task_count),
+		       mpp_task->task_index, mpp_task->state);
 
 	mpp_session_pop_pending(session, mpp_task);
 	return ret;
@@ -1356,7 +1347,8 @@
 		/* set the ccu-domain for current device */
 		ccu_info = queue->cores[0]->iommu_info;
 		cur_info = dec->mpp.iommu_info;
-		cur_info->domain = ccu_info->domain;
+		if (cur_info)
+			cur_info->domain = ccu_info->domain;
 		mpp_iommu_attach(cur_info);
 	}
 
@@ -1839,36 +1831,6 @@
 	return flag;
 }
 
-static int rkvdec2_ccu_link_session_detach(struct mpp_dev *mpp,
-					   struct mpp_taskqueue *queue)
-{
-	mutex_lock(&queue->session_lock);
-	while (atomic_read(&queue->detach_count)) {
-		struct mpp_session *session = NULL;
-
-		session = list_first_entry_or_null(&queue->session_detach,
-						   struct mpp_session,
-						   session_link);
-		if (session) {
-			list_del_init(&session->session_link);
-			atomic_dec(&queue->detach_count);
-		}
-
-		mutex_unlock(&queue->session_lock);
-
-		if (session) {
-			mpp_dbg_session("%s detach count %d\n", dev_name(mpp->dev),
-					atomic_read(&queue->detach_count));
-			mpp_session_deinit(session);
-		}
-
-		mutex_lock(&queue->session_lock);
-	}
-	mutex_unlock(&queue->session_lock);
-
-	return 0;
-}
-
 void rkvdec2_soft_ccu_worker(struct kthread_work *work_s)
 {
 	struct mpp_task *mpp_task;
@@ -1943,7 +1905,7 @@
 		rkvdec2_ccu_power_off(queue, dec->ccu);
 
 	/* 5. check session detach out of queue */
-	rkvdec2_ccu_link_session_detach(mpp, queue);
+	mpp_session_cleanup_detach(queue, work_s);
 
 	mpp_debug_leave();
 }
@@ -2366,9 +2328,7 @@
 	writel(RKVDEC_CCU_BIT_CFG_DONE, ccu->reg_base + RKVDEC_CCU_CFG_DONE_BASE);
 	mpp_task_run_end(mpp_task, timing_en);
 
-	/* pending to running */
 	set_bit(TASK_STATE_RUNNING, &mpp_task->state);
-	mpp_taskqueue_pending_to_run(queue, mpp_task);
 	mpp_dbg_ccu("session %d task %d iova=%08x task->state=%lx link_mode=%08x\n",
 		    mpp_task->session->index, mpp_task->task_index,
 		    (u32)task->table->iova, mpp_task->state,
@@ -2541,6 +2501,7 @@
 
 		rkvdec2_ccu_power_on(queue, dec->ccu);
 		rkvdec2_hard_ccu_enqueue(dec->ccu, mpp_task, queue, mpp);
+		mpp_taskqueue_pending_to_run(queue, mpp_task);
 	}
 
 	/* 4. poweroff when running and pending list are empty */
diff --git a/kernel/drivers/video/rockchip/mpp/mpp_rkvenc.c b/kernel/drivers/video/rockchip/mpp/mpp_rkvenc.c
index 85a83f1..c71c03f 100644
--- a/kernel/drivers/video/rockchip/mpp/mpp_rkvenc.c
+++ b/kernel/drivers/video/rockchip/mpp/mpp_rkvenc.c
@@ -1232,7 +1232,7 @@
 	}
 	INIT_WORK(&enc->iommu_work, rkvenc_iommu_handle_work);
 
-	mpp->iommu_info->hdl = rkvenc_iommu_fault_handle;
+	mpp->fault_handler = rkvenc_iommu_fault_handle;
 
 	return ret;
 }
diff --git a/kernel/drivers/video/rockchip/mpp/mpp_rkvenc2.c b/kernel/drivers/video/rockchip/mpp/mpp_rkvenc2.c
index 2dc50bb..5bd7054 100644
--- a/kernel/drivers/video/rockchip/mpp/mpp_rkvenc2.c
+++ b/kernel/drivers/video/rockchip/mpp/mpp_rkvenc2.c
@@ -178,6 +178,12 @@
 #define RKVENC2_REG_SLICE_NUM_BASE	(0x4034)
 #define RKVENC2_REG_SLICE_LEN_BASE	(0x4038)
 
+#define RKVENC2_REG_ST_BSB		(0x402c)
+#define RKVENC2_REG_ADR_BSBT		(0x2b0)
+#define RKVENC2_REG_ADR_BSBB		(0x2b4)
+#define RKVENC2_REG_ADR_BSBR		(0x2b8)
+#define RKVENC2_REG_ADR_BSBS		(0x2bc)
+
 union rkvenc2_slice_len_info {
 	u32 val;
 
@@ -282,6 +288,8 @@
 	dma_addr_t sram_iova;
 	u32 sram_enabled;
 	struct page *rcb_page;
+
+	u32 bs_overflow;
 
 #ifdef CONFIG_PM_DEVFREQ
 	struct rockchip_opp_info opp_info;
@@ -1290,6 +1298,8 @@
 	struct rkvenc_hw_info *hw = enc->hw_info;
 	struct mpp_task *mpp_task = NULL;
 	struct rkvenc_task *task = NULL;
+	u32 int_clear = 1;
+	u32 irq_mask = 0;
 	int ret = IRQ_NONE;
 
 	mpp_debug_enter();
@@ -1311,12 +1321,12 @@
 			wake_up(&mpp_task->wait);
 		}
 
-		mpp_write(mpp, hw->int_mask_base, 0x100);
-		mpp_write(mpp, hw->int_clr_base, 0xffffffff);
-		udelay(5);
-		mpp_write(mpp, hw->int_sta_base, 0);
-
+		irq_mask = INT_STA_ENC_DONE_STA;
 		ret = IRQ_WAKE_THREAD;
+		if (enc->bs_overflow) {
+			mpp->irq_status |= INT_STA_BSF_OFLW_STA;
+			enc->bs_overflow = 0;
+		}
 	} else if (mpp->irq_status & INT_STA_SLC_DONE_STA) {
 		if (task && task->task_split) {
 			mpp_time_part_diff(mpp_task);
@@ -1325,7 +1335,42 @@
 			wake_up(&mpp_task->wait);
 		}
 
-		mpp_write(mpp, hw->int_clr_base, INT_STA_SLC_DONE_STA);
+		irq_mask = INT_STA_ENC_DONE_STA;
+		int_clear = 0;
+	} else if (mpp->irq_status & INT_STA_BSF_OFLW_STA) {
+		u32 bs_rd = mpp_read(mpp, RKVENC2_REG_ADR_BSBR);
+		u32 bs_wr = mpp_read(mpp, RKVENC2_REG_ST_BSB);
+		u32 bs_top = mpp_read(mpp, RKVENC2_REG_ADR_BSBT);
+		u32 bs_bot = mpp_read(mpp, RKVENC2_REG_ADR_BSBB);
+
+		if (mpp_task)
+			dev_err(mpp->dev, "task %d found bitstream overflow [%#08x %#08x %#08x %#08x]\n",
+				mpp_task->task_index, bs_top, bs_bot, bs_wr, bs_rd);
+		bs_wr += 128;
+		if (bs_wr >= bs_top)
+			bs_wr = bs_bot;
+		/* clear int first */
+		mpp_write(mpp, hw->int_clr_base, mpp->irq_status);
+		/* update write addr for enc continue */
+		mpp_write(mpp, RKVENC2_REG_ADR_BSBS, bs_wr);
+		enc->bs_overflow = 1;
+		irq_mask = 0;
+		int_clear = 0;
+		ret = IRQ_HANDLED;
+	} else {
+		dev_err(mpp->dev, "found error status %08x\n", mpp->irq_status);
+
+		irq_mask = mpp->irq_status;
+		ret = IRQ_WAKE_THREAD;
+	}
+
+	if (irq_mask)
+		mpp_write(mpp, hw->int_mask_base, irq_mask);
+
+	if (int_clear) {
+		mpp_write(mpp, hw->int_clr_base, mpp->irq_status);
+		udelay(5);
+		mpp_write(mpp, hw->int_sta_base, 0);
 	}
 
 	mpp_debug_leave();
@@ -2004,38 +2049,31 @@
 
 	if (!enc_task->task_split || enc_task->task_split_done) {
 task_done_ret:
-		ret = wait_event_timeout(task->wait,
-					 test_bit(TASK_STATE_DONE, &task->state),
-					 msecs_to_jiffies(RKVENC2_WAIT_TIMEOUT_DELAY));
+		ret = wait_event_interruptible(task->wait, test_bit(TASK_STATE_DONE, &task->state));
+		if (ret == -ERESTARTSYS)
+			mpp_err("wait task break by signal in normal mode\n");
 
-		if (ret > 0)
-			return rkvenc2_task_default_process(mpp, task);
+		return rkvenc2_task_default_process(mpp, task);
 
-		rkvenc2_task_timeout_process(session, task);
-		return ret;
 	}
 
 	/* not slice return just wait all slice length */
 	if (!req) {
 		do {
-			ret = wait_event_timeout(task->wait,
-						 kfifo_out(&enc_task->slice_info, &slice_info, 1),
-						 msecs_to_jiffies(RKVENC2_WORK_TIMEOUT_DELAY));
-			if (ret > 0) {
-				mpp_dbg_slice("task %d rd %3d len %d %s\n",
-					      task_id, enc_task->slice_rd_cnt, slice_info.slice_len,
-					      slice_info.last ? "last" : "");
-
-				enc_task->slice_rd_cnt++;
-
-				if (slice_info.last)
-					goto task_done_ret;
-
-				continue;
+			ret = wait_event_interruptible(task->wait, kfifo_out(&enc_task->slice_info,
+									     &slice_info, 1));
+			if (ret == -ERESTARTSYS) {
+				mpp_err("wait task break by signal in slice all mode\n");
+				return 0;
 			}
+			mpp_dbg_slice("task %d rd %3d len %d %s\n",
+					task_id, enc_task->slice_rd_cnt, slice_info.slice_len,
+					slice_info.last ? "last" : "");
 
-			rkvenc2_task_timeout_process(session, task);
-			return ret;
+			enc_task->slice_rd_cnt++;
+
+			if (slice_info.last)
+				goto task_done_ret;
 		} while (1);
 	}
 
@@ -2050,40 +2088,41 @@
 
 	/* handle slice mode poll return */
 	do {
-		ret = wait_event_timeout(task->wait,
-					 kfifo_out(&enc_task->slice_info, &slice_info, 1),
-					 msecs_to_jiffies(RKVENC2_WORK_TIMEOUT_DELAY));
-		if (ret > 0) {
-			mpp_dbg_slice("core %d task %d rd %3d len %d %s\n", task_id,
-				      mpp->core_id, enc_task->slice_rd_cnt, slice_info.slice_len,
-				      slice_info.last ? "last" : "");
-			enc_task->slice_rd_cnt++;
-			if (cfg.count_ret < cfg.count_max) {
-				struct rkvenc_poll_slice_cfg __user *ucfg =
-					(struct rkvenc_poll_slice_cfg __user *)(req->data);
-				u32 __user *dst = (u32 __user *)(ucfg + 1);
-
-				/* Do NOT return here when put_user error. Just continue */
-				if (put_user(slice_info.val, dst + cfg.count_ret))
-					ret = -EFAULT;
-
-				cfg.count_ret++;
-				if (put_user(cfg.count_ret, &ucfg->count_ret))
-					ret = -EFAULT;
-			}
-
-			if (slice_info.last) {
-				enc_task->task_split_done = 1;
-				goto task_done_ret;
-			}
-
-			if (cfg.count_ret >= cfg.count_max)
-				return 0;
-
-			if (ret < 0)
-				return ret;
+		ret = wait_event_interruptible(task->wait, kfifo_out(&enc_task->slice_info,
+								     &slice_info, 1));
+		if (ret == -ERESTARTSYS) {
+			mpp_err("wait task break by signal in slice one mode\n");
+			return 0;
 		}
-	} while (ret > 0);
+		mpp_dbg_slice("core %d task %d rd %3d len %d %s\n", task_id,
+				mpp->core_id, enc_task->slice_rd_cnt, slice_info.slice_len,
+				slice_info.last ? "last" : "");
+		enc_task->slice_rd_cnt++;
+		if (cfg.count_ret < cfg.count_max) {
+			struct rkvenc_poll_slice_cfg __user *ucfg =
+				(struct rkvenc_poll_slice_cfg __user *)(req->data);
+			u32 __user *dst = (u32 __user *)(ucfg + 1);
+
+			/* Do NOT return here when put_user error. Just continue */
+			if (put_user(slice_info.val, dst + cfg.count_ret))
+				ret = -EFAULT;
+
+			cfg.count_ret++;
+			if (put_user(cfg.count_ret, &ucfg->count_ret))
+				ret = -EFAULT;
+		}
+
+		if (slice_info.last) {
+			enc_task->task_split_done = 1;
+			goto task_done_ret;
+		}
+
+		if (cfg.count_ret >= cfg.count_max)
+			return 0;
+
+		if (ret < 0)
+			return ret;
+	} while (!ret);
 
 	rkvenc2_task_timeout_process(session, task);
 
@@ -2243,8 +2282,10 @@
 		ccu_info = ccu->main_core->iommu_info;
 		cur_info = enc->mpp.iommu_info;
 
-		cur_info->domain = ccu_info->domain;
-		cur_info->rw_sem = ccu_info->rw_sem;
+		if (cur_info) {
+			cur_info->domain = ccu_info->domain;
+			cur_info->rw_sem = ccu_info->rw_sem;
+		}
 		mpp_iommu_attach(cur_info);
 
 		/* increase main core message capacity */
@@ -2422,7 +2463,7 @@
 	}
 	mpp->session_max_buffers = RKVENC_SESSION_MAX_BUFFERS;
 	enc->hw_info = to_rkvenc_info(mpp->var->hw_info);
-	mpp->iommu_info->hdl = rkvenc2_iommu_fault_handle;
+	mpp->fault_handler = rkvenc2_iommu_fault_handle;
 	rkvenc_procfs_init(mpp);
 	rkvenc_procfs_ccu_init(mpp);
 
diff --git a/kernel/drivers/video/rockchip/mpp/mpp_vepu2.c b/kernel/drivers/video/rockchip/mpp/mpp_vepu2.c
index 12aef5e..395a5b6 100644
--- a/kernel/drivers/video/rockchip/mpp/mpp_vepu2.c
+++ b/kernel/drivers/video/rockchip/mpp/mpp_vepu2.c
@@ -314,43 +314,40 @@
 
 static void *vepu_prepare(struct mpp_dev *mpp, struct mpp_task *mpp_task)
 {
-	struct mpp_taskqueue *queue = mpp->queue;
 	struct vepu_dev *enc = to_vepu_dev(mpp);
 	struct vepu_ccu *ccu = enc->ccu;
 	unsigned long core_idle;
 	unsigned long flags;
-	u32 core_id_max;
 	s32 core_id;
 	u32 i;
 
 	spin_lock_irqsave(&ccu->lock, flags);
 
-	core_idle = queue->core_idle;
-	core_id_max = queue->core_id_max;
+	core_idle = ccu->core_idle;
 
-	for (i = 0; i <= core_id_max; i++) {
-		struct mpp_dev *mpp = queue->cores[i];
+	for (i = 0; i < ccu->core_num; i++) {
+		struct mpp_dev *mpp = ccu->cores[i];
 
 		if (mpp && mpp->disable)
-			clear_bit(i, &core_idle);
+			clear_bit(mpp->core_id, &core_idle);
 	}
 
-	core_id = find_first_bit(&ccu->core_idle, ccu->core_num);
-	core_id = array_index_nospec(core_id, MPP_MAX_CORE_NUM);
-	if (core_id >= core_id_max + 1 || !queue->cores[core_id]) {
+	core_id = find_first_bit(&core_idle, ccu->core_num);
+	if (core_id >= ARRAY_SIZE(ccu->cores)) {
 		mpp_task = NULL;
 		mpp_dbg_core("core %d all busy %lx\n", core_id, ccu->core_idle);
-	} else {
-		unsigned long core_idle = ccu->core_idle;
-
-		clear_bit(core_id, &ccu->core_idle);
-		mpp_task->mpp = ccu->cores[core_id];
-		mpp_task->core_id = core_id;
-
-		mpp_dbg_core("core cnt %d core %d set idle %lx -> %lx\n",
-			     ccu->core_num, core_id, core_idle, ccu->core_idle);
+		goto done;
 	}
 
+	core_id = array_index_nospec(core_id, MPP_MAX_CORE_NUM);
+	clear_bit(core_id, &ccu->core_idle);
+	mpp_task->mpp = ccu->cores[core_id];
+	mpp_task->core_id = core_id;
+
+	mpp_dbg_core("core cnt %d core %d set idle %lx -> %lx\n",
+		     ccu->core_num, core_id, core_idle, ccu->core_idle);
+
+done:
 	spin_unlock_irqrestore(&ccu->lock, flags);
 
 	return mpp_task;
@@ -1050,7 +1047,8 @@
 		ccu_info = ccu->main_core->iommu_info;
 		cur_info = enc->mpp.iommu_info;
 
-		cur_info->domain = ccu_info->domain;
+		if (cur_info)
+			cur_info->domain = ccu_info->domain;
 		mpp_iommu_attach(cur_info);
 	}
 	enc->ccu = ccu;
diff --git a/kernel/drivers/video/rockchip/rga3/include/rga.h b/kernel/drivers/video/rockchip/rga3/include/rga.h
index f6be6eaf..34a9245 100644
--- a/kernel/drivers/video/rockchip/rga3/include/rga.h
+++ b/kernel/drivers/video/rockchip/rga3/include/rga.h
@@ -139,6 +139,7 @@
 	RGA_YUV_VDS			= 0x1 << 10,
 	RGA_OSD				= 0x1 << 11,
 	RGA_PRE_INTR			= 0x1 << 12,
+	RGA_FULL_CSC			= 0x1 << 13,
 };
 
 enum rga_surf_format {
@@ -201,6 +202,55 @@
 	RGA_FORMAT_RGBA_2BPP		= 0x30,
 
 	RGA_FORMAT_UNKNOWN		= 0x100,
+};
+
+enum rga_alpha_mode {
+	RGA_ALPHA_STRAIGHT		= 0,
+	RGA_ALPHA_INVERSE		= 1,
+};
+
+enum rga_global_blend_mode {
+	RGA_ALPHA_GLOBAL		= 0,
+	RGA_ALPHA_PER_PIXEL		= 1,
+	RGA_ALPHA_PER_PIXEL_GLOBAL	= 2,
+};
+
+enum rga_alpha_cal_mode {
+	RGA_ALPHA_SATURATION		= 0,
+	RGA_ALPHA_NO_SATURATION		= 1,
+};
+
+enum rga_factor_mode {
+	RGA_ALPHA_ZERO			= 0,
+	RGA_ALPHA_ONE			= 1,
+	/*
+	 *   When used as a factor for the SRC channel, it indicates
+	 * the use of the DST channel's alpha value, and vice versa.
+	 */
+	RGA_ALPHA_OPPOSITE		= 2,
+	RGA_ALPHA_OPPOSITE_INVERSE	= 3,
+	RGA_ALPHA_OWN			= 4,
+};
+
+enum rga_color_mode {
+	RGA_ALPHA_PRE_MULTIPLIED	= 0,
+	RGA_ALPHA_NO_PRE_MULTIPLIED	= 1,
+};
+
+enum rga_alpha_blend_mode {
+	RGA_ALPHA_NONE			= 0,
+	RGA_ALPHA_BLEND_SRC,
+	RGA_ALPHA_BLEND_DST,
+	RGA_ALPHA_BLEND_SRC_OVER,
+	RGA_ALPHA_BLEND_DST_OVER,
+	RGA_ALPHA_BLEND_SRC_IN,
+	RGA_ALPHA_BLEND_DST_IN,
+	RGA_ALPHA_BLEND_SRC_OUT,
+	RGA_ALPHA_BLEND_DST_OUT,
+	RGA_ALPHA_BLEND_SRC_ATOP,
+	RGA_ALPHA_BLEND_DST_ATOP,
+	RGA_ALPHA_BLEND_XOR,
+	RGA_ALPHA_BLEND_CLEAR,
 };
 
 #define RGA_SCHED_PRIORITY_DEFAULT 0
@@ -329,6 +379,16 @@
 	struct rga_csc_coe coe_y;
 	struct rga_csc_coe coe_u;
 	struct rga_csc_coe coe_v;
+};
+
+struct rga_csc_range {
+	uint16_t max;
+	uint16_t min;
+};
+
+struct rga_csc_clip {
+	struct rga_csc_range y;
+	struct rga_csc_range uv;
 };
 
 struct rga_mosaic_info {
@@ -507,6 +567,12 @@
 	uint16_t enable;
 };
 
+struct rga_feature {
+	uint32_t global_alpha_en:1;
+	uint32_t full_csc_clip_en:1;
+	uint32_t user_close_fence:1;
+};
+
 struct rga_req {
 	/* (enum) process mode sel */
 	uint8_t render_mode;
@@ -563,7 +629,7 @@
 	/* porter duff alpha mode sel */
 	uint8_t PD_mode;
 
-	/* global alpha value */
+	/* legacy: global alpha value */
 	uint8_t alpha_global_value;
 
 	/* rop2/3/4 code scan from rop code table*/
@@ -625,7 +691,28 @@
 
 	struct rga_pre_intr_info pre_intr_info;
 
-	uint8_t reservr[59];
+	/* global alpha */
+	uint8_t fg_global_alpha;
+	uint8_t bg_global_alpha;
+
+	struct rga_feature feature;
+
+	struct rga_csc_clip full_csc_clip;
+
+	uint8_t reservr[43];
+};
+
+struct rga_alpha_config {
+	bool enable;
+	bool fg_pre_multiplied;
+	bool bg_pre_multiplied;
+	bool fg_pixel_alpha_en;
+	bool bg_pixel_alpha_en;
+	bool fg_global_alpha_en;
+	bool bg_global_alpha_en;
+	uint16_t fg_global_alpha_value;
+	uint16_t bg_global_alpha_value;
+	enum rga_alpha_blend_mode mode;
 };
 
 struct rga2_req {
@@ -672,29 +759,7 @@
 	/* ([7] = 1 gradient fill mode sel) */
 	u16 alpha_rop_flag;
 
-	/* [0]	 SrcAlphaMode0		 */
-	/* [2:1] SrcGlobalAlphaMode0	*/
-	/* [3]	 SrcAlphaSelectMode0	*/
-	/* [6:4] SrcFactorMode0		 */
-	/* [7]	 SrcColorMode		 */
-
-	/* [8]	 DstAlphaMode0		 */
-	/* [10:9] DstGlobalAlphaMode0	*/
-	/* [11]	DstAlphaSelectMode0	*/
-	/* [14:12] DstFactorMode0		 */
-	/* [15]	DstColorMode0		 */
-	u16 alpha_mode_0;
-
-	/* [0]	 SrcAlphaMode1		 */
-	/* [2:1] SrcGlobalAlphaMode1	*/
-	/* [3]	 SrcAlphaSelectMode1	*/
-	/* [6:4] SrcFactorMode1		 */
-
-	/* [8]	 DstAlphaMode1		 */
-	/* [10:9] DstGlobalAlphaMode1	*/
-	/* [11]	DstAlphaSelectMode1	*/
-	/* [14:12] DstFactorMode1		 */
-	u16 alpha_mode_1;
+	struct rga_alpha_config alpha_config;
 
 	/* 0 1 2 3 */
 	u8 scale_bicu_mode;
@@ -782,8 +847,10 @@
 
 	u16 alpha_rop_flag;
 
-	u16 alpha_mode_0;
-	u16 alpha_mode_1;
+	struct rga_alpha_config alpha_config;
+
+	/* for abb mode presever alpha. */
+	bool abb_alpha_pass;
 
 	u8 scale_bicu_mode;
 
diff --git a/kernel/drivers/video/rockchip/rga3/include/rga2_reg_info.h b/kernel/drivers/video/rockchip/rga3/include/rga2_reg_info.h
index 6a5601b..add2f41 100644
--- a/kernel/drivers/video/rockchip/rga3/include/rga2_reg_info.h
+++ b/kernel/drivers/video/rockchip/rga3/include/rga2_reg_info.h
@@ -434,6 +434,43 @@
 
 #define RGA2_VSP_BICUBIC_LIMIT				1996
 
+union rga2_color_ctrl {
+	uint32_t value;
+	struct {
+		uint32_t dst_color_mode:1;
+		uint32_t src_color_mode:1;
+
+		uint32_t dst_factor_mode:3;
+		uint32_t src_factor_mode:3;
+
+		uint32_t dst_alpha_cal_mode:1;
+		uint32_t src_alpha_cal_mode:1;
+
+		uint32_t dst_blend_mode:2;
+		uint32_t src_blend_mode:2;
+
+		uint32_t dst_alpha_mode:1;
+		uint32_t src_alpha_mode:1;
+	} bits;
+};
+
+union rga2_alpha_ctrl {
+	uint32_t value;
+	struct {
+		uint32_t dst_factor_mode:3;
+		uint32_t src_factor_mode:3;
+
+		uint32_t dst_alpha_cal_mode:1;
+		uint32_t src_alpha_cal_mode:1;
+
+		uint32_t dst_blend_mode:2;
+		uint32_t src_blend_mode:2;
+
+		uint32_t dst_alpha_mode:1;
+		uint32_t src_alpha_mode:1;
+	} bits;
+};
+
 extern const struct rga_backend_ops rga2_ops;
 
 #endif
diff --git a/kernel/drivers/video/rockchip/rga3/include/rga3_reg_info.h b/kernel/drivers/video/rockchip/rga3/include/rga3_reg_info.h
index 88d05a5..4db80cf 100644
--- a/kernel/drivers/video/rockchip/rga3/include/rga3_reg_info.h
+++ b/kernel/drivers/video/rockchip/rga3/include/rga3_reg_info.h
@@ -489,6 +489,32 @@
 #define RGA3_ROT_BIT_X_MIRROR			BIT(1)
 #define RGA3_ROT_BIT_Y_MIRROR			BIT(2)
 
+union rga3_color_ctrl {
+	uint32_t value;
+	struct {
+		uint32_t color_mode:1;
+		uint32_t alpha_mode:1;
+		uint32_t blend_mode:2;
+		uint32_t alpha_cal_mode:1;
+		uint32_t factor_mode:3;
+
+		uint32_t reserved:8;
+
+		uint32_t global_alpha:8;
+	} bits;
+};
+
+union rga3_alpha_ctrl {
+	uint32_t value;
+	struct {
+		uint32_t reserved:1;
+		uint32_t alpha_mode:1;
+		uint32_t blend_mode:2;
+		uint32_t alpha_cal_mode:1;
+		uint32_t factor_mode:3;
+	} bits;
+};
+
 extern const struct rga_backend_ops rga3_ops;
 
 #endif
diff --git a/kernel/drivers/video/rockchip/rga3/include/rga_common.h b/kernel/drivers/video/rockchip/rga3/include/rga_common.h
index 0b2ad9e..67aad7b 100644
--- a/kernel/drivers/video/rockchip/rga3/include/rga_common.h
+++ b/kernel/drivers/video/rockchip/rga3/include/rga_common.h
@@ -34,9 +34,7 @@
 const char *rga_get_format_name(uint32_t format);
 const char *rga_get_render_mode_str(uint8_t mode);
 const char *rga_get_rotate_mode_str(uint8_t mode);
-const char *rga_get_blend_mode_str(uint16_t alpha_rop_flag,
-				   uint16_t alpha_mode_0,
-				   uint16_t alpha_mode_1);
+const char *rga_get_blend_mode_str(enum rga_alpha_blend_mode mode);
 const char *rga_get_memory_type_str(uint8_t type);
 const char *rga_get_mmu_type_str(enum rga_mmu mmu_type);
 
diff --git a/kernel/drivers/video/rockchip/rga3/include/rga_drv.h b/kernel/drivers/video/rockchip/rga3/include/rga_drv.h
index e42493b..fff02e1 100644
--- a/kernel/drivers/video/rockchip/rga3/include/rga_drv.h
+++ b/kernel/drivers/video/rockchip/rga3/include/rga_drv.h
@@ -16,6 +16,7 @@
 #include <linux/dma-mapping.h>
 #include <linux/err.h>
 #include <linux/fb.h>
+#include <linux/fdtable.h>
 #include <linux/fs.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
@@ -48,7 +49,6 @@
 
 #include <linux/iommu.h>
 #include <linux/iova.h>
-#include <linux/dma-iommu.h>
 #include <linux/pagemap.h>
 
 #ifdef CONFIG_DMABUF_CACHE
@@ -86,8 +86,8 @@
 #define STR(x) STR_HELPER(x)
 
 #define DRIVER_MAJOR_VERISON		1
-#define DRIVER_MINOR_VERSION		2
-#define DRIVER_REVISION_VERSION		26
+#define DRIVER_MINOR_VERSION		3
+#define DRIVER_REVISION_VERSION		0
 #define DRIVER_PATCH_VERSION
 
 #define DRIVER_VERSION (STR(DRIVER_MAJOR_VERISON) "." STR(DRIVER_MINOR_VERSION) \
@@ -272,6 +272,7 @@
 	struct rga_req rga_command_base;
 	uint32_t cmd_reg[32 * 8];
 	struct rga_full_csc full_csc;
+	struct rga_csc_clip full_csc_clip;
 	struct rga_pre_intr_info pre_intr_info;
 
 	struct rga_job_buffer src_buffer;
@@ -379,6 +380,7 @@
 	 */
 	struct mm_struct *current_mm;
 
+	struct rga_feature feature;
 	/* TODO: add some common work */
 };
 
diff --git a/kernel/drivers/video/rockchip/rga3/include/rga_mm.h b/kernel/drivers/video/rockchip/rga3/include/rga_mm.h
index 3c27615..0bb8e92 100644
--- a/kernel/drivers/video/rockchip/rga3/include/rga_mm.h
+++ b/kernel/drivers/video/rockchip/rga3/include/rga_mm.h
@@ -18,6 +18,8 @@
 	RGA_MEM_NEED_USE_IOMMU		= 1 << 1,
 	/* Flag this is a physical contiguous memory. */
 	RGA_MEM_PHYSICAL_CONTIGUOUS	= 1 << 2,
+	/* need force flush cache */
+	RGA_MEM_FORCE_FLUSH_CACHE	= 1 << 3,
 };
 
 struct rga_mm {
diff --git a/kernel/drivers/video/rockchip/rga3/rga2_reg_info.c b/kernel/drivers/video/rockchip/rga3/rga2_reg_info.c
index a624778..309159d 100644
--- a/kernel/drivers/video/rockchip/rga3/rga2_reg_info.c
+++ b/kernel/drivers/video/rockchip/rga3/rga2_reg_info.c
@@ -1277,104 +1277,253 @@
 	u32 *bRGA_ALPHA_CTRL0;
 	u32 *bRGA_ALPHA_CTRL1;
 	u32 *bRGA_FADING_CTRL;
-	u32 reg0 = 0;
-	u32 reg1 = 0;
+	u32 reg = 0;
+	union rga2_color_ctrl color_ctrl;
+	union rga2_alpha_ctrl alpha_ctrl;
+	struct rga_alpha_config *config;
 
 	bRGA_ALPHA_CTRL0 = (u32 *) (base + RGA2_ALPHA_CTRL0_OFFSET);
 	bRGA_ALPHA_CTRL1 = (u32 *) (base + RGA2_ALPHA_CTRL1_OFFSET);
 	bRGA_FADING_CTRL = (u32 *) (base + RGA2_FADING_CTRL_OFFSET);
 
-	reg0 =
-		((reg0 & (~m_RGA2_ALPHA_CTRL0_SW_ALPHA_ROP_0)) |
+	color_ctrl.value = 0;
+	alpha_ctrl.value = 0;
+	config = &msg->alpha_config;
+
+	color_ctrl.bits.src_color_mode =
+		config->fg_pre_multiplied ? RGA_ALPHA_PRE_MULTIPLIED : RGA_ALPHA_NO_PRE_MULTIPLIED;
+	color_ctrl.bits.dst_color_mode =
+		config->bg_pre_multiplied ? RGA_ALPHA_PRE_MULTIPLIED : RGA_ALPHA_NO_PRE_MULTIPLIED;
+
+	if (config->fg_pixel_alpha_en)
+		color_ctrl.bits.src_blend_mode =
+			config->fg_global_alpha_en ? RGA_ALPHA_PER_PIXEL_GLOBAL :
+			RGA_ALPHA_PER_PIXEL;
+	else
+		color_ctrl.bits.src_blend_mode = RGA_ALPHA_GLOBAL;
+
+	if (config->bg_pixel_alpha_en)
+		color_ctrl.bits.dst_blend_mode =
+			config->bg_global_alpha_en ? RGA_ALPHA_PER_PIXEL_GLOBAL :
+			RGA_ALPHA_PER_PIXEL;
+	else
+		color_ctrl.bits.dst_blend_mode = RGA_ALPHA_GLOBAL;
+
+	/*
+	 * Since the hardware uses 256 as 1, the original alpha value needs to
+	 * be + (alpha >> 7).
+	 */
+	color_ctrl.bits.src_alpha_cal_mode = RGA_ALPHA_SATURATION;
+	color_ctrl.bits.dst_alpha_cal_mode = RGA_ALPHA_SATURATION;
+
+	/* porter duff alpha enable */
+	switch (config->mode) {
+	case RGA_ALPHA_BLEND_SRC:
+		/*
+		 * SRC mode:
+		 *	Sf = 1, Df = 0;
+		 *	[Rc,Ra] = [Sc,Sa];
+		 */
+		color_ctrl.bits.src_alpha_mode = RGA_ALPHA_STRAIGHT;
+		color_ctrl.bits.src_factor_mode = RGA_ALPHA_ONE;
+
+		color_ctrl.bits.dst_alpha_mode = RGA_ALPHA_STRAIGHT;
+		color_ctrl.bits.dst_factor_mode = RGA_ALPHA_ZERO;
+
+		break;
+
+	case RGA_ALPHA_BLEND_DST:
+		/*
+		 * SRC mode:
+		 *	Sf = 0, Df = 1;
+		 *	[Rc,Ra] = [Dc,Da];
+		 */
+		color_ctrl.bits.src_alpha_mode = RGA_ALPHA_STRAIGHT;
+		color_ctrl.bits.src_factor_mode = RGA_ALPHA_ZERO;
+
+		color_ctrl.bits.dst_alpha_mode = RGA_ALPHA_STRAIGHT;
+		color_ctrl.bits.dst_factor_mode = RGA_ALPHA_ONE;
+
+		break;
+
+	case RGA_ALPHA_BLEND_SRC_OVER:
+		/*
+		 * SRC-OVER mode:
+		 *	Sf = 1, Df = (1 - Sa)
+		 *	[Rc,Ra] = [ Sc + (1 - Sa) * Dc, Sa + (1 - Sa) * Da ]
+		 */
+		color_ctrl.bits.src_alpha_mode = RGA_ALPHA_STRAIGHT;
+		color_ctrl.bits.src_factor_mode = RGA_ALPHA_ONE;
+
+		color_ctrl.bits.dst_alpha_mode = RGA_ALPHA_STRAIGHT;
+		color_ctrl.bits.dst_factor_mode = RGA_ALPHA_OPPOSITE_INVERSE;
+
+		break;
+
+	case RGA_ALPHA_BLEND_DST_OVER:
+		/*
+		 * DST-OVER mode:
+		 *	Sf = (1 - Da) , Df = 1
+		 *	[Rc,Ra] = [ Sc * (1 - Da) + Dc, Sa * (1 - Da) + Da ]
+		 */
+		color_ctrl.bits.src_alpha_mode = RGA_ALPHA_STRAIGHT;
+		color_ctrl.bits.src_factor_mode = RGA_ALPHA_OPPOSITE_INVERSE;
+
+		color_ctrl.bits.dst_alpha_mode = RGA_ALPHA_STRAIGHT;
+		color_ctrl.bits.dst_factor_mode = RGA_ALPHA_ONE;
+
+		break;
+
+	case RGA_ALPHA_BLEND_SRC_IN:
+		/*
+		 * SRC-IN mode:
+		 *	Sf = Da , Df = 0
+		 *	[Rc,Ra] = [ Sc * Da, Sa * Da ]
+		 */
+		color_ctrl.bits.src_alpha_mode = RGA_ALPHA_STRAIGHT;
+		color_ctrl.bits.src_factor_mode = RGA_ALPHA_OPPOSITE;
+
+		color_ctrl.bits.dst_alpha_mode = RGA_ALPHA_STRAIGHT;
+		color_ctrl.bits.dst_factor_mode = RGA_ALPHA_ZERO;
+
+		break;
+
+	case RGA_ALPHA_BLEND_DST_IN:
+		/*
+		 * DST-IN mode:
+		 *	Sf = 0 , Df = Sa
+		 *	[Rc,Ra] = [ Dc * Sa, Da * Sa ]
+		 */
+		color_ctrl.bits.src_alpha_mode = RGA_ALPHA_STRAIGHT;
+		color_ctrl.bits.src_factor_mode = RGA_ALPHA_ZERO;
+
+		color_ctrl.bits.dst_alpha_mode = RGA_ALPHA_STRAIGHT;
+		color_ctrl.bits.dst_factor_mode = RGA_ALPHA_OPPOSITE;
+
+		break;
+
+	case RGA_ALPHA_BLEND_SRC_OUT:
+		/*
+		 * SRC-OUT mode:
+		 *	Sf = (1 - Da) , Df = 0
+		 *	[Rc,Ra] = [ Sc * (1 - Da), Sa * (1 - Da) ]
+		 */
+		color_ctrl.bits.src_alpha_mode = RGA_ALPHA_STRAIGHT;
+		color_ctrl.bits.src_factor_mode = RGA_ALPHA_OPPOSITE_INVERSE;
+
+		color_ctrl.bits.dst_alpha_mode = RGA_ALPHA_STRAIGHT;
+		color_ctrl.bits.dst_factor_mode = RGA_ALPHA_ZERO;
+
+		break;
+
+	case RGA_ALPHA_BLEND_DST_OUT:
+		/*
+		 * DST-OUT mode:
+		 *	Sf = 0 , Df = (1 - Sa)
+		 *	[Rc,Ra] = [ Dc * (1 - Sa), Da * (1 - Sa) ]
+		 */
+		color_ctrl.bits.src_alpha_mode = RGA_ALPHA_STRAIGHT;
+		color_ctrl.bits.src_factor_mode = RGA_ALPHA_ZERO;
+
+		color_ctrl.bits.dst_alpha_mode = RGA_ALPHA_STRAIGHT;
+		color_ctrl.bits.dst_factor_mode = RGA_ALPHA_OPPOSITE_INVERSE;
+
+		break;
+
+	case RGA_ALPHA_BLEND_SRC_ATOP:
+		/*
+		 * SRC-ATOP mode:
+		 *	Sf = Da , Df = (1 - Sa)
+		 *	[Rc,Ra] = [ Sc * Da + Dc * (1 - Sa), Sa * Da + Da * (1 - Sa) ]
+		 */
+		color_ctrl.bits.src_alpha_mode = RGA_ALPHA_STRAIGHT;
+		color_ctrl.bits.src_factor_mode = RGA_ALPHA_OPPOSITE;
+
+		color_ctrl.bits.dst_alpha_mode = RGA_ALPHA_STRAIGHT;
+		color_ctrl.bits.dst_factor_mode = RGA_ALPHA_OPPOSITE_INVERSE;
+
+		break;
+
+	case RGA_ALPHA_BLEND_DST_ATOP:
+		/*
+		 * DST-ATOP mode:
+		 *	Sf = (1 - Da) , Df = Sa
+		 *	[Rc,Ra] = [ Sc * (1 - Da) + Dc * Sa, Sa * (1 - Da) + Da * Sa ]
+		 */
+		color_ctrl.bits.src_alpha_mode = RGA_ALPHA_STRAIGHT;
+		color_ctrl.bits.src_factor_mode = RGA_ALPHA_OPPOSITE_INVERSE;
+
+		color_ctrl.bits.dst_alpha_mode = RGA_ALPHA_STRAIGHT;
+		color_ctrl.bits.dst_factor_mode = RGA_ALPHA_OPPOSITE;
+
+		break;
+
+	case RGA_ALPHA_BLEND_XOR:
+		/*
+		 * DST-XOR mode:
+		 *	Sf = (1 - Da) , Df = (1 - Sa)
+		 *	[Rc,Ra] = [ Sc * (1 - Da) + Dc * (1 - Sa), Sa * (1 - Da) + Da * (1 - Sa) ]
+		 */
+		color_ctrl.bits.src_alpha_mode = RGA_ALPHA_STRAIGHT;
+		color_ctrl.bits.src_factor_mode = RGA_ALPHA_OPPOSITE_INVERSE;
+
+		color_ctrl.bits.dst_alpha_mode = RGA_ALPHA_STRAIGHT;
+		color_ctrl.bits.dst_factor_mode = RGA_ALPHA_OPPOSITE_INVERSE;
+
+		break;
+
+	case RGA_ALPHA_BLEND_CLEAR:
+		/*
+		 * DST-CLEAR mode:
+		 *	Sf = 0 , Df = 0
+		 *	[Rc,Ra] = [ 0, 0 ]
+		 */
+		color_ctrl.bits.src_alpha_mode = RGA_ALPHA_STRAIGHT;
+		color_ctrl.bits.src_factor_mode = RGA_ALPHA_ZERO;
+
+		color_ctrl.bits.dst_alpha_mode = RGA_ALPHA_STRAIGHT;
+		color_ctrl.bits.dst_factor_mode = RGA_ALPHA_ZERO;
+
+		break;
+
+	default:
+		break;
+	}
+
+	alpha_ctrl.bits.src_blend_mode = color_ctrl.bits.src_blend_mode;
+	alpha_ctrl.bits.dst_blend_mode = color_ctrl.bits.dst_blend_mode;
+
+	alpha_ctrl.bits.src_alpha_cal_mode = color_ctrl.bits.src_alpha_cal_mode;
+	alpha_ctrl.bits.dst_alpha_cal_mode = color_ctrl.bits.dst_alpha_cal_mode;
+
+	alpha_ctrl.bits.src_alpha_mode = color_ctrl.bits.src_alpha_mode;
+	alpha_ctrl.bits.src_factor_mode = color_ctrl.bits.src_factor_mode;
+
+	alpha_ctrl.bits.dst_alpha_mode = color_ctrl.bits.dst_alpha_mode;
+	alpha_ctrl.bits.dst_factor_mode = color_ctrl.bits.dst_factor_mode;
+
+	reg =
+		((reg & (~m_RGA2_ALPHA_CTRL0_SW_ALPHA_ROP_0)) |
 		 (s_RGA2_ALPHA_CTRL0_SW_ALPHA_ROP_0(msg->alpha_rop_flag)));
-	reg0 =
-		((reg0 & (~m_RGA2_ALPHA_CTRL0_SW_ALPHA_ROP_SEL)) |
+	reg =
+		((reg & (~m_RGA2_ALPHA_CTRL0_SW_ALPHA_ROP_SEL)) |
 		 (s_RGA2_ALPHA_CTRL0_SW_ALPHA_ROP_SEL
 		 (msg->alpha_rop_flag >> 1)));
-	reg0 =
-		((reg0 & (~m_RGA2_ALPHA_CTRL0_SW_ROP_MODE)) |
+	reg =
+		((reg & (~m_RGA2_ALPHA_CTRL0_SW_ROP_MODE)) |
 		 (s_RGA2_ALPHA_CTRL0_SW_ROP_MODE(msg->rop_mode)));
-	reg0 =
-		((reg0 & (~m_RGA2_ALPHA_CTRL0_SW_SRC_GLOBAL_ALPHA)) |
+	reg =
+		((reg & (~m_RGA2_ALPHA_CTRL0_SW_SRC_GLOBAL_ALPHA)) |
 		 (s_RGA2_ALPHA_CTRL0_SW_SRC_GLOBAL_ALPHA
-		 (msg->src_a_global_val)));
-	reg0 =
-		((reg0 & (~m_RGA2_ALPHA_CTRL0_SW_DST_GLOBAL_ALPHA)) |
+		 ((uint8_t)config->fg_global_alpha_value)));
+	reg =
+		((reg & (~m_RGA2_ALPHA_CTRL0_SW_DST_GLOBAL_ALPHA)) |
 		 (s_RGA2_ALPHA_CTRL0_SW_DST_GLOBAL_ALPHA
-		 (msg->dst_a_global_val)));
+		 ((uint8_t)config->bg_global_alpha_value)));
 
-	reg1 =
-		((reg1 & (~m_RGA2_ALPHA_CTRL1_SW_DST_COLOR_M0)) |
-		 (s_RGA2_ALPHA_CTRL1_SW_DST_COLOR_M0
-		 (msg->alpha_mode_0 >> 15)));
-	reg1 =
-		((reg1 & (~m_RGA2_ALPHA_CTRL1_SW_SRC_COLOR_M0)) |
-		 (s_RGA2_ALPHA_CTRL1_SW_SRC_COLOR_M0
-		 (msg->alpha_mode_0 >> 7)));
-	reg1 =
-		((reg1 & (~m_RGA2_ALPHA_CTRL1_SW_DST_FACTOR_M0)) |
-		 (s_RGA2_ALPHA_CTRL1_SW_DST_FACTOR_M0
-		 (msg->alpha_mode_0 >> 12)));
-	reg1 =
-		((reg1 & (~m_RGA2_ALPHA_CTRL1_SW_SRC_FACTOR_M0)) |
-		 (s_RGA2_ALPHA_CTRL1_SW_SRC_FACTOR_M0
-		 (msg->alpha_mode_0 >> 4)));
-	reg1 =
-		((reg1 & (~m_RGA2_ALPHA_CTRL1_SW_DST_ALPHA_CAL_M0)) |
-		 (s_RGA2_ALPHA_CTRL1_SW_DST_ALPHA_CAL_M0
-		 (msg->alpha_mode_0 >> 11)));
-	reg1 =
-		((reg1 & (~m_RGA2_ALPHA_CTRL1_SW_SRC_ALPHA_CAL_M0)) |
-		 (s_RGA2_ALPHA_CTRL1_SW_SRC_ALPHA_CAL_M0
-		 (msg->alpha_mode_0 >> 3)));
-	reg1 =
-		((reg1 & (~m_RGA2_ALPHA_CTRL1_SW_DST_BLEND_M0)) |
-		 (s_RGA2_ALPHA_CTRL1_SW_DST_BLEND_M0
-		 (msg->alpha_mode_0 >> 9)));
-	reg1 =
-		((reg1 & (~m_RGA2_ALPHA_CTRL1_SW_SRC_BLEND_M0)) |
-		 (s_RGA2_ALPHA_CTRL1_SW_SRC_BLEND_M0
-		 (msg->alpha_mode_0 >> 1)));
-	reg1 =
-		((reg1 & (~m_RGA2_ALPHA_CTRL1_SW_DST_ALPHA_M0)) |
-		 (s_RGA2_ALPHA_CTRL1_SW_DST_ALPHA_M0
-		 (msg->alpha_mode_0 >> 8)));
-	reg1 =
-		((reg1 & (~m_RGA2_ALPHA_CTRL1_SW_SRC_ALPHA_M0)) |
-		 (s_RGA2_ALPHA_CTRL1_SW_SRC_ALPHA_M0
-		 (msg->alpha_mode_0 >> 0)));
+	*bRGA_ALPHA_CTRL0 = reg;
+	*bRGA_ALPHA_CTRL1 = color_ctrl.value | (alpha_ctrl.value << 16);
 
-	reg1 =
-		((reg1 & (~m_RGA2_ALPHA_CTRL1_SW_DST_FACTOR_M1)) |
-		 (s_RGA2_ALPHA_CTRL1_SW_DST_FACTOR_M1
-		 (msg->alpha_mode_1 >> 12)));
-	reg1 =
-		((reg1 & (~m_RGA2_ALPHA_CTRL1_SW_SRC_FACTOR_M1)) |
-		 (s_RGA2_ALPHA_CTRL1_SW_SRC_FACTOR_M1
-		 (msg->alpha_mode_1 >> 4)));
-	reg1 =
-		((reg1 & (~m_RGA2_ALPHA_CTRL1_SW_DST_ALPHA_CAL_M1)) |
-		 (s_RGA2_ALPHA_CTRL1_SW_DST_ALPHA_CAL_M1
-		 (msg->alpha_mode_1 >> 11)));
-	reg1 =
-		((reg1 & (~m_RGA2_ALPHA_CTRL1_SW_SRC_ALPHA_CAL_M1)) |
-		 (s_RGA2_ALPHA_CTRL1_SW_SRC_ALPHA_CAL_M1
-		 (msg->alpha_mode_1 >> 3)));
-	reg1 =
-		((reg1 & (~m_RGA2_ALPHA_CTRL1_SW_DST_BLEND_M1)) |
-		 (s_RGA2_ALPHA_CTRL1_SW_DST_BLEND_M1(msg->alpha_mode_1 >> 9)));
-	reg1 =
-		((reg1 & (~m_RGA2_ALPHA_CTRL1_SW_SRC_BLEND_M1)) |
-		 (s_RGA2_ALPHA_CTRL1_SW_SRC_BLEND_M1(msg->alpha_mode_1 >> 1)));
-	reg1 =
-		((reg1 & (~m_RGA2_ALPHA_CTRL1_SW_DST_ALPHA_M1)) |
-		 (s_RGA2_ALPHA_CTRL1_SW_DST_ALPHA_M1(msg->alpha_mode_1 >> 8)));
-	reg1 =
-		((reg1 & (~m_RGA2_ALPHA_CTRL1_SW_SRC_ALPHA_M1)) |
-		 (s_RGA2_ALPHA_CTRL1_SW_SRC_ALPHA_M1(msg->alpha_mode_1 >> 0)));
-
-	*bRGA_ALPHA_CTRL0 = reg0;
-	*bRGA_ALPHA_CTRL1 = reg1;
 
 	if ((msg->alpha_rop_flag >> 2) & 1) {
 		*bRGA_FADING_CTRL = (1 << 24) | (msg->fading_b_value << 16) |
@@ -1776,8 +1925,6 @@
 static void rga_cmd_to_rga2_cmd(struct rga_scheduler_t *scheduler,
 				struct rga_req *req_rga, struct rga2_req *req)
 {
-	u16 alpha_mode_0, alpha_mode_1;
-
 	if (req_rga->render_mode == 6)
 		req->render_mode = UPDATE_PALETTE_TABLE_MODE;
 	else if (req_rga->render_mode == 7)
@@ -1877,6 +2024,8 @@
 
 	req->palette_mode = req_rga->palette_mode;
 	req->yuv2rgb_mode = req_rga->yuv2rgb_mode;
+	if (req_rga->full_csc.flag & 0x1)
+		req->full_csc_en = 1;
 	req->endian_mode = req_rga->endian_mode;
 	req->rgb2yuv_mode = 0;
 
@@ -1918,110 +2067,52 @@
 
 	if (((req_rga->alpha_rop_flag) & 1)) {
 		if ((req_rga->alpha_rop_flag >> 3) & 1) {
-			/* porter duff alpha enable */
-			switch (req_rga->PD_mode) {
-			/* dst = 0 */
-			case 0:
-				break;
-			/* dst = src */
-			case 1:
-				req->alpha_mode_0 = 0x0212;
-				req->alpha_mode_1 = 0x0212;
-				break;
-			/* dst = dst */
-			case 2:
-				req->alpha_mode_0 = 0x1202;
-				req->alpha_mode_1 = 0x1202;
-				break;
-			/* dst = (256*sc + (256 - sa)*dc) >> 8 */
-			case 3:
-				if ((req_rga->alpha_rop_mode & 3) == 0) {
-					/* both use globalAlpha. */
-					alpha_mode_0 = 0x3010;
-					alpha_mode_1 = 0x3010;
-				} else if ((req_rga->alpha_rop_mode & 3) == 1) {
-					/* Do not use globalAlpha. */
-					alpha_mode_0 = 0x3212;
-					alpha_mode_1 = 0x3212;
-				} else if ((req_rga->alpha_rop_mode & 3) == 2) {
-					/*
-					 * dst use globalAlpha,
-					 * and dst has pixelAlpha.
-					 */
-					alpha_mode_0 = 0x3014;
-					alpha_mode_1 = 0x3014;
-				} else {
-					/*
-					 * dst use globalAlpha, and
-					 * dst does not have pixelAlpha.
-					 */
-					alpha_mode_0 = 0x3012;
-					alpha_mode_1 = 0x3012;
-				}
-				req->alpha_mode_0 = alpha_mode_0;
-				req->alpha_mode_1 = alpha_mode_1;
-				break;
-			/* dst = (sc*(256-da) + 256*dc) >> 8 */
-			case 4:
-				/* Do not use globalAlpha. */
-				req->alpha_mode_0 = 0x1232;
-				req->alpha_mode_1 = 0x1232;
-				break;
-			/* dst = (da*sc) >> 8 */
-			case 5:
-				break;
-			/* dst = (sa*dc) >> 8 */
-			case 6:
-				break;
-			/* dst = ((256-da)*sc) >> 8 */
-			case 7:
-				break;
-			/* dst = ((256-sa)*dc) >> 8 */
-			case 8:
-				break;
-			/* dst = (da*sc + (256-sa)*dc) >> 8 */
-			case 9:
-				req->alpha_mode_0 = 0x3040;
-				req->alpha_mode_1 = 0x3040;
-				break;
-			/* dst = ((256-da)*sc + (sa*dc)) >> 8 */
-			case 10:
-				break;
-			/* dst = ((256-da)*sc + (256-sa)*dc) >> 8 */
-			case 11:
-				break;
-			case 12:
-				req->alpha_mode_0 = 0x0010;
-				req->alpha_mode_1 = 0x0820;
-				break;
-			default:
-				break;
-			}
+			req->alpha_config.enable = true;
 
-			if (req->osd_info.enable) {
-				/* set dst(osd_block) real color mode */
-				if (req->alpha_mode_0 & (0x01 << 9))
-					req->alpha_mode_0 |= (1 << 15);
-			}
-
-			/* Real color mode */
 			if ((req_rga->alpha_rop_flag >> 9) & 1) {
-				if (req->alpha_mode_0 & (0x01 << 1))
-					req->alpha_mode_0 |= (1 << 7);
-				if (req->alpha_mode_0 & (0x01 << 9))
-					req->alpha_mode_0 |= (1 << 15);
+				req->alpha_config.fg_pre_multiplied = false;
+				req->alpha_config.bg_pre_multiplied = false;
+			} else if (req->osd_info.enable) {
+				req->alpha_config.fg_pre_multiplied = true;
+				/* set dst(osd_block) real color mode */
+				req->alpha_config.bg_pre_multiplied = false;
+			} else {
+				req->alpha_config.fg_pre_multiplied = true;
+				req->alpha_config.bg_pre_multiplied = true;
 			}
-		} else {
-			if ((req_rga->alpha_rop_mode & 3) == 0) {
-				req->alpha_mode_0 = 0x3040;
-				req->alpha_mode_1 = 0x3040;
-			} else if ((req_rga->alpha_rop_mode & 3) == 1) {
-				req->alpha_mode_0 = 0x3042;
-				req->alpha_mode_1 = 0x3242;
-			} else if ((req_rga->alpha_rop_mode & 3) == 2) {
-				req->alpha_mode_0 = 0x3044;
-				req->alpha_mode_1 = 0x3044;
+
+			req->alpha_config.fg_pixel_alpha_en = rga_is_alpha_format(req->src.format);
+			if (req->bitblt_mode)
+				req->alpha_config.bg_pixel_alpha_en =
+					rga_is_alpha_format(req->src1.format);
+			else
+				req->alpha_config.bg_pixel_alpha_en =
+					rga_is_alpha_format(req->dst.format);
+
+			if (req_rga->feature.global_alpha_en) {
+				if (req_rga->fg_global_alpha < 0xff) {
+					req->alpha_config.fg_global_alpha_en = true;
+					req->alpha_config.fg_global_alpha_value =
+						req_rga->fg_global_alpha;
+				} else if (!req->alpha_config.fg_pixel_alpha_en) {
+					req->alpha_config.fg_global_alpha_en = true;
+					req->alpha_config.fg_global_alpha_value = 0xff;
+				}
+
+				if (req_rga->bg_global_alpha < 0xff) {
+					req->alpha_config.bg_global_alpha_en = true;
+					req->alpha_config.bg_global_alpha_value =
+						req_rga->bg_global_alpha;
+				} else if (!req->alpha_config.bg_pixel_alpha_en) {
+					req->alpha_config.bg_global_alpha_en = true;
+					req->alpha_config.bg_global_alpha_value = 0xff;
+				}
+			} else {
+				req->alpha_config.bg_global_alpha_value = 0xff;
+				req->alpha_config.bg_global_alpha_value = 0xff;
 			}
+
+			req->alpha_config.mode = req_rga->PD_mode;
 		}
 	}
 
@@ -2094,9 +2185,9 @@
 	}
 
 	if (i == RGA_RESET_TIMEOUT)
-		pr_err("RAG2 soft reset timeout.\n");
+		pr_err("RAG2 core[%d] soft reset timeout.\n", scheduler->core);
 	else
-		pr_info("RGA2 soft reset complete.\n");
+		pr_info("RGA2 core[%d] soft reset complete.\n", scheduler->core);
 
 }
 
@@ -2216,11 +2307,14 @@
 	pr_info("mmu: src=%.2x src1=%.2x dst=%.2x els=%.2x\n",
 		req->mmu_info.src0_mmu_flag, req->mmu_info.src1_mmu_flag,
 		req->mmu_info.dst_mmu_flag, req->mmu_info.els_mmu_flag);
-	pr_info("alpha: flag %x mode0=%x mode1=%x\n", req->alpha_rop_flag,
-		req->alpha_mode_0, req->alpha_mode_1);
-	pr_info("blend mode is %s\n",
-		rga_get_blend_mode_str(req->alpha_rop_flag, req->alpha_mode_0,
-					req->alpha_mode_1));
+	pr_info("alpha: flag %x mode=%s\n",
+		req->alpha_rop_flag, rga_get_blend_mode_str(req->alpha_config.mode));
+	pr_info("alpha: pre_multi=[%d,%d] pixl=[%d,%d] glb=[%d,%d]\n",
+		req->alpha_config.fg_pre_multiplied, req->alpha_config.bg_pre_multiplied,
+		req->alpha_config.fg_pixel_alpha_en, req->alpha_config.bg_pixel_alpha_en,
+		req->alpha_config.fg_global_alpha_en, req->alpha_config.bg_global_alpha_en);
+	pr_info("alpha: fg_global_alpha=%x bg_global_alpha=%x\n",
+		req->alpha_config.fg_global_alpha_value, req->alpha_config.bg_global_alpha_value);
 	pr_info("yuv2rgb mode is %x\n", req->yuv2rgb_mode);
 }
 
@@ -2239,7 +2333,24 @@
 	memset(&req, 0x0, sizeof(req));
 
 	rga_cmd_to_rga2_cmd(scheduler, &job->rga_command_base, &req);
-	memcpy(&job->full_csc, &job->rga_command_base.full_csc, sizeof(job->full_csc));
+	if (req.full_csc_en) {
+		memcpy(&job->full_csc, &job->rga_command_base.full_csc, sizeof(job->full_csc));
+		if (job->rga_command_base.feature.full_csc_clip_en) {
+			memcpy(&job->full_csc_clip, &job->rga_command_base.full_csc_clip,
+			       sizeof(job->full_csc_clip));
+		} else {
+			job->full_csc_clip.y.max = 0xff;
+			job->full_csc_clip.y.min = 0x0;
+			job->full_csc_clip.uv.max = 0xff;
+			job->full_csc_clip.uv.min = 0x0;
+		}
+
+	} else {
+		job->full_csc_clip.y.max = 0xff;
+		job->full_csc_clip.y.min = 0x0;
+		job->full_csc_clip.uv.max = 0xff;
+		job->full_csc_clip.uv.min = 0x0;
+	}
 	memcpy(&job->pre_intr_info, &job->rga_command_base.pre_intr_info,
 	       sizeof(job->pre_intr_info));
 
@@ -2390,19 +2501,13 @@
 
 static void rga2_set_reg_full_csc(struct rga_job *job, struct rga_scheduler_t *scheduler)
 {
-	uint8_t clip_y_max, clip_y_min;
-	uint8_t clip_uv_max, clip_uv_min;
-
-	clip_y_max = 0xff;
-	clip_y_min = 0x0;
-	clip_uv_max = 0xff;
-	clip_uv_min = 0;
-
 	/* full csc coefficient */
 	/* Y coefficient */
-	rga_write(job->full_csc.coe_y.r_v | (clip_y_max << 16) | (clip_y_min << 24),
+	rga_write(job->full_csc.coe_y.r_v |
+		  (job->full_csc_clip.y.max << 16) | (job->full_csc_clip.y.min << 24),
 		  RGA2_DST_CSC_00, scheduler);
-	rga_write(job->full_csc.coe_y.g_y | (clip_uv_max << 16) | (clip_uv_min << 24),
+	rga_write(job->full_csc.coe_y.g_y |
+		  (job->full_csc_clip.uv.max << 16) | (job->full_csc_clip.uv.min << 24),
 		  RGA2_DST_CSC_01, scheduler);
 	rga_write(job->full_csc.coe_y.b_u, RGA2_DST_CSC_02, scheduler);
 	rga_write(job->full_csc.coe_y.off, RGA2_DST_CSC_OFF0, scheduler);
diff --git a/kernel/drivers/video/rockchip/rga3/rga3_reg_info.c b/kernel/drivers/video/rockchip/rga3/rga3_reg_info.c
index f6e4345..5c1945b 100644
--- a/kernel/drivers/video/rockchip/rga3/rga3_reg_info.c
+++ b/kernel/drivers/video/rockchip/rga3/rga3_reg_info.c
@@ -390,9 +390,9 @@
 	 */
 
 	/* do not use win0 src size except fbcd */
-	*bRGA3_WIN0_SRC_SIZE = (msg->win0.src_act_w +
-		msg->win0.x_offset) | ((msg->win0.y_offset +
-		msg->win0.src_act_h) << 16);
+	/* in FBCD, src_width needs to be aligned at 16 */
+	*bRGA3_WIN0_SRC_SIZE = ALIGN(msg->win0.src_act_w + msg->win0.x_offset, 16) |
+			       (ALIGN(msg->win0.y_offset + msg->win0.src_act_h, 16) << 16);
 	*bRGA3_WIN0_ACT_SIZE =
 		msg->win0.src_act_w | (msg->win0.src_act_h << 16);
 	*bRGA3_WIN0_DST_SIZE =
@@ -1028,6 +1028,9 @@
 	u32 *bRGA3_OVLP_OFF;
 
 	u32 reg;
+	union rga3_color_ctrl top_color_ctrl, bottom_color_ctrl;
+	union rga3_alpha_ctrl top_alpha_ctrl, bottom_alpha_ctrl;
+	struct rga_alpha_config *config;
 
 	bRGA_OVERLAP_TOP_CTRL = (u32 *) (base + RGA3_OVLP_TOP_CTRL_OFFSET);
 	bRGA_OVERLAP_BOT_CTRL = (u32 *) (base + RGA3_OVLP_BOT_CTRL_OFFSET);
@@ -1039,98 +1042,249 @@
 
 	/* Alpha blend */
 	/*bot -> win0(dst), top -> win1(src). */
-	reg = 0;
-	reg =
-		((reg & (~m_RGA3_OVLP_TOP_CTRL_SW_TOP_COLOR_M0)) |
-		 (s_RGA3_OVLP_TOP_CTRL_SW_TOP_COLOR_M0
-		 (msg->alpha_mode_0 >> 7)));
-	reg =
-		((reg & (~m_RGA3_OVLP_TOP_CTRL_SW_TOP_ALPHA_M0)) |
-		 (s_RGA3_OVLP_TOP_CTRL_SW_TOP_ALPHA_M0
-		 (msg->alpha_mode_0 >> 0)));
-	reg =
-		((reg & (~m_RGA3_OVLP_TOP_CTRL_SW_TOP_BLEND_M0)) |
-		 (s_RGA3_OVLP_TOP_CTRL_SW_TOP_BLEND_M0
-		 (msg->alpha_mode_0 >> 1)));
-	reg =
-		((reg & (~m_RGA3_OVLP_TOP_CTRL_SW_TOP_ALPHA_CAL_M0)) |
-		 (s_RGA3_OVLP_TOP_CTRL_SW_TOP_ALPHA_CAL_M0
-		 (msg->alpha_mode_0 >> 3)));
-	reg =
-		((reg & (~m_RGA3_OVLP_TOP_CTRL_SW_TOP_FACTOR_M0)) |
-		 (s_RGA3_OVLP_TOP_CTRL_SW_TOP_FACTOR_M0
-		 (msg->alpha_mode_0 >> 4)));
-	reg =
-		((reg & (~m_RGA3_OVLP_TOP_CTRL_SW_TOP_GLOBAL_ALPHA)) |
-		 (s_RGA3_OVLP_TOP_CTRL_SW_TOP_GLOBAL_ALPHA
-		 (msg->win1_a_global_val)));
-	*bRGA_OVERLAP_TOP_CTRL = reg;
+	top_color_ctrl.value = 0;
+	bottom_color_ctrl.value = 0;
+	top_alpha_ctrl.value = 0;
+	bottom_alpha_ctrl.value = 0;
+	config = &msg->alpha_config;
 
-	reg = 0;
-	reg =
-		((reg & (~m_RGA3_OVLP_BOT_CTRL_SW_BOT_COLOR_M0)) |
-		 (s_RGA3_OVLP_BOT_CTRL_SW_BOT_COLOR_M0
-		 (msg->alpha_mode_0 >> 15)));
-	reg =
-		((reg & (~m_RGA3_OVLP_BOT_CTRL_SW_BOT_ALPHA_M0)) |
-		 (s_RGA3_OVLP_BOT_CTRL_SW_BOT_ALPHA_M0
-		 (msg->alpha_mode_0 >> 8)));
-	reg =
-		((reg & (~m_RGA3_OVLP_BOT_CTRL_SW_BOT_BLEND_M0)) |
-		 (s_RGA3_OVLP_BOT_CTRL_SW_BOT_BLEND_M0
-		 (msg->alpha_mode_0 >> 9)));
-	reg =
-		((reg & (~m_RGA3_OVLP_BOT_CTRL_SW_BOT_ALPHA_CAL_M0)) |
-		 (s_RGA3_OVLP_BOT_CTRL_SW_BOT_ALPHA_CAL_M0
-		 (msg->alpha_mode_0 >> 11)));
-	reg =
-		((reg & (~m_RGA3_OVLP_BOT_CTRL_SW_BOT_FACTOR_M0)) |
-		 (s_RGA3_OVLP_BOT_CTRL_SW_BOT_FACTOR_M0
-		 (msg->alpha_mode_0 >> 12)));
-	reg =
-		((reg & (~m_RGA3_OVLP_BOT_CTRL_SW_BOT_GLOBAL_ALPHA)) |
-		 (s_RGA3_OVLP_BOT_CTRL_SW_BOT_GLOBAL_ALPHA
-		 (msg->win0_a_global_val)));
-	*bRGA_OVERLAP_BOT_CTRL = reg;
+	if (config->fg_pixel_alpha_en)
+		top_color_ctrl.bits.blend_mode =
+			config->fg_global_alpha_en ? RGA_ALPHA_PER_PIXEL_GLOBAL :
+			RGA_ALPHA_PER_PIXEL;
+	else
+		top_color_ctrl.bits.blend_mode = RGA_ALPHA_GLOBAL;
 
-	reg = 0;
-	reg =
-		((reg & (~m_RGA3_OVLP_TOP_ALPHA_SW_TOP_ALPHA_M1)) |
-		 (s_RGA3_OVLP_TOP_ALPHA_SW_TOP_ALPHA_M1
-		 (msg->alpha_mode_1 >> 0)));
-	reg =
-		((reg & (~m_RGA3_OVLP_TOP_ALPHA_SW_TOP_BLEND_M1)) |
-		 (s_RGA3_OVLP_TOP_ALPHA_SW_TOP_BLEND_M1
-		 (msg->alpha_mode_1 >> 1)));
-	reg =
-		((reg & (~m_RGA3_OVLP_TOP_ALPHA_SW_TOP_ALPHA_CAL_M1)) |
-		 (s_RGA3_OVLP_TOP_ALPHA_SW_TOP_ALPHA_CAL_M1
-		 (msg->alpha_mode_1 >> 3)));
-	reg =
-		((reg & (~m_RGA3_OVLP_TOP_ALPHA_SW_TOP_FACTOR_M1)) |
-		 (s_RGA3_OVLP_TOP_ALPHA_SW_TOP_FACTOR_M1
-		 (msg->alpha_mode_1 >> 4)));
-	*bRGA_OVERLAP_TOP_ALPHA = reg;
+	if (config->bg_pixel_alpha_en)
+		bottom_color_ctrl.bits.blend_mode =
+			config->bg_global_alpha_en ? RGA_ALPHA_PER_PIXEL_GLOBAL :
+			RGA_ALPHA_PER_PIXEL;
+	else
+		bottom_color_ctrl.bits.blend_mode = RGA_ALPHA_GLOBAL;
 
-	reg = 0;
-	reg =
-		((reg & (~m_RGA3_OVLP_BOT_ALPHA_SW_BOT_ALPHA_M1)) |
-		 (s_RGA3_OVLP_BOT_ALPHA_SW_BOT_ALPHA_M1
-		 (msg->alpha_mode_1 >> 8)));
-	reg =
-		((reg & (~m_RGA3_OVLP_BOT_ALPHA_SW_BOT_BLEND_M1)) |
-		 (s_RGA3_OVLP_BOT_ALPHA_SW_BOT_BLEND_M1
-		 (msg->alpha_mode_1 >> 9)));
-	reg =
-		((reg & (~m_RGA3_OVLP_BOT_ALPHA_SW_BOT_ALPHA_CAL_M1)) |
-		 (s_RGA3_OVLP_BOT_ALPHA_SW_BOT_ALPHA_CAL_M1
-		 (msg->alpha_mode_1 >> 11)));
-	reg =
-		((reg & (~m_RGA3_OVLP_BOT_ALPHA_SW_BOT_FACTOR_M1)) |
-		 (s_RGA3_OVLP_BOT_ALPHA_SW_BOT_FACTOR_M1
-		 (msg->alpha_mode_1 >> 12)));
+	/*
+	 * Since the hardware uses 256 as 1, the original alpha value needs to
+	 * be + (alpha >> 7).
+	 */
+	top_color_ctrl.bits.alpha_cal_mode = RGA_ALPHA_SATURATION;
+	bottom_color_ctrl.bits.alpha_cal_mode = RGA_ALPHA_SATURATION;
 
-	*bRGA_OVERLAP_BOT_ALPHA = reg;
+	top_color_ctrl.bits.global_alpha = config->fg_global_alpha_value;
+	bottom_color_ctrl.bits.global_alpha = config->bg_global_alpha_value;
+
+	/* porter duff alpha enable */
+	switch (config->mode) {
+	case RGA_ALPHA_BLEND_SRC:
+		/*
+		 * SRC mode:
+		 *	Sf = 1, Df = 0;
+		 *	[Rc,Ra] = [Sc,Sa];
+		 */
+		top_color_ctrl.bits.alpha_mode = RGA_ALPHA_STRAIGHT;
+		top_color_ctrl.bits.factor_mode = RGA_ALPHA_ONE;
+
+		bottom_color_ctrl.bits.alpha_mode = RGA_ALPHA_STRAIGHT;
+		bottom_color_ctrl.bits.factor_mode = RGA_ALPHA_ZERO;
+
+		break;
+
+	case RGA_ALPHA_BLEND_DST:
+		/*
+		 * SRC mode:
+		 *	Sf = 0, Df = 1;
+		 *	[Rc,Ra] = [Dc,Da];
+		 */
+		top_color_ctrl.bits.alpha_mode = RGA_ALPHA_STRAIGHT;
+		top_color_ctrl.bits.factor_mode = RGA_ALPHA_ZERO;
+
+		bottom_color_ctrl.bits.alpha_mode = RGA_ALPHA_STRAIGHT;
+		bottom_color_ctrl.bits.factor_mode = RGA_ALPHA_ONE;
+
+		break;
+
+	case RGA_ALPHA_BLEND_SRC_OVER:
+		/*
+		 * SRC-OVER mode:
+		 *	Sf = 1, Df = (1 - Sa)
+		 *	[Rc,Ra] = [ Sc + (1 - Sa) * Dc, Sa + (1 - Sa) * Da ]
+		 */
+		top_color_ctrl.bits.alpha_mode = RGA_ALPHA_STRAIGHT;
+		top_color_ctrl.bits.factor_mode = RGA_ALPHA_ONE;
+
+		bottom_color_ctrl.bits.alpha_mode = RGA_ALPHA_STRAIGHT;
+		bottom_color_ctrl.bits.factor_mode = RGA_ALPHA_OPPOSITE_INVERSE;
+
+		break;
+
+	case RGA_ALPHA_BLEND_DST_OVER:
+		/*
+		 * DST-OVER mode:
+		 *	Sf = (1 - Da) , Df = 1
+		 *	[Rc,Ra] = [ Sc * (1 - Da) + Dc, Sa * (1 - Da) + Da ]
+		 */
+		top_color_ctrl.bits.alpha_mode = RGA_ALPHA_STRAIGHT;
+		top_color_ctrl.bits.factor_mode = RGA_ALPHA_OPPOSITE_INVERSE;
+
+		bottom_color_ctrl.bits.alpha_mode = RGA_ALPHA_STRAIGHT;
+		bottom_color_ctrl.bits.factor_mode = RGA_ALPHA_ONE;
+
+		break;
+
+	case RGA_ALPHA_BLEND_SRC_IN:
+		/*
+		 * SRC-IN mode:
+		 *	Sf = Da , Df = 0
+		 *	[Rc,Ra] = [ Sc * Da, Sa * Da ]
+		 */
+		top_color_ctrl.bits.alpha_mode = RGA_ALPHA_STRAIGHT;
+		top_color_ctrl.bits.factor_mode = RGA_ALPHA_OPPOSITE;
+
+		bottom_color_ctrl.bits.alpha_mode = RGA_ALPHA_STRAIGHT;
+		bottom_color_ctrl.bits.factor_mode = RGA_ALPHA_ZERO;
+
+		break;
+
+	case RGA_ALPHA_BLEND_DST_IN:
+		/*
+		 * DST-IN mode:
+		 *	Sf = 0 , Df = Sa
+		 *	[Rc,Ra] = [ Dc * Sa, Da * Sa ]
+		 */
+		top_color_ctrl.bits.alpha_mode = RGA_ALPHA_STRAIGHT;
+		top_color_ctrl.bits.factor_mode = RGA_ALPHA_ZERO;
+
+		bottom_color_ctrl.bits.alpha_mode = RGA_ALPHA_STRAIGHT;
+		bottom_color_ctrl.bits.factor_mode = RGA_ALPHA_OPPOSITE;
+
+		break;
+
+	case RGA_ALPHA_BLEND_SRC_OUT:
+		/*
+		 * SRC-OUT mode:
+		 *	Sf = (1 - Da) , Df = 0
+		 *	[Rc,Ra] = [ Sc * (1 - Da), Sa * (1 - Da) ]
+		 */
+		top_color_ctrl.bits.alpha_mode = RGA_ALPHA_STRAIGHT;
+		top_color_ctrl.bits.factor_mode = RGA_ALPHA_OPPOSITE_INVERSE;
+
+		bottom_color_ctrl.bits.alpha_mode = RGA_ALPHA_STRAIGHT;
+		bottom_color_ctrl.bits.factor_mode = RGA_ALPHA_ZERO;
+
+		break;
+
+	case RGA_ALPHA_BLEND_DST_OUT:
+		/*
+		 * DST-OUT mode:
+		 *	Sf = 0 , Df = (1 - Sa)
+		 *	[Rc,Ra] = [ Dc * (1 - Sa), Da * (1 - Sa) ]
+		 */
+		top_color_ctrl.bits.alpha_mode = RGA_ALPHA_STRAIGHT;
+		top_color_ctrl.bits.factor_mode = RGA_ALPHA_ZERO;
+
+		bottom_color_ctrl.bits.alpha_mode = RGA_ALPHA_STRAIGHT;
+		bottom_color_ctrl.bits.factor_mode = RGA_ALPHA_OPPOSITE_INVERSE;
+
+		break;
+
+	case RGA_ALPHA_BLEND_SRC_ATOP:
+		/*
+		 * SRC-ATOP mode:
+		 *	Sf = Da , Df = (1 - Sa)
+		 *	[Rc,Ra] = [ Sc * Da + Dc * (1 - Sa), Sa * Da + Da * (1 - Sa) ]
+		 */
+		top_color_ctrl.bits.alpha_mode = RGA_ALPHA_STRAIGHT;
+		top_color_ctrl.bits.factor_mode = RGA_ALPHA_OPPOSITE;
+
+		bottom_color_ctrl.bits.alpha_mode = RGA_ALPHA_STRAIGHT;
+		bottom_color_ctrl.bits.factor_mode = RGA_ALPHA_OPPOSITE_INVERSE;
+
+		break;
+
+	case RGA_ALPHA_BLEND_DST_ATOP:
+		/*
+		 * DST-ATOP mode:
+		 *	Sf = (1 - Da) , Df = Sa
+		 *	[Rc,Ra] = [ Sc * (1 - Da) + Dc * Sa, Sa * (1 - Da) + Da * Sa ]
+		 */
+		top_color_ctrl.bits.alpha_mode = RGA_ALPHA_STRAIGHT;
+		top_color_ctrl.bits.factor_mode = RGA_ALPHA_OPPOSITE_INVERSE;
+
+		bottom_color_ctrl.bits.alpha_mode = RGA_ALPHA_STRAIGHT;
+		bottom_color_ctrl.bits.factor_mode = RGA_ALPHA_OPPOSITE;
+
+		break;
+
+	case RGA_ALPHA_BLEND_XOR:
+		/*
+		 * DST-XOR mode:
+		 *	Sf = (1 - Da) , Df = (1 - Sa)
+		 *	[Rc,Ra] = [ Sc * (1 - Da) + Dc * (1 - Sa), Sa * (1 - Da) + Da * (1 - Sa) ]
+		 */
+		top_color_ctrl.bits.alpha_mode = RGA_ALPHA_STRAIGHT;
+		top_color_ctrl.bits.factor_mode = RGA_ALPHA_OPPOSITE_INVERSE;
+
+		bottom_color_ctrl.bits.alpha_mode = RGA_ALPHA_STRAIGHT;
+		bottom_color_ctrl.bits.factor_mode = RGA_ALPHA_OPPOSITE_INVERSE;
+
+		break;
+
+	case RGA_ALPHA_BLEND_CLEAR:
+		/*
+		 * DST-CLEAR mode:
+		 *	Sf = 0 , Df = 0
+		 *	[Rc,Ra] = [ 0, 0 ]
+		 */
+		top_color_ctrl.bits.alpha_mode = RGA_ALPHA_STRAIGHT;
+		top_color_ctrl.bits.factor_mode = RGA_ALPHA_ZERO;
+
+		bottom_color_ctrl.bits.alpha_mode = RGA_ALPHA_STRAIGHT;
+		bottom_color_ctrl.bits.factor_mode = RGA_ALPHA_ZERO;
+
+		break;
+
+	default:
+		break;
+	}
+
+	if (!config->enable && msg->abb_alpha_pass) {
+		/*
+		 * enabled by default bot_blend_m1 && bot_alpha_cal_m1 for src channel(win0)
+		 * In ABB mode, the number will be fetched according to 16*16, so it needs to
+		 * be enabled top_blend_m1 && top_alpha_cal_m1 for dst channel(wr).
+		 */
+		top_color_ctrl.bits.color_mode = RGA_ALPHA_PRE_MULTIPLIED;
+
+		top_alpha_ctrl.bits.blend_mode = RGA_ALPHA_PER_PIXEL;
+		top_alpha_ctrl.bits.alpha_cal_mode = RGA_ALPHA_NO_SATURATION;
+
+		bottom_color_ctrl.bits.color_mode = RGA_ALPHA_PRE_MULTIPLIED;
+
+		bottom_alpha_ctrl.bits.blend_mode = RGA_ALPHA_PER_PIXEL;
+		bottom_alpha_ctrl.bits.alpha_cal_mode = RGA_ALPHA_NO_SATURATION;
+	} else {
+		top_color_ctrl.bits.color_mode =
+			config->fg_pre_multiplied ?
+				RGA_ALPHA_PRE_MULTIPLIED : RGA_ALPHA_NO_PRE_MULTIPLIED;
+
+		top_alpha_ctrl.bits.blend_mode = top_color_ctrl.bits.blend_mode;
+		top_alpha_ctrl.bits.alpha_cal_mode = top_color_ctrl.bits.alpha_cal_mode;
+		top_alpha_ctrl.bits.alpha_mode = top_color_ctrl.bits.alpha_mode;
+		top_alpha_ctrl.bits.factor_mode = top_color_ctrl.bits.factor_mode;
+
+		bottom_color_ctrl.bits.color_mode =
+			config->bg_pre_multiplied ?
+				RGA_ALPHA_PRE_MULTIPLIED : RGA_ALPHA_NO_PRE_MULTIPLIED;
+
+		bottom_alpha_ctrl.bits.blend_mode = bottom_color_ctrl.bits.blend_mode;
+		bottom_alpha_ctrl.bits.alpha_cal_mode = bottom_color_ctrl.bits.alpha_cal_mode;
+		bottom_alpha_ctrl.bits.alpha_mode = bottom_color_ctrl.bits.alpha_mode;
+		bottom_alpha_ctrl.bits.factor_mode = bottom_color_ctrl.bits.factor_mode;
+	}
+
+	*bRGA_OVERLAP_TOP_CTRL = top_color_ctrl.value;
+	*bRGA_OVERLAP_BOT_CTRL = bottom_color_ctrl.value;
+	*bRGA_OVERLAP_TOP_ALPHA = top_alpha_ctrl.value;
+	*bRGA_OVERLAP_BOT_ALPHA = bottom_alpha_ctrl.value;
 
 	/* set RGA_OVERLAP_CTRL */
 	reg = 0;
@@ -1166,9 +1320,8 @@
 	 * warning: if m1 & m0 need config split,need to redesign
 	 * this judge, which consider RGBA8888 format
 	 */
-	if (msg->alpha_mode_1 > 0 && msg->alpha_mode_0 > 0)
-		reg = ((reg & (~m_RGA3_OVLP_CTRL_SW_TOP_ALPHA_EN)) |
-			 (s_RGA3_OVLP_CTRL_SW_TOP_ALPHA_EN(1)));
+	reg = ((reg & (~m_RGA3_OVLP_CTRL_SW_TOP_ALPHA_EN)) |
+	       (s_RGA3_OVLP_CTRL_SW_TOP_ALPHA_EN(config->enable)));
 
 	*bRGA_OVERLAP_CTRL = reg;
 
@@ -1261,7 +1414,6 @@
 /* TODO: common part */
 static void rga_cmd_to_rga3_cmd(struct rga_req *req_rga, struct rga3_req *req)
 {
-	u16 alpha_mode_0, alpha_mode_1;
 	struct rga_img_info_t tmp;
 
 	req->render_mode = BITBLT_MODE;
@@ -1324,8 +1476,8 @@
 	if (!(req_rga->alpha_rop_flag & 1)) {
 		if (!rga_is_alpha_format(req_rga->src.format) &&
 		    rga_is_alpha_format(req_rga->dst.format)) {
-			req->win0_a_global_val = 0xff;
-			req->win1_a_global_val = 0xff;
+			req->alpha_config.fg_global_alpha_value = 0xff;
+			req->alpha_config.bg_global_alpha_value = 0xff;
 		}
 	}
 
@@ -1345,7 +1497,7 @@
 		 * be enabled top_blend_m1 && top_alpha_cal_m1 for dst channel(wr).
 		 */
 		if (rga_is_alpha_format(req_rga->src.format))
-			req->alpha_mode_1 = 0x0a0a;
+			req->abb_alpha_pass = true;
 
 		set_win_info(&req->win0, &req_rga->src);
 
@@ -1375,7 +1527,7 @@
 		 * be enabled bot_blend_m1 && bot_alpha_cal_m1 for src1/dst channel(win0).
 		 */
 		if (rga_is_alpha_format(req_rga->src.format))
-			req->alpha_mode_1 = 0x0a0a;
+			req->abb_alpha_pass = true;
 
 		if (req_rga->pat.yrgb_addr != 0) {
 			if (req_rga->src.yrgb_addr == req_rga->dst.yrgb_addr) {
@@ -1478,103 +1630,43 @@
 	/* Alpha blend mode */
 	if (((req_rga->alpha_rop_flag) & 1)) {
 		if ((req_rga->alpha_rop_flag >> 3) & 1) {
-			/* porter duff alpha enable */
-			switch (req_rga->PD_mode) {
-			/* dst = 0 */
-			case 0:
-				break;
-			/* dst = src */
-			case 1:
-				req->alpha_mode_0 = 0x0212;
-				req->alpha_mode_1 = 0x0212;
-				break;
-			/* dst = dst */
-			case 2:
-				req->alpha_mode_0 = 0x1202;
-				req->alpha_mode_1 = 0x1202;
-				break;
-			/* dst = (256*sc + (256 - sa)*dc) >> 8 */
-			case 3:
-				if ((req_rga->alpha_rop_mode & 3) == 0) {
-					/* both use globalAlpha. */
-					alpha_mode_0 = 0x3010;
-					alpha_mode_1 = 0x3010;
-				} else if ((req_rga->alpha_rop_mode & 3) == 1) {
-					/* Do not use globalAlpha. */
-					alpha_mode_0 = 0x3212;
-					alpha_mode_1 = 0x3212;
-				} else if ((req_rga->alpha_rop_mode & 3) == 2) {
-					/*
-					 * dst use globalAlpha,
-					 * and dst has pixelAlpha.
-					 */
-					alpha_mode_0 = 0x3014;
-					alpha_mode_1 = 0x3014;
-				} else {
-					/*
-					 * dst use globalAlpha,
-					 * and dst does not have pixelAlpha.
-					 */
-					alpha_mode_0 = 0x3012;
-					alpha_mode_1 = 0x3012;
-				}
-				req->alpha_mode_0 = alpha_mode_0;
-				req->alpha_mode_1 = alpha_mode_1;
-				break;
-			/* dst = (sc*(256-da) + 256*dc) >> 8 */
-			case 4:
-				/* Do not use globalAlpha. */
-				req->alpha_mode_0 = 0x1232;
-				req->alpha_mode_1 = 0x1232;
-				break;
-			/* dst = (da*sc) >> 8 */
-			case 5:
-				break;
-			/* dst = (sa*dc) >> 8 */
-			case 6:
-				break;
-			/* dst = ((256-da)*sc) >> 8 */
-			case 7:
-				break;
-			/* dst = ((256-sa)*dc) >> 8 */
-			case 8:
-				break;
-			/* dst = (da*sc + (256-sa)*dc) >> 8 */
-			case 9:
-				req->alpha_mode_0 = 0x3040;
-				req->alpha_mode_1 = 0x3040;
-				break;
-			/* dst = ((256-da)*sc + (sa*dc)) >> 8 */
-			case 10:
-				break;
-			/* dst = ((256-da)*sc + (256-sa)*dc) >> 8 */
-			case 11:
-				break;
-			case 12:
-				req->alpha_mode_0 = 0x0010;
-				req->alpha_mode_1 = 0x0820;
-				break;
-			default:
-				break;
-			}
-			/* Real color mode */
+			req->alpha_config.enable = true;
+
 			if ((req_rga->alpha_rop_flag >> 9) & 1) {
-				if (req->alpha_mode_0 & (0x01 << 1))
-					req->alpha_mode_0 |= (1 << 7);
-				if (req->alpha_mode_0 & (0x01 << 9))
-					req->alpha_mode_0 |= (1 << 15);
+				req->alpha_config.fg_pre_multiplied = false;
+				req->alpha_config.bg_pre_multiplied = false;
+			} else {
+				req->alpha_config.fg_pre_multiplied = true;
+				req->alpha_config.bg_pre_multiplied = true;
 			}
-		} else {
-			if ((req_rga->alpha_rop_mode & 3) == 0) {
-				req->alpha_mode_0 = 0x3040;
-				req->alpha_mode_1 = 0x3040;
-			} else if ((req_rga->alpha_rop_mode & 3) == 1) {
-				req->alpha_mode_0 = 0x3042;
-				req->alpha_mode_1 = 0x3242;
-			} else if ((req_rga->alpha_rop_mode & 3) == 2) {
-				req->alpha_mode_0 = 0x3044;
-				req->alpha_mode_1 = 0x3044;
+
+			req->alpha_config.fg_pixel_alpha_en = rga_is_alpha_format(req->win1.format);
+			req->alpha_config.bg_pixel_alpha_en = rga_is_alpha_format(req->win0.format);
+
+			if (req_rga->feature.global_alpha_en) {
+				if (req_rga->fg_global_alpha < 0xff) {
+					req->alpha_config.fg_global_alpha_en = true;
+					req->alpha_config.fg_global_alpha_value =
+						req_rga->fg_global_alpha;
+				} else if (!req->alpha_config.fg_pixel_alpha_en) {
+					req->alpha_config.fg_global_alpha_en = true;
+					req->alpha_config.fg_global_alpha_value = 0xff;
+				}
+
+				if (req_rga->bg_global_alpha < 0xff) {
+					req->alpha_config.bg_global_alpha_en = true;
+					req->alpha_config.bg_global_alpha_value =
+						req_rga->bg_global_alpha;
+				} else if (!req->alpha_config.bg_pixel_alpha_en) {
+					req->alpha_config.bg_global_alpha_en = true;
+					req->alpha_config.bg_global_alpha_value = 0xff;
+				}
+			} else {
+				req->alpha_config.bg_global_alpha_value = 0xff;
+				req->alpha_config.bg_global_alpha_value = 0xff;
 			}
+
+			req->alpha_config.mode = req_rga->PD_mode;
 		}
 	}
 
@@ -1651,10 +1743,11 @@
 	}
 
 	if (i == RGA_RESET_TIMEOUT)
-		pr_err("RGA3 soft reset timeout. SYS_CTRL[0x%x], RO_SRST[0x%x]\n",
-		       rga_read(RGA3_SYS_CTRL, scheduler), rga_read(RGA3_RO_SRST, scheduler));
+		pr_err("RGA3 core[%d] soft reset timeout. SYS_CTRL[0x%x], RO_SRST[0x%x]\n",
+		       scheduler->core, rga_read(RGA3_SYS_CTRL, scheduler),
+		       rga_read(RGA3_RO_SRST, scheduler));
 	else
-		pr_info("RGA3 soft reset complete.\n");
+		pr_info("RGA3 core[%d] soft reset complete.\n", scheduler->core);
 }
 
 static int rga3_scale_check(const struct rga3_req *req)
@@ -1821,11 +1914,14 @@
 	pr_info("mmu: win0 = %.2x win1 = %.2x wr = %.2x\n",
 		req->mmu_info.src0_mmu_flag, req->mmu_info.src1_mmu_flag,
 		req->mmu_info.dst_mmu_flag);
-	pr_info("alpha: flag %x mode0=%x mode1=%x\n", req->alpha_rop_flag,
-		req->alpha_mode_0, req->alpha_mode_1);
-	pr_info("blend mode is %s\n",
-		rga_get_blend_mode_str(req->alpha_rop_flag, req->alpha_mode_0,
-					req->alpha_mode_1));
+	pr_info("alpha: flag %x mode=%s\n",
+		req->alpha_rop_flag, rga_get_blend_mode_str(req->alpha_config.mode));
+	pr_info("alpha: pre_multi=[%d,%d] pixl=[%d,%d] glb=[%d,%d]\n",
+		req->alpha_config.fg_pre_multiplied, req->alpha_config.bg_pre_multiplied,
+		req->alpha_config.fg_pixel_alpha_en, req->alpha_config.bg_pixel_alpha_en,
+		req->alpha_config.fg_global_alpha_en, req->alpha_config.bg_global_alpha_en);
+	pr_info("alpha: fg_global_alpha=%x bg_global_alpha=%x\n",
+		req->alpha_config.fg_global_alpha_value, req->alpha_config.bg_global_alpha_value);
 	pr_info("yuv2rgb mode is %x\n", req->yuv2rgb_mode);
 }
 
diff --git a/kernel/drivers/video/rockchip/rga3/rga_common.c b/kernel/drivers/video/rockchip/rga3/rga_common.c
index dfe1103..6f8b579 100644
--- a/kernel/drivers/video/rockchip/rga3/rga_common.c
+++ b/kernel/drivers/video/rockchip/rga3/rga_common.c
@@ -529,20 +529,49 @@
 	}
 }
 
-const char *rga_get_blend_mode_str(uint16_t alpha_rop_flag,
-				   uint16_t alpha_mode_0,
-				   uint16_t alpha_mode_1)
+const char *rga_get_blend_mode_str(enum rga_alpha_blend_mode mode)
 {
-	if (alpha_rop_flag == 0) {
+	switch (mode) {
+	case RGA_ALPHA_NONE:
 		return "no blend";
-	} else if (alpha_rop_flag == 0x9) {
-		if (alpha_mode_0 == 0x381A && alpha_mode_1 == 0x381A)
-			return "105 src + (1-src.a)*dst";
-		else if (alpha_mode_0 == 0x483A && alpha_mode_1 == 0x483A)
-			return "405 src.a * src + (1-src.a) * dst";
-		else
-			return "check reg for more imformation";
-	} else {
+
+	case RGA_ALPHA_BLEND_SRC:
+		return "src";
+
+	case RGA_ALPHA_BLEND_DST:
+		return "dst";
+
+	case RGA_ALPHA_BLEND_SRC_OVER:
+		return "src-over";
+
+	case RGA_ALPHA_BLEND_DST_OVER:
+		return "dst-over";
+
+	case RGA_ALPHA_BLEND_SRC_IN:
+		return "src-in";
+
+	case RGA_ALPHA_BLEND_DST_IN:
+		return "dst-in";
+
+	case RGA_ALPHA_BLEND_SRC_OUT:
+		return "src-out";
+
+	case RGA_ALPHA_BLEND_DST_OUT:
+		return "dst-out";
+
+	case RGA_ALPHA_BLEND_SRC_ATOP:
+		return "src-atop";
+
+	case RGA_ALPHA_BLEND_DST_ATOP:
+		return "dst-atop";
+
+	case RGA_ALPHA_BLEND_XOR:
+		return "xor";
+
+	case RGA_ALPHA_BLEND_CLEAR:
+		return "clear";
+
+	default:
 		return "check reg for more imformation";
 	}
 }
diff --git a/kernel/drivers/video/rockchip/rga3/rga_debugger.c b/kernel/drivers/video/rockchip/rga3/rga_debugger.c
index 27357bc..fe21031 100644
--- a/kernel/drivers/video/rockchip/rga3/rga_debugger.c
+++ b/kernel/drivers/video/rockchip/rga3/rga_debugger.c
@@ -621,7 +621,11 @@
 #ifdef CONFIG_ROCKCHIP_RGA_PROC_FS
 static int rga_procfs_open(struct inode *inode, struct file *file)
 {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0)
+	struct rga_debugger_node *node = pde_data(inode);
+#else
 	struct rga_debugger_node *node = PDE_DATA(inode);
+#endif
 
 	return single_open(file, node->info_ent->show, node);
 }
@@ -836,6 +840,10 @@
 	struct file *file;
 	size_t size = 0;
 	loff_t pos = 0;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0)
+	int ret;
+	struct iosys_map map;
+#endif
 	void *kvaddr = NULL;
 	void *kvaddr_origin = NULL;
 
@@ -848,7 +856,12 @@
 			return -EINVAL;
 		}
 
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0)
+		ret = dma_buf_vmap(dump_buffer->dma_buffer->dma_buf, &map);
+		kvaddr = ret ? NULL : map.vaddr;
+#else
 		kvaddr = dma_buf_vmap(dump_buffer->dma_buffer->dma_buf);
+#endif
 		if (!kvaddr) {
 			pr_err("can't vmap the dma buffer!\n");
 			return -EINVAL;
@@ -918,7 +931,11 @@
 	switch (dump_buffer->type) {
 	case RGA_DMA_BUFFER:
 	case RGA_DMA_BUFFER_PTR:
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0)
+		dma_buf_vunmap(dump_buffer->dma_buffer->dma_buf, &map);
+#else
 		dma_buf_vunmap(dump_buffer->dma_buffer->dma_buf, kvaddr_origin);
+#endif
 		break;
 	case RGA_VIRTUAL_ADDRESS:
 		vunmap(kvaddr_origin);
diff --git a/kernel/drivers/video/rockchip/rga3/rga_dma_buf.c b/kernel/drivers/video/rockchip/rga3/rga_dma_buf.c
index 7180100..db34db5 100644
--- a/kernel/drivers/video/rockchip/rga3/rga_dma_buf.c
+++ b/kernel/drivers/video/rockchip/rga3/rga_dma_buf.c
@@ -203,7 +203,7 @@
 					    size_t size, u64 dma_limit,
 					    struct device *dev)
 {
-	struct rga_iommu_dma_cookie *cookie = domain->iova_cookie;
+	struct rga_iommu_dma_cookie *cookie = (void *)domain->iova_cookie;
 	struct iova_domain *iovad = &cookie->iovad;
 	unsigned long shift, iova_len, iova = 0;
 
@@ -246,7 +246,7 @@
 static void rga_iommu_dma_free_iova(struct iommu_domain *domain,
 				    dma_addr_t iova, size_t size)
 {
-	struct rga_iommu_dma_cookie *cookie = domain->iova_cookie;
+	struct rga_iommu_dma_cookie *cookie = (void *)domain->iova_cookie;
 	struct iova_domain *iovad = &cookie->iovad;
 
 	free_iova_fast(iovad, iova_pfn(iovad, iova), size >> iova_shift(iovad));
@@ -285,7 +285,7 @@
 	}
 
 	domain = rga_iommu_get_dma_domain(rga_dev);
-	cookie = domain->iova_cookie;
+	cookie = (void *)domain->iova_cookie;
 	iovad = &cookie->iovad;
 	align_size = iova_align(iovad, size);
 
@@ -330,7 +330,7 @@
 	}
 
 	domain = rga_iommu_get_dma_domain(rga_dev);
-	cookie = domain->iova_cookie;
+	cookie = (void *)domain->iova_cookie;
 	iovad = &cookie->iovad;
 	align_size = iova_align(iovad, size);
 
@@ -394,12 +394,20 @@
 {
 	int ret = 0;
 	void *vaddr;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0)
+	struct iosys_map map;
+#endif
 	struct dma_buf *dma_buf;
 
 	dma_buf = rga_dma_buffer->dma_buf;
 
 	if (!IS_ERR_OR_NULL(dma_buf)) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0)
+		ret = dma_buf_vmap(dma_buf, &map);
+		vaddr = ret ? NULL : map.vaddr;
+#else
 		vaddr = dma_buf_vmap(dma_buf);
+#endif
 		if (vaddr) {
 			ret = rga_virtual_memory_check(vaddr, img->vir_w,
 				img->vir_h, img->format, img->yrgb_addr);
@@ -407,8 +415,11 @@
 			pr_err("can't vmap the dma buffer!\n");
 			return -EINVAL;
 		}
-
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0)
+		dma_buf_vunmap(dma_buf, &map);
+#else
 		dma_buf_vunmap(dma_buf, vaddr);
+#endif
 	}
 
 	return ret;
diff --git a/kernel/drivers/video/rockchip/rga3/rga_hw_config.c b/kernel/drivers/video/rockchip/rga3/rga_hw_config.c
index 42d7bdb..4af4a85 100644
--- a/kernel/drivers/video/rockchip/rga3/rga_hw_config.c
+++ b/kernel/drivers/video/rockchip/rga3/rga_hw_config.c
@@ -292,9 +292,8 @@
 
 	.feature = RGA_COLOR_FILL | RGA_COLOR_PALETTE |
 		   RGA_COLOR_KEY | RGA_ROP_CALCULATE |
-		   RGA_NN_QUANTIZE | RGA_DITHER,
-	.csc_r2y_mode = RGA_MODE_CSC_BT601L | RGA_MODE_CSC_BT601F |
-			RGA_MODE_CSC_BT709,
+		   RGA_NN_QUANTIZE | RGA_DITHER | RGA_FULL_CSC,
+	.csc_r2y_mode = RGA_MODE_CSC_BT601L | RGA_MODE_CSC_BT601F,
 	.csc_y2r_mode = RGA_MODE_CSC_BT601L | RGA_MODE_CSC_BT601F |
 			RGA_MODE_CSC_BT709,
 	.mmu = RGA_MMU,
@@ -318,7 +317,7 @@
 		   RGA_COLOR_KEY | RGA_ROP_CALCULATE |
 		   RGA_NN_QUANTIZE | RGA_DITHER | RGA_MOSAIC |
 		   RGA_YIN_YOUT | RGA_YUV_HDS | RGA_YUV_VDS |
-		   RGA_OSD | RGA_PRE_INTR,
+		   RGA_OSD | RGA_PRE_INTR | RGA_FULL_CSC,
 	.csc_r2y_mode = RGA_MODE_CSC_BT601L | RGA_MODE_CSC_BT601F |
 			RGA_MODE_CSC_BT709,
 	.csc_y2r_mode = RGA_MODE_CSC_BT601L | RGA_MODE_CSC_BT601F |
@@ -344,7 +343,7 @@
 		   RGA_COLOR_KEY | RGA_ROP_CALCULATE |
 		   RGA_NN_QUANTIZE | RGA_DITHER | RGA_MOSAIC |
 		   RGA_YIN_YOUT | RGA_YUV_HDS | RGA_YUV_VDS |
-		   RGA_OSD | RGA_PRE_INTR,
+		   RGA_OSD | RGA_PRE_INTR | RGA_FULL_CSC,
 	.csc_r2y_mode = RGA_MODE_CSC_BT601L | RGA_MODE_CSC_BT601F |
 			RGA_MODE_CSC_BT709,
 	.csc_y2r_mode = RGA_MODE_CSC_BT601L | RGA_MODE_CSC_BT601F |
diff --git a/kernel/drivers/video/rockchip/rga3/rga_job.c b/kernel/drivers/video/rockchip/rga3/rga_job.c
index 09e1997..647fc2b 100644
--- a/kernel/drivers/video/rockchip/rga3/rga_job.c
+++ b/kernel/drivers/video/rockchip/rga3/rga_job.c
@@ -536,8 +536,21 @@
 		       __func__, acquire_fence_fd);
 		return -EINVAL;
 	}
-	/* close acquire fence fd */
-	ksys_close(acquire_fence_fd);
+
+	if (!request->feature.user_close_fence) {
+		/* close acquire fence fd */
+#ifdef CONFIG_NO_GKI
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0)
+		close_fd(acquire_fence_fd);
+#else
+		ksys_close(acquire_fence_fd);
+#endif
+#else
+		pr_err("Please update the driver to v1.2.28 to prevent acquire_fence_fd leaks.");
+		return -EFAULT;
+#endif
+	}
+
 
 	ret = rga_dma_fence_get_status(acquire_fence);
 	if (ret < 0) {
@@ -646,7 +659,8 @@
 					scheduler->ops->soft_reset(scheduler);
 				}
 
-				pr_err("reset core[%d] by request abort", scheduler->core);
+				pr_err("reset core[%d] by request[%d] abort",
+				       scheduler->core, request->id);
 				running_abort_count++;
 			}
 		}
@@ -753,12 +767,13 @@
 				} else if (!test_bit(RGA_JOB_STATE_DONE, &job->state) &&
 					   test_bit(RGA_JOB_STATE_FINISH, &job->state)) {
 					spin_unlock_irqrestore(&scheduler->irq_lock, flags);
-					pr_err("hardware has finished, but the software has timeout!\n");
+					pr_err("request[%d] hardware has finished, but the software has timeout!\n",
+					       request->id);
 					return -EBUSY;
 				} else if (!test_bit(RGA_JOB_STATE_DONE, &job->state) &&
 					   !test_bit(RGA_JOB_STATE_FINISH, &job->state)) {
 					spin_unlock_irqrestore(&scheduler->irq_lock, flags);
-					pr_err("hardware has timeout.\n");
+					pr_err("request[%d] hardware has timeout.\n", request->id);
 					return -EBUSY;
 				}
 			}
@@ -831,7 +846,7 @@
 	struct rga_pending_request_manager *request_manager = rga_drvdata->pend_request_manager;
 
 	if (rga_request_commit(request))
-		pr_err("rga request commit failed!\n");
+		pr_err("rga request[%d] commit failed!\n", request->id);
 
 	mutex_lock(&request_manager->lock);
 	rga_request_put(request);
@@ -845,6 +860,7 @@
 	struct rga_pending_request_manager *request_manager;
 	struct rga_request *request;
 	int finished_count, failed_count;
+	bool is_finished = false;
 	unsigned long flags;
 
 	request_manager = rga_drvdata->pend_request_manager;
@@ -893,7 +909,7 @@
 
 		rga_dma_fence_signal(request->release_fence, request->ret);
 
-		wake_up(&request->finished_wq);
+		is_finished = true;
 
 		if (DEBUGGER_EN(MSG))
 			pr_info("request[%d] finished %d failed %d\n",
@@ -906,7 +922,12 @@
 	}
 
 	mutex_lock(&request_manager->lock);
+
+	if (is_finished)
+		wake_up(&request->finished_wq);
+
 	rga_request_put(request);
+
 	mutex_unlock(&request_manager->lock);
 
 	return 0;
@@ -960,6 +981,7 @@
 	request->sync_mode = user_request->sync_mode;
 	request->mpi_config_flags = user_request->mpi_config_flags;
 	request->acquire_fence_fd = user_request->acquire_fence_fd;
+	request->feature = task_list[0].feature;
 
 	spin_unlock_irqrestore(&request->lock, flags);
 
@@ -1095,7 +1117,7 @@
 request_commit:
 	ret = rga_request_commit(request);
 	if (ret < 0) {
-		pr_err("rga request commit failed!\n");
+		pr_err("rga request[%d] commit failed!\n", request->id);
 		goto err_put_release_fence;
 	}
 
diff --git a/kernel/drivers/video/rockchip/rga3/rga_mm.c b/kernel/drivers/video/rockchip/rga3/rga_mm.c
index cd461b5..d261833 100644
--- a/kernel/drivers/video/rockchip/rga3/rga_mm.c
+++ b/kernel/drivers/video/rockchip/rga3/rga_mm.c
@@ -577,6 +577,13 @@
 		mm_flag |= RGA_MEM_PHYSICAL_CONTIGUOUS;
 	}
 
+	/*
+	 * Some userspace virtual addresses do not have an
+	 * interface for flushing the cache, so it is mandatory
+	 * to flush the cache when the virtual address is used.
+	 */
+	mm_flag |= RGA_MEM_FORCE_FLUSH_CACHE;
+
 	if (!rga_mm_check_memory_limit(scheduler, mm_flag)) {
 		pr_err("scheduler core[%d] unsupported mm_flag[0x%x]!\n",
 		       scheduler->core, mm_flag);
@@ -1295,13 +1302,6 @@
 	struct sg_table *sgt;
 	struct rga_scheduler_t *scheduler;
 
-	sgt = rga_mm_lookup_sgt(buffer);
-	if (sgt == NULL) {
-		pr_err("%s(%d), failed to get sgt, core = 0x%x\n",
-		       __func__, __LINE__, job->core);
-		return -EINVAL;
-	}
-
 	scheduler = buffer->dma_buffer->scheduler;
 	if (scheduler == NULL) {
 		pr_err("%s(%d), failed to get scheduler, core = 0x%x\n",
@@ -1309,7 +1309,18 @@
 		return -EFAULT;
 	}
 
-	dma_sync_sg_for_device(scheduler->dev, sgt->sgl, sgt->orig_nents, dir);
+	if (buffer->mm_flag & RGA_MEM_PHYSICAL_CONTIGUOUS) {
+		dma_sync_single_for_device(scheduler->dev, buffer->phys_addr, buffer->size, dir);
+	} else {
+		sgt = rga_mm_lookup_sgt(buffer);
+		if (sgt == NULL) {
+			pr_err("%s(%d), failed to get sgt, core = 0x%x\n",
+			       __func__, __LINE__, job->core);
+			return -EINVAL;
+		}
+
+		dma_sync_sg_for_device(scheduler->dev, sgt->sgl, sgt->orig_nents, dir);
+	}
 
 	return 0;
 }
@@ -1321,13 +1332,6 @@
 	struct sg_table *sgt;
 	struct rga_scheduler_t *scheduler;
 
-	sgt = rga_mm_lookup_sgt(buffer);
-	if (sgt == NULL) {
-		pr_err("%s(%d), failed to get sgt, core = 0x%x\n",
-		       __func__, __LINE__, job->core);
-		return -EINVAL;
-	}
-
 	scheduler = buffer->dma_buffer->scheduler;
 	if (scheduler == NULL) {
 		pr_err("%s(%d), failed to get scheduler, core = 0x%x\n",
@@ -1335,7 +1339,18 @@
 		return -EFAULT;
 	}
 
-	dma_sync_sg_for_cpu(scheduler->dev, sgt->sgl, sgt->orig_nents, dir);
+	if (buffer->mm_flag & RGA_MEM_PHYSICAL_CONTIGUOUS) {
+		dma_sync_single_for_cpu(scheduler->dev, buffer->phys_addr, buffer->size, dir);
+	} else {
+		sgt = rga_mm_lookup_sgt(buffer);
+		if (sgt == NULL) {
+			pr_err("%s(%d), failed to get sgt, core = 0x%x\n",
+			       __func__, __LINE__, job->core);
+			return -EINVAL;
+		}
+
+		dma_sync_sg_for_cpu(scheduler->dev, sgt->sgl, sgt->orig_nents, dir);
+	}
 
 	return 0;
 }
@@ -1434,7 +1449,7 @@
 		goto put_internal_buffer;
 	}
 
-	if (internal_buffer->type == RGA_VIRTUAL_ADDRESS) {
+	if (internal_buffer->mm_flag & RGA_MEM_FORCE_FLUSH_CACHE) {
 		/*
 		 * Some userspace virtual addresses do not have an
 		 * interface for flushing the cache, so it is mandatory
@@ -1463,7 +1478,7 @@
 			      struct rga_internal_buffer *internal_buffer,
 			      enum dma_data_direction dir)
 {
-	if (internal_buffer->type == RGA_VIRTUAL_ADDRESS && dir != DMA_NONE)
+	if (internal_buffer->mm_flag & RGA_MEM_FORCE_FLUSH_CACHE && dir != DMA_NONE)
 		if (rga_mm_sync_dma_sg_for_cpu(internal_buffer, job, dir))
 			pr_err("sync sgt for cpu error!\n");
 
@@ -1574,6 +1589,53 @@
 
 	req = &job->rga_command_base;
 	mm = rga_drvdata->mm;
+
+	switch (req->render_mode) {
+	case BITBLT_MODE:
+	case COLOR_PALETTE_MODE:
+		if (unlikely(req->src.yrgb_addr <= 0)) {
+			pr_err("render_mode[0x%x] src0 channel handle[%ld] must is valid!",
+			       req->render_mode, (unsigned long)req->src.yrgb_addr);
+			return -EINVAL;
+		}
+
+		if (unlikely(req->dst.yrgb_addr <= 0)) {
+			pr_err("render_mode[0x%x] dst channel handle[%ld] must is valid!",
+			       req->render_mode, (unsigned long)req->dst.yrgb_addr);
+			return -EINVAL;
+		}
+
+		if (req->bsfilter_flag) {
+			if (unlikely(req->pat.yrgb_addr <= 0)) {
+				pr_err("render_mode[0x%x] src1/pat channel handle[%ld] must is valid!",
+				       req->render_mode, (unsigned long)req->pat.yrgb_addr);
+				return -EINVAL;
+			}
+		}
+
+		break;
+	case COLOR_FILL_MODE:
+		if (unlikely(req->dst.yrgb_addr <= 0)) {
+			pr_err("render_mode[0x%x] dst channel handle[%ld] must is valid!",
+			       req->render_mode, (unsigned long)req->dst.yrgb_addr);
+			return -EINVAL;
+		}
+
+		break;
+
+	case UPDATE_PALETTE_TABLE_MODE:
+	case UPDATE_PATTEN_BUF_MODE:
+		if (unlikely(req->pat.yrgb_addr <= 0)) {
+			pr_err("render_mode[0x%x] lut/pat channel handle[%ld] must is valid!, req->render_mode",
+			       req->render_mode, (unsigned long)req->pat.yrgb_addr);
+			return -EINVAL;
+		}
+
+		break;
+	default:
+		pr_err("%s, unknown render mode!\n", __func__);
+		break;
+	}
 
 	if (likely(req->src.yrgb_addr > 0)) {
 		ret = rga_mm_get_channel_handle_info(mm, job, &req->src,
@@ -1765,7 +1827,7 @@
 					    struct rga_job_buffer *job_buffer,
 					    enum dma_data_direction dir)
 {
-	if (job_buffer->addr->type == RGA_VIRTUAL_ADDRESS && dir != DMA_NONE)
+	if (job_buffer->addr->mm_flag & RGA_MEM_FORCE_FLUSH_CACHE && dir != DMA_NONE)
 		if (rga_mm_sync_dma_sg_for_cpu(job_buffer->addr, job, dir))
 			pr_err("sync sgt for cpu error!\n");
 
@@ -1802,12 +1864,7 @@
 		goto error_unmap_buffer;
 	}
 
-	if (buffer->type == RGA_VIRTUAL_ADDRESS) {
-		/*
-		 * Some userspace virtual addresses do not have an
-		 * interface for flushing the cache, so it is mandatory
-		 * to flush the cache when the virtual address is used.
-		 */
+	if (buffer->mm_flag & RGA_MEM_FORCE_FLUSH_CACHE) {
 		ret = rga_mm_sync_dma_sg_for_device(buffer, job, dir);
 		if (ret < 0) {
 			pr_err("sync sgt for device error!\n");
diff --git a/kernel/drivers/video/rockchip/rga3/rga_policy.c b/kernel/drivers/video/rockchip/rga3/rga_policy.c
index 9801e6d..1afab89 100644
--- a/kernel/drivers/video/rockchip/rga3/rga_policy.c
+++ b/kernel/drivers/video/rockchip/rga3/rga_policy.c
@@ -46,6 +46,53 @@
 	return feature;
 }
 
+static bool rga_check_csc_constant(const struct rga_hw_data *data, struct rga_req *rga_base,
+				   uint32_t mode, uint32_t flag)
+{
+	if (mode & flag)
+		return true;
+
+	if ((rga_base->full_csc.flag & 0x1) && (data->feature & RGA_FULL_CSC))
+		return true;
+
+	return false;
+}
+
+static bool rga_check_csc(const struct rga_hw_data *data, struct rga_req *rga_base)
+{
+	switch (rga_base->yuv2rgb_mode) {
+	case 0x1:
+		return rga_check_csc_constant(data, rga_base,
+					      data->csc_y2r_mode, RGA_MODE_CSC_BT601L);
+	case 0x2:
+		return rga_check_csc_constant(data, rga_base,
+					      data->csc_y2r_mode, RGA_MODE_CSC_BT601F);
+	case 0x3:
+		return rga_check_csc_constant(data, rga_base,
+					      data->csc_y2r_mode, RGA_MODE_CSC_BT709);
+	case 0x1 << 2:
+		return rga_check_csc_constant(data, rga_base,
+					      data->csc_r2y_mode, RGA_MODE_CSC_BT601F);
+	case 0x2 << 2:
+		return rga_check_csc_constant(data, rga_base,
+					      data->csc_r2y_mode, RGA_MODE_CSC_BT601L);
+	case 0x3 << 2:
+		return rga_check_csc_constant(data, rga_base,
+					      data->csc_r2y_mode, RGA_MODE_CSC_BT709);
+	default:
+		break;
+	}
+
+	if ((rga_base->full_csc.flag & 0x1)) {
+		if (data->feature & RGA_FULL_CSC)
+			return true;
+		else
+			return false;
+	}
+
+	return true;
+}
+
 static bool rga_check_resolution(const struct rga_rect_range *range, int width, int height)
 {
 	if (width > range->max.width || height > range->max.height)
@@ -324,6 +371,13 @@
 			continue;
 		}
 
+		if (!rga_check_csc(data, rga_base)) {
+			if (DEBUGGER_EN(MSG))
+				pr_info("core = %d, break on rga_check_csc",
+					scheduler->core);
+			continue;
+		}
+
 		optional_cores |= scheduler->core;
 	}
 
diff --git a/kernel/drivers/video/rockchip/vehicle/vehicle_ad.h b/kernel/drivers/video/rockchip/vehicle/vehicle_ad.h
index 66d156f..a83233b 100644
--- a/kernel/drivers/video/rockchip/vehicle/vehicle_ad.h
+++ b/kernel/drivers/video/rockchip/vehicle/vehicle_ad.h
@@ -63,6 +63,7 @@
 	u32 channel_reso[PAD_MAX];
 	u8 detect_status;
 	u8 last_detect_status;
+	int drop_frames;
 };
 
 int vehicle_generic_sensor_write(struct vehicle_ad_dev *ad, char reg, char *pval);
diff --git a/kernel/drivers/video/rockchip/vehicle/vehicle_ad_nvp6324.c b/kernel/drivers/video/rockchip/vehicle/vehicle_ad_nvp6324.c
index 4c1b3da..af9274d 100644
--- a/kernel/drivers/video/rockchip/vehicle/vehicle_ad_nvp6324.c
+++ b/kernel/drivers/video/rockchip/vehicle/vehicle_ad_nvp6324.c
@@ -96,6 +96,848 @@
 
 #define SENSOR_ID(_msb, _lsb)		((_msb) << 8 | (_lsb))
 
+/* NTSC Preview resolution setting*/
+static struct rk_sensor_reg sensor_preview_data_ntsc_30hz[] = {
+	{0xff, 0x04},
+	{0xa0, 0x24},
+	{0xa1, 0x24},
+	{0xa2, 0x24},
+	{0xa3, 0x24},
+	{0xa4, 0x24},
+	{0xa5, 0x24},
+	{0xa6, 0x24},
+	{0xa7, 0x24},
+	{0xa8, 0x24},
+	{0xa9, 0x24},
+	{0xaa, 0x24},
+	{0xab, 0x24},
+	{0xac, 0x24},
+	{0xad, 0x24},
+	{0xae, 0x24},
+	{0xaf, 0x24},
+	{0xb0, 0x24},
+	{0xb1, 0x24},
+	{0xb2, 0x24},
+	{0xb3, 0x24},
+	{0xb4, 0x24},
+	{0xb5, 0x24},
+	{0xb6, 0x24},
+	{0xb7, 0x24},
+	{0xb8, 0x24},
+	{0xb9, 0x24},
+	{0xba, 0x24},
+	{0xbb, 0x24},
+	{0xbc, 0x24},
+	{0xbd, 0x24},
+	{0xbe, 0x24},
+	{0xbf, 0x24},
+	{0xc0, 0x24},
+	{0xc1, 0x24},
+	{0xc2, 0x24},
+	{0xc3, 0x24},
+	{0xff, 0x21},
+	{0x07, 0x80},
+	{0x07, 0x00},
+	{0xff, 0x0A},
+	{0x77, 0x8F},
+	{0xF7, 0x8F},
+	{0xff, 0x0B},
+	{0x77, 0x8F},
+	{0xF7, 0x8F},
+
+	{0xFF, 0x21},
+	{0x40, 0xAC},
+	{0x41, 0x10},
+	{0x42, 0x03},
+	{0x43, 0x43},
+	{0x11, 0x04},
+	{0x10, 0x0A},
+	{0x12, 0x06},
+	{0x13, 0x09},
+	{0x17, 0x01},
+	{0x18, 0x0D},
+	{0x15, 0x04},
+	{0x14, 0x16},
+	{0x16, 0x05},
+	{0x19, 0x05},
+	{0x1A, 0x0A},
+	{0x1B, 0x08},
+	{0x1C, 0x07},
+	{0x44, 0x00},
+	{0x49, 0xF3},
+	{0x49, 0xF0},
+	{0x44, 0x02},
+	{0x08, 0x40}, //0x40:non-continue;0x48:continuous
+	{0x0F, 0x01},
+	{0x38, 0x1E},
+	{0x39, 0x1E},
+	{0x3A, 0x1E},
+	{0x3B, 0x1E},
+	{0x07, 0x0f}, //0x07:2lane;0x0f:4lane
+	{0x2D, 0x01}, //0x00:2lane;0x01:4lane
+	{0x45, 0x02},
+	{0xFF, 0x13},
+	{0x30, 0x00},
+	{0x31, 0x00},
+	{0x32, 0x00},
+
+	{0xFF, 0x00},
+	{0x00, 0x00},
+	{0x01, 0x00},
+	{0x02, 0x00},
+	{0x03, 0x00},
+	{0x04, 0x0e}, //sd_mode
+	{0x05, 0x0e},
+	{0x06, 0x0e},
+	{0x07, 0x0e},
+	{0x08, 0x00}, //ahd_mode
+	{0x09, 0x00},
+	{0x0a, 0x00},
+	{0x0b, 0x00},
+	{0x0c, 0x00},
+	{0x0d, 0x00},
+	{0x0e, 0x00},
+	{0x0f, 0x00},
+	{0x10, 0xa0}, //video_format
+	{0x11, 0xa0},
+	{0x12, 0xa0},
+	{0x13, 0xa0},
+	{0x14, 0x00},
+	{0x15, 0x00},
+	{0x16, 0x00},
+	{0x17, 0x00},
+	{0x18, 0x13},
+	{0x19, 0x13},
+	{0x1a, 0x13},
+	{0x1b, 0x13},
+	{0x1c, 0x1a},
+	{0x1d, 0x1a},
+	{0x1e, 0x1a},
+	{0x1f, 0x1a},
+	{0x20, 0x00},
+	{0x21, 0x00},
+	{0x22, 0x00},
+	{0x23, 0x00},
+	{0x24, 0x90}, //contrast
+	{0x25, 0x90},
+	{0x26, 0x90},
+	{0x27, 0x90},
+	{0x28, 0x90}, //black_level
+	{0x29, 0x90},
+	{0x2a, 0x90},
+	{0x2b, 0x90},
+	{0x30, 0x00}, //y_peaking_mode
+	{0x31, 0x00},
+	{0x32, 0x00},
+	{0x33, 0x00},
+	{0x34, 0x08}, //y_fir_mode
+	{0x35, 0x08},
+	{0x36, 0x08},
+	{0x37, 0x08},
+	{0x40, 0x00},
+	{0x41, 0x00},
+	{0x42, 0x00},
+	{0x43, 0x00},
+	{0x44, 0x00},
+	{0x45, 0x00},
+	{0x46, 0x00},
+	{0x47, 0x00},
+	{0x48, 0x00},
+	{0x49, 0x00},
+	{0x4a, 0x00},
+	{0x4b, 0x00},
+	{0x4c, 0xfe},
+	{0x4d, 0xfe},
+	{0x4e, 0xfe},
+	{0x4f, 0xfe},
+	{0x50, 0xfb},
+	{0x51, 0xfb},
+	{0x52, 0xfb},
+	{0x53, 0xfb},
+	{0x58, 0x80},
+	{0x59, 0x80},
+	{0x5a, 0x80},
+	{0x5b, 0x80},
+	{0x5c, 0x82}, //pal_cm_off
+	{0x5d, 0x82},
+	{0x5e, 0x82},
+	{0x5f, 0x82},
+	{0x60, 0x10},
+	{0x61, 0x10},
+	{0x62, 0x10},
+	{0x63, 0x10},
+	{0x64, 0x18}, //y_delay
+	{0x65, 0x18},
+	{0x66, 0x18},
+	{0x67, 0x18},
+	{0x68, 0x70}, //h_delay_a //h_delay_lsb
+	{0x69, 0x70},
+	{0x6a, 0x70},
+	{0x6b, 0x70},
+	{0x6c, 0x00},
+	{0x6d, 0x00},
+	{0x6e, 0x00},
+	{0x6f, 0x00},
+	{0x70, 0x9e}, //v_crop_start
+	{0x71, 0x9e},
+	{0x72, 0x9e},
+	{0x73, 0x9e},
+	{0x78, 0xc0},
+	{0x79, 0xc0},
+	{0x7a, 0xc0},
+	{0x7b, 0xc0},
+
+	{0xFF, 0x01},
+	{0x7C, 0x00},
+	{0x84, 0x04},
+	{0x85, 0x04},
+	{0x86, 0x04},
+	{0x87, 0x04},
+	{0x88, 0x01},
+	{0x89, 0x01},
+	{0x8a, 0x01},
+	{0x8b, 0x01},
+	{0x8c, 0x02},
+	{0x8d, 0x02},
+	{0x8e, 0x02},
+	{0x8f, 0x02},
+	{0xEC, 0x00},
+	{0xED, 0x00},
+	{0xEE, 0x00},
+	{0xEF, 0x00},
+
+	{0xFF, 0x05},
+	{0x00, 0xd0},
+	{0x01, 0x2c},
+	{0x05, 0x20}, //d_agc_option
+	{0x1d, 0x0c},
+	{0x21, 0x20}, //sub contrast
+	{0x24, 0x2a},
+	{0x25, 0xdc}, //fsc_lock_mode
+	{0x26, 0x40},
+	{0x27, 0x57},
+	{0x28, 0x80}, //s_point
+	{0x2b, 0xc0}, //saturation_b
+	{0x31, 0x82},
+	{0x32, 0x10},
+	{0x38, 0x00},
+	{0x47, 0x04},
+	{0x50, 0x84},
+	{0x53, 0x04},
+	{0x57, 0x00},
+	{0x58, 0x77},
+	{0x59, 0x00},
+	{0x5C, 0x78},
+	{0x5F, 0x00},
+	{0x62, 0x20},
+	{0x64, 0x01},
+	{0x65, 0x00},
+	{0x69, 0x00},
+	{0x6E, 0x00}, //VBLK_EXT_EN
+	{0x6F, 0x00}, //VBLK_EXT_[7:0]
+	{0x90, 0x01}, //comb_mode
+	{0x92, 0x00},
+	{0x94, 0x00},
+	{0x95, 0x00},
+	{0xa9, 0x00},
+	{0xb5, 0x00},
+	{0xb7, 0xfc},
+	{0xb8, 0xb8},
+	{0xb9, 0x72},
+	{0xbb, 0x0f},
+	{0xd1, 0x30}, //burst_dec_c
+	{0xd5, 0x80},
+
+	{0xFF, 0x09},
+	{0x40, 0x00},
+	{0x41, 0x00},
+	{0x42, 0x00},
+	{0x43, 0x00},
+	{0x44, 0x00},
+	{0x45, 0x00},
+	{0x46, 0x00},
+	{0x47, 0x00},
+	{0x50, 0x30},
+	{0x51, 0x6f},
+	{0x52, 0x67},
+	{0x53, 0x48},
+	{0x54, 0x30},
+	{0x55, 0x6f},
+	{0x56, 0x67},
+	{0x57, 0x48},
+	{0x58, 0x30},
+	{0x59, 0x6f},
+	{0x5a, 0x67},
+	{0x5b, 0x48},
+	{0x5c, 0x30},
+	{0x5d, 0x6f},
+	{0x5e, 0x67},
+	{0x5f, 0x48},
+	{0x96, 0x10},
+	{0x97, 0x10},
+	{0x98, 0x00},
+	{0x99, 0x00},
+	{0x9a, 0x00},
+	{0x9b, 0x00},
+	{0x9c, 0x00},
+	{0x9d, 0x00},
+	{0x9e, 0x00},
+	{0xb6, 0x10},
+	{0xb7, 0x10},
+	{0xb8, 0x00},
+	{0xb9, 0x00},
+	{0xba, 0x00},
+	{0xbb, 0x00},
+	{0xbc, 0x00},
+	{0xbd, 0x00},
+	{0xbe, 0x00},
+	{0xd6, 0x10},
+	{0xd7, 0x10},
+	{0xd8, 0x00},
+	{0xd9, 0x00},
+	{0xda, 0x00},
+	{0xdb, 0x00},
+	{0xdc, 0x00},
+	{0xdd, 0x00},
+	{0xde, 0x00},
+	{0xf6, 0x10},
+	{0xf7, 0x10},
+	{0xf8, 0x00},
+	{0xf9, 0x00},
+	{0xfa, 0x00},
+	{0xfb, 0x00},
+	{0xfc, 0x00},
+	{0xfd, 0x00},
+	{0xfe, 0x00},
+
+	{0xff, 0x0a},
+	{0x3d, 0x00},
+	{0x3c, 0x00},
+	{0x30, 0xac},
+	{0x31, 0x78},
+	{0x32, 0x17},
+	{0x33, 0xc1},
+	{0x34, 0x40},
+	{0x35, 0x00},
+	{0x36, 0xc3},
+	{0x37, 0x0a},
+	{0x38, 0x00},
+	{0x39, 0x02},
+	{0x3a, 0x00},
+	{0x3b, 0xb2},
+	{0x25, 0x10},
+	{0x27, 0x1e},
+	{0xbd, 0x00},
+	{0xbc, 0x00},
+	{0xb0, 0xac},
+	{0xb1, 0x78},
+	{0xb2, 0x17},
+	{0xb3, 0xc1},
+	{0xb4, 0x40},
+	{0xb5, 0x00},
+	{0xb6, 0xc3},
+	{0xb7, 0x0a},
+	{0xb8, 0x00},
+	{0xb9, 0x02},
+	{0xba, 0x00},
+	{0xbb, 0xb2},
+	{0xa5, 0x10},
+	{0xa7, 0x1e},
+
+	{0xff, 0x0b},
+	{0x3d, 0x00},
+	{0x3c, 0x00},
+	{0x30, 0xac},
+	{0x31, 0x78},
+	{0x32, 0x17},
+	{0x33, 0xc1},
+	{0x34, 0x40},
+	{0x35, 0x00},
+	{0x36, 0xc3},
+	{0x37, 0x0a},
+	{0x38, 0x00},
+	{0x39, 0x02},
+	{0x3a, 0x00},
+	{0x3b, 0xb2},
+	{0x25, 0x10},
+	{0x27, 0x1e},
+	{0xbd, 0x00},
+	{0xbc, 0x00},
+	{0xb0, 0xac},
+	{0xb1, 0x78},
+	{0xb2, 0x17},
+	{0xb3, 0xc1},
+	{0xb4, 0x40},
+	{0xb5, 0x00},
+	{0xb6, 0xc3},
+	{0xb7, 0x0a},
+	{0xb8, 0x00},
+	{0xb9, 0x02},
+	{0xba, 0x00},
+	{0xbb, 0xb2},
+	{0xa5, 0x10},
+	{0xa7, 0x1e},
+
+	{0xFF, 0x21},
+	{0x3E, 0x00},
+	{0x3F, 0x00},
+	{0xFF, 0x20},
+	{0x01, 0xaa}, //0x00:1/1;0x55:1/2;0xaa:1/4
+	{0x00, 0x00},
+	{0x40, 0x01},
+	{0x0F, 0x00},
+	{0x0D, 0x01}, //0x01:4lane;0x00:2lane
+	{0x40, 0x00},
+	{0x00, 0xff}, //0xff:ch1/2/3/4 0x33:ch1/2 0x11:ch1
+
+	{0xFF, 0x01},
+	{0xC8, 0x00},
+	{0xC9, 0x00},
+	{0xCA, 0x00},
+	{0xCB, 0x00},
+
+	//pattern enabled
+	{0xFF, 0x00},
+	{0x1C, 0x1A},
+	{0x1D, 0x1A},
+	{0x1E, 0x1A},
+	{0x1F, 0x1A},
+
+	{0xFF, 0x05},
+	{0x6A, 0x80},
+	{0xFF, 0x06},
+	{0x6A, 0x80},
+	{0xFF, 0x07},
+	{0x6A, 0x80},
+	{0xFF, 0x08},
+	{0x6A, 0x80},
+	{0xFF, 0x21}, //add frame num
+	{0x3E, 0x11}, //1 : Fix to 1 for Odd Field, 2 for Even Field
+	{0x3F, 0x11}, //1 : Fix to 1 for Odd Field, 2 for Even Field
+	SensorEnd
+};
+
+/* Pal Preview resolution setting*/
+static struct rk_sensor_reg sensor_preview_data_pal_25hz[] = {
+	{0xff, 0x04},
+	{0xa0, 0x24},
+	{0xa1, 0x24},
+	{0xa2, 0x24},
+	{0xa3, 0x24},
+	{0xa4, 0x24},
+	{0xa5, 0x24},
+	{0xa6, 0x24},
+	{0xa7, 0x24},
+	{0xa8, 0x24},
+	{0xa9, 0x24},
+	{0xaa, 0x24},
+	{0xab, 0x24},
+	{0xac, 0x24},
+	{0xad, 0x24},
+	{0xae, 0x24},
+	{0xaf, 0x24},
+	{0xb0, 0x24},
+	{0xb1, 0x24},
+	{0xb2, 0x24},
+	{0xb3, 0x24},
+	{0xb4, 0x24},
+	{0xb5, 0x24},
+	{0xb6, 0x24},
+	{0xb7, 0x24},
+	{0xb8, 0x24},
+	{0xb9, 0x24},
+	{0xba, 0x24},
+	{0xbb, 0x24},
+	{0xbc, 0x24},
+	{0xbd, 0x24},
+	{0xbe, 0x24},
+	{0xbf, 0x24},
+	{0xc0, 0x24},
+	{0xc1, 0x24},
+	{0xc2, 0x24},
+	{0xc3, 0x24},
+	{0xff, 0x21},
+	{0x07, 0x80},
+	{0x07, 0x00},
+	{0xff, 0x0A},
+	{0x77, 0x8F},
+	{0xF7, 0x8F},
+	{0xff, 0x0B},
+	{0x77, 0x8F},
+	{0xF7, 0x8F},
+
+	{0xFF, 0x21},
+	{0x40, 0xAC},
+	{0x41, 0x10},
+	{0x42, 0x03},
+	{0x43, 0x43},
+	{0x11, 0x04},
+	{0x10, 0x0A},
+	{0x12, 0x06},
+	{0x13, 0x09},
+	{0x17, 0x01},
+	{0x18, 0x0D},
+	{0x15, 0x04},
+	{0x14, 0x16},
+	{0x16, 0x05},
+	{0x19, 0x05},
+	{0x1A, 0x0A},
+	{0x1B, 0x08},
+	{0x1C, 0x07},
+	{0x44, 0x00},
+	{0x49, 0xF3},
+	{0x49, 0xF0},
+	{0x44, 0x02},
+	{0x08, 0x40}, //0x40:non-continue;0x48:continuous
+	{0x0F, 0x01},
+	{0x38, 0x1E},
+	{0x39, 0x1E},
+	{0x3A, 0x1E},
+	{0x3B, 0x1E},
+	{0x07, 0x0f}, //0x07:2lane;0x0f:4lane
+	{0x2D, 0x01}, //0x00:2lane;0x01:4lane
+	{0x45, 0x02},
+	{0xFF, 0x13},
+	{0x30, 0x00},
+	{0x31, 0x00},
+	{0x32, 0x00},
+
+	{0xFF, 0x00},
+	{0x00, 0x00},
+	{0x01, 0x00},
+	{0x02, 0x00},
+	{0x03, 0x00},
+	{0x04, 0x0f}, //sd_mode
+	{0x05, 0x0f},
+	{0x06, 0x0f},
+	{0x07, 0x0f},
+	{0x08, 0x00}, //ahd_mode
+	{0x09, 0x00},
+	{0x0a, 0x00},
+	{0x0b, 0x00},
+	{0x0c, 0x00},
+	{0x0d, 0x00},
+	{0x0e, 0x00},
+	{0x0f, 0x00},
+	{0x10, 0xdd}, //video_format
+	{0x11, 0xdd},
+	{0x12, 0xdd},
+	{0x13, 0xdd},
+	{0x14, 0x00},
+	{0x15, 0x00},
+	{0x16, 0x00},
+	{0x17, 0x00},
+	{0x18, 0x13},
+	{0x19, 0x13},
+	{0x1a, 0x13},
+	{0x1b, 0x13},
+	{0x1c, 0x1a},
+	{0x1d, 0x1a},
+	{0x1e, 0x1a},
+	{0x1f, 0x1a},
+	{0x20, 0x00},
+	{0x21, 0x00},
+	{0x22, 0x00},
+	{0x23, 0x00},
+	{0x24, 0x90}, //contrast
+	{0x25, 0x90},
+	{0x26, 0x90},
+	{0x27, 0x90},
+	{0x28, 0x90}, //black_level
+	{0x29, 0x90},
+	{0x2a, 0x90},
+	{0x2b, 0x90},
+	{0x30, 0x00}, //y_peaking_mode
+	{0x31, 0x00},
+	{0x32, 0x00},
+	{0x33, 0x00},
+	{0x34, 0x08}, //y_fir_mode
+	{0x35, 0x08},
+	{0x36, 0x08},
+	{0x37, 0x08},
+	{0x40, 0x00},
+	{0x41, 0x00},
+	{0x42, 0x00},
+	{0x43, 0x00},
+	{0x44, 0x00},
+	{0x45, 0x00},
+	{0x46, 0x00},
+	{0x47, 0x00},
+	{0x48, 0x00},
+	{0x49, 0x00},
+	{0x4a, 0x00},
+	{0x4b, 0x00},
+	{0x4c, 0xfe},
+	{0x4d, 0xfe},
+	{0x4e, 0xfe},
+	{0x4f, 0xfe},
+	{0x50, 0xfb},
+	{0x51, 0xfb},
+	{0x52, 0xfb},
+	{0x53, 0xfb},
+	{0x58, 0x80},
+	{0x59, 0x80},
+	{0x5a, 0x80},
+	{0x5b, 0x80},
+	{0x5c, 0x82}, //pal_cm_off
+	{0x5d, 0x82},
+	{0x5e, 0x82},
+	{0x5f, 0x82},
+	{0x60, 0x10},
+	{0x61, 0x10},
+	{0x62, 0x10},
+	{0x63, 0x10},
+	{0x64, 0x07}, //y_delay
+	{0x65, 0x07},
+	{0x66, 0x07},
+	{0x67, 0x07},
+	{0x68, 0x68}, //h_delay_a //h_delay_lsb
+	{0x69, 0x68},
+	{0x6a, 0x68},
+	{0x6b, 0x68},
+	{0x6c, 0x00},
+	{0x6d, 0x00},
+	{0x6e, 0x00},
+	{0x6f, 0x00},
+	{0x70, 0x3f}, //v_crop_start
+	{0x71, 0x3f},
+	{0x72, 0x3f},
+	{0x73, 0x3f},
+	{0x78, 0x21},
+	{0x79, 0x21},
+	{0x7a, 0x21},
+	{0x7b, 0x21},
+
+	{0xFF, 0x01},
+	{0x7C, 0x00},
+	{0x84, 0x04},
+	{0x85, 0x04},
+	{0x86, 0x04},
+	{0x87, 0x04},
+	{0x88, 0x01},
+	{0x89, 0x01},
+	{0x8a, 0x01},
+	{0x8b, 0x01},
+	{0x8c, 0x02},
+	{0x8d, 0x02},
+	{0x8e, 0x02},
+	{0x8f, 0x02},
+	{0xEC, 0x00},
+	{0xED, 0x00},
+	{0xEE, 0x00},
+	{0xEF, 0x00},
+
+	{0xFF, 0x05},
+	{0x00, 0xd0},
+	{0x01, 0x2c},
+	{0x05, 0x20}, //d_agc_option
+	{0x1d, 0x0c},
+	{0x21, 0x20}, //sub contrast
+	{0x24, 0x2a},
+	{0x25, 0xcc}, //fsc_lock_mode
+	{0x26, 0x40},
+	{0x27, 0x57},
+	{0x28, 0x80}, //s_point
+	{0x2b, 0xc0}, //saturation_b
+	{0x31, 0x02},
+	{0x32, 0x10},
+	{0x38, 0x00},
+	{0x47, 0xEE},
+	{0x50, 0xc6},
+	{0x53, 0x04},
+	{0x57, 0x00},
+	{0x58, 0x77},
+	{0x59, 0x00},
+	{0x5C, 0x78},
+	{0x5F, 0x00},
+	{0x62, 0x20},
+	{0x64, 0x01},
+	{0x65, 0x00},
+	{0x69, 0x00},
+	{0x6E, 0x00}, //VBLK_EXT_EN
+	{0x6F, 0x00}, //VBLK_EXT_[7:0]
+	{0x90, 0x0d}, //comb_mode
+	{0x92, 0x00},
+	{0x94, 0x00},
+	{0x95, 0x00},
+	{0xa9, 0x00},
+	{0xb5, 0x00},
+	{0xb7, 0xfc},
+	{0xb8, 0xb8},
+	{0xb9, 0x72},
+	{0xbb, 0x0f},
+	{0xd1, 0x30}, //burst_dec_c
+	{0xd5, 0x80},
+
+	{0xFF, 0x09},
+	{0x40, 0x00},
+	{0x41, 0x00},
+	{0x42, 0x00},
+	{0x43, 0x00},
+	{0x44, 0x00},
+	{0x45, 0x00},
+	{0x46, 0x00},
+	{0x47, 0x00},
+	{0x50, 0x30},
+	{0x51, 0x6f},
+	{0x52, 0x67},
+	{0x53, 0x48},
+	{0x54, 0x30},
+	{0x55, 0x6f},
+	{0x56, 0x67},
+	{0x57, 0x48},
+	{0x58, 0x30},
+	{0x59, 0x6f},
+	{0x5a, 0x67},
+	{0x5b, 0x48},
+	{0x5c, 0x30},
+	{0x5d, 0x6f},
+	{0x5e, 0x67},
+	{0x5f, 0x48},
+	{0x96, 0x10},
+	{0x97, 0x10},
+	{0x98, 0x00},
+	{0x99, 0x00},
+	{0x9a, 0x00},
+	{0x9b, 0x00},
+	{0x9c, 0x00},
+	{0x9d, 0x00},
+	{0x9e, 0x00},
+	{0xb6, 0x10},
+	{0xb7, 0x10},
+	{0xb8, 0x00},
+	{0xb9, 0x00},
+	{0xba, 0x00},
+	{0xbb, 0x00},
+	{0xbc, 0x00},
+	{0xbd, 0x00},
+	{0xbe, 0x00},
+	{0xd6, 0x10},
+	{0xd7, 0x10},
+	{0xd8, 0x00},
+	{0xd9, 0x00},
+	{0xda, 0x00},
+	{0xdb, 0x00},
+	{0xdc, 0x00},
+	{0xdd, 0x00},
+	{0xde, 0x00},
+	{0xf6, 0x10},
+	{0xf7, 0x10},
+	{0xf8, 0x00},
+	{0xf9, 0x00},
+	{0xfa, 0x00},
+	{0xfb, 0x00},
+	{0xfc, 0x00},
+	{0xfd, 0x00},
+	{0xfe, 0x00},
+
+	{0xff, 0x0a},
+	{0x3d, 0x00},
+	{0x3c, 0x00},
+	{0x30, 0xac},
+	{0x31, 0x78},
+	{0x32, 0x17},
+	{0x33, 0xc1},
+	{0x34, 0x40},
+	{0x35, 0x00},
+	{0x36, 0xc3},
+	{0x37, 0x0a},
+	{0x38, 0x00},
+	{0x39, 0x02},
+	{0x3a, 0x00},
+	{0x3b, 0xb2},
+	{0x25, 0x10},
+	{0x27, 0x1e},
+	{0xbd, 0x00},
+	{0xbc, 0x00},
+	{0xb0, 0xac},
+	{0xb1, 0x78},
+	{0xb2, 0x17},
+	{0xb3, 0xc1},
+	{0xb4, 0x40},
+	{0xb5, 0x00},
+	{0xb6, 0xc3},
+	{0xb7, 0x0a},
+	{0xb8, 0x00},
+	{0xb9, 0x02},
+	{0xba, 0x00},
+	{0xbb, 0xb2},
+	{0xa5, 0x10},
+	{0xa7, 0x1e},
+
+	{0xff, 0x0b},
+	{0x3d, 0x00},
+	{0x3c, 0x00},
+	{0x30, 0xac},
+	{0x31, 0x78},
+	{0x32, 0x17},
+	{0x33, 0xc1},
+	{0x34, 0x40},
+	{0x35, 0x00},
+	{0x36, 0xc3},
+	{0x37, 0x0a},
+	{0x38, 0x00},
+	{0x39, 0x02},
+	{0x3a, 0x00},
+	{0x3b, 0xb2},
+	{0x25, 0x10},
+	{0x27, 0x1e},
+	{0xbd, 0x00},
+	{0xbc, 0x00},
+	{0xb0, 0xac},
+	{0xb1, 0x78},
+	{0xb2, 0x17},
+	{0xb3, 0xc1},
+	{0xb4, 0x40},
+	{0xb5, 0x00},
+	{0xb6, 0xc3},
+	{0xb7, 0x0a},
+	{0xb8, 0x00},
+	{0xb9, 0x02},
+	{0xba, 0x00},
+	{0xbb, 0xb2},
+	{0xa5, 0x10},
+	{0xa7, 0x1e},
+
+	{0xFF, 0x21},
+	{0x3E, 0x00},
+	{0x3F, 0x00},
+	{0xFF, 0x20},
+	{0x01, 0xaa}, //0x00:1/1;0x55:1/2;0xaa:1/4
+	{0x00, 0x00},
+	{0x40, 0x01},
+	{0x0F, 0x00},
+	{0x0D, 0x01}, //0x01:4lane;0x00:2lane
+	{0x40, 0x00},
+	{0x00, 0xff}, //0xff:ch1/2/3/4 0x33:ch1/2 0x11:ch1
+
+	{0xFF, 0x01},
+	{0xC8, 0x00},
+	{0xC9, 0x00},
+	{0xCA, 0x00},
+	{0xCB, 0x00},
+
+	//pattern enabled
+	{0xFF, 0x00},
+	{0x1C, 0x1A},
+	{0x1D, 0x1A},
+	{0x1E, 0x1A},
+	{0x1F, 0x1A},
+
+	{0xFF, 0x05},
+	{0x6A, 0x80},
+	{0xFF, 0x06},
+	{0x6A, 0x80},
+	{0xFF, 0x07},
+	{0x6A, 0x80},
+	{0xFF, 0x08},
+	{0x6A, 0x80},
+	{0xFF, 0x21}, //add frame num
+	{0x3E, 0x11}, //1 : Fix to 1 for Odd Field, 2 for Even Field
+	{0x3F, 0x11}, //1 : Fix to 1 for Odd Field, 2 for Even Field
+	SensorEnd
+};
+
 /* 720p Preview resolution setting*/
 static struct rk_sensor_reg sensor_preview_data_720p_25hz[] = {
 	{0xff, 0x04},
@@ -923,6 +1765,34 @@
 	int i = 0;
 
 	switch (cvstd) {
+	case CVSTD_PAL:
+		ad->cfg.width = FORCE_PAL_WIDTH;
+		ad->cfg.height = FORCE_PAL_HEIGHT;
+		ad->cfg.start_x = 0;
+		ad->cfg.start_y = 0;
+		ad->cfg.input_format = CIF_INPUT_FORMAT_PAL;
+		ad->cfg.output_format = FORCE_CIF_OUTPUT_FORMAT;
+		ad->cfg.field_order = 1;
+		ad->cfg.yuv_order = 0;/*00 - UYVY*/
+		ad->cfg.href = 0;
+		ad->cfg.vsync = 0;
+		ad->cfg.frame_rate = 25;//25	 30
+		ad->cfg.mipi_freq = JAGUAR1_LINK_FREQ_320M;
+		break;
+	case CVSTD_NTSC:
+		ad->cfg.width = FORCE_NTSC_WIDTH;
+		ad->cfg.height = FORCE_NTSC_HEIGHT;
+		ad->cfg.start_x = 0;
+		ad->cfg.start_y = 0;
+		ad->cfg.input_format = CIF_INPUT_FORMAT_NTSC;
+		ad->cfg.output_format = FORCE_CIF_OUTPUT_FORMAT;
+		ad->cfg.field_order = 1;
+		ad->cfg.yuv_order = 0;/*00 - UYVY*/
+		ad->cfg.href = 0;
+		ad->cfg.vsync = 0;
+		ad->cfg.frame_rate = 30;//25	 30
+		ad->cfg.mipi_freq = JAGUAR1_LINK_FREQ_320M;
+		break;
 	case CVSTD_720P25:
 		ad->cfg.width = 1280;
 		ad->cfg.height = 720;
@@ -1009,6 +1879,14 @@
 	int i;
 
 	switch (cvstd) {
+	case CVSTD_NTSC:
+		VEHICLE_DG("%s, init CVSTD_NTSC mode", __func__);
+		sensor = sensor_preview_data_ntsc_30hz;
+		break;
+	case CVSTD_PAL:
+		VEHICLE_DG("%s, init CVSTD_PAL mode", __func__);
+		sensor = sensor_preview_data_pal_25hz;
+		break;
 	case CVSTD_720P25:
 		VEHICLE_DG("%s, init CVSTD_720P25 mode)", __func__);
 		sensor = sensor_preview_data_720p_25hz;
@@ -1072,6 +1950,7 @@
 	}
 
 	nvp6324_g_addev->cfg.ad_ready = true;
+	nvp6324_g_addev->cfg.drop_frames = nvp6324_g_addev->drop_frames;
 
 	*cfg = &nvp6324_g_addev->cfg;
 
@@ -1167,10 +2046,10 @@
 		VEHICLE_DG("%s(%d): 1080P25", __func__, __LINE__);
 	} else if (cvstd == 0x00) {
 		cvstd_mode = CVSTD_NTSC;
-		VEHICLE_DG("%s(%d): 720H NTSC\n", __func__, __LINE__);
+		VEHICLE_DG("%s(%d): 960H NTSC\n", __func__, __LINE__);
 	} else if (cvstd == 0x10) {
 		cvstd_mode = CVSTD_PAL;
-		VEHICLE_DG("%s(%d): 720H PAL\n", __func__, __LINE__);
+		VEHICLE_DG("%s(%d): 960H PAL\n", __func__, __LINE__);
 	} else if (cvstd == 0xff) {
 		cvstd_mode = cvstd_old;
 		VEHICLE_DG("%s(%d): no ahd plugin!\n", __func__, __LINE__);
diff --git a/kernel/drivers/video/rockchip/vehicle/vehicle_cfg.h b/kernel/drivers/video/rockchip/vehicle/vehicle_cfg.h
index b03b6a8..96241b7 100644
--- a/kernel/drivers/video/rockchip/vehicle/vehicle_cfg.h
+++ b/kernel/drivers/video/rockchip/vehicle/vehicle_cfg.h
@@ -139,6 +139,7 @@
 	/*0:no, 1:90; 2:180; 4:270; 0x10:mirror-y; 0x20:mirror-x*/
 	int rotate_mirror;
 	struct rkmodule_csi_dphy_param *dphy_param;
+	int drop_frames;
 };
 
 #endif
diff --git a/kernel/drivers/video/rockchip/vehicle/vehicle_cif.c b/kernel/drivers/video/rockchip/vehicle/vehicle_cif.c
index 7d2e08e..8e83a60 100644
--- a/kernel/drivers/video/rockchip/vehicle/vehicle_cif.c
+++ b/kernel/drivers/video/rockchip/vehicle/vehicle_cif.c
@@ -2266,6 +2266,31 @@
 	return index;
 }
 
+static enum cif_reg_index get_reg_index_of_frm_num(int channel_id)
+{
+	enum cif_reg_index index;
+
+	switch (channel_id) {
+	case 0:
+		index = CIF_REG_MIPI_FRAME_NUM_VC0;
+		break;
+	case 1:
+		index = CIF_REG_MIPI_FRAME_NUM_VC1;
+		break;
+	case 2:
+		index = CIF_REG_MIPI_FRAME_NUM_VC2;
+		break;
+	case 3:
+		index = CIF_REG_MIPI_FRAME_NUM_VC3;
+		break;
+	default:
+		index = CIF_REG_MIPI_FRAME_NUM_VC0;
+		break;
+	}
+
+	return index;
+}
+
 static enum cif_reg_index get_reg_index_of_frm1_y_addr(int channel_id)
 {
 	enum cif_reg_index index;
@@ -2758,14 +2783,14 @@
 	VEHICLE_DG("%s, LINE=%d, channel->fmt_val = 0x%x", __func__, __LINE__, channel->fmt_val);
 	if (cfg->input_format == CIF_INPUT_FORMAT_PAL ||
 		cfg->input_format == CIF_INPUT_FORMAT_NTSC) {
-		VEHICLE_DG("CVBS IN PAL or NTSC config.");
+		VEHICLE_INFO("CVBS IN PAL or NTSC config.");
 		channel->virtual_width *= 2;
 		cif->interlaced_enable = true;
 		cif->interlaced_offset = channel->width;
 		cif->interlaced_counts = 0;
 		cif->interlaced_buffer = 0;
 		channel->height /= 2;
-		VEHICLE_DG("do denterlaced.\n");
+		VEHICLE_INFO("do denterlaced.\n");
 	}
 
 	channel->data_type = get_data_type(cfg->mbus_code,
@@ -3930,7 +3955,7 @@
 	static unsigned long temp_y_addr, temp_uv_addr;
 	int commit_buf = 0;
 	struct vehicle_rkcif_dummy_buffer *dummy_buf = &cif->dummy_buf;
-
+	u32 frm_num_reg, frame_id = 0;
 	VEHICLE_DG("@%s, enter, mipi_id(%d)\n", __func__, mipi_id);
 
 	if ((frame_ready > 1) || (cif->cif_cfg.buf_num < 2) ||
@@ -3946,6 +3971,10 @@
 		frm0_addr_uv = get_reg_index_of_frm0_uv_addr(mipi_id);
 		frm1_addr_y = get_reg_index_of_frm1_y_addr(mipi_id);
 		frm1_addr_uv = get_reg_index_of_frm1_uv_addr(mipi_id);
+		frm_num_reg = get_reg_index_of_frm_num(mipi_id);
+		frame_id = rkcif_read_reg(cif, frm_num_reg);
+		VEHICLE_DG("@%s, frm_num_reg(0x%x), frame_id:0x%x\n", __func__,
+			   frm_num_reg, frame_id);
 	} else {
 		frm0_addr_y = get_dvp_reg_index_of_frm0_y_addr(mipi_id);
 		frm0_addr_uv = get_dvp_reg_index_of_frm0_uv_addr(mipi_id);
@@ -3979,10 +4008,11 @@
 		uv_addr = temp_uv_addr;
 		commit_buf = 0;
 	} else {
-		if ((cif->interlaced_counts % 2) == 0) {
+		if ((frame_id != 0 && (frame_id & 0xffff) % 2 == 0) ||
+		    (frame_id == 0 && (cif->interlaced_counts % 2 == 0))) {
 			temp_y_addr = vehicle_flinger_request_cif_buffer();
 			if (temp_y_addr == 0) {
-				VEHICLE_INFO("%s,warnning request buffer failed\n", __func__);
+				VEHICLE_DGERR("%s,warnning request buffer failed\n", __func__);
 				spin_unlock(&cif->vbq_lock);
 				return -1;
 			}
@@ -3995,6 +4025,11 @@
 			//uv_addr = temp_uv_addr;
 			uv_addr = temp_uv_addr + cif->interlaced_offset;
 			commit_buf = 0; //even & odd field add
+			if (temp_y_addr == 0) {
+				VEHICLE_DGERR("%s,warnning temp_y_addr is NULL!\n", __func__);
+				spin_unlock(&cif->vbq_lock);
+				return -1;
+			}
 		}
 		WARN_ON(y_addr == cif->interlaced_offset);
 		WARN_ON(uv_addr == cif->interlaced_offset);
@@ -4453,6 +4488,37 @@
 	return IRQ_HANDLED;
 }
 
+#define vehicle_csi2_err_strncat(dst_str, src_str) {\
+	if (strlen(dst_str) + strlen(src_str) < CSI_ERRSTR_LEN)\
+		strncat(dst_str, src_str, strlen(src_str)); }
+
+static void vehicle_csi2_find_err_vc(int val, char *vc_info)
+{
+	int i;
+	char cur_str[CSI_VCINFO_LEN] = {0};
+
+	memset(vc_info, 0, sizeof(*vc_info));
+	for (i = 0; i < 4; i++) {
+		if ((val >> i) & 0x1) {
+			snprintf(cur_str, CSI_VCINFO_LEN, " %d", i);
+			if (strlen(vc_info) + strlen(cur_str) < CSI_VCINFO_LEN)
+				strncat(vc_info, cur_str, strlen(cur_str));
+		}
+	}
+}
+
+static void vehicle_csi2_err_print_work(struct work_struct *work)
+{
+	struct vehicle_csi2_err_state_work *err_state = container_of(work,
+							struct vehicle_csi2_err_state_work,
+							work);
+
+	pr_err("mipi_csi2: ERR%d:0x%x %s\n", err_state->err_num,
+		err_state->err_val, err_state->err_str);
+	if (err_state->err_num == 1)
+		pr_info("mipi_csi2: err_stat:0x%x\n", err_state->err_stat);
+}
+
 static irqreturn_t vehicle_csirx_irq1(int irq, void *data)
 {
 	struct vehicle_cif *cif = (struct vehicle_cif *)data;
@@ -4460,6 +4526,9 @@
 	struct csi2_err_stats *err_list = NULL;
 	unsigned long err_stat = 0;
 	u32 val;
+	char err_str[CSI_ERRSTR_LEN] = {0};
+	char cur_str[CSI_ERRSTR_LEN] = {0};
+	char vc_info[CSI_VCINFO_LEN] = {0};
 
 	val = read_reg(hw->csi2_base, CSIHOST_ERR1);
 	if (val) {
@@ -4469,53 +4538,69 @@
 		if (val & CSIHOST_ERR1_PHYERR_SPTSYNCHS) {
 			err_list = &hw->err_list[RK_CSI2_ERR_SOTSYN];
 			err_list->cnt++;
-			VEHICLE_DGERR(
-			"ERR1: start of transmission error, reg: 0x%x,cnt:%d\n",
-				val, err_list->cnt);
+
+			vehicle_csi2_find_err_vc(val & 0xf, vc_info);
+			snprintf(cur_str, CSI_ERRSTR_LEN, "(sot sync,lane:%s) ", vc_info);
+			vehicle_csi2_err_strncat(err_str, cur_str);
 		}
 
 		if (val & CSIHOST_ERR1_ERR_BNDRY_MATCH) {
 			err_list = &hw->err_list[RK_CSI2_ERR_FS_FE_MIS];
 			err_list->cnt++;
-			VEHICLE_DGERR(
-			"ERR1: error matching frame start with frame end, reg: 0x%x,cnt:%d\n",
-				val, err_list->cnt);
+			vehicle_csi2_find_err_vc((val >> 4) & 0xf, vc_info);
+			snprintf(cur_str, CSI_ERRSTR_LEN, "(fs/fe miss,vc:%s) ", vc_info);
+			vehicle_csi2_err_strncat(err_str, cur_str);
+
 		}
 
 		if (val & CSIHOST_ERR1_ERR_SEQ) {
 			err_list = &hw->err_list[RK_CSI2_ERR_FRM_SEQ_ERR];
 			err_list->cnt++;
-			VEHICLE_DGERR("ERR1: incorrect frame sequence detected, reg: 0x%x,cnt:%d\n",
-				val, err_list->cnt);
+			vehicle_csi2_find_err_vc((val >> 8) & 0xf, vc_info);
+			snprintf(cur_str, CSI_ERRSTR_LEN, "(f_seq,vc:%s) ", vc_info);
+			vehicle_csi2_err_strncat(err_str, cur_str);
+
 		}
 
 		if (val & CSIHOST_ERR1_ERR_FRM_DATA) {
 			err_list = &hw->err_list[RK_CSI2_ERR_CRC_ONCE];
 			err_list->cnt++;
-			VEHICLE_DGERR("ERR1: at least one crc error, reg: 0x%x\n,cnt:%d",
-				val, err_list->cnt);
+			vehicle_csi2_find_err_vc((val >> 12) & 0xf, vc_info);
+			snprintf(cur_str, CSI_ERRSTR_LEN, "(err_data,vc:%s) ", vc_info);
+			vehicle_csi2_err_strncat(err_str, cur_str);
+
 		}
 
 		if (val & CSIHOST_ERR1_ERR_CRC) {
 			err_list = &hw->err_list[RK_CSI2_ERR_CRC];
 			err_list->cnt++;
-			VEHICLE_DGERR("ERR1: crc errors, reg: 0x%x, cnt:%d\n",
-				 val, err_list->cnt);
+			vehicle_csi2_find_err_vc((val >> 24) & 0xf, vc_info);
+			snprintf(cur_str, CSI_ERRSTR_LEN, "(crc,vc:%s) ", vc_info);
+			vehicle_csi2_err_strncat(err_str, cur_str);
+
 		}
 
 		if (val & CSIHOST_ERR1_ERR_ECC2) {
 			err_list = &hw->err_list[RK_CSI2_ERR_CRC];
 			err_list->cnt++;
-			VEHICLE_DGERR("ERR1: ecc errors, reg: 0x%x, cnt:%d\n",
-				 val, err_list->cnt);
-		}
-		if (val & CSIHOST_ERR1_ERR_CTRL)
-			VEHICLE_DGERR("ERR1: ctrl errors, reg: 0x%x\n", val);
+			snprintf(cur_str, CSI_ERRSTR_LEN, "(ecc2) ");
+			vehicle_csi2_err_strncat(err_str, cur_str);
 
+		}
+		if (val & CSIHOST_ERR1_ERR_CTRL) {
+			vehicle_csi2_find_err_vc((val >> 16) & 0xf, vc_info);
+			snprintf(cur_str, CSI_ERRSTR_LEN, "(ctrl,vc:%s) ", vc_info);
+			vehicle_csi2_err_strncat(err_str, cur_str);
+		}
 		hw->err_list[RK_CSI2_ERR_ALL].cnt++;
 		err_stat = ((hw->err_list[RK_CSI2_ERR_FS_FE_MIS].cnt & 0xff) << 8) |
 			    ((hw->err_list[RK_CSI2_ERR_ALL].cnt) & 0xff);
-		VEHICLE_INFO("%s: err_stat: %x\n", err_stat);
+
+		cif->err_state.err_val = val;
+		cif->err_state.err_num = 1;
+		cif->err_state.err_stat = err_stat;
+		strscpy(cif->err_state.err_str, err_str, CSI_ERRSTR_LEN);
+		queue_work(cif->err_state.err_print_wq, &cif->err_state.work);
 
 	}
 
@@ -4527,22 +4612,41 @@
 	struct vehicle_cif *cif = (struct vehicle_cif *)data;
 	struct csi2_dphy_hw *hw = cif->dphy_hw;
 	u32 val;
+	char cur_str[CSI_ERRSTR_LEN] = {0};
+	char err_str[CSI_ERRSTR_LEN] = {0};
+	char vc_info[CSI_VCINFO_LEN] = {0};
 
 	val = read_reg(hw->csi2_base, CSIHOST_ERR2);
 	if (val) {
-		if (val & CSIHOST_ERR2_PHYERR_ESC)
-			VEHICLE_DGERR("ERR2: escape entry error(ULPM), reg: 0x%x\n", val);
-		if (val & CSIHOST_ERR2_PHYERR_SOTHS)
-			VEHICLE_DGERR(
-				"ERR2: start of transmission error, reg: 0x%x\n", val);
-		if (val & CSIHOST_ERR2_ECC_CORRECTED)
-			VEHICLE_DGERR(
-				"ERR2: header error detected and corrected, reg: 0x%x\n", val);
-		if (val & CSIHOST_ERR2_ERR_ID)
-			VEHICLE_DGERR(
-				"ERR2: unrecognized data type detected, reg: 0x%x\n", val);
-		if (val & CSIHOST_ERR2_PHYERR_CODEHS)
-			VEHICLE_DGERR("ERR2: receive error code, reg: 0x%x\n", val);
+		if (val & CSIHOST_ERR2_PHYERR_ESC) {
+			vehicle_csi2_find_err_vc(val & 0xf, vc_info);
+			snprintf(cur_str, CSI_ERRSTR_LEN, "(ULPM,lane:%s) ", vc_info);
+			vehicle_csi2_err_strncat(err_str, cur_str);
+		}
+		if (val & CSIHOST_ERR2_PHYERR_SOTHS) {
+			vehicle_csi2_find_err_vc((val >> 4) & 0xf, vc_info);
+			snprintf(cur_str, CSI_ERRSTR_LEN, "(sot,lane:%s) ", vc_info);
+			vehicle_csi2_err_strncat(err_str, cur_str);
+		}
+		if (val & CSIHOST_ERR2_ECC_CORRECTED) {
+			vehicle_csi2_find_err_vc((val >> 8) & 0xf, vc_info);
+			snprintf(cur_str, CSI_ERRSTR_LEN, "(ecc,vc:%s) ", vc_info);
+			vehicle_csi2_err_strncat(err_str, cur_str);
+		}
+		if (val & CSIHOST_ERR2_ERR_ID) {
+			vehicle_csi2_find_err_vc((val >> 12) & 0xf, vc_info);
+			snprintf(cur_str, CSI_ERRSTR_LEN, "(err id,vc:%s) ", vc_info);
+			vehicle_csi2_err_strncat(err_str, cur_str);
+		}
+		if (val & CSIHOST_ERR2_PHYERR_CODEHS) {
+			snprintf(cur_str, CSI_ERRSTR_LEN, "(err code) ");
+			vehicle_csi2_err_strncat(err_str, cur_str);
+		}
+		cif->err_state.err_val = val;
+		cif->err_state.err_num = 2;
+		strscpy(cif->err_state.err_str, err_str, CSI_ERRSTR_LEN);
+		queue_work(cif->err_state.err_print_wq, &cif->err_state.work);
+
 	}
 
 	return IRQ_HANDLED;
@@ -4661,6 +4765,7 @@
 	cif->stopping = true;
 	cancel_delayed_work_sync(&(cif->work));
 	flush_delayed_work(&(cif->work));
+	cancel_work_sync(&cif->err_state.work);
 
 	ret = wait_event_timeout(cif->wq_stopped,
 				 cif->state != RKCIF_STATE_STREAMING,
@@ -5118,6 +5223,7 @@
 	if (inf_id == RKCIF_MIPI_LVDS) {
 		/* 5. set csi2-mipi-dphy reg */
 		if (cif->dphy_hw->chip_id == CHIP_ID_RK3588 ||
+		    cif->dphy_hw->chip_id == CHIP_ID_RK3568 ||
 		    cif->dphy_hw->chip_id == CHIP_ID_RK3562)
 			cif->dphy_hw->csi2_dphy_base = cif->csi2_dphy_base;
 
@@ -5140,6 +5246,13 @@
 	init_waitqueue_head(&cif->wq_stopped);
 
 	spin_lock_init(&cif->vbq_lock);
+
+	INIT_WORK(&cif->err_state.work, vehicle_csi2_err_print_work);
+	cif->err_state.err_print_wq = create_workqueue("cis2_err_print_queue");
+	if (cif->err_state.err_print_wq == NULL) {
+		dev_err(dev, "%s: %s create failed.\n", __func__,
+			"csi2_err_print_wq");
+	}
 
 	return 0;
 }
@@ -5211,6 +5324,10 @@
 		free_irq(cif->csi2_irq1, cif);
 		free_irq(cif->csi2_irq2, cif);
 	}
+	if (cif->err_state.err_print_wq) {
+		flush_workqueue(cif->err_state.err_print_wq);
+		destroy_workqueue(cif->err_state.err_print_wq);
+	}
 
 	return 0;
 }
diff --git a/kernel/drivers/video/rockchip/vehicle/vehicle_cif.h b/kernel/drivers/video/rockchip/vehicle/vehicle_cif.h
index e5ab191..3c75694 100644
--- a/kernel/drivers/video/rockchip/vehicle/vehicle_cif.h
+++ b/kernel/drivers/video/rockchip/vehicle/vehicle_cif.h
@@ -92,6 +92,15 @@
 	unsigned int	crop_st_y;
 };
 
+struct vehicle_csi2_err_state_work {
+	struct workqueue_struct *err_print_wq;
+	struct work_struct work;
+	char err_str[CSI_ERRSTR_LEN];
+	u32 err_val;
+	u32 err_num;
+	unsigned long err_stat;
+};
+
 struct vehicle_cif {
 	struct		device *dev;
 	struct		device_node *phy_node;
@@ -137,6 +146,7 @@
 	bool		stopping;
 	struct mutex	stream_lock;
 	enum rkcif_state	state;
+	struct vehicle_csi2_err_state_work err_state;
 };
 
 int vehicle_cif_init_mclk(struct vehicle_cif *cif);
diff --git a/kernel/drivers/video/rockchip/vehicle/vehicle_dev.c b/kernel/drivers/video/rockchip/vehicle/vehicle_dev.c
index 7e9ece0..29c0f67 100644
--- a/kernel/drivers/video/rockchip/vehicle/vehicle_dev.c
+++ b/kernel/drivers/video/rockchip/vehicle/vehicle_dev.c
@@ -106,12 +106,11 @@
 	return 0;
 }
 
-static void __exit vechile_module_exit(void)
+void vechile_module_exit(void)
 {
 	misc_deregister(&vechile_dev);
 }
 
 module_init(vechile_module_init);
-module_exit(vechile_module_exit);
 
 MODULE_LICENSE("GPL");
diff --git a/kernel/drivers/video/rockchip/vehicle/vehicle_flinger.c b/kernel/drivers/video/rockchip/vehicle/vehicle_flinger.c
index 5fd2795..e2c0e67 100644
--- a/kernel/drivers/video/rockchip/vehicle/vehicle_flinger.c
+++ b/kernel/drivers/video/rockchip/vehicle/vehicle_flinger.c
@@ -46,7 +46,6 @@
 static int vehicle_dump_cif;
 static int vehicle_dump_rga;
 static int vehicle_dump_vop;
-static bool nv12_display = true;
 
 enum force_value {
 	FORCE_WIDTH = 1920,
@@ -326,8 +325,11 @@
 	if (inited)
 		return 0;
 
+	VEHICLE_INFO("%s: v_cfg->rotate_mirror(0x%x)\n", __func__, v_cfg->rotate_mirror);
+
 	// if (FORCE_ROTATION == RGA_TRANSFORM_ROT_270 || FORCE_ROTATION == RGA_TRANSFORM_ROT_90) {
-	if (v_cfg->rotate_mirror == 0x01  || v_cfg->rotate_mirror == 0x04) {
+	if ((v_cfg->rotate_mirror & RGA_TRANSFORM_ROT_MASK) == 0x01 ||
+	    (v_cfg->rotate_mirror & RGA_TRANSFORM_ROT_MASK) == 0x04) {
 		w = FORCE_WIDTH;
 		h = ALIGN(FORCE_HEIGHT, 64);
 		s = ALIGN(FORCE_HEIGHT, 64);
@@ -387,7 +389,8 @@
 		// f = HAL_PIXEL_FORMAT_RGBX_8888;
 		// if (FORCE_ROTATION == RGA_TRANSFORM_ROT_270 ||
 		//	FORCE_ROTATION == RGA_TRANSFORM_ROT_90)
-		if (v_cfg->rotate_mirror == 0x01  || v_cfg->rotate_mirror == 0x04)
+		if ((v_cfg->rotate_mirror & RGA_TRANSFORM_ROT_MASK) == 0x01 ||
+		    (v_cfg->rotate_mirror & RGA_TRANSFORM_ROT_MASK) == 0x04)
 			ret = rk_flinger_alloc_buffer(flg, buffer, h, w, s, f);
 		else
 			ret = rk_flinger_alloc_buffer(flg, buffer, w, h, s, f);
@@ -528,7 +531,7 @@
 	src_rect = &buffer->src;
 	dst_rect = &buffer->dst;
 
-	switch (buffer->rotation) {
+	switch (buffer->rotation & RGA_TRANSFORM_ROT_MASK) {
 	case RGA_TRANSFORM_ROT_90:
 	case RGA_TRANSFORM_ROT_270:
 		dst_rect->x = src_rect->x;
@@ -662,7 +665,10 @@
 	rga_request.rotate_mode = 0;
 	rga_request.sina = 0;
 	rga_request.cosa = 0;
-	rga_request.yuv2rgb_mode = 0x1 << 0; // limit range
+
+	rga_request.yuv2rgb_mode = 0x0 << 0; // yuvtoyuv config 0
+	/* yuv to rgb color space transform if need  */
+	//rga_request.yuv2rgb_mode = 0x1 << 0; // limit range
 	//rga_request.yuv2rgb_mode = 0x2 << 0; // full range
 
 	rga_request.src.act_w = src_buffer->src.w;
@@ -685,7 +691,7 @@
 	rga_request.dst.yrgb_addr = dst_buffer->fd;
 	rga_request.dst.uv_addr = 0;
 	rga_request.dst.v_addr = 0;
-	rga_request.dst.format =  RGA_FORMAT_RGBX_8888;
+	rga_request.dst.format =  RGA_FORMAT_YCrCb_420_SP;
 
 	rga_request.scale_mode = 1;
 
@@ -834,7 +840,7 @@
 		rga_request.rotate_mode = 0;
 		rga_request.sina = 0;
 		rga_request.cosa = 0;
-		rga_request.dst.vir_w = ALIGN(ds, 64);
+		rga_request.dst.vir_w = ds;
 		rga_request.dst.vir_h = dh;
 		rga_request.dst.act_w = dw;
 		rga_request.dst.act_h = dh;
@@ -843,7 +849,7 @@
 		break;
 	case RGA_TRANSFORM_FLIP_H:/*x mirror*/
 		rga_request.rotate_mode = 2;
-		rga_request.dst.vir_w = ALIGN(ds, 64);
+		rga_request.dst.vir_w = ds;
 		rga_request.dst.vir_h = dh;
 		rga_request.dst.act_w = dw;
 		rga_request.dst.act_h = dh;
@@ -852,7 +858,7 @@
 		break;
 	case RGA_TRANSFORM_FLIP_V:/*y mirror*/
 		rga_request.rotate_mode = 3;
-		rga_request.dst.vir_w = ALIGN(ds, 64);
+		rga_request.dst.vir_w = ds;
 		rga_request.dst.vir_h = dh;
 		rga_request.dst.act_w = dw;
 		rga_request.dst.act_h = dh;
@@ -863,7 +869,7 @@
 		rga_request.rotate_mode = 1;
 		rga_request.sina = 65536;
 		rga_request.cosa = 0;
-		rga_request.dst.vir_w = ALIGN(ds, 64);
+		rga_request.dst.vir_w = ds;
 		rga_request.dst.vir_h = dh;
 		rga_request.dst.act_w = dh;
 		rga_request.dst.act_h = dw;
@@ -874,7 +880,7 @@
 		rga_request.rotate_mode = 1;
 		rga_request.sina = 0;
 		rga_request.cosa = -65536;
-		rga_request.dst.vir_w = ALIGN(ds, 64);
+		rga_request.dst.vir_w = ds;
 		rga_request.dst.vir_h = dh;
 		rga_request.dst.act_w = dw;
 		rga_request.dst.act_h = dh;
@@ -885,7 +891,7 @@
 		rga_request.rotate_mode = 1;
 		rga_request.sina = -65536;
 		rga_request.cosa = 0;
-		rga_request.dst.vir_w = ALIGN(ds, 64);
+		rga_request.dst.vir_w = ds;
 		rga_request.dst.vir_h = dh;
 		rga_request.dst.act_w = dh;
 		rga_request.dst.act_h = dw;
@@ -896,7 +902,7 @@
 		rga_request.rotate_mode = 0;
 		rga_request.sina = 0;
 		rga_request.cosa = 0;
-		rga_request.dst.vir_w = ALIGN(ds, 64);
+		rga_request.dst.vir_w = ds;
 		rga_request.dst.vir_h = dh;
 		rga_request.dst.act_w = dw;
 		rga_request.dst.act_h = dh;
@@ -952,19 +958,41 @@
 
 static int rk_flinger_rga_render(struct flinger *flinger,
 				 struct graphic_buffer *src_buffer,
-				 struct graphic_buffer *dst_buffer)
+				 struct graphic_buffer *dst_buffer,
+				 struct graphic_buffer *tmp_buffer)
 {
+	int rotation;
+
 	if (!flinger || !src_buffer || !dst_buffer)
 		return -EINVAL;
 
 	if (dst_buffer && dst_buffer->rel_fence)
 		dst_buffer->rel_fence = NULL;
 
-	rk_flinger_rga_blit(flinger, src_buffer, dst_buffer);
-	rk_flinger_fill_buffer_rects(dst_buffer, &src_buffer->dst,
-				     &src_buffer->dst);
-	dst_buffer->src.f = src_buffer->dst.f;
+	if ((src_buffer->rotation & RGA_TRANSFORM_ROT_MASK) &&
+		(src_buffer->rotation & RGA_TRANSFORM_FLIP_MASK)) {
 
+		rotation = flinger->v_cfg.rotate_mirror;
+		/* 1. rotate */
+		src_buffer->rotation = rotation & RGA_TRANSFORM_ROT_MASK;
+		rk_flinger_rga_blit(flinger, src_buffer, tmp_buffer);
+		rk_flinger_fill_buffer_rects(tmp_buffer, &src_buffer->dst,
+					     &src_buffer->dst);
+		tmp_buffer->src.f = src_buffer->dst.f;
+		tmp_buffer->rotation = rotation & RGA_TRANSFORM_FLIP_MASK;
+		/* 2. mirror */
+		rk_flinger_rga_blit(flinger, tmp_buffer, dst_buffer);
+		rk_flinger_fill_buffer_rects(dst_buffer, &tmp_buffer->dst,
+					     &tmp_buffer->dst);
+		dst_buffer->src.f = src_buffer->dst.f;
+
+		src_buffer->rotation = rotation;
+	} else {
+		rk_flinger_rga_blit(flinger, src_buffer, dst_buffer);
+		rk_flinger_fill_buffer_rects(dst_buffer, &src_buffer->dst,
+					     &src_buffer->dst);
+		dst_buffer->src.f = src_buffer->dst.f;
+	}
 	/* save rga out buffer */
 	if (vehicle_dump_rga) {
 		struct file *filep = NULL;
@@ -1074,6 +1102,7 @@
 	rockchip_drm_direct_show_commit(flinger->drm_dev, &commit_info);
 }
 
+static int drop_frames_number;
 static int rk_flinger_vop_show(struct flinger *flinger,
 			       struct graphic_buffer *buffer)
 {
@@ -1082,6 +1111,12 @@
 
 	VEHICLE_DG("flinger vop show buffer wxh(%zux%zu)\n",
 					buffer->src.w, buffer->src.h);
+	if (drop_frames_number > 0) {
+		VEHICLE_INFO("%s discard the frame num(%d)!\n", __func__, drop_frames_number);
+		drop_frames_number--;
+		return 0;
+	}
+
 	if (!flinger->running)
 		return 0;
 
@@ -1160,9 +1195,11 @@
 					    FORCE_XOFFSET, FORCE_YOFFSET,
 					    v_cfg->width, v_cfg->height,
 					    v_cfg->width, FORCE_FORMAT);
-			rk_flinger_set_buffer_rotation(buffer, FORCE_ROTATION);
+			rk_flinger_set_buffer_rotation(buffer, v_cfg->rotate_mirror);
 			rk_flinger_cacultae_dst_rect_by_rotation(buffer);
 			buffer->dst.f = buffer->src.f;
+			VEHICLE_INFO("buffer[%d]->rotation(%d).\n",
+				      i, buffer->rotation);
 		}
 	}
 }
@@ -1294,20 +1331,12 @@
 			VEHICLE_DG("it is ypbpr signal\n");
 			iep_buffer = &(flg->target_buffer[NUM_TARGET_BUFFERS - 1]);
 			iep_buffer->state = ACQUIRE;
-			//scaler by rga for rgbx8888/rgb888/rgb565 display
-			if (!nv12_display) {
-				rk_flinger_rga_render(flg, src_buffer, iep_buffer);
-				src_buffer->state = FREE;
-				rk_flinger_rga_scaler(flg, iep_buffer, dst_buffer);
-				iep_buffer->state = FREE;
-				rk_flinger_vop_show(flg, dst_buffer);
-			} else {
-				rk_flinger_rga_render(flg, src_buffer, dst_buffer);
-				src_buffer->state = FREE;
-				rk_flinger_vop_show(flg, dst_buffer);
-				// rk_flinger_vop_show(flg, src_buffer);
-			}
-
+			//scaler by rga to force widthxheight display
+			rk_flinger_rga_render(flg, src_buffer, iep_buffer, dst_buffer);
+			src_buffer->state = FREE;
+			rk_flinger_rga_scaler(flg, iep_buffer, dst_buffer);
+			iep_buffer->state = FREE;
+			rk_flinger_vop_show(flg, dst_buffer);
 			for (i = 0; i < NUM_TARGET_BUFFERS; i++) {
 				buffer = &(flinger->target_buffer[i]);
 				if (buffer->state == DISPLAY)
@@ -1318,7 +1347,7 @@
 		} else {
 			// cvbs
 			VEHICLE_DG("it is a cvbs signal\n");
-			rk_flinger_rga_render(flg, src_buffer, dst_buffer);
+			rk_flinger_rga_render(flg, src_buffer, dst_buffer, iep_buffer);
 			src_buffer->state = FREE;
 			rk_flinger_iep_deinterlace(flg, dst_buffer, iep_buffer);
 			dst_buffer->state = FREE;
@@ -1384,18 +1413,27 @@
 
 static bool vehicle_rotation_param_check(struct vehicle_cfg *v_cfg)
 {
-	switch (v_cfg->rotate_mirror) {
+	switch (v_cfg->rotate_mirror & RGA_TRANSFORM_ROT_MASK) {
 	case RGA_TRANSFORM_ROT_90:
 	case RGA_TRANSFORM_ROT_270:
 	case RGA_TRANSFORM_ROT_0:
 	case RGA_TRANSFORM_ROT_180:
+		return true;
+	default:
+		VEHICLE_INFO("invalid rotate-mirror param %d\n",
+					v_cfg->rotate_mirror);
+		v_cfg->rotate_mirror = v_cfg->rotate_mirror & RGA_TRANSFORM_FLIP_MASK;
+		return false;
+	}
+
+	switch (v_cfg->rotate_mirror & RGA_TRANSFORM_FLIP_MASK) {
 	case RGA_TRANSFORM_FLIP_H:
 	case RGA_TRANSFORM_FLIP_V:
 		return true;
 	default:
 		VEHICLE_INFO("invalid rotate-mirror param %d\n",
 					v_cfg->rotate_mirror);
-		v_cfg->rotate_mirror = 0;
+		v_cfg->rotate_mirror = v_cfg->rotate_mirror & RGA_TRANSFORM_ROT_MASK;
 		return false;
 	}
 }
@@ -1450,6 +1488,7 @@
 	flg->cvbs_field_count = 0;
 	memcpy(&flg->v_cfg, v_cfg, sizeof(struct vehicle_cfg));
 	flg->running = true;
+	drop_frames_number = v_cfg->drop_frames;
 
 	return 0;
 }
@@ -1475,9 +1514,8 @@
 	int i;
 
 	src_buffer = NULL;
-	found = last_src_index + 1;
 	for (i = 1; i < NUM_SOURCE_BUFFERS; i++) {
-		found = (found + i) % NUM_SOURCE_BUFFERS;
+		found = (last_src_index + i) % NUM_SOURCE_BUFFERS;
 		VEHICLE_DG("%s,flg->source_buffer[%d].state(%d)",
 			__func__, found, flg->source_buffer[found].state);
 		if (flg->source_buffer[found].state == FREE) {
diff --git a/kernel/drivers/video/rockchip/vehicle/vehicle_generic_sensor.c b/kernel/drivers/video/rockchip/vehicle/vehicle_generic_sensor.c
index bafe0c8..ccb45fd 100644
--- a/kernel/drivers/video/rockchip/vehicle/vehicle_generic_sensor.c
+++ b/kernel/drivers/video/rockchip/vehicle/vehicle_generic_sensor.c
@@ -285,6 +285,12 @@
 		if (of_property_read_u32(cp, "mclk_rate", &ad->mclk_rate))
 			VEHICLE_DGERR("Get %s mclk_rate failed!\n", cp->name);
 
+		if (of_property_read_u32(cp, "drop_frames",
+					 &ad->drop_frames)) {
+			VEHICLE_DGERR("%s:Get sensor, drop-frames failed!\n", __func__);
+			ad->drop_frames = 0; //default drop frames;
+		}
+
 		if (of_property_read_u32(cp, "rst_active", &ad->rst_active))
 			VEHICLE_DGERR("Get %s rst_active failed!", cp->name);
 
diff --git a/kernel/drivers/video/rockchip/vehicle/vehicle_main.c b/kernel/drivers/video/rockchip/vehicle/vehicle_main.c
index 46f947e..a3ffdd1 100644
--- a/kernel/drivers/video/rockchip/vehicle/vehicle_main.c
+++ b/kernel/drivers/video/rockchip/vehicle/vehicle_main.c
@@ -171,7 +171,7 @@
 
 	gpio_reverse_on = vehicle_gpio_reverse_check(gpiod);
 	gpio_reverse_on = TEST_GPIO & gpio_reverse_on;
-	VEHICLE_DG(
+	VEHICLE_INFO(
 	"%s, gpio = reverse %s, width = %d, sensor_ready = %d, state=%d dvr_apk_need_start = %d\n",
 	__func__, gpio_reverse_on ? "on" : "over",
 	v_cfg->width, v_cfg->ad_ready, v->state, dvr_apk_need_start);
@@ -201,7 +201,7 @@
 				vehicle_close();
 				vehicle_ad_stream(&v->ad, 0);
 				v->state = STATE_CLOSE;
-			} else if (gpio_reverse_on) {  //  reverse on & video format change
+			} else if (gpio_reverse_on && !v->android_is_ready) { //video fmt change
 				vehicle_open_close();
 				vehicle_open(v_cfg);
 				msleep(100);
@@ -244,7 +244,7 @@
 				vehicle_close();
 				vehicle_ad_stream(&v->ad, 0);
 				v->state = STATE_CLOSE;
-			} else if (gpio_reverse_on) {  //  reverse on & video format change
+			} else if (gpio_reverse_on && !v->android_is_ready) { //video fmt change
 				vehicle_open_close();
 				vehicle_ad_stream(&v->ad, 0);
 				vehicle_ad_channel_set(&g_vehicle->ad, 0);
@@ -287,7 +287,7 @@
 				vehicle_close();
 				vehicle_ad_stream(&v->ad, 0);
 				v->state = STATE_CLOSE;
-			} else if (gpio_reverse_on) {  //  reverse on & video format change
+			} else if (gpio_reverse_on && !v->android_is_ready) { //video fmt change
 				vehicle_open_close();
 				vehicle_ad_stream(&v->ad, 0);
 				vehicle_ad_channel_set(&g_vehicle->ad, 0);
@@ -487,6 +487,7 @@
 #endif
 	// msleep(1000);
 	vehicle_exit_complete_notify(v);
+	vechile_module_exit();
 	return 0;
 }
 
diff --git a/kernel/drivers/video/rockchip/vehicle/vehicle_main.h b/kernel/drivers/video/rockchip/vehicle/vehicle_main.h
index 2f4a782..4d66db1 100644
--- a/kernel/drivers/video/rockchip/vehicle/vehicle_main.h
+++ b/kernel/drivers/video/rockchip/vehicle/vehicle_main.h
@@ -14,5 +14,6 @@
 void vehicle_cif_error_notify(int last_line);
 void vehicle_android_is_ready_notify(void);
 void vehicle_apk_state_change(char crtc[22]);
+void vechile_module_exit(void);
 
 #endif
diff --git a/kernel/fs/afs/dir_silly.c b/kernel/fs/afs/dir_silly.c
index 9a6a0ec..dae9a57 100644
--- a/kernel/fs/afs/dir_silly.c
+++ b/kernel/fs/afs/dir_silly.c
@@ -239,7 +239,7 @@
 	struct dentry *alias;
 	int ret;
 
-	DECLARE_SWAIT_QUEUE_HEAD_ONSTACK(wq);
+	DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
 
 	_enter("%p{%pd},%llx", dentry, dentry, vnode->fid.vnode);
 
diff --git a/kernel/fs/aio.c b/kernel/fs/aio.c
index 1a78979..2a9dfa5 100644
--- a/kernel/fs/aio.c
+++ b/kernel/fs/aio.c
@@ -43,6 +43,7 @@
 #include <linux/mount.h>
 #include <linux/pseudo_fs.h>
 
+#include <asm/kmap_types.h>
 #include <linux/uaccess.h>
 #include <linux/nospec.h>
 
@@ -1761,7 +1762,7 @@
 		list_del_init(&req->wait.entry);
 		list_del(&iocb->ki_list);
 		iocb->ki_res.res = mangle_poll(mask);
-		if (iocb->ki_eventfd && !eventfd_signal_allowed()) {
+		if (iocb->ki_eventfd && eventfd_signal_count()) {
 			iocb = NULL;
 			INIT_WORK(&req->work, aio_poll_put_work);
 			schedule_work(&req->work);
diff --git a/kernel/fs/btrfs/ctree.h b/kernel/fs/btrfs/ctree.h
index fabbf6c..bcc6848 100644
--- a/kernel/fs/btrfs/ctree.h
+++ b/kernel/fs/btrfs/ctree.h
@@ -17,6 +17,7 @@
 #include <linux/wait.h>
 #include <linux/slab.h>
 #include <trace/events/btrfs.h>
+#include <asm/kmap_types.h>
 #include <asm/unaligned.h>
 #include <linux/pagemap.h>
 #include <linux/btrfs.h>
diff --git a/kernel/fs/cifs/readdir.c b/kernel/fs/cifs/readdir.c
index d5165a7..799be3a 100644
--- a/kernel/fs/cifs/readdir.c
+++ b/kernel/fs/cifs/readdir.c
@@ -81,7 +81,7 @@
 	struct inode *inode;
 	struct super_block *sb = parent->d_sb;
 	struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
-	DECLARE_SWAIT_QUEUE_HEAD_ONSTACK(wq);
+	DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
 
 	cifs_dbg(FYI, "%s: for %s\n", __func__, name->name);
 
diff --git a/kernel/fs/dcache.c b/kernel/fs/dcache.c
index ddc695a..cb588ce 100644
--- a/kernel/fs/dcache.c
+++ b/kernel/fs/dcache.c
@@ -2503,10 +2503,9 @@
 static inline unsigned start_dir_add(struct inode *dir)
 {
 
-	preempt_disable_rt();
 	for (;;) {
-		unsigned n = dir->__i_dir_seq;
-		if (!(n & 1) && cmpxchg(&dir->__i_dir_seq, n, n + 1) == n)
+		unsigned n = dir->i_dir_seq;
+		if (!(n & 1) && cmpxchg(&dir->i_dir_seq, n, n + 1) == n)
 			return n;
 		cpu_relax();
 	}
@@ -2514,30 +2513,26 @@
 
 static inline void end_dir_add(struct inode *dir, unsigned n)
 {
-	smp_store_release(&dir->__i_dir_seq, n + 2);
-	preempt_enable_rt();
+	smp_store_release(&dir->i_dir_seq, n + 2);
 }
 
 static void d_wait_lookup(struct dentry *dentry)
 {
-	struct swait_queue __wait;
-
-	if (!d_in_lookup(dentry))
-		return;
-
-	INIT_LIST_HEAD(&__wait.task_list);
-	do {
-		prepare_to_swait_exclusive(dentry->d_wait, &__wait, TASK_UNINTERRUPTIBLE);
-		spin_unlock(&dentry->d_lock);
-		schedule();
-		spin_lock(&dentry->d_lock);
-	} while (d_in_lookup(dentry));
-	finish_swait(dentry->d_wait, &__wait);
+	if (d_in_lookup(dentry)) {
+		DECLARE_WAITQUEUE(wait, current);
+		add_wait_queue(dentry->d_wait, &wait);
+		do {
+			set_current_state(TASK_UNINTERRUPTIBLE);
+			spin_unlock(&dentry->d_lock);
+			schedule();
+			spin_lock(&dentry->d_lock);
+		} while (d_in_lookup(dentry));
+	}
 }
 
 struct dentry *d_alloc_parallel(struct dentry *parent,
 				const struct qstr *name,
-				struct swait_queue_head *wq)
+				wait_queue_head_t *wq)
 {
 	unsigned int hash = name->hash;
 	struct hlist_bl_head *b = in_lookup_hash(parent, hash);
@@ -2551,7 +2546,7 @@
 
 retry:
 	rcu_read_lock();
-	seq = smp_load_acquire(&parent->d_inode->__i_dir_seq);
+	seq = smp_load_acquire(&parent->d_inode->i_dir_seq);
 	r_seq = read_seqbegin(&rename_lock);
 	dentry = __d_lookup_rcu(parent, name, &d_seq);
 	if (unlikely(dentry)) {
@@ -2579,7 +2574,7 @@
 	}
 
 	hlist_bl_lock(b);
-	if (unlikely(READ_ONCE(parent->d_inode->__i_dir_seq) != seq)) {
+	if (unlikely(READ_ONCE(parent->d_inode->i_dir_seq) != seq)) {
 		hlist_bl_unlock(b);
 		rcu_read_unlock();
 		goto retry;
@@ -2652,7 +2647,7 @@
 	hlist_bl_lock(b);
 	dentry->d_flags &= ~DCACHE_PAR_LOOKUP;
 	__hlist_bl_del(&dentry->d_u.d_in_lookup_hash);
-	swake_up_all(dentry->d_wait);
+	wake_up_all(dentry->d_wait);
 	dentry->d_wait = NULL;
 	hlist_bl_unlock(b);
 	INIT_HLIST_NODE(&dentry->d_u.d_alias);
diff --git a/kernel/fs/eventfd.c b/kernel/fs/eventfd.c
index 8f017bd..4a14295 100644
--- a/kernel/fs/eventfd.c
+++ b/kernel/fs/eventfd.c
@@ -25,6 +25,8 @@
 #include <linux/idr.h>
 #include <linux/uio.h>
 
+DEFINE_PER_CPU(int, eventfd_wake_count);
+
 static DEFINE_IDA(eventfd_ida);
 
 struct eventfd_ctx {
@@ -51,21 +53,21 @@
 	 * Deadlock or stack overflow issues can happen if we recurse here
 	 * through waitqueue wakeup handlers. If the caller users potentially
 	 * nested waitqueues with custom wakeup handlers, then it should
-	 * check eventfd_signal_allowed() before calling this function. If
-	 * it returns false, the eventfd_signal() call should be deferred to a
+	 * check eventfd_signal_count() before calling this function. If
+	 * it returns true, the eventfd_signal() call should be deferred to a
 	 * safe context.
 	 */
-	if (WARN_ON_ONCE(current->in_eventfd_signal))
+	if (WARN_ON_ONCE(this_cpu_read(eventfd_wake_count)))
 		return 0;
 
 	spin_lock_irqsave(&ctx->wqh.lock, flags);
-	current->in_eventfd_signal = 1;
+	this_cpu_inc(eventfd_wake_count);
 	if (ULLONG_MAX - ctx->count < n)
 		n = ULLONG_MAX - ctx->count;
 	ctx->count += n;
 	if (waitqueue_active(&ctx->wqh))
 		wake_up_locked_poll(&ctx->wqh, EPOLLIN | mask);
-	current->in_eventfd_signal = 0;
+	this_cpu_dec(eventfd_wake_count);
 	spin_unlock_irqrestore(&ctx->wqh.lock, flags);
 
 	return n;
diff --git a/kernel/fs/fscache/internal.h b/kernel/fs/fscache/internal.h
index 7dae569..64aa552 100644
--- a/kernel/fs/fscache/internal.h
+++ b/kernel/fs/fscache/internal.h
@@ -95,6 +95,7 @@
 extern struct kobject *fscache_root;
 extern struct workqueue_struct *fscache_object_wq;
 extern struct workqueue_struct *fscache_op_wq;
+DECLARE_PER_CPU(wait_queue_head_t, fscache_object_cong_wait);
 
 extern unsigned int fscache_hash(unsigned int salt, unsigned int *data, unsigned int n);
 
diff --git a/kernel/fs/fscache/main.c b/kernel/fs/fscache/main.c
index 85f8cf3..4207f98 100644
--- a/kernel/fs/fscache/main.c
+++ b/kernel/fs/fscache/main.c
@@ -41,6 +41,8 @@
 struct workqueue_struct *fscache_object_wq;
 struct workqueue_struct *fscache_op_wq;
 
+DEFINE_PER_CPU(wait_queue_head_t, fscache_object_cong_wait);
+
 /* these values serve as lower bounds, will be adjusted in fscache_init() */
 static unsigned fscache_object_max_active = 4;
 static unsigned fscache_op_max_active = 2;
@@ -136,6 +138,7 @@
 static int __init fscache_init(void)
 {
 	unsigned int nr_cpus = num_possible_cpus();
+	unsigned int cpu;
 	int ret;
 
 	fscache_object_max_active =
@@ -158,6 +161,9 @@
 	if (!fscache_op_wq)
 		goto error_op_wq;
 
+	for_each_possible_cpu(cpu)
+		init_waitqueue_head(&per_cpu(fscache_object_cong_wait, cpu));
+
 	ret = fscache_proc_init();
 	if (ret < 0)
 		goto error_proc;
diff --git a/kernel/fs/fscache/object.c b/kernel/fs/fscache/object.c
index fb9794d..cb2146e 100644
--- a/kernel/fs/fscache/object.c
+++ b/kernel/fs/fscache/object.c
@@ -807,8 +807,6 @@
 }
 EXPORT_SYMBOL(fscache_object_destroy);
 
-static DECLARE_WAIT_QUEUE_HEAD(fscache_object_cong_wait);
-
 /*
  * enqueue an object for metadata-type processing
  */
@@ -817,12 +815,16 @@
 	_enter("{OBJ%x}", object->debug_id);
 
 	if (fscache_get_object(object, fscache_obj_get_queue) >= 0) {
+		wait_queue_head_t *cong_wq =
+			&get_cpu_var(fscache_object_cong_wait);
 
 		if (queue_work(fscache_object_wq, &object->work)) {
 			if (fscache_object_congested())
-				wake_up(&fscache_object_cong_wait);
+				wake_up(cong_wq);
 		} else
 			fscache_put_object(object, fscache_obj_put_queue);
+
+		put_cpu_var(fscache_object_cong_wait);
 	}
 }
 
@@ -840,15 +842,16 @@
  */
 bool fscache_object_sleep_till_congested(signed long *timeoutp)
 {
+	wait_queue_head_t *cong_wq = this_cpu_ptr(&fscache_object_cong_wait);
 	DEFINE_WAIT(wait);
 
 	if (fscache_object_congested())
 		return true;
 
-	add_wait_queue_exclusive(&fscache_object_cong_wait, &wait);
+	add_wait_queue_exclusive(cong_wq, &wait);
 	if (!fscache_object_congested())
 		*timeoutp = schedule_timeout(*timeoutp);
-	finish_wait(&fscache_object_cong_wait, &wait);
+	finish_wait(cong_wq, &wait);
 
 	return fscache_object_congested();
 }
diff --git a/kernel/fs/fuse/readdir.c b/kernel/fs/fuse/readdir.c
index ee88468..d5294e6 100644
--- a/kernel/fs/fuse/readdir.c
+++ b/kernel/fs/fuse/readdir.c
@@ -160,7 +160,7 @@
 	struct inode *dir = d_inode(parent);
 	struct fuse_conn *fc;
 	struct inode *inode;
-	DECLARE_SWAIT_QUEUE_HEAD_ONSTACK(wq);
+	DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
 
 	if (!o->nodeid) {
 		/*
diff --git a/kernel/fs/inode.c b/kernel/fs/inode.c
index 996bd56..d9c748b 100644
--- a/kernel/fs/inode.c
+++ b/kernel/fs/inode.c
@@ -158,7 +158,7 @@
 	inode->i_bdev = NULL;
 	inode->i_cdev = NULL;
 	inode->i_link = NULL;
-	inode->__i_dir_seq = 0;
+	inode->i_dir_seq = 0;
 	inode->i_rdev = 0;
 	inode->dirtied_when = 0;
 
diff --git a/kernel/fs/namei.c b/kernel/fs/namei.c
index e02fcec..887bc1f 100644
--- a/kernel/fs/namei.c
+++ b/kernel/fs/namei.c
@@ -1616,7 +1616,7 @@
 {
 	struct dentry *dentry, *old;
 	struct inode *inode = dir->d_inode;
-	DECLARE_SWAIT_QUEUE_HEAD_ONSTACK(wq);
+	DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
 
 	/* Don't go there if it's already dead */
 	if (unlikely(IS_DEADDIR(inode)))
@@ -3112,7 +3112,7 @@
 	struct dentry *dentry;
 	int error, create_error = 0;
 	umode_t mode = op->mode;
-	DECLARE_SWAIT_QUEUE_HEAD_ONSTACK(wq);
+	DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
 
 	if (unlikely(IS_DEADDIR(dir_inode)))
 		return ERR_PTR(-ENOENT);
diff --git a/kernel/fs/namespace.c b/kernel/fs/namespace.c
index 6947740..6d1f11a 100644
--- a/kernel/fs/namespace.c
+++ b/kernel/fs/namespace.c
@@ -14,7 +14,6 @@
 #include <linux/mnt_namespace.h>
 #include <linux/user_namespace.h>
 #include <linux/namei.h>
-#include <linux/delay.h>
 #include <linux/security.h>
 #include <linux/cred.h>
 #include <linux/idr.h>
@@ -322,11 +321,8 @@
 	 * incremented count after it has set MNT_WRITE_HOLD.
 	 */
 	smp_mb();
-	while (READ_ONCE(mnt->mnt.mnt_flags) & MNT_WRITE_HOLD) {
-		preempt_enable();
-		cpu_chill();
-		preempt_disable();
-	}
+	while (READ_ONCE(mnt->mnt.mnt_flags) & MNT_WRITE_HOLD)
+		cpu_relax();
 	/*
 	 * After the slowpath clears MNT_WRITE_HOLD, mnt_is_readonly will
 	 * be set to match its requirements. So we must not load that until
diff --git a/kernel/fs/nfs/dir.c b/kernel/fs/nfs/dir.c
index bc8a78e..9f88ca7 100644
--- a/kernel/fs/nfs/dir.c
+++ b/kernel/fs/nfs/dir.c
@@ -484,7 +484,7 @@
 		unsigned long dir_verifier)
 {
 	struct qstr filename = QSTR_INIT(entry->name, entry->len);
-	DECLARE_SWAIT_QUEUE_HEAD_ONSTACK(wq);
+	DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
 	struct dentry *dentry;
 	struct dentry *alias;
 	struct inode *inode;
@@ -1660,7 +1660,7 @@
 		    struct file *file, unsigned open_flags,
 		    umode_t mode)
 {
-	DECLARE_SWAIT_QUEUE_HEAD_ONSTACK(wq);
+	DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
 	struct nfs_open_context *ctx;
 	struct dentry *res;
 	struct iattr attr = { .ia_valid = ATTR_OPEN };
diff --git a/kernel/fs/nfs/unlink.c b/kernel/fs/nfs/unlink.c
index f86c98a..b27ebdc 100644
--- a/kernel/fs/nfs/unlink.c
+++ b/kernel/fs/nfs/unlink.c
@@ -13,7 +13,7 @@
 #include <linux/sunrpc/clnt.h>
 #include <linux/nfs_fs.h>
 #include <linux/sched.h>
-#include <linux/swait.h>
+#include <linux/wait.h>
 #include <linux/namei.h>
 #include <linux/fsnotify.h>
 
@@ -180,7 +180,7 @@
 
 	data->cred = get_current_cred();
 	data->res.dir_attr = &data->dir_attr;
-	init_swait_queue_head(&data->wq);
+	init_waitqueue_head(&data->wq);
 
 	status = -EBUSY;
 	spin_lock(&dentry->d_lock);
diff --git a/kernel/fs/proc/array.c b/kernel/fs/proc/array.c
index decaa77..18a4588 100644
--- a/kernel/fs/proc/array.c
+++ b/kernel/fs/proc/array.c
@@ -384,9 +384,9 @@
 static void task_cpus_allowed(struct seq_file *m, struct task_struct *task)
 {
 	seq_printf(m, "Cpus_allowed:\t%*pb\n",
-		   cpumask_pr_args(&task->cpus_mask));
+		   cpumask_pr_args(task->cpus_ptr));
 	seq_printf(m, "Cpus_allowed_list:\t%*pbl\n",
-		   cpumask_pr_args(&task->cpus_mask));
+		   cpumask_pr_args(task->cpus_ptr));
 }
 
 static inline void task_core_dumping(struct seq_file *m, struct mm_struct *mm)
diff --git a/kernel/fs/proc/base.c b/kernel/fs/proc/base.c
index efab1a9..6325298 100644
--- a/kernel/fs/proc/base.c
+++ b/kernel/fs/proc/base.c
@@ -96,7 +96,6 @@
 #include <linux/posix-timers.h>
 #include <linux/time_namespace.h>
 #include <linux/resctrl.h>
-#include <linux/swait.h>
 #include <linux/cpufreq_times.h>
 #include <trace/events/oom.h>
 #include "internal.h"
@@ -2040,7 +2039,7 @@
 
 	child = d_hash_and_lookup(dir, &qname);
 	if (!child) {
-		DECLARE_SWAIT_QUEUE_HEAD_ONSTACK(wq);
+		DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
 		child = d_alloc_parallel(dir, &qname, &wq);
 		if (IS_ERR(child))
 			goto end_instantiate;
diff --git a/kernel/fs/proc/proc_sysctl.c b/kernel/fs/proc/proc_sysctl.c
index a1a964b..070d2df 100644
--- a/kernel/fs/proc/proc_sysctl.c
+++ b/kernel/fs/proc/proc_sysctl.c
@@ -683,7 +683,7 @@
 
 	child = d_lookup(dir, &qname);
 	if (!child) {
-		DECLARE_SWAIT_QUEUE_HEAD_ONSTACK(wq);
+		DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
 		child = d_alloc_parallel(dir, &qname, &wq);
 		if (IS_ERR(child))
 			return false;
diff --git a/kernel/fs/pstore/platform.c b/kernel/fs/pstore/platform.c
index 11af5fe..417582b 100644
--- a/kernel/fs/pstore/platform.c
+++ b/kernel/fs/pstore/platform.c
@@ -386,8 +386,7 @@
  * end of the buffer.
  */
 static void pstore_dump(struct kmsg_dumper *dumper,
-			enum kmsg_dump_reason reason,
-			struct kmsg_dumper_iter *iter)
+			enum kmsg_dump_reason reason)
 {
 	unsigned long	total = 0;
 	const char	*why;
@@ -439,7 +438,7 @@
 		dst_size -= header_size;
 
 		/* Write dump contents. */
-		if (!kmsg_dump_get_buffer(iter, true, dst + header_size,
+		if (!kmsg_dump_get_buffer(dumper, true, dst + header_size,
 					  dst_size, &dump_size))
 			break;
 
diff --git a/kernel/include/asm-generic/Kbuild b/kernel/include/asm-generic/Kbuild
index 267f6df..d1300c6 100644
--- a/kernel/include/asm-generic/Kbuild
+++ b/kernel/include/asm-generic/Kbuild
@@ -30,7 +30,7 @@
 mandatory-y += irq_regs.h
 mandatory-y += irq_work.h
 mandatory-y += kdebug.h
-mandatory-y += kmap_size.h
+mandatory-y += kmap_types.h
 mandatory-y += kprobes.h
 mandatory-y += linkage.h
 mandatory-y += local.h
diff --git a/kernel/include/asm-generic/bug.h b/kernel/include/asm-generic/bug.h
index 6fc3396..76a10e0 100644
--- a/kernel/include/asm-generic/bug.h
+++ b/kernel/include/asm-generic/bug.h
@@ -175,7 +175,6 @@
 })
 
 #else /* !CONFIG_BUG */
-
 #ifndef HAVE_ARCH_BUG
 #define BUG() do {} while (1)
 #endif
diff --git a/kernel/include/asm-generic/hardirq.h b/kernel/include/asm-generic/hardirq.h
index 7317e82..d14214d 100644
--- a/kernel/include/asm-generic/hardirq.h
+++ b/kernel/include/asm-generic/hardirq.h
@@ -7,13 +7,9 @@
 
 typedef struct {
 	unsigned int __softirq_pending;
-#ifdef ARCH_WANTS_NMI_IRQSTAT
-	unsigned int __nmi_count;
-#endif
 } ____cacheline_aligned irq_cpustat_t;
 
-DECLARE_PER_CPU_ALIGNED(irq_cpustat_t, irq_stat);
-
+#include <linux/irq_cpustat.h>	/* Standard mappings for irq_cpustat_t above */
 #include <linux/irq.h>
 
 #ifndef ack_bad_irq
diff --git a/kernel/include/asm-generic/kmap_size.h b/kernel/include/asm-generic/kmap_size.h
deleted file mode 100644
index 9d6c778..0000000
--- a/kernel/include/asm-generic/kmap_size.h
+++ /dev/null
@@ -1,12 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-#ifndef _ASM_GENERIC_KMAP_SIZE_H
-#define _ASM_GENERIC_KMAP_SIZE_H
-
-/* For debug this provides guard pages between the maps */
-#ifdef CONFIG_DEBUG_HIGHMEM
-# define KM_MAX_IDX	33
-#else
-# define KM_MAX_IDX	16
-#endif
-
-#endif
diff --git a/kernel/include/asm-generic/kmap_types.h b/kernel/include/asm-generic/kmap_types.h
new file mode 100644
index 0000000..9f95b7b
--- /dev/null
+++ b/kernel/include/asm-generic/kmap_types.h
@@ -0,0 +1,11 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _ASM_GENERIC_KMAP_TYPES_H
+#define _ASM_GENERIC_KMAP_TYPES_H
+
+#ifdef __WITH_KM_FENCE
+# define KM_TYPE_NR 41
+#else
+# define KM_TYPE_NR 20
+#endif
+
+#endif
diff --git a/kernel/include/asm-generic/preempt.h b/kernel/include/asm-generic/preempt.h
index ac255e8..b4d43a4 100644
--- a/kernel/include/asm-generic/preempt.h
+++ b/kernel/include/asm-generic/preempt.h
@@ -79,9 +79,6 @@
 }
 
 #ifdef CONFIG_PREEMPTION
-#ifdef CONFIG_PREEMPT_RT
-extern void preempt_schedule_lock(void);
-#endif
 extern asmlinkage void preempt_schedule(void);
 #define __preempt_schedule() preempt_schedule()
 extern asmlinkage void preempt_schedule_notrace(void);
diff --git a/kernel/include/drm/bridge/analogix_dp.h b/kernel/include/drm/bridge/analogix_dp.h
index e912503..09f0dfe 100644
--- a/kernel/include/drm/bridge/analogix_dp.h
+++ b/kernel/include/drm/bridge/analogix_dp.h
@@ -43,6 +43,11 @@
 	bool ssc;
 
 	bool split_mode;
+
+	/* split with other display interface */
+	bool dual_connector_split;
+	bool left_display;
+
 	struct analogix_dp_device *left;
 	struct analogix_dp_device *right;
 
diff --git a/kernel/include/drm/bridge/dw_hdmi.h b/kernel/include/drm/bridge/dw_hdmi.h
index 302e453..aca7ae8 100644
--- a/kernel/include/drm/bridge/dw_hdmi.h
+++ b/kernel/include/drm/bridge/dw_hdmi.h
@@ -244,6 +244,8 @@
 	int (*get_next_hdr_data)(void *data, struct edid *edid,
 				 struct drm_connector *connector);
 	struct dw_hdmi_link_config *(*get_link_cfg)(void *data);
+	void (*set_hdcp_status)(void *data, u8 status);
+	void (*set_hdcp2_enable)(void *data, bool enable);
 	void (*set_grf_cfg)(void *data);
 	u64 (*get_grf_color_fmt)(void *data);
 	void (*convert_to_split_mode)(struct drm_display_mode *mode);
@@ -256,10 +258,12 @@
 	void (*set_prev_bus_format)(void *data, unsigned long bus_format);
 	int (*get_colorimetry)(void *data, struct edid *edid);
 	void (*set_ddc_io)(void *data, bool enable);
+	void (*set_hdcp14_mem)(void *data, bool enable);
 
 	/* Vendor Property support */
 	const struct dw_hdmi_property_ops *property_ops;
 	struct drm_connector *connector;
+	struct drm_bridge *bridge;
 };
 
 struct dw_hdmi_cec_wake_ops {
diff --git a/kernel/include/drm/bridge/dw_mipi_dsi.h b/kernel/include/drm/bridge/dw_mipi_dsi.h
index f89b047..3b86b7d 100644
--- a/kernel/include/drm/bridge/dw_mipi_dsi.h
+++ b/kernel/include/drm/bridge/dw_mipi_dsi.h
@@ -55,6 +55,7 @@
 
 	const struct dw_mipi_dsi_phy_ops *phy_ops;
 	const struct dw_mipi_dsi_host_ops *host_ops;
+	void (*stream_standby)(void *priv_data, bool standby);
 
 	void *priv_data;
 };
diff --git a/kernel/include/drm/drm_crtc.h b/kernel/include/drm/drm_crtc.h
index 3a3d9d8..59b51a0 100644
--- a/kernel/include/drm/drm_crtc.h
+++ b/kernel/include/drm/drm_crtc.h
@@ -287,16 +287,7 @@
 	 * NULL) is an array of &struct drm_color_lut.
 	 */
 	struct drm_property_blob *gamma_lut;
-#if defined(CONFIG_ROCKCHIP_DRM_CUBIC_LUT)
-	/**
-	 * @cubic_lut:
-	 *
-	 * Cubic Lookup table for converting pixel data. See
-	 * drm_crtc_enable_color_mgmt(). The blob (if not NULL) is a 3D array
-	 * of &struct drm_color_lut.
-	 */
-	struct drm_property_blob *cubic_lut;
-#endif
+
 	/**
 	 * @target_vblank:
 	 *
diff --git a/kernel/include/drm/drm_mode_config.h b/kernel/include/drm/drm_mode_config.h
index 76d1145..a18f73e 100644
--- a/kernel/include/drm/drm_mode_config.h
+++ b/kernel/include/drm/drm_mode_config.h
@@ -794,19 +794,6 @@
 	 */
 	struct drm_property *gamma_lut_size_property;
 
-#if defined(CONFIG_ROCKCHIP_DRM_CUBIC_LUT)
-	/**
-	 * @cubic_lut_property: Optional CRTC property to set the 3D LUT used to
-	 * convert color spaces.
-	 */
-	struct drm_property *cubic_lut_property;
-	/**
-	 * @cubic_lut_size_property: Optional CRTC property for the size of the
-	 * 3D LUT as supported by the driver (read-only).
-	 */
-	struct drm_property *cubic_lut_size_property;
-#endif
-
 	/**
 	 * @suggested_x_property: Optional connector property with a hint for
 	 * the position of the output on the host's screen.
diff --git a/kernel/include/dt-bindings/mfd/rockchip-serdes.h b/kernel/include/dt-bindings/mfd/rockchip-serdes.h
new file mode 100644
index 0000000..610a1e3
--- /dev/null
+++ b/kernel/include/dt-bindings/mfd/rockchip-serdes.h
@@ -0,0 +1,151 @@
+/* SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause */
+/*
+ * This header provides macros for Rockchip SerDes device bindings.
+ *
+ * Copyright (c) 2022 Rockchip Electronics Co., Ltd.
+ */
+
+#ifndef __DT_BINDINGS_MFD_ROCKCHIP_SERDES_H
+#define __DT_BINDINGS_MFD_ROCKCHIP_SERDES_H
+
+#define RK_SERDES_RGB_RX	(1 << 0)
+#define RK_SERDES_LVDS_RX0	(1 << 1)
+#define RK_SERDES_LVDS_RX1	(1 << 2)
+#define RK_SERDES_DSI_RX0	(1 << 3)
+#define RK_SERDES_DSI_RX1	(1 << 4)
+#define RK_SERDES_DUAL_LVDS_RX	(1 << 5)
+
+#define RK_SERDES_RGB_TX	(1 << 0)
+#define RK_SERDES_LVDS_TX0	(1 << 1)
+#define RK_SERDES_LVDS_TX1	(1 << 2)
+#define RK_SERDES_DSI_TX0	(1 << 3)
+#define RK_SERDES_DSI_TX1	(1 << 4)
+#define RK_SERDES_DUAL_LVDS_TX	(1 << 5)
+
+/* Serdes camera stream port */
+#define RK_SERDES_CSI_RX0	(1 << 16)
+#define RK_SERDES_DVP_RX	(1 << 18)
+
+#define RK_SERDES_CSI_TX0	(1 << 16)
+#define RK_SERDES_CSI_TX1	(1 << 17)
+#define RK_SERDES_DVP_TX	(1 << 18)
+
+/* Serdes chip type */
+#define RK_SERDES_CHIP_LOCAL	0
+#define RK_SERDES_CHIP_REMOTE0	1
+#define RK_SERDES_CHIP_REMOTE1	2
+
+/* Seredes pin control */
+#define RK_SERDES_SER_GPIO_BANK0	(0)
+#define RK_SERDES_SER_GPIO_BANK1	(1)
+#define RK_SERDES_DES_GPIO_BANK0	(5)
+#define RK_SERDES_DES_GPIO_BANK1	(6)
+
+/*
+ * Elements values convention: ffff f000 0000 00ss dddd dddd eepp xxxx
+ * - ffff f : Flag MUX/PUL/PEN/DRV/SMT
+ * - ss : Schmitt value
+ * - dddd dddd : Drive Level value
+ * - ee : Pull Enable value
+ * - pp : Pull Mode value
+ * - xxxx : IOMUX Function value
+ */
+#define RK_SERDES_FLAG_MUX	(1 << 31)
+#define RK_SERDES_FLAG_PUL	(1 << 30)
+#define RK_SERDES_FLAG_PEN	(1 << 29)
+#define RK_SERDES_FLAG_DRV	(1 << 28)
+#define RK_SERDES_FLAG_SMT	(1 << 27)
+#define RK_SERDES_SHIFT_MUX	(0)
+#define RK_SERDES_SHIFT_PUL	(4)
+#define RK_SERDES_SHIFT_PEN	(6)
+#define RK_SERDES_SHIFT_DRV	(8)
+#define RK_SERDES_SHIFT_SMT	(16)
+#define RK_SERDES_MASK_MUX	(0xFU << RK_SERDES_SHIFT_MUX)
+#define RK_SERDES_MASK_PUL	(0x3U << RK_SERDES_SHIFT_PUL)
+#define RK_SERDES_MASK_PEN	(0x3U << RK_SERDES_SHIFT_PEN)
+#define RK_SERDES_MASK_DRV	(0xFFU << RK_SERDES_SHIFT_DRV)
+#define RK_SERDES_MASK_SMT	(0x3U << RK_SERDES_SHIFT_SMT)
+
+#define RK_SERDES_PIN_CONFIG_MUX_FUNC0		(0x0 << RK_SERDES_SHIFT_MUX | RK_SERDES_FLAG_MUX)
+#define RK_SERDES_PIN_CONFIG_MUX_FUNC1		(0x1 << RK_SERDES_SHIFT_MUX | RK_SERDES_FLAG_MUX)
+#define RK_SERDES_PIN_CONFIG_MUX_FUNC2		(0x2 << RK_SERDES_SHIFT_MUX | RK_SERDES_FLAG_MUX)
+#define RK_SERDES_PIN_CONFIG_MUX_FUNC3		(0x3 << RK_SERDES_SHIFT_MUX | RK_SERDES_FLAG_MUX)
+#define RK_SERDES_PIN_CONFIG_MUX_FUNC4		(0x4 << RK_SERDES_SHIFT_MUX | RK_SERDES_FLAG_MUX)
+#define RK_SERDES_PIN_CONFIG_MUX_FUNC5		(0x5 << RK_SERDES_SHIFT_MUX | RK_SERDES_FLAG_MUX)
+#define RK_SERDES_PIN_CONFIG_MUX_FUNC6		(0x6 << RK_SERDES_SHIFT_MUX | RK_SERDES_FLAG_MUX)
+#define RK_SERDES_PIN_CONFIG_MUX_FUNC7		(0x7 << RK_SERDES_SHIFT_MUX | RK_SERDES_FLAG_MUX)
+#define RK_SERDES_PIN_CONFIG_MUX_DEFAULT	RK_SERDES_PIN_CONFIG_MUX_FUNC0
+
+#define RK_SERDES_PIN_CONFIG_PUL_NORMAL		(0x0 << RK_SERDES_SHIFT_PUL | RK_SERDES_FLAG_PUL)
+#define RK_SERDES_PIN_CONFIG_PUL_UP		(0x1 << RK_SERDES_SHIFT_PUL | RK_SERDES_FLAG_PUL)
+#define RK_SERDES_PIN_CONFIG_PUL_DOWN		(0x2 << RK_SERDES_SHIFT_PUL | RK_SERDES_FLAG_PUL)
+#define RK_SERDES_PIN_CONFIG_PUL_KEEP		(0x3 << RK_SERDES_SHIFT_PUL | RK_SERDES_FLAG_PUL)
+#define RK_SERDES_PIN_CONFIG_PUL_DEFAULT	RK_SERDES_PIN_CONFIG_PUL_NORMAL
+
+#define RK_SERDES_PIN_CONFIG_PEN_DISABLE	(0x0 << RK_SERDES_SHIFT_PEN | RK_SERDES_FLAG_PEN)
+#define RK_SERDES_PIN_CONFIG_PEN_ENABLE		(0x1 << RK_SERDES_SHIFT_PEN | RK_SERDES_FLAG_PEN)
+#define RK_SERDES_PIN_CONFIG_PEN_DEFAULT	RK_SERDES_PIN_CONFIG_PEN_DISABLE
+
+#define RK_SERDES_PIN_CONFIG_DRV_LEVEL0		(0x0 << RK_SERDES_SHIFT_DRV | RK_SERDES_FLAG_DRV)
+#define RK_SERDES_PIN_CONFIG_DRV_LEVEL1		(0x1 << RK_SERDES_SHIFT_DRV | RK_SERDES_FLAG_DRV)
+#define RK_SERDES_PIN_CONFIG_DRV_LEVEL2		(0x2 << RK_SERDES_SHIFT_DRV | RK_SERDES_FLAG_DRV)
+#define RK_SERDES_PIN_CONFIG_DRV_LEVEL3		(0x3 << RK_SERDES_SHIFT_DRV | RK_SERDES_FLAG_DRV)
+#define RK_SERDES_PIN_CONFIG_DRV_LEVEL4		(0x4 << RK_SERDES_SHIFT_DRV | RK_SERDES_FLAG_DRV)
+#define RK_SERDES_PIN_CONFIG_DRV_LEVEL5		(0x5 << RK_SERDES_SHIFT_DRV | RK_SERDES_FLAG_DRV)
+#define RK_SERDES_PIN_CONFIG_DRV_LEVEL6		(0x6 << RK_SERDES_SHIFT_DRV | RK_SERDES_FLAG_DRV)
+#define RK_SERDES_PIN_CONFIG_DRV_LEVEL7		(0x7 << RK_SERDES_SHIFT_DRV | RK_SERDES_FLAG_DRV)
+#define RK_SERDES_PIN_CONFIG_DRV_LEVEL_DEFAULT	RK_SERDES_PIN_CONFIG_DRV_LEVEL2
+
+#define RK_SERDES_PIN_CONFIG_SMT_DISABLE	(0x0 << RK_SERDES_SHIFT_SMT | RK_SERDES_FLAG_SMT)
+#define RK_SERDES_PIN_CONFIG_SMT_ENABLE		(0x1 << RK_SERDES_SHIFT_SMT | RK_SERDES_FLAG_SMT)
+#define RK_SERDES_PIN_CONFIG_SMT_DEFAULT	RK_SERDES_PIN_CONFIG_SMT_DISABLE
+
+#define RK_SERDES_PIN_CONFIG_MAX		0xFFFFFFFFU
+
+#define RK_SERDES_GPIO_PIN_A0			0x00000001U  /*!< Pin 0 selected    */
+#define RK_SERDES_GPIO_PIN_A1			0x00000002U  /*!< Pin 1 selected    */
+#define RK_SERDES_GPIO_PIN_A2			0x00000004U  /*!< Pin 2 selected    */
+#define RK_SERDES_GPIO_PIN_A3			0x00000008U  /*!< Pin 3 selected    */
+#define RK_SERDES_GPIO_PIN_A4			0x00000010U  /*!< Pin 4 selected    */
+#define RK_SERDES_GPIO_PIN_A5			0x00000020U  /*!< Pin 5 selected    */
+#define RK_SERDES_GPIO_PIN_A6			0x00000040U  /*!< Pin 6 selected    */
+#define RK_SERDES_GPIO_PIN_A7			0x00000080U  /*!< Pin 7 selected    */
+#define RK_SERDES_GPIO_PIN_B0			0x00000100U  /*!< Pin 8 selected    */
+#define RK_SERDES_GPIO_PIN_B1			0x00000200U  /*!< Pin 9 selected    */
+#define RK_SERDES_GPIO_PIN_B2			0x00000400U  /*!< Pin 10 selected   */
+#define RK_SERDES_GPIO_PIN_B3			0x00000800U  /*!< Pin 11 selected   */
+#define RK_SERDES_GPIO_PIN_B4			0x00001000U  /*!< Pin 12 selected   */
+#define RK_SERDES_GPIO_PIN_B5			0x00002000U  /*!< Pin 13 selected   */
+#define RK_SERDES_GPIO_PIN_B6			0x00004000U  /*!< Pin 14 selected   */
+#define RK_SERDES_GPIO_PIN_B7			0x00008000U  /*!< Pin 15 selected   */
+#define RK_SERDES_GPIO_PIN_C0			0x00010000U  /*!< Pin 16 selected   */
+#define RK_SERDES_GPIO_PIN_C1			0x00020000U  /*!< Pin 17 selected   */
+#define RK_SERDES_GPIO_PIN_C2			0x00040000U  /*!< Pin 18 selected   */
+#define RK_SERDES_GPIO_PIN_C3			0x00080000U  /*!< Pin 19 selected   */
+#define RK_SERDES_GPIO_PIN_C4			0x00100000U  /*!< Pin 20 selected   */
+#define RK_SERDES_GPIO_PIN_C5			0x00200000U  /*!< Pin 21 selected   */
+#define RK_SERDES_GPIO_PIN_C6			0x00400000U  /*!< Pin 22 selected   */
+#define RK_SERDES_GPIO_PIN_C7			0x00800000U  /*!< Pin 23 selected   */
+#define RK_SERDES_GPIO_PIN_D0			0x01000000U  /*!< Pin 24 selected   */
+#define RK_SERDES_GPIO_PIN_D1			0x02000000U  /*!< Pin 25 selected   */
+#define RK_SERDES_GPIO_PIN_D2			0x04000000U  /*!< Pin 26 selected   */
+#define RK_SERDES_GPIO_PIN_D3			0x08000000U  /*!< Pin 27 selected   */
+#define RK_SERDES_GPIO_PIN_D4			0x10000000U  /*!< Pin 28 selected   */
+#define RK_SERDES_GPIO_PIN_D5			0x20000000U  /*!< Pin 29 selected   */
+#define RK_SERDES_GPIO_PIN_D6			0x40000000U  /*!< Pin 30 selected   */
+#define RK_SERDES_GPIO_PIN_D7			0x80000000U  /*!< Pin 31 selected   */
+#define RK_SERDES_GPIO_PIN_ALL			(0xFFFFFFFFU)  /*!< All pins selected */
+
+/* passthrough function */
+#define RK_SERDES_PASSTHROUGH_GPI_GPO_0		(0)
+#define RK_SERDES_PASSTHROUGH_GPI_GPO_1		(1)
+#define RK_SERDES_PASSTHROUGH_GPI_GPO_2		(2)
+#define RK_SERDES_PASSTHROUGH_GPI_GPO_3		(3)
+#define RK_SERDES_PASSTHROUGH_GPI_GPO_4		(4)
+#define RK_SERDES_PASSTHROUGH_GPI_GPO_5		(5)
+#define RK_SERDES_PASSTHROUGH_GPI_GPO_6		(6)
+#define RK_SERDES_PASSTHROUGH_IRQ		(7)
+#define RK_SERDES_PASSTHROUGH_UART_0		(8)
+#define RK_SERDES_PASSTHROUGH_UART_1		(9)
+#define RK_SERDES_PASSTHROUGH_SPI		(10)
+#endif /* __DT_BINDINGS_MFD_ROCKCHIP_SERDES_H */
diff --git a/kernel/include/dt-bindings/soc/rockchip-amp.h b/kernel/include/dt-bindings/soc/rockchip-amp.h
new file mode 100644
index 0000000..0681e92
--- /dev/null
+++ b/kernel/include/dt-bindings/soc/rockchip-amp.h
@@ -0,0 +1,7 @@
+/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */
+#ifndef _DT_BINDINGS_SOC_ROCKCHIP_AMP_H
+#define _DT_BINDINGS_SOC_ROCKCHIP_AMP_H
+
+#define CPU_GET_AFFINITY(cpu, cluster) ((cpu) << 0 | ((cluster) << 8))
+#define GIC_AMP_IRQ_CFG_ROUTE(_irq, _prio, _aff) (_irq) (_prio) (_aff)
+#endif
diff --git a/kernel/include/linux/blkdev.h b/kernel/include/linux/blkdev.h
index 87e9394..0b16df9 100644
--- a/kernel/include/linux/blkdev.h
+++ b/kernel/include/linux/blkdev.h
@@ -163,7 +163,7 @@
 	 */
 	union {
 		struct hlist_node hash;	/* merge hash */
-		struct llist_node ipi_list;
+		struct list_head ipi_list;
 	};
 
 	/*
diff --git a/kernel/include/linux/bottom_half.h b/kernel/include/linux/bottom_half.h
index eed86eb..a19519f 100644
--- a/kernel/include/linux/bottom_half.h
+++ b/kernel/include/linux/bottom_half.h
@@ -4,7 +4,7 @@
 
 #include <linux/preempt.h>
 
-#if defined(CONFIG_PREEMPT_RT) || defined(CONFIG_TRACE_IRQFLAGS)
+#ifdef CONFIG_TRACE_IRQFLAGS
 extern void __local_bh_disable_ip(unsigned long ip, unsigned int cnt);
 #else
 static __always_inline void __local_bh_disable_ip(unsigned long ip, unsigned int cnt)
@@ -31,11 +31,5 @@
 {
 	__local_bh_enable_ip(_THIS_IP_, SOFTIRQ_DISABLE_OFFSET);
 }
-
-#ifdef CONFIG_PREEMPT_RT
-extern bool local_bh_blocked(void);
-#else
-static inline bool local_bh_blocked(void) { return false; }
-#endif
 
 #endif /* _LINUX_BH_H */
diff --git a/kernel/include/linux/console.h b/kernel/include/linux/console.h
index e6ff518..4b1e26c 100644
--- a/kernel/include/linux/console.h
+++ b/kernel/include/linux/console.h
@@ -16,7 +16,6 @@
 
 #include <linux/atomic.h>
 #include <linux/types.h>
-#include <linux/printk.h>
 
 struct vc_data;
 struct console_font_op;
@@ -138,12 +137,10 @@
 #define CON_ANYTIME	(16) /* Safe to call when cpu is offline */
 #define CON_BRL		(32) /* Used for a braille device */
 #define CON_EXTENDED	(64) /* Use the extended output format a la /dev/kmsg */
-#define CON_HANDOVER	(128) /* Device was previously a boot console. */
 
 struct console {
 	char	name[16];
 	void	(*write)(struct console *, const char *, unsigned);
-	void	(*write_atomic)(struct console *co, const char *s, unsigned int count);
 	int	(*read)(struct console *, char *, unsigned);
 	struct tty_driver *(*device)(struct console *, int *);
 	void	(*unblank)(void);
@@ -153,11 +150,6 @@
 	short	flags;
 	short	index;
 	int	cflag;
-#ifdef CONFIG_PRINTK
-	char	sync_buf[CONSOLE_LOG_MAX];
-#endif
-	atomic64_t printk_seq;
-	struct task_struct *thread;
 	void	*data;
 	struct	 console *next;
 };
@@ -237,8 +229,5 @@
 /* For deferred console takeover */
 void dummycon_register_output_notifier(struct notifier_block *nb);
 void dummycon_unregister_output_notifier(struct notifier_block *nb);
-
-extern void console_atomic_lock(unsigned int *flags);
-extern void console_atomic_unlock(unsigned int flags);
 
 #endif /* _LINUX_CONSOLE_H */
diff --git a/kernel/include/linux/cpuhotplug.h b/kernel/include/linux/cpuhotplug.h
index b78092e..843bb05 100644
--- a/kernel/include/linux/cpuhotplug.h
+++ b/kernel/include/linux/cpuhotplug.h
@@ -153,7 +153,6 @@
 	CPUHP_AP_ONLINE,
 	CPUHP_TEARDOWN_CPU,
 	CPUHP_AP_ONLINE_IDLE,
-	CPUHP_AP_SCHED_WAIT_EMPTY,
 	CPUHP_AP_SMPBOOT_THREADS,
 	CPUHP_AP_X86_VDSO_VMA_ONLINE,
 	CPUHP_AP_IRQ_AFFINITY_ONLINE,
diff --git a/kernel/include/linux/cpumask.h b/kernel/include/linux/cpumask.h
index 383684e..f0d895d 100644
--- a/kernel/include/linux/cpumask.h
+++ b/kernel/include/linux/cpumask.h
@@ -199,11 +199,6 @@
 	return cpumask_next_and(-1, src1p, src2p);
 }
 
-static inline int cpumask_any_distribute(const struct cpumask *srcp)
-{
-	return cpumask_first(srcp);
-}
-
 #define for_each_cpu(cpu, mask)			\
 	for ((cpu) = 0; (cpu) < 1; (cpu)++, (void)mask)
 #define for_each_cpu_not(cpu, mask)		\
@@ -257,7 +252,6 @@
 unsigned int cpumask_local_spread(unsigned int i, int node);
 int cpumask_any_and_distribute(const struct cpumask *src1p,
 			       const struct cpumask *src2p);
-int cpumask_any_distribute(const struct cpumask *srcp);
 
 /**
  * for_each_cpu - iterate over every cpu in a mask
diff --git a/kernel/include/linux/dcache.h b/kernel/include/linux/dcache.h
index 7676d3d..8836cf7 100644
--- a/kernel/include/linux/dcache.h
+++ b/kernel/include/linux/dcache.h
@@ -107,7 +107,7 @@
 
 	union {
 		struct list_head d_lru;		/* LRU list */
-		struct swait_queue_head *d_wait;	/* in-lookup ones only */
+		wait_queue_head_t *d_wait;	/* in-lookup ones only */
 	};
 	struct list_head d_child;	/* child of parent list */
 	struct list_head d_subdirs;	/* our children */
@@ -247,7 +247,7 @@
 extern struct dentry * d_alloc(struct dentry *, const struct qstr *);
 extern struct dentry * d_alloc_anon(struct super_block *);
 extern struct dentry * d_alloc_parallel(struct dentry *, const struct qstr *,
-					struct swait_queue_head *);
+					wait_queue_head_t *);
 extern struct dentry * d_splice_alias(struct inode *, struct dentry *);
 extern struct dentry * d_add_ci(struct dentry *, struct inode *, struct qstr *);
 extern struct dentry * d_exact_alias(struct dentry *, struct inode *);
diff --git a/kernel/include/linux/delay.h b/kernel/include/linux/delay.h
index 0f636a4..abecbcc 100644
--- a/kernel/include/linux/delay.h
+++ b/kernel/include/linux/delay.h
@@ -84,10 +84,4 @@
 		msleep(DIV_ROUND_UP(usecs, 1000));
 }
 
-#ifdef CONFIG_PREEMPT_RT
-extern void cpu_chill(void);
-#else
-# define cpu_chill()	cpu_relax()
-#endif
-
 #endif /* defined(_LINUX_DELAY_H) */
diff --git a/kernel/include/linux/entry-common.h b/kernel/include/linux/entry-common.h
index b902af1..46c4247 100644
--- a/kernel/include/linux/entry-common.h
+++ b/kernel/include/linux/entry-common.h
@@ -67,9 +67,9 @@
 # define ARCH_EXIT_TO_USER_MODE_WORK		(0)
 #endif
 
-#define EXIT_TO_USER_MODE_WORK						    \
-	(_TIF_SIGPENDING | _TIF_NOTIFY_RESUME | _TIF_UPROBE |		    \
-	 _TIF_NEED_RESCHED_MASK | _TIF_PATCH_PENDING | _TIF_NOTIFY_SIGNAL | \
+#define EXIT_TO_USER_MODE_WORK						\
+	(_TIF_SIGPENDING | _TIF_NOTIFY_RESUME | _TIF_UPROBE |		\
+	 _TIF_NEED_RESCHED | _TIF_PATCH_PENDING | _TIF_NOTIFY_SIGNAL |	\
 	 ARCH_EXIT_TO_USER_MODE_WORK)
 
 /**
diff --git a/kernel/include/linux/eventfd.h b/kernel/include/linux/eventfd.h
index 3f24ec0..ce1cf42 100644
--- a/kernel/include/linux/eventfd.h
+++ b/kernel/include/linux/eventfd.h
@@ -14,7 +14,6 @@
 #include <linux/err.h>
 #include <linux/percpu-defs.h>
 #include <linux/percpu.h>
-#include <linux/sched.h>
 
 /*
  * CAREFUL: Check include/uapi/asm-generic/fcntl.h when defining
@@ -44,9 +43,11 @@
 int eventfd_ctx_remove_wait_queue(struct eventfd_ctx *ctx, wait_queue_entry_t *wait,
 				  __u64 *cnt);
 
-static inline bool eventfd_signal_allowed(void)
+DECLARE_PER_CPU(int, eventfd_wake_count);
+
+static inline bool eventfd_signal_count(void)
 {
-	return !current->in_eventfd_signal;
+	return this_cpu_read(eventfd_wake_count);
 }
 
 #else /* CONFIG_EVENTFD */
@@ -83,9 +84,9 @@
 	return -ENOSYS;
 }
 
-static inline bool eventfd_signal_allowed(void)
+static inline bool eventfd_signal_count(void)
 {
-	return true;
+	return false;
 }
 
 #endif
diff --git a/kernel/include/linux/fs.h b/kernel/include/linux/fs.h
index 40d75b1..7297765 100644
--- a/kernel/include/linux/fs.h
+++ b/kernel/include/linux/fs.h
@@ -716,7 +716,7 @@
 		struct block_device	*i_bdev;
 		struct cdev		*i_cdev;
 		char			*i_link;
-		unsigned		__i_dir_seq;
+		unsigned		i_dir_seq;
 	};
 
 	__u32			i_generation;
diff --git a/kernel/include/linux/hardirq.h b/kernel/include/linux/hardirq.h
index 76878b3..754f67a 100644
--- a/kernel/include/linux/hardirq.h
+++ b/kernel/include/linux/hardirq.h
@@ -6,7 +6,6 @@
 #include <linux/preempt.h>
 #include <linux/lockdep.h>
 #include <linux/ftrace_irq.h>
-#include <linux/sched.h>
 #include <linux/vtime.h>
 #include <asm/hardirq.h>
 
@@ -33,9 +32,9 @@
  */
 #define __irq_enter()					\
 	do {						\
+		account_irq_enter_time(current);	\
 		preempt_count_add(HARDIRQ_OFFSET);	\
 		lockdep_hardirq_enter();		\
-		account_hardirq_enter(current);		\
 	} while (0)
 
 /*
@@ -63,8 +62,8 @@
  */
 #define __irq_exit()					\
 	do {						\
-		account_hardirq_exit(current);		\
 		lockdep_hardirq_exit();			\
+		account_irq_exit_time(current);		\
 		preempt_count_sub(HARDIRQ_OFFSET);	\
 	} while (0)
 
@@ -116,6 +115,7 @@
 	do {							\
 		lockdep_off();					\
 		arch_nmi_enter();				\
+		printk_nmi_enter();				\
 		BUG_ON(in_nmi() == NMI_MASK);			\
 		__preempt_count_add(NMI_OFFSET + HARDIRQ_OFFSET);	\
 	} while (0)
@@ -134,6 +134,7 @@
 	do {							\
 		BUG_ON(!in_nmi());				\
 		__preempt_count_sub(NMI_OFFSET + HARDIRQ_OFFSET);	\
+		printk_nmi_exit();				\
 		arch_nmi_exit();				\
 		lockdep_on();					\
 	} while (0)
diff --git a/kernel/include/linux/highmem-internal.h b/kernel/include/linux/highmem-internal.h
deleted file mode 100644
index f9bc6ac..0000000
--- a/kernel/include/linux/highmem-internal.h
+++ /dev/null
@@ -1,222 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-#ifndef _LINUX_HIGHMEM_INTERNAL_H
-#define _LINUX_HIGHMEM_INTERNAL_H
-
-/*
- * Outside of CONFIG_HIGHMEM to support X86 32bit iomap_atomic() cruft.
- */
-#ifdef CONFIG_KMAP_LOCAL
-void *__kmap_local_pfn_prot(unsigned long pfn, pgprot_t prot);
-void *__kmap_local_page_prot(struct page *page, pgprot_t prot);
-void kunmap_local_indexed(void *vaddr);
-void kmap_local_fork(struct task_struct *tsk);
-void __kmap_local_sched_out(void);
-void __kmap_local_sched_in(void);
-static inline void kmap_assert_nomap(void)
-{
-	DEBUG_LOCKS_WARN_ON(current->kmap_ctrl.idx);
-}
-#else
-static inline void kmap_local_fork(struct task_struct *tsk) { }
-static inline void kmap_assert_nomap(void) { }
-#endif
-
-#ifdef CONFIG_HIGHMEM
-#include <asm/highmem.h>
-
-#ifndef ARCH_HAS_KMAP_FLUSH_TLB
-static inline void kmap_flush_tlb(unsigned long addr) { }
-#endif
-
-#ifndef kmap_prot
-#define kmap_prot PAGE_KERNEL
-#endif
-
-void *kmap_high(struct page *page);
-void kunmap_high(struct page *page);
-void __kmap_flush_unused(void);
-struct page *__kmap_to_page(void *addr);
-
-static inline void *kmap(struct page *page)
-{
-	void *addr;
-
-	might_sleep();
-	if (!PageHighMem(page))
-		addr = page_address(page);
-	else
-		addr = kmap_high(page);
-	kmap_flush_tlb((unsigned long)addr);
-	return addr;
-}
-
-static inline void kunmap(struct page *page)
-{
-	might_sleep();
-	if (!PageHighMem(page))
-		return;
-	kunmap_high(page);
-}
-
-static inline struct page *kmap_to_page(void *addr)
-{
-	return __kmap_to_page(addr);
-}
-
-static inline void kmap_flush_unused(void)
-{
-	__kmap_flush_unused();
-}
-
-static inline void *kmap_local_page(struct page *page)
-{
-	return __kmap_local_page_prot(page, kmap_prot);
-}
-
-static inline void *kmap_local_page_prot(struct page *page, pgprot_t prot)
-{
-	return __kmap_local_page_prot(page, prot);
-}
-
-static inline void *kmap_local_pfn(unsigned long pfn)
-{
-	return __kmap_local_pfn_prot(pfn, kmap_prot);
-}
-
-static inline void __kunmap_local(void *vaddr)
-{
-	kunmap_local_indexed(vaddr);
-}
-
-static inline void *kmap_atomic(struct page *page)
-{
-	if (IS_ENABLED(CONFIG_PREEMPT_RT))
-		migrate_disable();
-	else
-		preempt_disable();
-	pagefault_disable();
-	return __kmap_local_page_prot(page, kmap_prot);
-}
-
-static inline void __kunmap_atomic(void *addr)
-{
-	kunmap_local_indexed(addr);
-	pagefault_enable();
-	if (IS_ENABLED(CONFIG_PREEMPT_RT))
-		migrate_enable();
-	else
-		preempt_enable();
-}
-
-unsigned int __nr_free_highpages(void);
-extern atomic_long_t _totalhigh_pages;
-
-static inline unsigned int nr_free_highpages(void)
-{
-	return __nr_free_highpages();
-}
-
-static inline unsigned long totalhigh_pages(void)
-{
-	return (unsigned long)atomic_long_read(&_totalhigh_pages);
-}
-
-static inline void totalhigh_pages_inc(void)
-{
-	atomic_long_inc(&_totalhigh_pages);
-}
-
-static inline void totalhigh_pages_add(long count)
-{
-	atomic_long_add(count, &_totalhigh_pages);
-}
-
-#else /* CONFIG_HIGHMEM */
-
-static inline struct page *kmap_to_page(void *addr)
-{
-	return virt_to_page(addr);
-}
-
-static inline void *kmap(struct page *page)
-{
-	might_sleep();
-	return page_address(page);
-}
-
-static inline void kunmap_high(struct page *page) { }
-static inline void kmap_flush_unused(void) { }
-
-static inline void kunmap(struct page *page)
-{
-#ifdef ARCH_HAS_FLUSH_ON_KUNMAP
-	kunmap_flush_on_unmap(page_address(page));
-#endif
-}
-
-static inline void *kmap_local_page(struct page *page)
-{
-	return page_address(page);
-}
-
-static inline void *kmap_local_page_prot(struct page *page, pgprot_t prot)
-{
-	return kmap_local_page(page);
-}
-
-static inline void *kmap_local_pfn(unsigned long pfn)
-{
-	return kmap_local_page(pfn_to_page(pfn));
-}
-
-static inline void __kunmap_local(void *addr)
-{
-#ifdef ARCH_HAS_FLUSH_ON_KUNMAP
-	kunmap_flush_on_unmap(addr);
-#endif
-}
-
-static inline void *kmap_atomic(struct page *page)
-{
-	if (IS_ENABLED(CONFIG_PREEMPT_RT))
-		migrate_disable();
-	else
-		preempt_disable();
-	pagefault_disable();
-	return page_address(page);
-}
-
-static inline void __kunmap_atomic(void *addr)
-{
-#ifdef ARCH_HAS_FLUSH_ON_KUNMAP
-	kunmap_flush_on_unmap(addr);
-#endif
-	pagefault_enable();
-	if (IS_ENABLED(CONFIG_PREEMPT_RT))
-		migrate_enable();
-	else
-		preempt_enable();
-}
-
-static inline unsigned int nr_free_highpages(void) { return 0; }
-static inline unsigned long totalhigh_pages(void) { return 0UL; }
-
-#endif /* CONFIG_HIGHMEM */
-
-/*
- * Prevent people trying to call kunmap_atomic() as if it were kunmap()
- * kunmap_atomic() should get the return value of kmap_atomic, not the page.
- */
-#define kunmap_atomic(__addr)					\
-do {								\
-	BUILD_BUG_ON(__same_type((__addr), struct page *));	\
-	__kunmap_atomic(__addr);				\
-} while (0)
-
-#define kunmap_local(__addr)					\
-do {								\
-	BUILD_BUG_ON(__same_type((__addr), struct page *));	\
-	__kunmap_local(__addr);					\
-} while (0)
-
-#endif
diff --git a/kernel/include/linux/highmem.h b/kernel/include/linux/highmem.h
index be24378..220e92c 100644
--- a/kernel/include/linux/highmem.h
+++ b/kernel/include/linux/highmem.h
@@ -11,119 +11,6 @@
 
 #include <asm/cacheflush.h>
 
-#include "highmem-internal.h"
-
-/**
- * kmap - Map a page for long term usage
- * @page:	Pointer to the page to be mapped
- *
- * Returns: The virtual address of the mapping
- *
- * Can only be invoked from preemptible task context because on 32bit
- * systems with CONFIG_HIGHMEM enabled this function might sleep.
- *
- * For systems with CONFIG_HIGHMEM=n and for pages in the low memory area
- * this returns the virtual address of the direct kernel mapping.
- *
- * The returned virtual address is globally visible and valid up to the
- * point where it is unmapped via kunmap(). The pointer can be handed to
- * other contexts.
- *
- * For highmem pages on 32bit systems this can be slow as the mapping space
- * is limited and protected by a global lock. In case that there is no
- * mapping slot available the function blocks until a slot is released via
- * kunmap().
- */
-static inline void *kmap(struct page *page);
-
-/**
- * kunmap - Unmap the virtual address mapped by kmap()
- * @addr:	Virtual address to be unmapped
- *
- * Counterpart to kmap(). A NOOP for CONFIG_HIGHMEM=n and for mappings of
- * pages in the low memory area.
- */
-static inline void kunmap(struct page *page);
-
-/**
- * kmap_to_page - Get the page for a kmap'ed address
- * @addr:	The address to look up
- *
- * Returns: The page which is mapped to @addr.
- */
-static inline struct page *kmap_to_page(void *addr);
-
-/**
- * kmap_flush_unused - Flush all unused kmap mappings in order to
- *		       remove stray mappings
- */
-static inline void kmap_flush_unused(void);
-
-/**
- * kmap_local_page - Map a page for temporary usage
- * @page:	Pointer to the page to be mapped
- *
- * Returns: The virtual address of the mapping
- *
- * Can be invoked from any context.
- *
- * Requires careful handling when nesting multiple mappings because the map
- * management is stack based. The unmap has to be in the reverse order of
- * the map operation:
- *
- * addr1 = kmap_local_page(page1);
- * addr2 = kmap_local_page(page2);
- * ...
- * kunmap_local(addr2);
- * kunmap_local(addr1);
- *
- * Unmapping addr1 before addr2 is invalid and causes malfunction.
- *
- * Contrary to kmap() mappings the mapping is only valid in the context of
- * the caller and cannot be handed to other contexts.
- *
- * On CONFIG_HIGHMEM=n kernels and for low memory pages this returns the
- * virtual address of the direct mapping. Only real highmem pages are
- * temporarily mapped.
- *
- * While it is significantly faster than kmap() for the higmem case it
- * comes with restrictions about the pointer validity. Only use when really
- * necessary.
- *
- * On HIGHMEM enabled systems mapping a highmem page has the side effect of
- * disabling migration in order to keep the virtual address stable across
- * preemption. No caller of kmap_local_page() can rely on this side effect.
- */
-static inline void *kmap_local_page(struct page *page);
-
-/**
- * kmap_atomic - Atomically map a page for temporary usage - Deprecated!
- * @page:	Pointer to the page to be mapped
- *
- * Returns: The virtual address of the mapping
- *
- * Effectively a wrapper around kmap_local_page() which disables pagefaults
- * and preemption.
- *
- * Do not use in new code. Use kmap_local_page() instead.
- */
-static inline void *kmap_atomic(struct page *page);
-
-/**
- * kunmap_atomic - Unmap the virtual address mapped by kmap_atomic()
- * @addr:	Virtual address to be unmapped
- *
- * Counterpart to kmap_atomic().
- *
- * Effectively a wrapper around kunmap_local() which additionally undoes
- * the side effects of kmap_atomic(), i.e. reenabling pagefaults and
- * preemption.
- */
-
-/* Highmem related interfaces for management code */
-static inline unsigned int nr_free_highpages(void);
-static inline unsigned long totalhigh_pages(void);
-
 #ifndef ARCH_HAS_FLUSH_ANON_PAGE
 static inline void flush_anon_page(struct vm_area_struct *vma, struct page *page, unsigned long vmaddr)
 {
@@ -142,6 +29,199 @@
 }
 #endif
 
+#include <asm/kmap_types.h>
+
+#ifdef CONFIG_HIGHMEM
+extern void *kmap_atomic_high_prot(struct page *page, pgprot_t prot);
+extern void kunmap_atomic_high(void *kvaddr);
+#include <asm/highmem.h>
+
+#ifndef ARCH_HAS_KMAP_FLUSH_TLB
+static inline void kmap_flush_tlb(unsigned long addr) { }
+#endif
+
+#ifndef kmap_prot
+#define kmap_prot PAGE_KERNEL
+#endif
+
+void *kmap_high(struct page *page);
+static inline void *kmap(struct page *page)
+{
+	void *addr;
+
+	might_sleep();
+	if (!PageHighMem(page))
+		addr = page_address(page);
+	else
+		addr = kmap_high(page);
+	kmap_flush_tlb((unsigned long)addr);
+	return addr;
+}
+
+void kunmap_high(struct page *page);
+
+static inline void kunmap(struct page *page)
+{
+	might_sleep();
+	if (!PageHighMem(page))
+		return;
+	kunmap_high(page);
+}
+
+/*
+ * kmap_atomic/kunmap_atomic is significantly faster than kmap/kunmap because
+ * no global lock is needed and because the kmap code must perform a global TLB
+ * invalidation when the kmap pool wraps.
+ *
+ * However when holding an atomic kmap it is not legal to sleep, so atomic
+ * kmaps are appropriate for short, tight code paths only.
+ *
+ * The use of kmap_atomic/kunmap_atomic is discouraged - kmap/kunmap
+ * gives a more generic (and caching) interface. But kmap_atomic can
+ * be used in IRQ contexts, so in some (very limited) cases we need
+ * it.
+ */
+static inline void *kmap_atomic_prot(struct page *page, pgprot_t prot)
+{
+	preempt_disable();
+	pagefault_disable();
+	if (!PageHighMem(page))
+		return page_address(page);
+	return kmap_atomic_high_prot(page, prot);
+}
+#define kmap_atomic(page)	kmap_atomic_prot(page, kmap_prot)
+
+/* declarations for linux/mm/highmem.c */
+unsigned int nr_free_highpages(void);
+extern atomic_long_t _totalhigh_pages;
+static inline unsigned long totalhigh_pages(void)
+{
+	return (unsigned long)atomic_long_read(&_totalhigh_pages);
+}
+
+static inline void totalhigh_pages_inc(void)
+{
+	atomic_long_inc(&_totalhigh_pages);
+}
+
+static inline void totalhigh_pages_dec(void)
+{
+	atomic_long_dec(&_totalhigh_pages);
+}
+
+static inline void totalhigh_pages_add(long count)
+{
+	atomic_long_add(count, &_totalhigh_pages);
+}
+
+static inline void totalhigh_pages_set(long val)
+{
+	atomic_long_set(&_totalhigh_pages, val);
+}
+
+void kmap_flush_unused(void);
+
+struct page *kmap_to_page(void *addr);
+
+#else /* CONFIG_HIGHMEM */
+
+static inline unsigned int nr_free_highpages(void) { return 0; }
+
+static inline struct page *kmap_to_page(void *addr)
+{
+	return virt_to_page(addr);
+}
+
+static inline unsigned long totalhigh_pages(void) { return 0UL; }
+
+static inline void *kmap(struct page *page)
+{
+	might_sleep();
+	return page_address(page);
+}
+
+static inline void kunmap_high(struct page *page)
+{
+}
+
+static inline void kunmap(struct page *page)
+{
+#ifdef ARCH_HAS_FLUSH_ON_KUNMAP
+	kunmap_flush_on_unmap(page_address(page));
+#endif
+}
+
+static inline void *kmap_atomic(struct page *page)
+{
+	preempt_disable();
+	pagefault_disable();
+	return page_address(page);
+}
+#define kmap_atomic_prot(page, prot)	kmap_atomic(page)
+
+static inline void kunmap_atomic_high(void *addr)
+{
+	/*
+	 * Mostly nothing to do in the CONFIG_HIGHMEM=n case as kunmap_atomic()
+	 * handles re-enabling faults + preemption
+	 */
+#ifdef ARCH_HAS_FLUSH_ON_KUNMAP
+	kunmap_flush_on_unmap(addr);
+#endif
+}
+
+#define kmap_atomic_pfn(pfn)	kmap_atomic(pfn_to_page(pfn))
+
+#define kmap_flush_unused()	do {} while(0)
+
+#endif /* CONFIG_HIGHMEM */
+
+#if defined(CONFIG_HIGHMEM) || defined(CONFIG_X86_32)
+
+DECLARE_PER_CPU(int, __kmap_atomic_idx);
+
+static inline int kmap_atomic_idx_push(void)
+{
+	int idx = __this_cpu_inc_return(__kmap_atomic_idx) - 1;
+
+#ifdef CONFIG_DEBUG_HIGHMEM
+	WARN_ON_ONCE(in_irq() && !irqs_disabled());
+	BUG_ON(idx >= KM_TYPE_NR);
+#endif
+	return idx;
+}
+
+static inline int kmap_atomic_idx(void)
+{
+	return __this_cpu_read(__kmap_atomic_idx) - 1;
+}
+
+static inline void kmap_atomic_idx_pop(void)
+{
+#ifdef CONFIG_DEBUG_HIGHMEM
+	int idx = __this_cpu_dec_return(__kmap_atomic_idx);
+
+	BUG_ON(idx < 0);
+#else
+	__this_cpu_dec(__kmap_atomic_idx);
+#endif
+}
+
+#endif
+
+/*
+ * Prevent people trying to call kunmap_atomic() as if it were kunmap()
+ * kunmap_atomic() should get the return value of kmap_atomic, not the page.
+ */
+#define kunmap_atomic(addr)                                     \
+do {                                                            \
+	BUILD_BUG_ON(__same_type((addr), struct page *));       \
+	kunmap_atomic_high(addr);                                  \
+	pagefault_enable();                                     \
+	preempt_enable();                                       \
+} while (0)
+
+
 /* when CONFIG_HIGHMEM is not set these will be plain clear/copy_page */
 #ifndef clear_user_highpage
 static inline void clear_user_highpage(struct page *page, unsigned long vaddr)
diff --git a/kernel/include/linux/iam20680.h b/kernel/include/linux/iam20680.h
new file mode 100644
index 0000000..237ff2f
--- /dev/null
+++ b/kernel/include/linux/iam20680.h
@@ -0,0 +1,238 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2021 Rockchip Electronics Co., Ltd.
+ */
+#ifndef __IAM20680_H
+#define __IAM20680_H
+
+#include <linux/ioctl.h>
+/**add***/
+#define IAM20680_PRECISION		16
+#define IAM20680_RANGE			2000000
+
+#define IAM20680_SMPLRT_DIV		0x19
+#define IAM20680_CONFIG			0x1A
+#define IAM20680_GYRO_CONFIG		0x1B
+#define IAM20680_ACCEL_CONFIG	0x1C
+#define IAM20680_ACCEL_CONFIG2	0x1D
+#define IAM20680_LP_ACCEL_ODR   0x1E
+#define IAM20680_WOM_THRESH	0x1F
+#define IAM20680_FIFO_EN			0x23
+#define IAM20680_INT_PIN_CFG		0x37
+#define IAM20680_INT_ENABLE		0x38
+#define IAM20680_DMP_INT_STATUS	0x39
+#define IAM20680_INT_STATUS		0x3A
+#define IAM20680_ACCEL_XOUT_H	0x3B
+#define IAM20680_TEMP_OUT_H	0x41
+#define IAM20680_GYRO_XOUT_H	0x43
+#define IAM20680_ACCEL_INTEL_CTRL 0x69
+#define IAM20680_USER_CTRL		0x6A
+#define IAM20680_PWR_MGMT_1		0x6B
+#define IAM20680_PWR_MGMT_2		0x6C
+#define IAM20680_PRGM_STRT_ADDRH     0x70
+#define IAM20680_FIFO_COUNTH		0x72
+#define IAM20680_FIFO_R_W		0x74
+#define IAM20680_WHOAMI			0x75
+
+#define IAM20680_DEVICE_ID		0xA9
+/*
+ * IAM20680_CONFIG
+ */
+#define DLPF_CFG_250HZ		0x00
+#define DLPF_CFG_184HZ		0x01
+#define DLPF_CFG_98HZ		0x02
+#define DLPF_CFG_41HZ		0x03
+#define DLPF_CFG_20HZ		0x04
+#define DLPF_CFG_10HZ		0x05
+#define DLPF_CFG_5HZ		0x06
+#define DLPF_CFG_3600HZ		0x07
+#define EXT_SYNC_SET_TEMP	0x08
+#define EXT_SYNC_SET_GYRO_X	0x10
+#define EXT_SYNC_SET_GYRO_Y	0x18
+#define EXT_SYNC_SET_GYRO_Z	0x20
+#define EXT_SYNC_SET_ACCEL_X	0x28
+#define EXT_SYNC_SET_ACCEL_Y	0x30
+#define EXT_SYNC_SET_ACCEL_Z	0x38
+
+
+/*
+ * IAM20680_GYRO_CONFIG
+ */
+#define GFSR_250DPS			(0<<3)
+#define GFSR_500DPS			(1<<3)
+#define GFSR_1000DPS		(2<<3)
+#define GFSR_2000DPS		(3<<3)
+
+/*
+ * IAM20680_ACCEL_CONFIG
+ */
+#define AFSR_2G		(0<<3)
+#define AFSR_4G		(1<<3)
+#define AFSR_8G		(2<<3)
+#define AFSR_16G	(3<<3)
+
+
+/*
+ * IAM20680_ACCEL_CONFIG2
+ */
+#define A_DLPF_CFG_460HZ		0x00
+#define A_DLPF_CFG_184HZ		0x01
+#define A_DLPF_CFG_92HZ		0x02
+#define A_DLPF_CFG_41HZ		0x03
+#define A_DLPF_CFG_20HZ		0x04
+#define A_DLPF_CFG_10HZ		0x05
+#define A_DLPF_CFG_5HZ			0x06
+//#define A_DLPF_CFG_460HZ      0x07
+#define BIT_FIFO_SIZE_1K                 0x40
+#define BIT_ACCEL_FCHOICE_B		0x08
+
+
+/*
+ * IAM20680_LP_ACCEL_ODR
+ */
+#define LPA_CLK_P24HZ	0x0
+#define LPA_CLK_P49HZ	0x1
+#define LPA_CLK_P98HZ	0x2
+#define LPA_CLK_1P95HZ	0x3
+#define LPA_CLK_3P91HZ	0x4
+#define LPA_CLK_7P81HZ	0x5
+#define LPA_CLK_15P63HZ	0x6
+#define LPA_CLK_31P25HZ	0x7
+#define LPA_CLK_62P50HZ	0x8
+#define LPA_CLK_125HZ	0x9
+#define LPA_CLK_250HZ	0xa
+#define LPA_CLK_500HZ	0xb
+
+
+/*
+ * IAM20680_PWR_MGMT_1
+ */
+#define BIT_H_RESET			(1<<7)
+#define BIT_SLEEP			(1<<6)
+#define BIT_CYCLE			(1<<5)
+#define BIT_GYRO_STANDBY	(1<<4)
+#define BIT_PD_PTAT			(1<<3)
+#define BIT_CLKSEL			(1<<0)
+
+#define CLKSEL_INTERNAL		0
+#define CLKSEL_PLL			1
+
+/*
+ * IAM20680_PWR_MGMT_2
+ */
+#define BIT_ACCEL_STBY              0x38
+#define BIT_GYRO_STBY               0x07
+#define BITS_LPA_WAKE_CTRL 0xC0
+#define BITS_LPA_WAKE_1HZ 0x00
+#define BITS_LPA_WAKE_2HZ 0x40
+#define BITS_LPA_WAKE_20HZ 0x80
+
+#define IAM20680_PWRM1_SLEEP				0x40
+#define IAM20680_PWRM1_GYRO_STANDBY		0x10
+#define IAM20680_PWRM2_ACCEL_DISABLE		0x38
+#define IAM20680_PWRM2_GYRO_DISABLE		0x07
+
+/*
+ * IAM20680_ACCEL_INTEL_CTRL
+ */
+#define BIT_ACCEL_INTEL_EN	0x80
+#define BIT_ACCEL_INTEL_MODE	0x40
+
+
+/*
+ * IAM20680_USER_CTRL
+ */
+#define BIT_FIFO_RST                    0x04
+#define BIT_DMP_RST                     0x08
+#define BIT_I2C_MST_EN                  0x20
+#define BIT_FIFO_EN                     0x40
+#define BIT_DMP_EN                      0x80
+
+
+/*
+ * IAM20680_FIFO_EN
+ */
+#define BIT_ACCEL_OUT           0x08
+#define BITS_GYRO_OUT           0x70
+
+
+/*
+ * IAM20680_INT_PIN_CFG
+ */
+#define BIT_BYPASS_EN           0x2
+
+/*
+ * IAM20680_INT_EN/INT_STATUS
+ */
+#define BIT_FIFO_OVERLOW	0x80
+#define BIT_MOT_INT				0x40
+#define BIT_MPU_RDY                 0x04
+#define BIT_DMP_INT                 0x02
+#define BIT_RAW_RDY                 0x01
+
+
+#define DMP_START_ADDR           0x400
+
+
+
+#define AXIS_NUM 3
+#define AXIS_ADC_BYTE 2
+#define SENSOR_PACKET (AXIS_NUM * AXIS_ADC_BYTE)
+
+
+
+
+
+/*
+ * self-test parameter
+ */
+
+#define DEF_ST_PRECISION            1000
+#define DEF_ST_IAM20680_ACCEL_LPF        2
+#define DEF_STABLE_TIME_ST 50
+#define DEF_SELFTEST_GYRO_FS            (0 << 3)
+#define DEF_SELFTEST_ACCEL_FS           (2 << 3)
+#define DEF_SELFTEST_6500_ACCEL_FS      (0 << 3)
+#define DEF_SW_SELFTEST_GYRO_FS	GFSR_2000DPS
+#define DEF_SW_SELFTEST_SENSITIVITY		((2000*DEF_ST_PRECISION)/32768)
+
+#define DEF_SW_SELFTEST_SAMPLE_COUNT 75
+#define DEF_SW_SELFTEST_SAMPLE_TIME 75
+#define DEF_SW_ACCEL_CAL_SAMPLE_TIME 50
+#define DEF_SW_SKIP_COUNT 10
+
+#define DEF_ST_6500_STABLE_TIME         20
+#define BYTES_PER_SENSOR        (6)
+#define DEF_SELFTEST_SAMPLE_RATE             0
+#define DEF_GYRO_WAIT_TIME          50
+#define THREE_AXIS              (3)
+#define INIT_ST_SAMPLES          200
+#define FIFO_COUNT_BYTE         (2)
+#define DEF_ST_TRY_TIMES            2
+#define REG_6500_XG_ST_DATA     0x0
+#define REG_6500_XA_ST_DATA     0xD
+#define BITS_SELF_TEST_EN		0xE0
+
+#define DEF_ST_SCALE                    (1L << 15)
+
+/*---- IAM20680 Self Test Pass/Fail Criteria ----*/
+/* Gyro Offset Max Value (dps) */
+#define DEF_GYRO_OFFSET_MAX             20
+/* Gyro Self Test Absolute Limits ST_AL (dps) */
+#define DEF_GYRO_ST_AL                  60
+/* Accel Self Test Absolute Limits ST_AL (mg) */
+#define DEF_ACCEL_ST_AL_MIN             225
+#define DEF_ACCEL_ST_AL_MAX             675
+#define DEF_6500_ACCEL_ST_SHIFT_DELTA   500
+#define DEF_6500_GYRO_CT_SHIFT_DELTA    500
+#define DEF_ST_IAM20680_ACCEL_LPF        2
+#define DEF_ST_6500_ACCEL_FS_MG         2000UL
+#define DEF_SELFTEST_6500_ACCEL_FS      (0 << 3)
+
+#define DEF_SELFTEST_GYRO_SENS          (32768 / 250)
+
+
+#define  GSENSOR_DEV_PATH    "/dev/mma8452_daemon"
+
+#endif
+
diff --git a/kernel/include/linux/interrupt.h b/kernel/include/linux/interrupt.h
index d21f7af..386ddf4 100644
--- a/kernel/include/linux/interrupt.h
+++ b/kernel/include/linux/interrupt.h
@@ -566,7 +566,7 @@
 asmlinkage void do_softirq(void);
 asmlinkage void __do_softirq(void);
 
-#if defined(__ARCH_HAS_DO_SOFTIRQ) && !defined(CONFIG_PREEMPT_RT)
+#ifdef __ARCH_HAS_DO_SOFTIRQ
 void do_softirq_own_stack(void);
 #else
 static inline void do_softirq_own_stack(void)
@@ -661,21 +661,26 @@
 	TASKLET_STATE_RUN	/* Tasklet is running (SMP only) */
 };
 
-#if defined(CONFIG_SMP) || defined(CONFIG_PREEMPT_RT)
+#ifdef CONFIG_SMP
 static inline int tasklet_trylock(struct tasklet_struct *t)
 {
 	return !test_and_set_bit(TASKLET_STATE_RUN, &(t)->state);
 }
 
-void tasklet_unlock(struct tasklet_struct *t);
-void tasklet_unlock_wait(struct tasklet_struct *t);
-void tasklet_unlock_spin_wait(struct tasklet_struct *t);
+static inline void tasklet_unlock(struct tasklet_struct *t)
+{
+	smp_mb__before_atomic();
+	clear_bit(TASKLET_STATE_RUN, &(t)->state);
+}
 
+static inline void tasklet_unlock_wait(struct tasklet_struct *t)
+{
+	while (test_bit(TASKLET_STATE_RUN, &(t)->state)) { barrier(); }
+}
 #else
-static inline int tasklet_trylock(struct tasklet_struct *t) { return 1; }
-static inline void tasklet_unlock(struct tasklet_struct *t) { }
-static inline void tasklet_unlock_wait(struct tasklet_struct *t) { }
-static inline void tasklet_unlock_spin_wait(struct tasklet_struct *t) { }
+#define tasklet_trylock(t) 1
+#define tasklet_unlock_wait(t) do { } while (0)
+#define tasklet_unlock(t) do { } while (0)
 #endif
 
 extern void __tasklet_schedule(struct tasklet_struct *t);
@@ -698,17 +703,6 @@
 {
 	atomic_inc(&t->count);
 	smp_mb__after_atomic();
-}
-
-/*
- * Do not use in new code. Disabling tasklets from atomic contexts is
- * error prone and should be avoided.
- */
-static inline void tasklet_disable_in_atomic(struct tasklet_struct *t)
-{
-	tasklet_disable_nosync(t);
-	tasklet_unlock_spin_wait(t);
-	smp_mb();
 }
 
 static inline void tasklet_disable(struct tasklet_struct *t)
diff --git a/kernel/include/linux/io-mapping.h b/kernel/include/linux/io-mapping.h
index 4bb8223..c75e4d3 100644
--- a/kernel/include/linux/io-mapping.h
+++ b/kernel/include/linux/io-mapping.h
@@ -60,20 +60,22 @@
 	iomap_free(mapping->base, mapping->size);
 }
 
-/* Temporary mappings which are only valid in the current context */
+/* Atomic map/unmap */
 static inline void __iomem *
-io_mapping_map_local_wc(struct io_mapping *mapping, unsigned long offset)
+io_mapping_map_atomic_wc(struct io_mapping *mapping,
+			 unsigned long offset)
 {
 	resource_size_t phys_addr;
 
 	BUG_ON(offset >= mapping->size);
 	phys_addr = mapping->base + offset;
-	return __iomap_local_pfn_prot(PHYS_PFN(phys_addr), mapping->prot);
+	return iomap_atomic_prot_pfn(PHYS_PFN(phys_addr), mapping->prot);
 }
 
-static inline void io_mapping_unmap_local(void __iomem *vaddr)
+static inline void
+io_mapping_unmap_atomic(void __iomem *vaddr)
 {
-	kunmap_local_indexed((void __force *)vaddr);
+	iounmap_atomic(vaddr);
 }
 
 static inline void __iomem *
@@ -95,7 +97,7 @@
 	iounmap(vaddr);
 }
 
-#else  /* HAVE_ATOMIC_IOMAP */
+#else
 
 #include <linux/uaccess.h>
 
@@ -142,19 +144,25 @@
 {
 }
 
-/* Temporary mappings which are only valid in the current context */
+/* Atomic map/unmap */
 static inline void __iomem *
-io_mapping_map_local_wc(struct io_mapping *mapping, unsigned long offset)
+io_mapping_map_atomic_wc(struct io_mapping *mapping,
+			 unsigned long offset)
 {
+	preempt_disable();
+	pagefault_disable();
 	return io_mapping_map_wc(mapping, offset, PAGE_SIZE);
 }
 
-static inline void io_mapping_unmap_local(void __iomem *vaddr)
+static inline void
+io_mapping_unmap_atomic(void __iomem *vaddr)
 {
 	io_mapping_unmap(vaddr);
+	pagefault_enable();
+	preempt_enable();
 }
 
-#endif /* !HAVE_ATOMIC_IOMAP */
+#endif /* HAVE_ATOMIC_IOMAP */
 
 static inline struct io_mapping *
 io_mapping_create_wc(resource_size_t base,
diff --git a/kernel/include/linux/irq_cpustat.h b/kernel/include/linux/irq_cpustat.h
new file mode 100644
index 0000000..6e8895c
--- /dev/null
+++ b/kernel/include/linux/irq_cpustat.h
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __irq_cpustat_h
+#define __irq_cpustat_h
+
+/*
+ * Contains default mappings for irq_cpustat_t, used by almost every
+ * architecture.  Some arch (like s390) have per cpu hardware pages and
+ * they define their own mappings for irq_stat.
+ *
+ * Keith Owens <kaos@ocs.com.au> July 2000.
+ */
+
+
+/*
+ * Simple wrappers reducing source bloat.  Define all irq_stat fields
+ * here, even ones that are arch dependent.  That way we get common
+ * definitions instead of differing sets for each arch.
+ */
+
+#ifndef __ARCH_IRQ_STAT
+DECLARE_PER_CPU_ALIGNED(irq_cpustat_t, irq_stat);	/* defined in asm/hardirq.h */
+#define __IRQ_STAT(cpu, member)	(per_cpu(irq_stat.member, cpu))
+#endif
+
+/* arch dependent irq_stat fields */
+#define nmi_count(cpu)		__IRQ_STAT((cpu), __nmi_count)	/* i386 */
+
+#endif	/* __irq_cpustat_h */
diff --git a/kernel/include/linux/irq_work.h b/kernel/include/linux/irq_work.h
index 2c00593..3082378 100644
--- a/kernel/include/linux/irq_work.h
+++ b/kernel/include/linux/irq_work.h
@@ -3,7 +3,6 @@
 #define _LINUX_IRQ_WORK_H
 
 #include <linux/smp_types.h>
-#include <linux/rcuwait.h>
 
 /*
  * An entry can be in one of four states:
@@ -23,7 +22,6 @@
 		};
 	};
 	void (*func)(struct irq_work *);
-	struct rcuwait irqwait;
 };
 
 static inline
@@ -31,34 +29,13 @@
 {
 	atomic_set(&work->flags, 0);
 	work->func = func;
-	rcuwait_init(&work->irqwait);
 }
 
 #define DEFINE_IRQ_WORK(name, _f) struct irq_work name = {	\
 		.flags = ATOMIC_INIT(0),			\
-		.func  = (_f),					\
-		.irqwait = __RCUWAIT_INITIALIZER(irqwait),	\
+		.func  = (_f)					\
 }
 
-#define __IRQ_WORK_INIT(_func, _flags) (struct irq_work){	\
-	.flags = ATOMIC_INIT(_flags),				\
-	.func = (_func),					\
-	.irqwait = __RCUWAIT_INITIALIZER(irqwait),		\
-}
-
-#define IRQ_WORK_INIT(_func) __IRQ_WORK_INIT(_func, 0)
-#define IRQ_WORK_INIT_LAZY(_func) __IRQ_WORK_INIT(_func, IRQ_WORK_LAZY)
-#define IRQ_WORK_INIT_HARD(_func) __IRQ_WORK_INIT(_func, IRQ_WORK_HARD_IRQ)
-
-static inline bool irq_work_is_busy(struct irq_work *work)
-{
-	return atomic_read(&work->flags) & IRQ_WORK_BUSY;
-}
-
-static inline bool irq_work_is_hard(struct irq_work *work)
-{
-	return atomic_read(&work->flags) & IRQ_WORK_HARD_IRQ;
-}
 
 bool irq_work_queue(struct irq_work *work);
 bool irq_work_queue_on(struct irq_work *work, int cpu);
diff --git a/kernel/include/linux/irqflags.h b/kernel/include/linux/irqflags.h
index a437b2e..3ed4e87 100644
--- a/kernel/include/linux/irqflags.h
+++ b/kernel/include/linux/irqflags.h
@@ -71,6 +71,14 @@
 do {						\
 	__this_cpu_dec(hardirq_context);	\
 } while (0)
+# define lockdep_softirq_enter()		\
+do {						\
+	current->softirq_context++;		\
+} while (0)
+# define lockdep_softirq_exit()			\
+do {						\
+	current->softirq_context--;		\
+} while (0)
 
 # define lockdep_hrtimer_enter(__hrtimer)		\
 ({							\
@@ -130,21 +138,6 @@
 # define lockdep_posixtimer_exit()		do { } while (0)
 # define lockdep_irq_work_enter(__work)		do { } while (0)
 # define lockdep_irq_work_exit(__work)		do { } while (0)
-#endif
-
-#if defined(CONFIG_TRACE_IRQFLAGS) && !defined(CONFIG_PREEMPT_RT)
-# define lockdep_softirq_enter()		\
-do {						\
-	current->softirq_context++;		\
-} while (0)
-# define lockdep_softirq_exit()			\
-do {						\
-	current->softirq_context--;		\
-} while (0)
-
-#else
-# define lockdep_softirq_enter()		do { } while (0)
-# define lockdep_softirq_exit()			do { } while (0)
 #endif
 
 #if defined(CONFIG_IRQSOFF_TRACER) || \
diff --git a/kernel/include/linux/kernel.h b/kernel/include/linux/kernel.h
index b7b75f5..c333dc6 100644
--- a/kernel/include/linux/kernel.h
+++ b/kernel/include/linux/kernel.h
@@ -204,7 +204,6 @@
 extern void ___might_sleep(const char *file, int line, int preempt_offset);
 extern void __might_sleep(const char *file, int line, int preempt_offset);
 extern void __cant_sleep(const char *file, int line, int preempt_offset);
-extern void __cant_migrate(const char *file, int line);
 
 /**
  * might_sleep - annotation for functions that can sleep
@@ -220,10 +219,6 @@
  */
 # define might_sleep() \
 	do { __might_sleep(__FILE__, __LINE__, 0); might_resched(); } while (0)
-
-# define might_sleep_no_state_check() \
-	do { ___might_sleep(__FILE__, __LINE__, 0); } while (0)
-
 /**
  * cant_sleep - annotation for functions that cannot sleep
  *
@@ -232,18 +227,6 @@
 # define cant_sleep() \
 	do { __cant_sleep(__FILE__, __LINE__, 0); } while (0)
 # define sched_annotate_sleep()	(current->task_state_change = 0)
-
-/**
- * cant_migrate - annotation for functions that cannot migrate
- *
- * Will print a stack trace if executed in code which is migratable
- */
-# define cant_migrate()							\
-	do {								\
-		if (IS_ENABLED(CONFIG_SMP))				\
-			__cant_migrate(__FILE__, __LINE__);		\
-	} while (0)
-
 /**
  * non_block_start - annotate the start of section where sleeping is prohibited
  *
@@ -267,9 +250,7 @@
   static inline void __might_sleep(const char *file, int line,
 				   int preempt_offset) { }
 # define might_sleep() do { might_resched(); } while (0)
-# define might_sleep_no_state_check() do { might_resched(); } while (0)
 # define cant_sleep() do { } while (0)
-# define cant_migrate()		do { } while (0)
 # define sched_annotate_sleep() do { } while (0)
 # define non_block_start() do { } while (0)
 # define non_block_end() do { } while (0)
@@ -277,6 +258,13 @@
 
 #define might_sleep_if(cond) do { if (cond) might_sleep(); } while (0)
 
+#ifndef CONFIG_PREEMPT_RT
+# define cant_migrate()		cant_sleep()
+#else
+  /* Placeholder for now */
+# define cant_migrate()		do { } while (0)
+#endif
+
 /**
  * abs - return absolute value of an argument
  * @x: the value.  If it is unsigned type, it is converted to signed type first.
diff --git a/kernel/include/linux/kmsg_dump.h b/kernel/include/linux/kmsg_dump.h
index 8667393..3378bcb 100644
--- a/kernel/include/linux/kmsg_dump.h
+++ b/kernel/include/linux/kmsg_dump.h
@@ -30,18 +30,6 @@
 };
 
 /**
- * struct kmsg_dumper_iter - iterator for kernel crash message dumper
- * @active:	Flag that specifies if this is currently dumping
- * @cur_seq:	Points to the oldest message to dump (private)
- * @next_seq:	Points after the newest message to dump (private)
- */
-struct kmsg_dumper_iter {
-	bool	active;
-	u64	cur_seq;
-	u64	next_seq;
-};
-
-/**
  * struct kmsg_dumper - kernel crash message dumper structure
  * @list:	Entry in the dumper list (private)
  * @dump:	Call into dumping code which will retrieve the data with
@@ -51,22 +39,33 @@
  */
 struct kmsg_dumper {
 	struct list_head list;
-	void (*dump)(struct kmsg_dumper *dumper, enum kmsg_dump_reason reason,
-		     struct kmsg_dumper_iter *iter);
+	void (*dump)(struct kmsg_dumper *dumper, enum kmsg_dump_reason reason);
 	enum kmsg_dump_reason max_reason;
+	bool active;
 	bool registered;
+
+	/* private state of the kmsg iterator */
+	u32 cur_idx;
+	u32 next_idx;
+	u64 cur_seq;
+	u64 next_seq;
 };
 
 #ifdef CONFIG_PRINTK
 void kmsg_dump(enum kmsg_dump_reason reason);
 
-bool kmsg_dump_get_line(struct kmsg_dumper_iter *iter, bool syslog,
+bool kmsg_dump_get_line_nolock(struct kmsg_dumper *dumper, bool syslog,
+			       char *line, size_t size, size_t *len);
+
+bool kmsg_dump_get_line(struct kmsg_dumper *dumper, bool syslog,
 			char *line, size_t size, size_t *len);
 
-bool kmsg_dump_get_buffer(struct kmsg_dumper_iter *iter, bool syslog,
-			  char *buf, size_t size, size_t *len_out);
+bool kmsg_dump_get_buffer(struct kmsg_dumper *dumper, bool syslog,
+			  char *buf, size_t size, size_t *len);
 
-void kmsg_dump_rewind(struct kmsg_dumper_iter *iter);
+void kmsg_dump_rewind_nolock(struct kmsg_dumper *dumper);
+
+void kmsg_dump_rewind(struct kmsg_dumper *dumper);
 
 int kmsg_dump_register(struct kmsg_dumper *dumper);
 
@@ -78,19 +77,30 @@
 {
 }
 
-static inline bool kmsg_dump_get_line(struct kmsg_dumper_iter *iter, bool syslog,
+static inline bool kmsg_dump_get_line_nolock(struct kmsg_dumper *dumper,
+					     bool syslog, const char *line,
+					     size_t size, size_t *len)
+{
+	return false;
+}
+
+static inline bool kmsg_dump_get_line(struct kmsg_dumper *dumper, bool syslog,
 				const char *line, size_t size, size_t *len)
 {
 	return false;
 }
 
-static inline bool kmsg_dump_get_buffer(struct kmsg_dumper_iter *iter, bool syslog,
+static inline bool kmsg_dump_get_buffer(struct kmsg_dumper *dumper, bool syslog,
 					char *buf, size_t size, size_t *len)
 {
 	return false;
 }
 
-static inline void kmsg_dump_rewind(struct kmsg_dumper_iter *iter)
+static inline void kmsg_dump_rewind_nolock(struct kmsg_dumper *dumper)
+{
+}
+
+static inline void kmsg_dump_rewind(struct kmsg_dumper *dumper)
 {
 }
 
diff --git a/kernel/include/linux/local_lock_internal.h b/kernel/include/linux/local_lock_internal.h
index 1b8ae03..3f02b81 100644
--- a/kernel/include/linux/local_lock_internal.h
+++ b/kernel/include/linux/local_lock_internal.h
@@ -7,39 +7,13 @@
 #include <linux/lockdep.h>
 
 typedef struct {
-#ifdef CONFIG_PREEMPT_RT
-	spinlock_t              lock;
-	struct task_struct      *owner;
-	int                     nestcnt;
-
-#elif defined(CONFIG_DEBUG_LOCK_ALLOC)
+#ifdef CONFIG_DEBUG_LOCK_ALLOC
 	struct lockdep_map	dep_map;
 	struct task_struct	*owner;
 #endif
 } local_lock_t;
 
-#ifdef CONFIG_PREEMPT_RT
-
-#define INIT_LOCAL_LOCK(lockname)	{	\
-	__SPIN_LOCK_UNLOCKED((lockname).lock),	\
-	.owner		= NULL,			\
-	.nestcnt	= 0,			\
-	}
-
-static inline void ___local_lock_init(local_lock_t *l)
-{
-	l->owner = NULL;
-	l->nestcnt = 0;
-}
-
-#define __local_lock_init(l)					\
-do {								\
-	spin_lock_init(&(l)->lock);				\
-	___local_lock_init(l);					\
-} while (0)
-
-#elif defined(CONFIG_DEBUG_LOCK_ALLOC)
-
+#ifdef CONFIG_DEBUG_LOCK_ALLOC
 # define LOCAL_LOCK_DEBUG_INIT(lockname)		\
 	.dep_map = {					\
 		.name = #lockname,			\
@@ -47,33 +21,7 @@
 		.lock_type = LD_LOCK_PERCPU,		\
 	},						\
 	.owner = NULL,
-#endif
 
-#ifdef CONFIG_PREEMPT_RT
-
-static inline void local_lock_acquire(local_lock_t *l)
-{
-	if (l->owner != current) {
-		spin_lock(&l->lock);
-		DEBUG_LOCKS_WARN_ON(l->owner);
-		DEBUG_LOCKS_WARN_ON(l->nestcnt);
-		l->owner = current;
-	}
-	l->nestcnt++;
-}
-
-static inline void local_lock_release(local_lock_t *l)
-{
-	DEBUG_LOCKS_WARN_ON(l->nestcnt == 0);
-	DEBUG_LOCKS_WARN_ON(l->owner != current);
-	if (--l->nestcnt)
-		return;
-
-	l->owner = NULL;
-	spin_unlock(&l->lock);
-}
-
-#elif defined(CONFIG_DEBUG_LOCK_ALLOC)
 static inline void local_lock_acquire(local_lock_t *l)
 {
 	lock_map_acquire(&l->dep_map);
@@ -99,47 +47,6 @@
 static inline void local_lock_debug_init(local_lock_t *l) { }
 #endif /* !CONFIG_DEBUG_LOCK_ALLOC */
 
-#ifdef CONFIG_PREEMPT_RT
-
-#define __local_lock(lock)					\
-	do {							\
-		migrate_disable();				\
-		local_lock_acquire(this_cpu_ptr(lock));		\
-	} while (0)
-
-#define __local_unlock(lock)					\
-	do {							\
-		local_lock_release(this_cpu_ptr(lock));		\
-		migrate_enable();				\
-	} while (0)
-
-#define __local_lock_irq(lock)					\
-	do {							\
-		migrate_disable();				\
-		local_lock_acquire(this_cpu_ptr(lock));		\
-	} while (0)
-
-#define __local_lock_irqsave(lock, flags)			\
-	do {							\
-		migrate_disable();				\
-		flags = 0;					\
-		local_lock_acquire(this_cpu_ptr(lock));		\
-	} while (0)
-
-#define __local_unlock_irq(lock)				\
-	do {							\
-		local_lock_release(this_cpu_ptr(lock));		\
-		migrate_enable();				\
-	} while (0)
-
-#define __local_unlock_irqrestore(lock, flags)			\
-	do {							\
-		local_lock_release(this_cpu_ptr(lock));		\
-		migrate_enable();				\
-	} while (0)
-
-#else
-
 #define INIT_LOCAL_LOCK(lockname)	{ LOCAL_LOCK_DEBUG_INIT(lockname) }
 
 #define __local_lock_init(lock)					\
@@ -159,12 +66,6 @@
 		local_lock_acquire(this_cpu_ptr(lock));		\
 	} while (0)
 
-#define __local_unlock(lock)					\
-	do {							\
-		local_lock_release(this_cpu_ptr(lock));		\
-		preempt_enable();				\
-	} while (0)
-
 #define __local_lock_irq(lock)					\
 	do {							\
 		local_irq_disable();				\
@@ -175,6 +76,12 @@
 	do {							\
 		local_irq_save(flags);				\
 		local_lock_acquire(this_cpu_ptr(lock));		\
+	} while (0)
+
+#define __local_unlock(lock)					\
+	do {							\
+		local_lock_release(this_cpu_ptr(lock));		\
+		preempt_enable();				\
 	} while (0)
 
 #define __local_unlock_irq(lock)				\
@@ -188,5 +95,3 @@
 		local_lock_release(this_cpu_ptr(lock));		\
 		local_irq_restore(flags);			\
 	} while (0)
-
-#endif
diff --git a/kernel/include/linux/mm_types.h b/kernel/include/linux/mm_types.h
index 6bfafed..c853f61 100644
--- a/kernel/include/linux/mm_types.h
+++ b/kernel/include/linux/mm_types.h
@@ -12,7 +12,6 @@
 #include <linux/completion.h>
 #include <linux/cpumask.h>
 #include <linux/uprobes.h>
-#include <linux/rcupdate.h>
 #include <linux/page-flags-layout.h>
 #include <linux/workqueue.h>
 #include <linux/seqlock.h>
@@ -591,9 +590,6 @@
 		bool tlb_flush_batched;
 #endif
 		struct uprobes_state uprobes_state;
-#ifdef CONFIG_PREEMPT_RT
-		struct rcu_head delayed_drop;
-#endif
 #ifdef CONFIG_HUGETLB_PAGE
 		atomic_long_t hugetlb_usage;
 #endif
diff --git a/kernel/include/linux/mtd/spinand.h b/kernel/include/linux/mtd/spinand.h
index 6080087..04dabc4 100644
--- a/kernel/include/linux/mtd/spinand.h
+++ b/kernel/include/linux/mtd/spinand.h
@@ -149,6 +149,7 @@
 /* feature register */
 #define REG_BLOCK_LOCK		0xa0
 #define BL_ALL_UNLOCKED		0x00
+#define HWP_EN			0x02 /* Skyhigh feature, Hardware write protection */
 
 /* configuration register */
 #define REG_CFG			0xb0
diff --git a/kernel/include/linux/mutex.h b/kernel/include/linux/mutex.h
index ceec142..05165a6 100644
--- a/kernel/include/linux/mutex.h
+++ b/kernel/include/linux/mutex.h
@@ -23,20 +23,6 @@
 
 struct ww_acquire_ctx;
 
-#ifdef CONFIG_DEBUG_LOCK_ALLOC
-# define __DEP_MAP_MUTEX_INITIALIZER(lockname)			\
-		, .dep_map = {					\
-			.name = #lockname,			\
-			.wait_type_inner = LD_WAIT_SLEEP,	\
-		}
-#else
-# define __DEP_MAP_MUTEX_INITIALIZER(lockname)
-#endif
-
-#ifdef CONFIG_PREEMPT_RT
-# include <linux/mutex_rt.h>
-#else
-
 /*
  * Simple, straightforward mutexes with strict semantics:
  *
@@ -84,6 +70,14 @@
 struct ww_class;
 struct ww_acquire_ctx;
 
+struct ww_mutex {
+	struct mutex base;
+	struct ww_acquire_ctx *ctx;
+#ifdef CONFIG_DEBUG_MUTEXES
+	struct ww_class *ww_class;
+#endif
+};
+
 /*
  * This is the control structure for tasks blocked on mutex,
  * which resides on the blocked task's kernel stack:
@@ -126,6 +120,16 @@
 									\
 	__mutex_init((mutex), #mutex, &__key);				\
 } while (0)
+
+#ifdef CONFIG_DEBUG_LOCK_ALLOC
+# define __DEP_MAP_MUTEX_INITIALIZER(lockname)			\
+		, .dep_map = {					\
+			.name = #lockname,			\
+			.wait_type_inner = LD_WAIT_SLEEP,	\
+		}
+#else
+# define __DEP_MAP_MUTEX_INITIALIZER(lockname)
+#endif
 
 #define __MUTEX_INITIALIZER(lockname) \
 		{ .owner = ATOMIC_LONG_INIT(0) \
@@ -221,7 +225,5 @@
  */
 extern /* __deprecated */ __must_check enum mutex_trylock_recursive_enum
 mutex_trylock_recursive(struct mutex *lock);
-
-#endif /* !PREEMPT_RT */
 
 #endif /* __LINUX_MUTEX_H */
diff --git a/kernel/include/linux/mutex_rt.h b/kernel/include/linux/mutex_rt.h
deleted file mode 100644
index f0b2e07..0000000
--- a/kernel/include/linux/mutex_rt.h
+++ /dev/null
@@ -1,130 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-#ifndef __LINUX_MUTEX_RT_H
-#define __LINUX_MUTEX_RT_H
-
-#ifndef __LINUX_MUTEX_H
-#error "Please include mutex.h"
-#endif
-
-#include <linux/rtmutex.h>
-
-/* FIXME: Just for __lockfunc */
-#include <linux/spinlock.h>
-
-struct mutex {
-	struct rt_mutex		lock;
-#ifdef CONFIG_DEBUG_LOCK_ALLOC
-	struct lockdep_map	dep_map;
-#endif
-};
-
-#define __MUTEX_INITIALIZER(mutexname)					\
-	{								\
-		.lock = __RT_MUTEX_INITIALIZER(mutexname.lock)		\
-		__DEP_MAP_MUTEX_INITIALIZER(mutexname)			\
-	}
-
-#define DEFINE_MUTEX(mutexname)						\
-	struct mutex mutexname = __MUTEX_INITIALIZER(mutexname)
-
-extern void __mutex_do_init(struct mutex *lock, const char *name, struct lock_class_key *key);
-extern void __lockfunc _mutex_lock(struct mutex *lock);
-extern void __lockfunc _mutex_lock_io_nested(struct mutex *lock, int subclass);
-extern int __lockfunc _mutex_lock_interruptible(struct mutex *lock);
-extern int __lockfunc _mutex_lock_killable(struct mutex *lock);
-extern void __lockfunc _mutex_lock_nested(struct mutex *lock, int subclass);
-extern void __lockfunc _mutex_lock_nest_lock(struct mutex *lock, struct lockdep_map *nest_lock);
-extern int __lockfunc _mutex_lock_interruptible_nested(struct mutex *lock, int subclass);
-extern int __lockfunc _mutex_lock_killable_nested(struct mutex *lock, int subclass);
-extern int __lockfunc _mutex_trylock(struct mutex *lock);
-extern void __lockfunc _mutex_unlock(struct mutex *lock);
-
-#define mutex_is_locked(l)		rt_mutex_is_locked(&(l)->lock)
-#define mutex_lock(l)			_mutex_lock(l)
-#define mutex_lock_interruptible(l)	_mutex_lock_interruptible(l)
-#define mutex_lock_killable(l)		_mutex_lock_killable(l)
-#define mutex_trylock(l)		_mutex_trylock(l)
-#define mutex_unlock(l)			_mutex_unlock(l)
-#define mutex_lock_io(l)		_mutex_lock_io_nested(l, 0);
-
-#define __mutex_owner(l)		((l)->lock.owner)
-
-#ifdef CONFIG_DEBUG_MUTEXES
-#define mutex_destroy(l)		rt_mutex_destroy(&(l)->lock)
-#else
-static inline void mutex_destroy(struct mutex *lock) {}
-#endif
-
-#ifdef CONFIG_DEBUG_LOCK_ALLOC
-# define mutex_lock_nested(l, s)	_mutex_lock_nested(l, s)
-# define mutex_lock_interruptible_nested(l, s) \
-					_mutex_lock_interruptible_nested(l, s)
-# define mutex_lock_killable_nested(l, s) \
-					_mutex_lock_killable_nested(l, s)
-# define mutex_lock_io_nested(l, s)	_mutex_lock_io_nested(l, s)
-
-# define mutex_lock_nest_lock(lock, nest_lock)				\
-do {									\
-	typecheck(struct lockdep_map *, &(nest_lock)->dep_map);		\
-	_mutex_lock_nest_lock(lock, &(nest_lock)->dep_map);		\
-} while (0)
-
-#else
-# define mutex_lock_nested(l, s)	_mutex_lock(l)
-# define mutex_lock_interruptible_nested(l, s) \
-					_mutex_lock_interruptible(l)
-# define mutex_lock_killable_nested(l, s) \
-					_mutex_lock_killable(l)
-# define mutex_lock_nest_lock(lock, nest_lock) mutex_lock(lock)
-# define mutex_lock_io_nested(l, s)	_mutex_lock_io_nested(l, s)
-#endif
-
-# define mutex_init(mutex)				\
-do {							\
-	static struct lock_class_key __key;		\
-							\
-	rt_mutex_init(&(mutex)->lock);			\
-	__mutex_do_init((mutex), #mutex, &__key);	\
-} while (0)
-
-# define __mutex_init(mutex, name, key)			\
-do {							\
-	rt_mutex_init(&(mutex)->lock);			\
-	__mutex_do_init((mutex), name, key);		\
-} while (0)
-
-/**
- * These values are chosen such that FAIL and SUCCESS match the
- * values of the regular mutex_trylock().
- */
-enum mutex_trylock_recursive_enum {
-	MUTEX_TRYLOCK_FAILED    = 0,
-	MUTEX_TRYLOCK_SUCCESS   = 1,
-	MUTEX_TRYLOCK_RECURSIVE,
-};
-/**
- * mutex_trylock_recursive - trylock variant that allows recursive locking
- * @lock: mutex to be locked
- *
- * This function should not be used, _ever_. It is purely for hysterical GEM
- * raisins, and once those are gone this will be removed.
- *
- * Returns:
- *  MUTEX_TRYLOCK_FAILED    - trylock failed,
- *  MUTEX_TRYLOCK_SUCCESS   - lock acquired,
- *  MUTEX_TRYLOCK_RECURSIVE - we already owned the lock.
- */
-int __rt_mutex_owner_current(struct rt_mutex *lock);
-
-static inline /* __deprecated */ __must_check enum mutex_trylock_recursive_enum
-mutex_trylock_recursive(struct mutex *lock)
-{
-	if (unlikely(__rt_mutex_owner_current(&lock->lock)))
-		return MUTEX_TRYLOCK_RECURSIVE;
-
-	return mutex_trylock(lock);
-}
-
-extern int atomic_dec_and_mutex_lock(atomic_t *cnt, struct mutex *lock);
-
-#endif
diff --git a/kernel/include/linux/nfs_xdr.h b/kernel/include/linux/nfs_xdr.h
index cd9e5b3..5491ad5 100644
--- a/kernel/include/linux/nfs_xdr.h
+++ b/kernel/include/linux/nfs_xdr.h
@@ -1675,7 +1675,7 @@
 	struct nfs_removeargs args;
 	struct nfs_removeres res;
 	struct dentry *dentry;
-	struct swait_queue_head wq;
+	wait_queue_head_t wq;
 	const struct cred *cred;
 	struct nfs_fattr dir_attr;
 	long timeout;
diff --git a/kernel/include/linux/notifier.h b/kernel/include/linux/notifier.h
index 723bc2d..2fb373a 100644
--- a/kernel/include/linux/notifier.h
+++ b/kernel/include/linux/notifier.h
@@ -58,7 +58,7 @@
 };
 
 struct atomic_notifier_head {
-	raw_spinlock_t lock;
+	spinlock_t lock;
 	struct notifier_block __rcu *head;
 };
 
@@ -78,7 +78,7 @@
 };
 
 #define ATOMIC_INIT_NOTIFIER_HEAD(name) do {	\
-		raw_spin_lock_init(&(name)->lock);	\
+		spin_lock_init(&(name)->lock);	\
 		(name)->head = NULL;		\
 	} while (0)
 #define BLOCKING_INIT_NOTIFIER_HEAD(name) do {	\
@@ -95,7 +95,7 @@
 		cleanup_srcu_struct(&(name)->srcu);
 
 #define ATOMIC_NOTIFIER_INIT(name) {				\
-		.lock = __RAW_SPIN_LOCK_UNLOCKED(name.lock),	\
+		.lock = __SPIN_LOCK_UNLOCKED(name.lock),	\
 		.head = NULL }
 #define BLOCKING_NOTIFIER_INIT(name) {				\
 		.rwsem = __RWSEM_INITIALIZER((name).rwsem),	\
diff --git a/kernel/include/linux/phy/phy-rockchip-typec.h b/kernel/include/linux/phy/phy-rockchip-typec.h
deleted file mode 100644
index 1d6af83..0000000
--- a/kernel/include/linux/phy/phy-rockchip-typec.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
- * Author: Lin Huang <hl@rock-chips.com>
- */
-
-#ifndef __PHY_ROCKCHIP_TYPEC_H
-#define __PHY_ROCKCHIP_TYPEC_H
-
-#if IS_ENABLED(CONFIG_PHY_ROCKCHIP_TYPEC)
-int tcphy_dp_set_phy_config(struct phy *phy, int link_rate, int lanes,
-			    u8 swing, u8 pre_emp);
-int tcphy_dp_set_lane_count(struct phy *phy, u8 lane_count);
-int tcphy_dp_set_link_rate(struct phy *phy, int link_rate, bool ssc_on);
-#else
-static inline int tcphy_dp_set_phy_config(struct phy *phy, int link_rate,
-					  int lanes, u8 swing, u8 pre_emp)
-{
-	return -ENODEV;
-}
-
-static inline int tcphy_dp_set_lane_count(struct phy *phy, u8 lane_count)
-{
-	return -ENODEV;
-}
-
-static inline int tcphy_dp_set_link_rate(struct phy *phy, int link_rate,
-					 bool ssc_on)
-{
-	return -ENODEV;
-}
-#endif
-
-#endif
diff --git a/kernel/include/linux/pid.h b/kernel/include/linux/pid.h
index 2f86f84..fa10acb 100644
--- a/kernel/include/linux/pid.h
+++ b/kernel/include/linux/pid.h
@@ -3,7 +3,6 @@
 #define _LINUX_PID_H
 
 #include <linux/rculist.h>
-#include <linux/atomic.h>
 #include <linux/wait.h>
 #include <linux/refcount.h>
 
diff --git a/kernel/include/linux/preempt.h b/kernel/include/linux/preempt.h
index 7b5b2ed..7d9c1c0 100644
--- a/kernel/include/linux/preempt.h
+++ b/kernel/include/linux/preempt.h
@@ -77,37 +77,31 @@
 /* preempt_count() and related functions, depends on PREEMPT_NEED_RESCHED */
 #include <asm/preempt.h>
 
-#define nmi_count()	(preempt_count() & NMI_MASK)
 #define hardirq_count()	(preempt_count() & HARDIRQ_MASK)
-#ifdef CONFIG_PREEMPT_RT
-# define softirq_count()	(current->softirq_disable_cnt & SOFTIRQ_MASK)
-#else
-# define softirq_count()	(preempt_count() & SOFTIRQ_MASK)
-#endif
-#define irq_count()	(nmi_count() | hardirq_count() | softirq_count())
+#define softirq_count()	(preempt_count() & SOFTIRQ_MASK)
+#define irq_count()	(preempt_count() & (HARDIRQ_MASK | SOFTIRQ_MASK \
+				 | NMI_MASK))
 
 /*
- * Macros to retrieve the current execution context:
+ * Are we doing bottom half or hardware interrupt processing?
  *
- * in_nmi()		- We're in NMI context
- * in_hardirq()		- We're in hard IRQ context
- * in_serving_softirq()	- We're in softirq context
- * in_task()		- We're in task context
- */
-#define in_nmi()		(nmi_count())
-#define in_hardirq()		(hardirq_count())
-#define in_serving_softirq()	(softirq_count() & SOFTIRQ_OFFSET)
-#define in_task()		(!(in_nmi() | in_hardirq() | in_serving_softirq()))
-
-/*
- * The following macros are deprecated and should not be used in new code:
- * in_irq()       - Obsolete version of in_hardirq()
+ * in_irq()       - We're in (hard) IRQ context
  * in_softirq()   - We have BH disabled, or are processing softirqs
  * in_interrupt() - We're in NMI,IRQ,SoftIRQ context or have BH disabled
+ * in_serving_softirq() - We're in softirq context
+ * in_nmi()       - We're in NMI context
+ * in_task()	  - We're in task context
+ *
+ * Note: due to the BH disabled confusion: in_softirq(),in_interrupt() really
+ *       should not be used in new code.
  */
 #define in_irq()		(hardirq_count())
 #define in_softirq()		(softirq_count())
 #define in_interrupt()		(irq_count())
+#define in_serving_softirq()	(softirq_count() & SOFTIRQ_OFFSET)
+#define in_nmi()		(preempt_count() & NMI_MASK)
+#define in_task()		(!(preempt_count() & \
+				   (NMI_MASK | HARDIRQ_MASK | SOFTIRQ_OFFSET)))
 
 /*
  * The preempt_count offset after preempt_disable();
@@ -121,11 +115,7 @@
 /*
  * The preempt_count offset after spin_lock()
  */
-#if !defined(CONFIG_PREEMPT_RT)
 #define PREEMPT_LOCK_OFFSET	PREEMPT_DISABLE_OFFSET
-#else
-#define PREEMPT_LOCK_OFFSET	0
-#endif
 
 /*
  * The preempt_count offset needed for things like:
@@ -174,31 +164,11 @@
 #define preempt_count_inc() preempt_count_add(1)
 #define preempt_count_dec() preempt_count_sub(1)
 
-#ifdef CONFIG_PREEMPT_LAZY
-#define add_preempt_lazy_count(val)	do { preempt_lazy_count() += (val); } while (0)
-#define sub_preempt_lazy_count(val)	do { preempt_lazy_count() -= (val); } while (0)
-#define inc_preempt_lazy_count()	add_preempt_lazy_count(1)
-#define dec_preempt_lazy_count()	sub_preempt_lazy_count(1)
-#define preempt_lazy_count()		(current_thread_info()->preempt_lazy_count)
-#else
-#define add_preempt_lazy_count(val)	do { } while (0)
-#define sub_preempt_lazy_count(val)	do { } while (0)
-#define inc_preempt_lazy_count()	do { } while (0)
-#define dec_preempt_lazy_count()	do { } while (0)
-#define preempt_lazy_count()		(0)
-#endif
-
 #ifdef CONFIG_PREEMPT_COUNT
 
 #define preempt_disable() \
 do { \
 	preempt_count_inc(); \
-	barrier(); \
-} while (0)
-
-#define preempt_lazy_disable() \
-do { \
-	inc_preempt_lazy_count(); \
 	barrier(); \
 } while (0)
 
@@ -208,13 +178,7 @@
 	preempt_count_dec(); \
 } while (0)
 
-#ifndef CONFIG_PREEMPT_RT
-# define preempt_enable_no_resched() sched_preempt_enable_no_resched()
-# define preempt_check_resched_rt() barrier();
-#else
-# define preempt_enable_no_resched() preempt_enable()
-# define preempt_check_resched_rt() preempt_check_resched()
-#endif
+#define preempt_enable_no_resched() sched_preempt_enable_no_resched()
 
 #define preemptible()	(preempt_count() == 0 && !irqs_disabled())
 
@@ -239,29 +203,11 @@
 		__preempt_schedule(); \
 } while (0)
 
-/*
- * open code preempt_check_resched() because it is not exported to modules and
- * used by local_unlock() or bpf_enable_instrumentation().
- */
-#define preempt_lazy_enable() \
-do { \
-	dec_preempt_lazy_count(); \
-	barrier(); \
-	if (should_resched(0)) \
-		__preempt_schedule(); \
-} while (0)
-
 #else /* !CONFIG_PREEMPTION */
 #define preempt_enable() \
 do { \
 	barrier(); \
 	preempt_count_dec(); \
-} while (0)
-
-#define preempt_lazy_enable() \
-do { \
-	dec_preempt_lazy_count(); \
-	barrier(); \
 } while (0)
 
 #define preempt_enable_notrace() \
@@ -302,11 +248,7 @@
 #define preempt_disable_notrace()		barrier()
 #define preempt_enable_no_resched_notrace()	barrier()
 #define preempt_enable_notrace()		barrier()
-#define preempt_check_resched_rt()		barrier()
 #define preemptible()				0
-
-#define preempt_lazy_disable()			barrier()
-#define preempt_lazy_enable()			barrier()
 
 #endif /* CONFIG_PREEMPT_COUNT */
 
@@ -326,21 +268,9 @@
 } while (0)
 #define preempt_fold_need_resched() \
 do { \
-	if (tif_need_resched_now()) \
+	if (tif_need_resched()) \
 		set_preempt_need_resched(); \
 } while (0)
-
-#ifdef CONFIG_PREEMPT_RT
-# define preempt_disable_rt()		preempt_disable()
-# define preempt_enable_rt()		preempt_enable()
-# define preempt_disable_nort()		barrier()
-# define preempt_enable_nort()		barrier()
-#else
-# define preempt_disable_rt()		barrier()
-# define preempt_enable_rt()		barrier()
-# define preempt_disable_nort()		preempt_disable()
-# define preempt_enable_nort()		preempt_enable()
-#endif
 
 #ifdef CONFIG_PREEMPT_NOTIFIERS
 
@@ -392,78 +322,34 @@
 
 #endif
 
-#ifdef CONFIG_SMP
-
-/*
- * Migrate-Disable and why it is undesired.
+/**
+ * migrate_disable - Prevent migration of the current task
  *
- * When a preempted task becomes elegible to run under the ideal model (IOW it
- * becomes one of the M highest priority tasks), it might still have to wait
- * for the preemptee's migrate_disable() section to complete. Thereby suffering
- * a reduction in bandwidth in the exact duration of the migrate_disable()
- * section.
+ * Maps to preempt_disable() which also disables preemption. Use
+ * migrate_disable() to annotate that the intent is to prevent migration,
+ * but not necessarily preemption.
  *
- * Per this argument, the change from preempt_disable() to migrate_disable()
- * gets us:
- *
- * - a higher priority tasks gains reduced wake-up latency; with preempt_disable()
- *   it would have had to wait for the lower priority task.
- *
- * - a lower priority tasks; which under preempt_disable() could've instantly
- *   migrated away when another CPU becomes available, is now constrained
- *   by the ability to push the higher priority task away, which might itself be
- *   in a migrate_disable() section, reducing it's available bandwidth.
- *
- * IOW it trades latency / moves the interference term, but it stays in the
- * system, and as long as it remains unbounded, the system is not fully
- * deterministic.
- *
- *
- * The reason we have it anyway.
- *
- * PREEMPT_RT breaks a number of assumptions traditionally held. By forcing a
- * number of primitives into becoming preemptible, they would also allow
- * migration. This turns out to break a bunch of per-cpu usage. To this end,
- * all these primitives employ migirate_disable() to restore this implicit
- * assumption.
- *
- * This is a 'temporary' work-around at best. The correct solution is getting
- * rid of the above assumptions and reworking the code to employ explicit
- * per-cpu locking or short preempt-disable regions.
- *
- * The end goal must be to get rid of migrate_disable(), alternatively we need
- * a schedulability theory that does not depend on abritrary migration.
- *
- *
- * Notes on the implementation.
- *
- * The implementation is particularly tricky since existing code patterns
- * dictate neither migrate_disable() nor migrate_enable() is allowed to block.
- * This means that it cannot use cpus_read_lock() to serialize against hotplug,
- * nor can it easily migrate itself into a pending affinity mask change on
- * migrate_enable().
- *
- *
- * Note: even non-work-conserving schedulers like semi-partitioned depends on
- *       migration, so migrate_disable() is not only a problem for
- *       work-conserving schedulers.
- *
+ * Can be invoked nested like preempt_disable() and needs the corresponding
+ * number of migrate_enable() invocations.
  */
-extern void migrate_disable(void);
-extern void migrate_enable(void);
-
-#else
-
-static inline void migrate_disable(void)
+static __always_inline void migrate_disable(void)
 {
-	preempt_lazy_disable();
+	preempt_disable();
 }
 
-static inline void migrate_enable(void)
+/**
+ * migrate_enable - Allow migration of the current task
+ *
+ * Counterpart to migrate_disable().
+ *
+ * As migrate_disable() can be invoked nested, only the outermost invocation
+ * reenables migration.
+ *
+ * Currently mapped to preempt_enable().
+ */
+static __always_inline void migrate_enable(void)
 {
-	preempt_lazy_enable();
+	preempt_enable();
 }
-
-#endif /* CONFIG_SMP */
 
 #endif /* __LINUX_PREEMPT_H */
diff --git a/kernel/include/linux/printk.h b/kernel/include/linux/printk.h
index bd827ae..14d13ec 100644
--- a/kernel/include/linux/printk.h
+++ b/kernel/include/linux/printk.h
@@ -45,12 +45,6 @@
 
 #define CONSOLE_EXT_LOG_MAX	8192
 
-/*
- * The maximum size of a record formatted for console printing
- * (i.e. with the prefix prepended to every line).
- */
-#define CONSOLE_LOG_MAX		4096
-
 /* printk's without a loglevel use this.. */
 #define MESSAGE_LOGLEVEL_DEFAULT CONFIG_MESSAGE_LOGLEVEL_DEFAULT
 
@@ -154,6 +148,18 @@
 void early_printk(const char *s, ...) { }
 #endif
 
+#ifdef CONFIG_PRINTK_NMI
+extern void printk_nmi_enter(void);
+extern void printk_nmi_exit(void);
+extern void printk_nmi_direct_enter(void);
+extern void printk_nmi_direct_exit(void);
+#else
+static inline void printk_nmi_enter(void) { }
+static inline void printk_nmi_exit(void) { }
+static inline void printk_nmi_direct_enter(void) { }
+static inline void printk_nmi_direct_exit(void) { }
+#endif /* PRINTK_NMI */
+
 struct dev_printk_info;
 
 #ifdef CONFIG_PRINTK
@@ -201,6 +207,8 @@
 void show_regs_print_info(const char *log_lvl);
 extern asmlinkage void dump_stack_lvl(const char *log_lvl) __cold;
 extern asmlinkage void dump_stack(void) __cold;
+extern void printk_safe_flush(void);
+extern void printk_safe_flush_on_panic(void);
 #else
 static inline __printf(1, 0)
 int vprintk(const char *s, va_list args)
@@ -266,6 +274,14 @@
 }
 
 static inline void dump_stack(void)
+{
+}
+
+static inline void printk_safe_flush(void)
+{
+}
+
+static inline void printk_safe_flush_on_panic(void)
 {
 }
 #endif
@@ -484,8 +500,6 @@
 #define pr_debug_once(fmt, ...)					\
 	no_printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)
 #endif
-
-bool pr_flush(int timeout_ms, bool reset_on_progress);
 
 /*
  * ratelimited messages with local ratelimit_state,
diff --git a/kernel/include/linux/rbtree.h b/kernel/include/linux/rbtree.h
index c33b0e1..d7db179 100644
--- a/kernel/include/linux/rbtree.h
+++ b/kernel/include/linux/rbtree.h
@@ -19,8 +19,18 @@
 
 #include <linux/kernel.h>
 #include <linux/stddef.h>
-#include <linux/rbtree_type.h>
 #include <linux/rcupdate.h>
+
+struct rb_node {
+	unsigned long  __rb_parent_color;
+	struct rb_node *rb_right;
+	struct rb_node *rb_left;
+} __attribute__((aligned(sizeof(long))));
+    /* The alignment might seem pointless, but allegedly CRIS needs it */
+
+struct rb_root {
+	struct rb_node *rb_node;
+};
 
 #define rb_parent(r)   ((struct rb_node *)((r)->__rb_parent_color & ~3))
 
@@ -102,6 +112,21 @@
 			typeof(*pos), field); 1; }); \
 	     pos = n)
 
+/*
+ * Leftmost-cached rbtrees.
+ *
+ * We do not cache the rightmost node based on footprint
+ * size vs number of potential users that could benefit
+ * from O(1) rb_last(). Just not worth it, users that want
+ * this feature can always implement the logic explicitly.
+ * Furthermore, users that want to cache both pointers may
+ * find it a bit asymmetric, but that's ok.
+ */
+struct rb_root_cached {
+	struct rb_root rb_root;
+	struct rb_node *rb_leftmost;
+};
+
 #define RB_ROOT_CACHED (struct rb_root_cached) { {NULL, }, NULL }
 
 /* Same as rb_first(), but O(1) */
diff --git a/kernel/include/linux/rbtree_type.h b/kernel/include/linux/rbtree_type.h
deleted file mode 100644
index 77a89dd..0000000
--- a/kernel/include/linux/rbtree_type.h
+++ /dev/null
@@ -1,31 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-#ifndef _LINUX_RBTREE_TYPE_H
-#define _LINUX_RBTREE_TYPE_H
-
-struct rb_node {
-	unsigned long  __rb_parent_color;
-	struct rb_node *rb_right;
-	struct rb_node *rb_left;
-} __attribute__((aligned(sizeof(long))));
-/* The alignment might seem pointless, but allegedly CRIS needs it */
-
-struct rb_root {
-	struct rb_node *rb_node;
-};
-
-/*
- * Leftmost-cached rbtrees.
- *
- * We do not cache the rightmost node based on footprint
- * size vs number of potential users that could benefit
- * from O(1) rb_last(). Just not worth it, users that want
- * this feature can always implement the logic explicitly.
- * Furthermore, users that want to cache both pointers may
- * find it a bit asymmetric, but that's ok.
- */
-struct rb_root_cached {
-	struct rb_root rb_root;
-	struct rb_node *rb_leftmost;
-};
-
-#endif
diff --git a/kernel/include/linux/rcupdate.h b/kernel/include/linux/rcupdate.h
index 1effcae..095b3b3 100644
--- a/kernel/include/linux/rcupdate.h
+++ b/kernel/include/linux/rcupdate.h
@@ -54,11 +54,6 @@
  * types of kernel builds, the rcu_read_lock() nesting depth is unknowable.
  */
 #define rcu_preempt_depth() (current->rcu_read_lock_nesting)
-#ifndef CONFIG_PREEMPT_RT
-#define sched_rcu_preempt_depth()	rcu_preempt_depth()
-#else
-static inline int sched_rcu_preempt_depth(void) { return 0; }
-#endif
 
 #else /* #ifdef CONFIG_PREEMPT_RCU */
 
@@ -83,8 +78,6 @@
 {
 	return 0;
 }
-
-#define sched_rcu_preempt_depth()	rcu_preempt_depth()
 
 #endif /* #else #ifdef CONFIG_PREEMPT_RCU */
 
@@ -336,8 +329,7 @@
 #define rcu_sleep_check()						\
 	do {								\
 		rcu_preempt_sleep_check();				\
-		if (!IS_ENABLED(CONFIG_PREEMPT_RT))			\
-		    RCU_LOCKDEP_WARN(lock_is_held(&rcu_bh_lock_map),	\
+		RCU_LOCKDEP_WARN(lock_is_held(&rcu_bh_lock_map),	\
 				 "Illegal context switch in RCU-bh read-side critical section"); \
 		RCU_LOCKDEP_WARN(lock_is_held(&rcu_sched_lock_map),	\
 				 "Illegal context switch in RCU-sched read-side critical section"); \
diff --git a/kernel/include/linux/rfkill-wlan.h b/kernel/include/linux/rfkill-wlan.h
index 431e71a..c1987c3 100644
--- a/kernel/include/linux/rfkill-wlan.h
+++ b/kernel/include/linux/rfkill-wlan.h
@@ -46,7 +46,14 @@
 	struct clk *ext_clk;
 };
 
+#if IS_REACHABLE(CONFIG_RFKILL_RK)
 int rfkill_get_wifi_power_state(int *power);
+#else
+static inline int rfkill_get_wifi_power_state(int *power)
+{
+	return -1;
+}
+#endif
 void *rockchip_mem_prealloc(int section, unsigned long size);
 int rfkill_set_wifi_bt_power(int on);
 int rockchip_wifi_power(int on);
diff --git a/kernel/include/linux/rk_hdmirx_class.h b/kernel/include/linux/rk_hdmirx_class.h
new file mode 100644
index 0000000..d3a3e43
--- /dev/null
+++ b/kernel/include/linux/rk_hdmirx_class.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2021 Rockchip Electronics Co. Ltd.
+ *
+ * Author: Dingxian Wen <shawn.wen@rock-chips.com>
+ */
+
+#ifndef __RK_HDMIRX__DEV_H__
+#define __RK_HDMIRX__DEV_H__
+
+struct class *rk_hdmirx_class(void);
+
+#endif
diff --git a/kernel/include/linux/rockchip/cpu.h b/kernel/include/linux/rockchip/cpu.h
index 7cf949c..beaaf92 100644
--- a/kernel/include/linux/rockchip/cpu.h
+++ b/kernel/include/linux/rockchip/cpu.h
@@ -28,6 +28,7 @@
 #define ROCKCHIP_CPU_RK3308		0x33080000
 #define ROCKCHIP_CPU_RK3528		0x35280000
 #define ROCKCHIP_CPU_RK3566		0x35660000
+#define ROCKCHIP_CPU_RK3567		0x35670000
 #define ROCKCHIP_CPU_RK3568		0x35680000
 
 #if IS_REACHABLE(CONFIG_ROCKCHIP_CPUINFO)
@@ -181,7 +182,8 @@
 {
 	if (rockchip_soc_id)
 		return (rockchip_soc_id & ROCKCHIP_CPU_MASK) == ROCKCHIP_CPU_RK3528;
-	return of_machine_is_compatible("rockchip,rk3528");
+	return of_machine_is_compatible("rockchip,rk3528") ||
+	       of_machine_is_compatible("rockchip,rk3528a");
 }
 #else
 static inline bool cpu_is_rk3528(void) { return false; }
@@ -195,6 +197,13 @@
 	return of_machine_is_compatible("rockchip,rk3566");
 }
 
+static inline bool cpu_is_rk3567(void)
+{
+	if (rockchip_soc_id)
+		return (rockchip_soc_id & ROCKCHIP_CPU_MASK) == ROCKCHIP_CPU_RK3567;
+	return of_machine_is_compatible("rockchip,rk3567");
+}
+
 static inline bool cpu_is_rk3568(void)
 {
 	if (rockchip_soc_id)
@@ -203,6 +212,7 @@
 }
 #else
 static inline bool cpu_is_rk3566(void) { return false; }
+static inline bool cpu_is_rk3567(void) { return false; }
 static inline bool cpu_is_rk3568(void) { return false; }
 #endif
 
@@ -223,7 +233,9 @@
 #define ROCKCHIP_SOC_RK3308B	(ROCKCHIP_CPU_RK3308 | 0x01)
 #define ROCKCHIP_SOC_RK3308BS	(ROCKCHIP_CPU_RK3308 | 0x02)
 #define ROCKCHIP_SOC_RK3528	(ROCKCHIP_CPU_RK3528 | 0x00)
+#define ROCKCHIP_SOC_RK3528A	(ROCKCHIP_CPU_RK3528 | 0x01)
 #define ROCKCHIP_SOC_RK3566	(ROCKCHIP_CPU_RK3566 | 0x00)
+#define ROCKCHIP_SOC_RK3567	(ROCKCHIP_CPU_RK3567 | 0x00)
 #define ROCKCHIP_SOC_RK3568	(ROCKCHIP_CPU_RK3568 | 0x00)
 
 #define ROCKCHIP_SOC(CPU, id, ID) \
@@ -252,7 +264,9 @@
 ROCKCHIP_SOC(RK3308, rk3308b, RK3308B)
 ROCKCHIP_SOC(RK3308, rk3308bs, RK3308BS)
 ROCKCHIP_SOC(RK3528, rk3528, RK3528)
+ROCKCHIP_SOC(RK3528, rk3528a, RK3528A)
 ROCKCHIP_SOC(RK3568, rk3566, RK3566)
+ROCKCHIP_SOC(RK3567, rk3567, RK3567)
 ROCKCHIP_SOC(RK3568, rk3568, RK3568)
 
 #endif
diff --git a/kernel/include/linux/rockchip/rockchip_sip.h b/kernel/include/linux/rockchip/rockchip_sip.h
index a0ff5c7..717f0e8 100644
--- a/kernel/include/linux/rockchip/rockchip_sip.h
+++ b/kernel/include/linux/rockchip/rockchip_sip.h
@@ -56,6 +56,7 @@
 #define SIP_HDCP_CONFIG			0x82000025
 #define SIP_WDT_CFG			0x82000026
 #define SIP_HDMIRX_CFG			0x82000027
+#define SIP_MCU_CFG			0x82000028
 
 #define TRUSTED_OS_HDCPKEY_INIT		0xB7000003
 
@@ -118,6 +119,28 @@
 /* wakeup state */
 #define REMOTECTL_PWRKEY_WAKEUP		0xdeadbeaf
 
+/* SIP_MCU_CFG child configs, MCU ID */
+enum {
+	RK_BUS_MCU,
+	RK_PMU_MCU,
+	RK_DDR_MCU,
+	RK_NPU_MCU,
+};
+
+#define RK_SIP_MCU_ID(type, id)		((type) << 8 | id)
+
+#define RK_SIP_CFG_BUSMCU_0_ID		RK_SIP_MCU_ID(RK_BUS_MCU, 0)
+#define RK_SIP_CFG_BUSMCU_1_ID		RK_SIP_MCU_ID(RK_BUS_MCU, 1)
+#define RK_SIP_CFG_PMUMCU_0_ID		RK_SIP_MCU_ID(RK_PMU_MCU, 0)
+#define RK_SIP_CFG_DDRMCU_0_ID		RK_SIP_MCU_ID(RK_DDR_MCU, 0)
+#define RK_SIP_CFG_NPUMCU_0_ID		RK_SIP_MCU_ID(RK_NPU_MCU, 0)
+
+/* SIP_MCU_CFG child configs */
+#define CONFIG_MCU_CODE_START_ADDR	0x01
+#define CONFIG_MCU_EXPERI_START_ADDR	0x02
+#define CONFIG_MCU_SRAM_START_ADDR	0x03
+#define CONFIG_MCU_EXSRAM_START_ADDR	0x04
+
 struct dram_addrmap_info {
 	u64 ch_mask[2];
 	u64 bk_mask[4];
@@ -159,6 +182,7 @@
 	SHARE_PAGE_TYPE_DDR_ADDRMAP,
 	SHARE_PAGE_TYPE_LAST_LOG,
 	SHARE_PAGE_TYPE_HDCP,
+	SHARE_PAGE_TYPE_SLEEP,
 	SHARE_PAGE_TYPE_MAX,
 } share_page_type_t;
 
@@ -251,6 +275,7 @@
 int sip_wdt_config(u32 sub_func, u32 arg1, u32 arg2, u32 arg3);
 int sip_hdmirx_config(u32 sub_func, u32 arg1, u32 arg2, u32 arg3);
 int sip_hdcpkey_init(u32 hdcp_id);
+int sip_smc_mcu_config(unsigned long mcu_id, unsigned long func, unsigned long arg2);
 #else
 static inline struct arm_smccc_res sip_smc_get_atf_version(void)
 {
@@ -407,6 +432,13 @@
 {
 	return 0;
 }
+
+static inline int sip_smc_mcu_config(unsigned long mcu_id,
+				     unsigned long func,
+				     unsigned long arg2)
+{
+	return SIP_RET_NOT_SUPPORTED;
+}
 #endif
 
 /* 32-bit OP-TEE context, never change order of members! */
diff --git a/kernel/include/linux/rtmutex.h b/kernel/include/linux/rtmutex.h
index b02009f..6fd615a 100644
--- a/kernel/include/linux/rtmutex.h
+++ b/kernel/include/linux/rtmutex.h
@@ -14,14 +14,10 @@
 #define __LINUX_RT_MUTEX_H
 
 #include <linux/linkage.h>
-#include <linux/rbtree_type.h>
-#include <linux/spinlock_types_raw.h>
+#include <linux/rbtree.h>
+#include <linux/spinlock_types.h>
 
 extern int max_lock_depth; /* for sysctl */
-
-#ifdef CONFIG_DEBUG_MUTEXES
-#include <linux/debug_locks.h>
-#endif
 
 /**
  * The rt_mutex structure
@@ -35,7 +31,12 @@
 	raw_spinlock_t		wait_lock;
 	struct rb_root_cached   waiters;
 	struct task_struct	*owner;
+#ifdef CONFIG_DEBUG_RT_MUTEXES
 	int			save_state;
+	const char		*name, *file;
+	int			line;
+	void			*magic;
+#endif
 #ifdef CONFIG_DEBUG_LOCK_ALLOC
 	struct lockdep_map	dep_map;
 #endif
@@ -48,7 +49,6 @@
  extern int rt_mutex_debug_check_no_locks_freed(const void *from,
 						unsigned long len);
  extern void rt_mutex_debug_check_no_locks_held(struct task_struct *task);
- extern void rt_mutex_debug_task_free(struct task_struct *tsk);
 #else
  static inline int rt_mutex_debug_check_no_locks_freed(const void *from,
 						       unsigned long len)
@@ -56,14 +56,24 @@
 	return 0;
  }
 # define rt_mutex_debug_check_no_locks_held(task)	do { } while (0)
-# define rt_mutex_debug_task_free(t)			do { } while (0)
 #endif
 
-#define rt_mutex_init(mutex) \
+#ifdef CONFIG_DEBUG_RT_MUTEXES
+# define __DEBUG_RT_MUTEX_INITIALIZER(mutexname) \
+	, .name = #mutexname, .file = __FILE__, .line = __LINE__
+
+# define rt_mutex_init(mutex) \
 do { \
 	static struct lock_class_key __key; \
 	__rt_mutex_init(mutex, __func__, &__key); \
 } while (0)
+
+ extern void rt_mutex_debug_task_free(struct task_struct *tsk);
+#else
+# define __DEBUG_RT_MUTEX_INITIALIZER(mutexname)
+# define rt_mutex_init(mutex)			__rt_mutex_init(mutex, NULL, NULL)
+# define rt_mutex_debug_task_free(t)			do { } while (0)
+#endif
 
 #ifdef CONFIG_DEBUG_LOCK_ALLOC
 #define __DEP_MAP_RT_MUTEX_INITIALIZER(mutexname) \
@@ -72,19 +82,12 @@
 #define __DEP_MAP_RT_MUTEX_INITIALIZER(mutexname)
 #endif
 
-#define __RT_MUTEX_INITIALIZER_PLAIN(mutexname) \
-	  .wait_lock = __RAW_SPIN_LOCK_UNLOCKED(mutexname.wait_lock) \
+#define __RT_MUTEX_INITIALIZER(mutexname) \
+	{ .wait_lock = __RAW_SPIN_LOCK_UNLOCKED(mutexname.wait_lock) \
 	, .waiters = RB_ROOT_CACHED \
 	, .owner = NULL \
-	__DEP_MAP_RT_MUTEX_INITIALIZER(mutexname)
-
-#define __RT_MUTEX_INITIALIZER(mutexname) \
-	{ __RT_MUTEX_INITIALIZER_PLAIN(mutexname) \
-	, .save_state = 0 }
-
-#define __RT_MUTEX_INITIALIZER_SAVE_STATE(mutexname) \
-	{ __RT_MUTEX_INITIALIZER_PLAIN(mutexname)    \
-	, .save_state = 1 }
+	__DEBUG_RT_MUTEX_INITIALIZER(mutexname) \
+	__DEP_MAP_RT_MUTEX_INITIALIZER(mutexname)}
 
 #define DEFINE_RT_MUTEX(mutexname) \
 	struct rt_mutex mutexname = __RT_MUTEX_INITIALIZER(mutexname)
@@ -112,6 +115,9 @@
 #endif
 
 extern int rt_mutex_lock_interruptible(struct rt_mutex *lock);
+extern int rt_mutex_timed_lock(struct rt_mutex *lock,
+			       struct hrtimer_sleeper *timeout);
+
 extern int rt_mutex_trylock(struct rt_mutex *lock);
 
 extern void rt_mutex_unlock(struct rt_mutex *lock);
diff --git a/kernel/include/linux/rwlock_rt.h b/kernel/include/linux/rwlock_rt.h
deleted file mode 100644
index aafdb0a..0000000
--- a/kernel/include/linux/rwlock_rt.h
+++ /dev/null
@@ -1,109 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-#ifndef __LINUX_RWLOCK_RT_H
-#define __LINUX_RWLOCK_RT_H
-
-#ifndef __LINUX_SPINLOCK_H
-#error Do not include directly. Use spinlock.h
-#endif
-
-extern void __lockfunc rt_write_lock(rwlock_t *rwlock);
-extern void __lockfunc rt_read_lock(rwlock_t *rwlock);
-extern int __lockfunc rt_write_trylock(rwlock_t *rwlock);
-extern int __lockfunc rt_read_trylock(rwlock_t *rwlock);
-extern void __lockfunc rt_write_unlock(rwlock_t *rwlock);
-extern void __lockfunc rt_read_unlock(rwlock_t *rwlock);
-extern int __lockfunc rt_read_can_lock(rwlock_t *rwlock);
-extern int __lockfunc rt_write_can_lock(rwlock_t *rwlock);
-extern void __rt_rwlock_init(rwlock_t *rwlock, char *name, struct lock_class_key *key);
-
-#define read_can_lock(rwlock)		rt_read_can_lock(rwlock)
-#define write_can_lock(rwlock)		rt_write_can_lock(rwlock)
-
-#define read_trylock(lock)	__cond_lock(lock, rt_read_trylock(lock))
-#define write_trylock(lock)	__cond_lock(lock, rt_write_trylock(lock))
-
-static inline int __write_trylock_rt_irqsave(rwlock_t *lock, unsigned long *flags)
-{
-	*flags = 0;
-	return rt_write_trylock(lock);
-}
-
-#define write_trylock_irqsave(lock, flags)		\
-	__cond_lock(lock, __write_trylock_rt_irqsave(lock, &(flags)))
-
-#define read_lock_irqsave(lock, flags)			\
-	do {						\
-		typecheck(unsigned long, flags);	\
-		rt_read_lock(lock);			\
-		flags = 0;				\
-	} while (0)
-
-#define write_lock_irqsave(lock, flags)			\
-	do {						\
-		typecheck(unsigned long, flags);	\
-		rt_write_lock(lock);			\
-		flags = 0;				\
-	} while (0)
-
-#define read_lock(lock)		rt_read_lock(lock)
-
-#define read_lock_bh(lock)				\
-	do {						\
-		local_bh_disable();			\
-		rt_read_lock(lock);			\
-	} while (0)
-
-#define read_lock_irq(lock)	read_lock(lock)
-
-#define write_lock(lock)	rt_write_lock(lock)
-
-#define write_lock_bh(lock)				\
-	do {						\
-		local_bh_disable();			\
-		rt_write_lock(lock);			\
-	} while (0)
-
-#define write_lock_irq(lock)	write_lock(lock)
-
-#define read_unlock(lock)	rt_read_unlock(lock)
-
-#define read_unlock_bh(lock)				\
-	do {						\
-		rt_read_unlock(lock);			\
-		local_bh_enable();			\
-	} while (0)
-
-#define read_unlock_irq(lock)	read_unlock(lock)
-
-#define write_unlock(lock)	rt_write_unlock(lock)
-
-#define write_unlock_bh(lock)				\
-	do {						\
-		rt_write_unlock(lock);			\
-		local_bh_enable();			\
-	} while (0)
-
-#define write_unlock_irq(lock)	write_unlock(lock)
-
-#define read_unlock_irqrestore(lock, flags)		\
-	do {						\
-		typecheck(unsigned long, flags);	\
-		(void) flags;				\
-		rt_read_unlock(lock);			\
-	} while (0)
-
-#define write_unlock_irqrestore(lock, flags) \
-	do {						\
-		typecheck(unsigned long, flags);	\
-		(void) flags;				\
-		rt_write_unlock(lock);			\
-	} while (0)
-
-#define rwlock_init(rwl)				\
-do {							\
-	static struct lock_class_key __key;		\
-							\
-	__rt_rwlock_init(rwl, #rwl, &__key);		\
-} while (0)
-
-#endif
diff --git a/kernel/include/linux/rwlock_types.h b/kernel/include/linux/rwlock_types.h
index 0ad226b..3bd03e1 100644
--- a/kernel/include/linux/rwlock_types.h
+++ b/kernel/include/linux/rwlock_types.h
@@ -1,10 +1,6 @@
 #ifndef __LINUX_RWLOCK_TYPES_H
 #define __LINUX_RWLOCK_TYPES_H
 
-#if !defined(__LINUX_SPINLOCK_TYPES_H)
-# error "Do not include directly, include spinlock_types.h"
-#endif
-
 /*
  * include/linux/rwlock_types.h - generic rwlock type definitions
  *				  and initializers
diff --git a/kernel/include/linux/rwlock_types_rt.h b/kernel/include/linux/rwlock_types_rt.h
deleted file mode 100644
index 4762391..0000000
--- a/kernel/include/linux/rwlock_types_rt.h
+++ /dev/null
@@ -1,56 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-#ifndef __LINUX_RWLOCK_TYPES_RT_H
-#define __LINUX_RWLOCK_TYPES_RT_H
-
-#ifndef __LINUX_SPINLOCK_TYPES_H
-#error "Do not include directly. Include spinlock_types.h instead"
-#endif
-
-#ifdef CONFIG_DEBUG_LOCK_ALLOC
-# define RW_DEP_MAP_INIT(lockname)	.dep_map = { .name = #lockname }
-#else
-# define RW_DEP_MAP_INIT(lockname)
-#endif
-
-typedef struct rt_rw_lock rwlock_t;
-
-#define __RW_LOCK_UNLOCKED(name) __RWLOCK_RT_INITIALIZER(name)
-
-#define DEFINE_RWLOCK(name) \
-	rwlock_t name = __RW_LOCK_UNLOCKED(name)
-
-/*
- * A reader biased implementation primarily for CPU pinning.
- *
- * Can be selected as general replacement for the single reader RT rwlock
- * variant
- */
-struct rt_rw_lock {
-	struct rt_mutex		rtmutex;
-	atomic_t		readers;
-#ifdef CONFIG_DEBUG_LOCK_ALLOC
-	struct lockdep_map	dep_map;
-#endif
-};
-
-#define READER_BIAS	(1U << 31)
-#define WRITER_BIAS	(1U << 30)
-
-#define __RWLOCK_RT_INITIALIZER(name)					\
-{									\
-	.readers = ATOMIC_INIT(READER_BIAS),				\
-	.rtmutex = __RT_MUTEX_INITIALIZER_SAVE_STATE(name.rtmutex),	\
-	RW_DEP_MAP_INIT(name)						\
-}
-
-void __rwlock_biased_rt_init(struct rt_rw_lock *lock, const char *name,
-			     struct lock_class_key *key);
-
-#define rwlock_biased_rt_init(rwlock)					\
-	do {								\
-		static struct lock_class_key __key;			\
-									\
-		__rwlock_biased_rt_init((rwlock), #rwlock, &__key);	\
-	} while (0)
-
-#endif
diff --git a/kernel/include/linux/rwsem-rt.h b/kernel/include/linux/rwsem-rt.h
deleted file mode 100644
index 0ba8aae..0000000
--- a/kernel/include/linux/rwsem-rt.h
+++ /dev/null
@@ -1,70 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-#ifndef _LINUX_RWSEM_RT_H
-#define _LINUX_RWSEM_RT_H
-
-#ifndef _LINUX_RWSEM_H
-#error "Include rwsem.h"
-#endif
-
-#include <linux/rtmutex.h>
-#include <linux/swait.h>
-
-#define READER_BIAS		(1U << 31)
-#define WRITER_BIAS		(1U << 30)
-
-struct rw_semaphore {
-	atomic_t		readers;
-	struct rt_mutex		rtmutex;
-#ifdef CONFIG_DEBUG_LOCK_ALLOC
-	struct lockdep_map	dep_map;
-#endif
-};
-
-#define __RWSEM_INITIALIZER(name)				\
-{								\
-	.readers = ATOMIC_INIT(READER_BIAS),			\
-	.rtmutex = __RT_MUTEX_INITIALIZER(name.rtmutex),	\
-	RW_DEP_MAP_INIT(name)					\
-}
-
-#define DECLARE_RWSEM(lockname) \
-	struct rw_semaphore lockname = __RWSEM_INITIALIZER(lockname)
-
-extern void  __rwsem_init(struct rw_semaphore *rwsem, const char *name,
-			  struct lock_class_key *key);
-
-#define __init_rwsem(sem, name, key)			\
-do {							\
-		rt_mutex_init(&(sem)->rtmutex);		\
-		__rwsem_init((sem), (name), (key));	\
-} while (0)
-
-#define init_rwsem(sem)					\
-do {							\
-	static struct lock_class_key __key;		\
-							\
-	__init_rwsem((sem), #sem, &__key);		\
-} while (0)
-
-static inline int rwsem_is_locked(struct rw_semaphore *sem)
-{
-	return atomic_read(&sem->readers) != READER_BIAS;
-}
-
-static inline int rwsem_is_contended(struct rw_semaphore *sem)
-{
-	return atomic_read(&sem->readers) > 0;
-}
-
-extern void __down_read(struct rw_semaphore *sem);
-extern int __down_read_interruptible(struct rw_semaphore *sem);
-extern int __down_read_killable(struct rw_semaphore *sem);
-extern int __down_read_trylock(struct rw_semaphore *sem);
-extern void __down_write(struct rw_semaphore *sem);
-extern int __must_check __down_write_killable(struct rw_semaphore *sem);
-extern int __down_write_trylock(struct rw_semaphore *sem);
-extern void __up_read(struct rw_semaphore *sem);
-extern void __up_write(struct rw_semaphore *sem);
-extern void __downgrade_write(struct rw_semaphore *sem);
-
-#endif
diff --git a/kernel/include/linux/rwsem.h b/kernel/include/linux/rwsem.h
index 4f71699..e851cd5 100644
--- a/kernel/include/linux/rwsem.h
+++ b/kernel/include/linux/rwsem.h
@@ -16,11 +16,6 @@
 #include <linux/spinlock.h>
 #include <linux/atomic.h>
 #include <linux/err.h>
-
-#ifdef CONFIG_PREEMPT_RT
-#include <linux/rwsem-rt.h>
-#else /* PREEMPT_RT */
-
 #ifdef CONFIG_RWSEM_SPIN_ON_OWNER
 #include <linux/osq_lock.h>
 #endif
@@ -139,13 +134,6 @@
 {
 	return !list_empty(&sem->wait_list);
 }
-
-#endif /* !PREEMPT_RT */
-
-/*
- * The functions below are the same for all rwsem implementations including
- * the RT specific variant.
- */
 
 /*
  * lock for reading
diff --git a/kernel/include/linux/sched.h b/kernel/include/linux/sched.h
index 0a13559..d3cc279 100644
--- a/kernel/include/linux/sched.h
+++ b/kernel/include/linux/sched.h
@@ -113,7 +113,11 @@
 					 __TASK_TRACED | EXIT_DEAD | EXIT_ZOMBIE | \
 					 TASK_PARKED)
 
+#define task_is_traced(task)		((task->state & __TASK_TRACED) != 0)
+
 #define task_is_stopped(task)		((task->state & __TASK_STOPPED) != 0)
+
+#define task_is_stopped_or_traced(task)	((task->state & (__TASK_STOPPED | __TASK_TRACED)) != 0)
 
 #ifdef CONFIG_DEBUG_ATOMIC_SLEEP
 
@@ -137,9 +141,6 @@
 		current->task_state_change = _THIS_IP_;		\
 		smp_store_mb(current->state, (state_value));	\
 	} while (0)
-
-#define __set_current_state_no_track(state_value)		\
-	current->state = (state_value);
 
 #define set_special_state(state_value)					\
 	do {								\
@@ -193,9 +194,6 @@
 
 #define set_current_state(state_value)					\
 	smp_store_mb(current->state, (state_value))
-
-#define __set_current_state_no_track(state_value)	\
-	__set_current_state(state_value)
 
 /*
  * set_special_state() should be used for those states when the blocking task
@@ -655,13 +653,6 @@
 	struct wake_q_node *next;
 };
 
-struct kmap_ctrl {
-#ifdef CONFIG_KMAP_LOCAL
-	int				idx;
-	pte_t				pteval[KM_MAX_IDX];
-#endif
-};
-
 struct task_struct {
 #ifdef CONFIG_THREAD_INFO_IN_TASK
 	/*
@@ -672,8 +663,6 @@
 #endif
 	/* -1 unrunnable, 0 runnable, >0 stopped: */
 	volatile long			state;
-	/* saved state for "spinlock sleepers" */
-	volatile long			saved_state;
 
 	/*
 	 * This begins the randomizable portion of task_struct. Only
@@ -753,11 +742,6 @@
 	int				nr_cpus_allowed;
 	const cpumask_t			*cpus_ptr;
 	cpumask_t			cpus_mask;
-	void				*migration_pending;
-#ifdef CONFIG_SMP
-	unsigned short			migration_disabled;
-#endif
-	unsigned short			migration_flags;
 
 #ifdef CONFIG_PREEMPT_RCU
 	int				rcu_read_lock_nesting;
@@ -862,10 +846,6 @@
 #ifdef CONFIG_PSI
 	/* Stalled due to lack of memory */
 	unsigned			in_memstall:1;
-#endif
-#ifdef CONFIG_EVENTFD
-	/* Recursion prevention for eventfd_signal() */
-	unsigned			in_eventfd_signal:1;
 #endif
 
 	unsigned long			atomic_flags; /* Flags requiring atomic access. */
@@ -1012,16 +992,11 @@
 	/* Signal handlers: */
 	struct signal_struct		*signal;
 	struct sighand_struct __rcu		*sighand;
-	struct sigqueue			*sigqueue_cache;
 	sigset_t			blocked;
 	sigset_t			real_blocked;
 	/* Restored if set_restore_sigmask() was used: */
 	sigset_t			saved_sigmask;
 	struct sigpending		pending;
-#ifdef CONFIG_PREEMPT_RT
-	/* TODO: move me into ->restart_block ? */
-	struct				kernel_siginfo forced_info;
-#endif
 	unsigned long			sas_ss_sp;
 	size_t				sas_ss_size;
 	unsigned int			sas_ss_flags;
@@ -1048,7 +1023,6 @@
 	raw_spinlock_t			pi_lock;
 
 	struct wake_q_node		wake_q;
-	struct wake_q_node		wake_q_sleeper;
 	int				wake_q_count;
 
 #ifdef CONFIG_RT_MUTEXES
@@ -1076,9 +1050,6 @@
 	int				softirqs_enabled;
 	int				softirq_context;
 	int				irq_config;
-#endif
-#ifdef CONFIG_PREEMPT_RT
-	int				softirq_disable_cnt;
 #endif
 
 #ifdef CONFIG_LOCKDEP
@@ -1365,7 +1336,6 @@
 	unsigned int			sequential_io;
 	unsigned int			sequential_io_avg;
 #endif
-	struct kmap_ctrl		kmap_ctrl;
 #ifdef CONFIG_DEBUG_ATOMIC_SLEEP
 	unsigned long			task_state_change;
 #endif
@@ -1834,7 +1804,6 @@
 
 extern int wake_up_state(struct task_struct *tsk, unsigned int state);
 extern int wake_up_process(struct task_struct *tsk);
-extern int wake_up_lock_sleeper(struct task_struct *tsk);
 extern void wake_up_new_task(struct task_struct *tsk);
 
 #ifdef CONFIG_SMP
@@ -1923,89 +1892,6 @@
 static inline int test_tsk_need_resched(struct task_struct *tsk)
 {
 	return unlikely(test_tsk_thread_flag(tsk,TIF_NEED_RESCHED));
-}
-
-#ifdef CONFIG_PREEMPT_LAZY
-static inline void set_tsk_need_resched_lazy(struct task_struct *tsk)
-{
-	set_tsk_thread_flag(tsk,TIF_NEED_RESCHED_LAZY);
-}
-
-static inline void clear_tsk_need_resched_lazy(struct task_struct *tsk)
-{
-	clear_tsk_thread_flag(tsk,TIF_NEED_RESCHED_LAZY);
-}
-
-static inline int test_tsk_need_resched_lazy(struct task_struct *tsk)
-{
-	return unlikely(test_tsk_thread_flag(tsk,TIF_NEED_RESCHED_LAZY));
-}
-
-static inline int need_resched_lazy(void)
-{
-	return test_thread_flag(TIF_NEED_RESCHED_LAZY);
-}
-
-static inline int need_resched_now(void)
-{
-	return test_thread_flag(TIF_NEED_RESCHED);
-}
-
-#else
-static inline void clear_tsk_need_resched_lazy(struct task_struct *tsk) { }
-static inline int need_resched_lazy(void) { return 0; }
-
-static inline int need_resched_now(void)
-{
-	return test_thread_flag(TIF_NEED_RESCHED);
-}
-
-#endif
-
-
-static inline bool __task_is_stopped_or_traced(struct task_struct *task)
-{
-	if (task->state & (__TASK_STOPPED | __TASK_TRACED))
-		return true;
-#ifdef CONFIG_PREEMPT_RT
-	if (task->saved_state & (__TASK_STOPPED | __TASK_TRACED))
-		return true;
-#endif
-	return false;
-}
-
-static inline bool task_is_stopped_or_traced(struct task_struct *task)
-{
-	bool traced_stopped;
-
-#ifdef CONFIG_PREEMPT_RT
-	unsigned long flags;
-
-	raw_spin_lock_irqsave(&task->pi_lock, flags);
-	traced_stopped = __task_is_stopped_or_traced(task);
-	raw_spin_unlock_irqrestore(&task->pi_lock, flags);
-#else
-	traced_stopped = __task_is_stopped_or_traced(task);
-#endif
-	return traced_stopped;
-}
-
-static inline bool task_is_traced(struct task_struct *task)
-{
-	bool traced = false;
-
-	if (task->state & __TASK_TRACED)
-		return true;
-#ifdef CONFIG_PREEMPT_RT
-	/* in case the task is sleeping on tasklist_lock */
-	raw_spin_lock_irq(&task->pi_lock);
-	if (task->state & __TASK_TRACED)
-		traced = true;
-	else if (task->saved_state & __TASK_TRACED)
-		traced = true;
-	raw_spin_unlock_irq(&task->pi_lock);
-#endif
-	return traced;
 }
 
 /*
diff --git a/kernel/include/linux/sched/hotplug.h b/kernel/include/linux/sched/hotplug.h
index c66332a..89fb9b0 100644
--- a/kernel/include/linux/sched/hotplug.h
+++ b/kernel/include/linux/sched/hotplug.h
@@ -15,10 +15,8 @@
 extern void sched_cpu_drain_rq_wait(unsigned int cpu);
 
 #ifdef CONFIG_HOTPLUG_CPU
-extern int sched_cpu_wait_empty(unsigned int cpu);
 extern int sched_cpu_dying(unsigned int cpu);
 #else
-# define sched_cpu_wait_empty	NULL
 # define sched_cpu_dying	NULL
 #endif
 
diff --git a/kernel/include/linux/sched/mm.h b/kernel/include/linux/sched/mm.h
index 6d39ad0..e3e5e14 100644
--- a/kernel/include/linux/sched/mm.h
+++ b/kernel/include/linux/sched/mm.h
@@ -49,17 +49,6 @@
 		__mmdrop(mm);
 }
 
-#ifdef CONFIG_PREEMPT_RT
-extern void __mmdrop_delayed(struct rcu_head *rhp);
-static inline void mmdrop_delayed(struct mm_struct *mm)
-{
-	if (atomic_dec_and_test(&mm->mm_count))
-		call_rcu(&mm->delayed_drop, __mmdrop_delayed);
-}
-#else
-# define mmdrop_delayed(mm)    mmdrop(mm)
-#endif
-
 /**
  * mmget() - Pin the address space associated with a &struct mm_struct.
  * @mm: The address space to pin.
diff --git a/kernel/include/linux/sched/rt.h b/kernel/include/linux/sched/rt.h
index 994c256..e5af028 100644
--- a/kernel/include/linux/sched/rt.h
+++ b/kernel/include/linux/sched/rt.h
@@ -39,12 +39,20 @@
 }
 extern void rt_mutex_setprio(struct task_struct *p, struct task_struct *pi_task);
 extern void rt_mutex_adjust_pi(struct task_struct *p);
+static inline bool tsk_is_pi_blocked(struct task_struct *tsk)
+{
+	return tsk->pi_blocked_on != NULL;
+}
 #else
 static inline struct task_struct *rt_mutex_get_top_task(struct task_struct *task)
 {
 	return NULL;
 }
 # define rt_mutex_adjust_pi(p)		do { } while (0)
+static inline bool tsk_is_pi_blocked(struct task_struct *tsk)
+{
+	return false;
+}
 #endif
 
 extern void normalize_rt_tasks(void);
diff --git a/kernel/include/linux/sched/wake_q.h b/kernel/include/linux/sched/wake_q.h
index b1bdbb6..1e05e56 100644
--- a/kernel/include/linux/sched/wake_q.h
+++ b/kernel/include/linux/sched/wake_q.h
@@ -60,17 +60,6 @@
 
 extern void wake_q_add(struct wake_q_head *head, struct task_struct *task);
 extern void wake_q_add_safe(struct wake_q_head *head, struct task_struct *task);
-extern void wake_q_add_sleeper(struct wake_q_head *head, struct task_struct *task);
-extern void __wake_up_q(struct wake_q_head *head, bool sleeper);
-
-static inline void wake_up_q(struct wake_q_head *head)
-{
-	__wake_up_q(head, false);
-}
-
-static inline void wake_up_q_sleeper(struct wake_q_head *head)
-{
-	__wake_up_q(head, true);
-}
+extern void wake_up_q(struct wake_q_head *head);
 
 #endif /* _LINUX_SCHED_WAKE_Q_H */
diff --git a/kernel/include/linux/sensor-dev.h b/kernel/include/linux/sensor-dev.h
index 271e4ee..81d3aa1 100644
--- a/kernel/include/linux/sensor-dev.h
+++ b/kernel/include/linux/sensor-dev.h
@@ -69,6 +69,7 @@
 	ACCEL_ID_ICM2060X,
 	ACCEL_ID_DA215S,
 	ACCEL_ID_DA228E,
+	ACCEL_ID_IAM20680,
 	COMPASS_ID_ALL,
 	COMPASS_ID_AK8975,
 	COMPASS_ID_AK8963,
@@ -95,6 +96,7 @@
 	GYRO_ID_MPU6880,
 	GYRO_ID_LSM330,
 	GYRO_ID_ICM2060X,
+	GYRO_ID_IAM20680,
 	LIGHT_ID_ALL,
 	LIGHT_ID_CM3217,
 	LIGHT_ID_CM3218,
diff --git a/kernel/include/linux/serial_8250.h b/kernel/include/linux/serial_8250.h
index 68d7563..2b70f73 100644
--- a/kernel/include/linux/serial_8250.h
+++ b/kernel/include/linux/serial_8250.h
@@ -7,7 +7,6 @@
 #ifndef _LINUX_SERIAL_8250_H
 #define _LINUX_SERIAL_8250_H
 
-#include <linux/atomic.h>
 #include <linux/serial_core.h>
 #include <linux/serial_reg.h>
 #include <linux/platform_device.h>
@@ -126,8 +125,6 @@
 #define MSR_SAVE_FLAGS UART_MSR_ANY_DELTA
 	unsigned char		msr_saved_flags;
 
-	atomic_t		console_printing;
-
 	struct uart_8250_dma	*dma;
 	const struct uart_8250_ops *ops;
 
@@ -183,8 +180,6 @@
 void serial8250_set_defaults(struct uart_8250_port *up);
 void serial8250_console_write(struct uart_8250_port *up, const char *s,
 			      unsigned int count);
-void serial8250_console_write_atomic(struct uart_8250_port *up, const char *s,
-				     unsigned int count);
 int serial8250_console_setup(struct uart_port *port, char *options, bool probe);
 int serial8250_console_exit(struct uart_port *port);
 
diff --git a/kernel/include/linux/shmem_fs.h b/kernel/include/linux/shmem_fs.h
index 4c47f8a..1bf2615 100644
--- a/kernel/include/linux/shmem_fs.h
+++ b/kernel/include/linux/shmem_fs.h
@@ -31,7 +31,7 @@
 	struct percpu_counter used_blocks;  /* How many are allocated */
 	unsigned long max_inodes;   /* How many inodes are allowed */
 	unsigned long free_inodes;  /* How many are left for allocation */
-	raw_spinlock_t stat_lock;   /* Serialize shmem_sb_info changes */
+	spinlock_t stat_lock;	    /* Serialize shmem_sb_info changes */
 	umode_t mode;		    /* Mount mode for root directory */
 	unsigned char huge;	    /* Whether to try for hugepages */
 	kuid_t uid;		    /* Mount uid for root directory */
diff --git a/kernel/include/linux/signal.h b/kernel/include/linux/signal.h
index d47a867..205526c 100644
--- a/kernel/include/linux/signal.h
+++ b/kernel/include/linux/signal.h
@@ -265,7 +265,6 @@
 }
 
 extern void flush_sigqueue(struct sigpending *queue);
-extern void flush_task_sigqueue(struct task_struct *tsk);
 
 /* Test if 'sig' is valid signal. Use this instead of testing _NSIG directly */
 static inline int valid_signal(unsigned long sig)
diff --git a/kernel/include/linux/skbuff.h b/kernel/include/linux/skbuff.h
index 1bcdb0f..f73efb3 100644
--- a/kernel/include/linux/skbuff.h
+++ b/kernel/include/linux/skbuff.h
@@ -297,7 +297,6 @@
 
 	__u32		qlen;
 	spinlock_t	lock;
-	raw_spinlock_t	raw_lock;
 };
 
 struct sk_buff;
@@ -1930,12 +1929,6 @@
 static inline void skb_queue_head_init(struct sk_buff_head *list)
 {
 	spin_lock_init(&list->lock);
-	__skb_queue_head_init(list);
-}
-
-static inline void skb_queue_head_init_raw(struct sk_buff_head *list)
-{
-	raw_spin_lock_init(&list->raw_lock);
 	__skb_queue_head_init(list);
 }
 
diff --git a/kernel/include/linux/smp.h b/kernel/include/linux/smp.h
index 559b811..7ce15c3 100644
--- a/kernel/include/linux/smp.h
+++ b/kernel/include/linux/smp.h
@@ -241,9 +241,6 @@
 #define get_cpu()		({ preempt_disable(); __smp_processor_id(); })
 #define put_cpu()		preempt_enable()
 
-#define get_cpu_light()		({ migrate_disable(); __smp_processor_id(); })
-#define put_cpu_light()		migrate_enable()
-
 /*
  * Callback to arch code if there's nosmp or maxcpus=0 on the
  * boot command line:
diff --git a/kernel/include/linux/spinlock.h b/kernel/include/linux/spinlock.h
index c3c7029..7989784 100644
--- a/kernel/include/linux/spinlock.h
+++ b/kernel/include/linux/spinlock.h
@@ -309,11 +309,7 @@
 })
 
 /* Include rwlock functions */
-#ifdef CONFIG_PREEMPT_RT
-# include <linux/rwlock_rt.h>
-#else
-# include <linux/rwlock.h>
-#endif
+#include <linux/rwlock.h>
 
 /*
  * Pull the _spin_*()/_read_*()/_write_*() functions/declarations:
@@ -323,10 +319,6 @@
 #else
 # include <linux/spinlock_api_up.h>
 #endif
-
-#ifdef CONFIG_PREEMPT_RT
-# include <linux/spinlock_rt.h>
-#else /* PREEMPT_RT */
 
 /*
  * Map the spin_lock functions to the raw variants for PREEMPT_RT=n
@@ -461,8 +453,6 @@
 }
 
 #define assert_spin_locked(lock)	assert_raw_spin_locked(&(lock)->rlock)
-
-#endif /* !PREEMPT_RT */
 
 /*
  * Pull the atomic_t declaration:
diff --git a/kernel/include/linux/spinlock_api_smp.h b/kernel/include/linux/spinlock_api_smp.h
index da38149..19a9be9 100644
--- a/kernel/include/linux/spinlock_api_smp.h
+++ b/kernel/include/linux/spinlock_api_smp.h
@@ -187,8 +187,6 @@
 	return 0;
 }
 
-#ifndef CONFIG_PREEMPT_RT
-# include <linux/rwlock_api_smp.h>
-#endif
+#include <linux/rwlock_api_smp.h>
 
 #endif /* __LINUX_SPINLOCK_API_SMP_H */
diff --git a/kernel/include/linux/spinlock_rt.h b/kernel/include/linux/spinlock_rt.h
deleted file mode 100644
index 3085132..0000000
--- a/kernel/include/linux/spinlock_rt.h
+++ /dev/null
@@ -1,155 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-#ifndef __LINUX_SPINLOCK_RT_H
-#define __LINUX_SPINLOCK_RT_H
-
-#ifndef __LINUX_SPINLOCK_H
-#error Do not include directly. Use spinlock.h
-#endif
-
-#include <linux/bug.h>
-
-extern void
-__rt_spin_lock_init(spinlock_t *lock, const char *name, struct lock_class_key *key);
-
-#define spin_lock_init(slock)				\
-do {							\
-	static struct lock_class_key __key;		\
-							\
-	rt_mutex_init(&(slock)->lock);			\
-	__rt_spin_lock_init(slock, #slock, &__key);	\
-} while (0)
-
-extern void __lockfunc rt_spin_lock(spinlock_t *lock);
-extern void __lockfunc rt_spin_lock_nested(spinlock_t *lock, int subclass);
-extern void __lockfunc rt_spin_lock_nest_lock(spinlock_t *lock, struct lockdep_map *nest_lock);
-extern void __lockfunc rt_spin_unlock(spinlock_t *lock);
-extern void __lockfunc rt_spin_lock_unlock(spinlock_t *lock);
-extern int __lockfunc rt_spin_trylock_irqsave(spinlock_t *lock, unsigned long *flags);
-extern int __lockfunc rt_spin_trylock_bh(spinlock_t *lock);
-extern int __lockfunc rt_spin_trylock(spinlock_t *lock);
-extern int atomic_dec_and_spin_lock(atomic_t *atomic, spinlock_t *lock);
-
-/*
- * lockdep-less calls, for derived types like rwlock:
- * (for trylock they can use rt_mutex_trylock() directly.
- * Migrate disable handling must be done at the call site.
- */
-extern void __lockfunc __rt_spin_lock(struct rt_mutex *lock);
-extern void __lockfunc __rt_spin_trylock(struct rt_mutex *lock);
-extern void __lockfunc __rt_spin_unlock(struct rt_mutex *lock);
-
-#define spin_lock(lock)			rt_spin_lock(lock)
-
-#define spin_lock_bh(lock)			\
-	do {					\
-		local_bh_disable();		\
-		rt_spin_lock(lock);		\
-	} while (0)
-
-#define spin_lock_irq(lock)		spin_lock(lock)
-
-#define spin_do_trylock(lock)		__cond_lock(lock, rt_spin_trylock(lock))
-
-#define spin_trylock(lock)			\
-({						\
-	int __locked;				\
-	__locked = spin_do_trylock(lock);	\
-	__locked;				\
-})
-
-#ifdef CONFIG_LOCKDEP
-# define spin_lock_nested(lock, subclass)		\
-	do {						\
-		rt_spin_lock_nested(lock, subclass);	\
-	} while (0)
-
-#define spin_lock_bh_nested(lock, subclass)		\
-	do {						\
-		local_bh_disable();			\
-		rt_spin_lock_nested(lock, subclass);	\
-	} while (0)
-
-# define spin_lock_nest_lock(lock, subclass)		\
-	do {                                                           \
-		typecheck(struct lockdep_map *, &(subclass)->dep_map);	\
-		rt_spin_lock_nest_lock(lock, &(subclass)->dep_map);	\
-	} while (0)
-
-# define spin_lock_irqsave_nested(lock, flags, subclass) \
-	do {						 \
-		typecheck(unsigned long, flags);	 \
-		flags = 0;				 \
-		rt_spin_lock_nested(lock, subclass);	 \
-	} while (0)
-#else
-# define spin_lock_nested(lock, subclass)	spin_lock(((void)(subclass), (lock)))
-# define spin_lock_nest_lock(lock, subclass)	spin_lock(((void)(subclass), (lock)))
-# define spin_lock_bh_nested(lock, subclass)	spin_lock_bh(((void)(subclass), (lock)))
-
-# define spin_lock_irqsave_nested(lock, flags, subclass) \
-	do {						 \
-		typecheck(unsigned long, flags);	 \
-		flags = 0;				 \
-		spin_lock(((void)(subclass), (lock)));	 \
-	} while (0)
-#endif
-
-#define spin_lock_irqsave(lock, flags)			 \
-	do {						 \
-		typecheck(unsigned long, flags);	 \
-		flags = 0;				 \
-		spin_lock(lock);			 \
-	} while (0)
-
-#define spin_unlock(lock)			rt_spin_unlock(lock)
-
-#define spin_unlock_bh(lock)				\
-	do {						\
-		rt_spin_unlock(lock);			\
-		local_bh_enable();			\
-	} while (0)
-
-#define spin_unlock_irq(lock)		spin_unlock(lock)
-
-#define spin_unlock_irqrestore(lock, flags)		\
-	do {						\
-		typecheck(unsigned long, flags);	\
-		(void) flags;				\
-		spin_unlock(lock);			\
-	} while (0)
-
-#define spin_trylock_bh(lock)	__cond_lock(lock, rt_spin_trylock_bh(lock))
-#define spin_trylock_irq(lock)	spin_trylock(lock)
-
-#define spin_trylock_irqsave(lock, flags)		\
-({							\
-	int __locked;					\
-							\
-	typecheck(unsigned long, flags);		\
-	flags = 0;					\
-	__locked = spin_trylock(lock);			\
-	__locked;					\
-})
-
-#ifdef CONFIG_GENERIC_LOCKBREAK
-# define spin_is_contended(lock)	((lock)->break_lock)
-#else
-# define spin_is_contended(lock)	(((void)(lock), 0))
-#endif
-
-static inline int spin_can_lock(spinlock_t *lock)
-{
-	return !rt_mutex_is_locked(&lock->lock);
-}
-
-static inline int spin_is_locked(spinlock_t *lock)
-{
-	return rt_mutex_is_locked(&lock->lock);
-}
-
-static inline void assert_spin_locked(spinlock_t *lock)
-{
-	BUG_ON(!spin_is_locked(lock));
-}
-
-#endif
diff --git a/kernel/include/linux/spinlock_types.h b/kernel/include/linux/spinlock_types.h
index 8d896d3..b981caa 100644
--- a/kernel/include/linux/spinlock_types.h
+++ b/kernel/include/linux/spinlock_types.h
@@ -9,15 +9,93 @@
  * Released under the General Public License (GPL).
  */
 
-#include <linux/spinlock_types_raw.h>
-
-#ifndef CONFIG_PREEMPT_RT
-# include <linux/spinlock_types_nort.h>
-# include <linux/rwlock_types.h>
+#if defined(CONFIG_SMP)
+# include <asm/spinlock_types.h>
 #else
-# include <linux/rtmutex.h>
-# include <linux/spinlock_types_rt.h>
-# include <linux/rwlock_types_rt.h>
+# include <linux/spinlock_types_up.h>
 #endif
 
+#include <linux/lockdep_types.h>
+
+typedef struct raw_spinlock {
+	arch_spinlock_t raw_lock;
+#ifdef CONFIG_DEBUG_SPINLOCK
+	unsigned int magic, owner_cpu;
+	void *owner;
+#endif
+#ifdef CONFIG_DEBUG_LOCK_ALLOC
+	struct lockdep_map dep_map;
+#endif
+} raw_spinlock_t;
+
+#define SPINLOCK_MAGIC		0xdead4ead
+
+#define SPINLOCK_OWNER_INIT	((void *)-1L)
+
+#ifdef CONFIG_DEBUG_LOCK_ALLOC
+# define RAW_SPIN_DEP_MAP_INIT(lockname)		\
+	.dep_map = {					\
+		.name = #lockname,			\
+		.wait_type_inner = LD_WAIT_SPIN,	\
+	}
+# define SPIN_DEP_MAP_INIT(lockname)			\
+	.dep_map = {					\
+		.name = #lockname,			\
+		.wait_type_inner = LD_WAIT_CONFIG,	\
+	}
+#else
+# define RAW_SPIN_DEP_MAP_INIT(lockname)
+# define SPIN_DEP_MAP_INIT(lockname)
+#endif
+
+#ifdef CONFIG_DEBUG_SPINLOCK
+# define SPIN_DEBUG_INIT(lockname)		\
+	.magic = SPINLOCK_MAGIC,		\
+	.owner_cpu = -1,			\
+	.owner = SPINLOCK_OWNER_INIT,
+#else
+# define SPIN_DEBUG_INIT(lockname)
+#endif
+
+#define __RAW_SPIN_LOCK_INITIALIZER(lockname)	\
+	{					\
+	.raw_lock = __ARCH_SPIN_LOCK_UNLOCKED,	\
+	SPIN_DEBUG_INIT(lockname)		\
+	RAW_SPIN_DEP_MAP_INIT(lockname) }
+
+#define __RAW_SPIN_LOCK_UNLOCKED(lockname)	\
+	(raw_spinlock_t) __RAW_SPIN_LOCK_INITIALIZER(lockname)
+
+#define DEFINE_RAW_SPINLOCK(x)	raw_spinlock_t x = __RAW_SPIN_LOCK_UNLOCKED(x)
+
+typedef struct spinlock {
+	union {
+		struct raw_spinlock rlock;
+
+#ifdef CONFIG_DEBUG_LOCK_ALLOC
+# define LOCK_PADSIZE (offsetof(struct raw_spinlock, dep_map))
+		struct {
+			u8 __padding[LOCK_PADSIZE];
+			struct lockdep_map dep_map;
+		};
+#endif
+	};
+} spinlock_t;
+
+#define ___SPIN_LOCK_INITIALIZER(lockname)	\
+	{					\
+	.raw_lock = __ARCH_SPIN_LOCK_UNLOCKED,	\
+	SPIN_DEBUG_INIT(lockname)		\
+	SPIN_DEP_MAP_INIT(lockname) }
+
+#define __SPIN_LOCK_INITIALIZER(lockname) \
+	{ { .rlock = ___SPIN_LOCK_INITIALIZER(lockname) } }
+
+#define __SPIN_LOCK_UNLOCKED(lockname) \
+	(spinlock_t) __SPIN_LOCK_INITIALIZER(lockname)
+
+#define DEFINE_SPINLOCK(x)	spinlock_t x = __SPIN_LOCK_UNLOCKED(x)
+
+#include <linux/rwlock_types.h>
+
 #endif /* __LINUX_SPINLOCK_TYPES_H */
diff --git a/kernel/include/linux/spinlock_types_nort.h b/kernel/include/linux/spinlock_types_nort.h
deleted file mode 100644
index e4549f0..0000000
--- a/kernel/include/linux/spinlock_types_nort.h
+++ /dev/null
@@ -1,39 +0,0 @@
-#ifndef __LINUX_SPINLOCK_TYPES_NORT_H
-#define __LINUX_SPINLOCK_TYPES_NORT_H
-
-#ifndef __LINUX_SPINLOCK_TYPES_H
-#error "Do not include directly. Include spinlock_types.h instead"
-#endif
-
-/*
- * The non RT version maps spinlocks to raw_spinlocks
- */
-typedef struct spinlock {
-	union {
-		struct raw_spinlock rlock;
-
-#ifdef CONFIG_DEBUG_LOCK_ALLOC
-# define LOCK_PADSIZE (offsetof(struct raw_spinlock, dep_map))
-		struct {
-			u8 __padding[LOCK_PADSIZE];
-			struct lockdep_map dep_map;
-		};
-#endif
-	};
-} spinlock_t;
-
-#define ___SPIN_LOCK_INITIALIZER(lockname)	\
-{						\
-	.raw_lock = __ARCH_SPIN_LOCK_UNLOCKED,	\
-	SPIN_DEBUG_INIT(lockname)		\
-	SPIN_DEP_MAP_INIT(lockname) }
-
-#define __SPIN_LOCK_INITIALIZER(lockname) \
-	{ { .rlock = ___SPIN_LOCK_INITIALIZER(lockname) } }
-
-#define __SPIN_LOCK_UNLOCKED(lockname) \
-	(spinlock_t) __SPIN_LOCK_INITIALIZER(lockname)
-
-#define DEFINE_SPINLOCK(x)	spinlock_t x = __SPIN_LOCK_UNLOCKED(x)
-
-#endif
diff --git a/kernel/include/linux/spinlock_types_raw.h b/kernel/include/linux/spinlock_types_raw.h
deleted file mode 100644
index 1d4a180..0000000
--- a/kernel/include/linux/spinlock_types_raw.h
+++ /dev/null
@@ -1,65 +0,0 @@
-#ifndef __LINUX_SPINLOCK_TYPES_RAW_H
-#define __LINUX_SPINLOCK_TYPES_RAW_H
-
-#include <linux/types.h>
-
-#if defined(CONFIG_SMP)
-# include <asm/spinlock_types.h>
-#else
-# include <linux/spinlock_types_up.h>
-#endif
-
-#include <linux/lockdep_types.h>
-
-typedef struct raw_spinlock {
-	arch_spinlock_t raw_lock;
-#ifdef CONFIG_DEBUG_SPINLOCK
-	unsigned int magic, owner_cpu;
-	void *owner;
-#endif
-#ifdef CONFIG_DEBUG_LOCK_ALLOC
-	struct lockdep_map dep_map;
-#endif
-} raw_spinlock_t;
-
-#define SPINLOCK_MAGIC		0xdead4ead
-
-#define SPINLOCK_OWNER_INIT	((void *)-1L)
-
-#ifdef CONFIG_DEBUG_LOCK_ALLOC
-# define RAW_SPIN_DEP_MAP_INIT(lockname)		\
-	.dep_map = {					\
-		.name = #lockname,			\
-		.wait_type_inner = LD_WAIT_SPIN,	\
-	}
-# define SPIN_DEP_MAP_INIT(lockname)			\
-	.dep_map = {					\
-		.name = #lockname,			\
-		.wait_type_inner = LD_WAIT_CONFIG,	\
-	}
-#else
-# define RAW_SPIN_DEP_MAP_INIT(lockname)
-# define SPIN_DEP_MAP_INIT(lockname)
-#endif
-
-#ifdef CONFIG_DEBUG_SPINLOCK
-# define SPIN_DEBUG_INIT(lockname)		\
-	.magic = SPINLOCK_MAGIC,		\
-	.owner_cpu = -1,			\
-	.owner = SPINLOCK_OWNER_INIT,
-#else
-# define SPIN_DEBUG_INIT(lockname)
-#endif
-
-#define __RAW_SPIN_LOCK_INITIALIZER(lockname)	\
-{						\
-	.raw_lock = __ARCH_SPIN_LOCK_UNLOCKED,	\
-	SPIN_DEBUG_INIT(lockname)		\
-	RAW_SPIN_DEP_MAP_INIT(lockname) }
-
-#define __RAW_SPIN_LOCK_UNLOCKED(lockname)	\
-	(raw_spinlock_t) __RAW_SPIN_LOCK_INITIALIZER(lockname)
-
-#define DEFINE_RAW_SPINLOCK(x)  raw_spinlock_t x = __RAW_SPIN_LOCK_UNLOCKED(x)
-
-#endif
diff --git a/kernel/include/linux/spinlock_types_rt.h b/kernel/include/linux/spinlock_types_rt.h
deleted file mode 100644
index 446da78..0000000
--- a/kernel/include/linux/spinlock_types_rt.h
+++ /dev/null
@@ -1,38 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-#ifndef __LINUX_SPINLOCK_TYPES_RT_H
-#define __LINUX_SPINLOCK_TYPES_RT_H
-
-#ifndef __LINUX_SPINLOCK_TYPES_H
-#error "Do not include directly. Include spinlock_types.h instead"
-#endif
-
-#include <linux/cache.h>
-
-/*
- * PREEMPT_RT: spinlocks - an RT mutex plus lock-break field:
- */
-typedef struct spinlock {
-	struct rt_mutex		lock;
-	unsigned int		break_lock;
-#ifdef CONFIG_DEBUG_LOCK_ALLOC
-	struct lockdep_map	dep_map;
-#endif
-} spinlock_t;
-
-#define __RT_SPIN_INITIALIZER(name) \
-	{ \
-	.wait_lock = __RAW_SPIN_LOCK_UNLOCKED(name.wait_lock), \
-	.save_state = 1, \
-	}
-/*
-.wait_list = PLIST_HEAD_INIT_RAW((name).lock.wait_list, (name).lock.wait_lock)
-*/
-
-#define __SPIN_LOCK_UNLOCKED(name)			\
-	{ .lock = __RT_SPIN_INITIALIZER(name.lock),		\
-	  SPIN_DEP_MAP_INIT(name) }
-
-#define DEFINE_SPINLOCK(name) \
-	spinlock_t name = __SPIN_LOCK_UNLOCKED(name)
-
-#endif
diff --git a/kernel/include/linux/spinlock_types_up.h b/kernel/include/linux/spinlock_types_up.h
index d9b371f..c09b6407 100644
--- a/kernel/include/linux/spinlock_types_up.h
+++ b/kernel/include/linux/spinlock_types_up.h
@@ -1,7 +1,7 @@
 #ifndef __LINUX_SPINLOCK_TYPES_UP_H
 #define __LINUX_SPINLOCK_TYPES_UP_H
 
-#if !defined(__LINUX_SPINLOCK_TYPES_H) && !defined(__LINUX_RT_MUTEX_H)
+#ifndef __LINUX_SPINLOCK_TYPES_H
 # error "please don't include this file directly"
 #endif
 
diff --git a/kernel/include/linux/stop_machine.h b/kernel/include/linux/stop_machine.h
index 46a87c8..ddafb3c 100644
--- a/kernel/include/linux/stop_machine.h
+++ b/kernel/include/linux/stop_machine.h
@@ -24,7 +24,6 @@
 struct cpu_stop_work {
 	struct list_head	list;		/* cpu_stopper->works */
 	cpu_stop_fn_t		fn;
-	unsigned long		caller;
 	void			*arg;
 	struct cpu_stop_done	*done;
 };
@@ -50,8 +49,6 @@
 		       struct cpu_stop_work *work_buf,
 		       struct cpu_stop_done *done);
 void cpu_stop_work_wait(struct cpu_stop_work *work_buf);
-
-extern void print_stop_info(const char *log_lvl, struct task_struct *task);
 
 #else	/* CONFIG_SMP */
 
@@ -96,8 +93,6 @@
 
 	return false;
 }
-
-static inline void print_stop_info(const char *log_lvl, struct task_struct *task) { }
 
 #endif	/* CONFIG_SMP */
 
diff --git a/kernel/include/linux/thread_info.h b/kernel/include/linux/thread_info.h
index 3cb02ce..f3040b0 100644
--- a/kernel/include/linux/thread_info.h
+++ b/kernel/include/linux/thread_info.h
@@ -110,17 +110,7 @@
 #define test_thread_flag(flag) \
 	test_ti_thread_flag(current_thread_info(), flag)
 
-#ifdef CONFIG_PREEMPT_LAZY
-#define tif_need_resched()	(test_thread_flag(TIF_NEED_RESCHED) || \
-				 test_thread_flag(TIF_NEED_RESCHED_LAZY))
-#define tif_need_resched_now()	(test_thread_flag(TIF_NEED_RESCHED))
-#define tif_need_resched_lazy()	test_thread_flag(TIF_NEED_RESCHED_LAZY))
-
-#else
-#define tif_need_resched()	test_thread_flag(TIF_NEED_RESCHED)
-#define tif_need_resched_now()	test_thread_flag(TIF_NEED_RESCHED)
-#define tif_need_resched_lazy()	0
-#endif
+#define tif_need_resched() test_thread_flag(TIF_NEED_RESCHED)
 
 #ifndef CONFIG_HAVE_ARCH_WITHIN_STACK_FRAMES
 static inline int arch_within_stack_frames(const void * const stack,
diff --git a/kernel/include/linux/trace_events.h b/kernel/include/linux/trace_events.h
index 3b3c9de..c57b793 100644
--- a/kernel/include/linux/trace_events.h
+++ b/kernel/include/linux/trace_events.h
@@ -67,8 +67,6 @@
 	unsigned char		flags;
 	unsigned char		preempt_count;
 	int			pid;
-	unsigned char		migrate_disable;
-	unsigned char		preempt_lazy_count;
 };
 
 #define TRACE_EVENT_TYPE_MAX						\
@@ -150,78 +148,17 @@
 
 enum print_line_t trace_handle_return(struct trace_seq *s);
 
-static inline void tracing_generic_entry_update(struct trace_entry *entry,
-						unsigned short type,
-						unsigned int trace_ctx)
-{
-	entry->preempt_count		= trace_ctx & 0xff;
-	entry->migrate_disable		= (trace_ctx >> 8) & 0xff;
-	entry->preempt_lazy_count	= (trace_ctx >> 16) & 0xff;
-	entry->pid			= current->pid;
-	entry->type			= type;
-	entry->flags			= trace_ctx >> 24;
-}
-
-unsigned int tracing_gen_ctx_irq_test(unsigned int irqs_status);
-
-enum trace_flag_type {
-	TRACE_FLAG_IRQS_OFF		= 0x01,
-	TRACE_FLAG_IRQS_NOSUPPORT	= 0x02,
-	TRACE_FLAG_NEED_RESCHED		= 0x04,
-	TRACE_FLAG_HARDIRQ		= 0x08,
-	TRACE_FLAG_SOFTIRQ		= 0x10,
-	TRACE_FLAG_PREEMPT_RESCHED	= 0x20,
-	TRACE_FLAG_NMI			= 0x40,
-	TRACE_FLAG_NEED_RESCHED_LAZY	= 0x80,
-};
-
-#ifdef CONFIG_TRACE_IRQFLAGS_SUPPORT
-static inline unsigned int tracing_gen_ctx_flags(unsigned long irqflags)
-{
-	unsigned int irq_status = irqs_disabled_flags(irqflags) ?
-		TRACE_FLAG_IRQS_OFF : 0;
-	return tracing_gen_ctx_irq_test(irq_status);
-}
-static inline unsigned int tracing_gen_ctx(void)
-{
-	unsigned long irqflags;
-
-	local_save_flags(irqflags);
-	return tracing_gen_ctx_flags(irqflags);
-}
-#else
-
-static inline unsigned int tracing_gen_ctx_flags(unsigned long irqflags)
-{
-	return tracing_gen_ctx_irq_test(TRACE_FLAG_IRQS_NOSUPPORT);
-}
-static inline unsigned int tracing_gen_ctx(void)
-{
-	return tracing_gen_ctx_irq_test(TRACE_FLAG_IRQS_NOSUPPORT);
-}
-#endif
-
-static inline unsigned int tracing_gen_ctx_dec(void)
-{
-	unsigned int trace_ctx;
-
-	trace_ctx = tracing_gen_ctx();
-	/*
-	 * Subtract one from the preeption counter if preemption is enabled,
-	 * see trace_event_buffer_reserve()for details.
-	 */
-	if (IS_ENABLED(CONFIG_PREEMPTION))
-		trace_ctx--;
-	return trace_ctx;
-}
-
+void tracing_generic_entry_update(struct trace_entry *entry,
+				  unsigned short type,
+				  unsigned long flags,
+				  int pc);
 struct trace_event_file;
 
 struct ring_buffer_event *
 trace_event_buffer_lock_reserve(struct trace_buffer **current_buffer,
 				struct trace_event_file *trace_file,
 				int type, unsigned long len,
-				unsigned int trace_ctx);
+				unsigned long flags, int pc);
 
 #define TRACE_RECORD_CMDLINE	BIT(0)
 #define TRACE_RECORD_TGID	BIT(1)
@@ -295,7 +232,8 @@
 	struct ring_buffer_event	*event;
 	struct trace_event_file		*trace_file;
 	void				*entry;
-	unsigned int			trace_ctx;
+	unsigned long			flags;
+	int				pc;
 	struct pt_regs			*regs;
 };
 
diff --git a/kernel/include/linux/u64_stats_sync.h b/kernel/include/linux/u64_stats_sync.h
index 66eb968..e81856c 100644
--- a/kernel/include/linux/u64_stats_sync.h
+++ b/kernel/include/linux/u64_stats_sync.h
@@ -66,7 +66,7 @@
 #include <linux/seqlock.h>
 
 struct u64_stats_sync {
-#if BITS_PER_LONG==32 && (defined(CONFIG_SMP) || defined(CONFIG_PREEMPT_RT))
+#if BITS_PER_LONG==32 && defined(CONFIG_SMP)
 	seqcount_t	seq;
 #endif
 };
@@ -115,7 +115,7 @@
 }
 #endif
 
-#if BITS_PER_LONG == 32 && (defined(CONFIG_SMP) || defined(CONFIG_PREEMPT_RT))
+#if BITS_PER_LONG == 32 && defined(CONFIG_SMP)
 #define u64_stats_init(syncp)	seqcount_init(&(syncp)->seq)
 #else
 static inline void u64_stats_init(struct u64_stats_sync *syncp)
@@ -125,19 +125,15 @@
 
 static inline void u64_stats_update_begin(struct u64_stats_sync *syncp)
 {
-#if BITS_PER_LONG == 32 && (defined(CONFIG_SMP) || defined(CONFIG_PREEMPT_RT))
-	if (IS_ENABLED(CONFIG_PREEMPT_RT))
-		preempt_disable();
+#if BITS_PER_LONG==32 && defined(CONFIG_SMP)
 	write_seqcount_begin(&syncp->seq);
 #endif
 }
 
 static inline void u64_stats_update_end(struct u64_stats_sync *syncp)
 {
-#if BITS_PER_LONG == 32 && (defined(CONFIG_SMP) || defined(CONFIG_PREEMPT_RT))
+#if BITS_PER_LONG==32 && defined(CONFIG_SMP)
 	write_seqcount_end(&syncp->seq);
-	if (IS_ENABLED(CONFIG_PREEMPT_RT))
-		preempt_enable();
 #endif
 }
 
@@ -146,11 +142,8 @@
 {
 	unsigned long flags = 0;
 
-#if BITS_PER_LONG == 32 && (defined(CONFIG_SMP) || defined(CONFIG_PREEMPT_RT))
-	if (IS_ENABLED(CONFIG_PREEMPT_RT))
-		preempt_disable();
-	else
-		local_irq_save(flags);
+#if BITS_PER_LONG==32 && defined(CONFIG_SMP)
+	local_irq_save(flags);
 	write_seqcount_begin(&syncp->seq);
 #endif
 	return flags;
@@ -160,18 +153,15 @@
 u64_stats_update_end_irqrestore(struct u64_stats_sync *syncp,
 				unsigned long flags)
 {
-#if BITS_PER_LONG == 32 && (defined(CONFIG_SMP) || defined(CONFIG_PREEMPT_RT))
+#if BITS_PER_LONG==32 && defined(CONFIG_SMP)
 	write_seqcount_end(&syncp->seq);
-	if (IS_ENABLED(CONFIG_PREEMPT_RT))
-		preempt_enable();
-	else
-		local_irq_restore(flags);
+	local_irq_restore(flags);
 #endif
 }
 
 static inline unsigned int __u64_stats_fetch_begin(const struct u64_stats_sync *syncp)
 {
-#if BITS_PER_LONG == 32 && (defined(CONFIG_SMP) || defined(CONFIG_PREEMPT_RT))
+#if BITS_PER_LONG==32 && defined(CONFIG_SMP)
 	return read_seqcount_begin(&syncp->seq);
 #else
 	return 0;
@@ -180,7 +170,7 @@
 
 static inline unsigned int u64_stats_fetch_begin(const struct u64_stats_sync *syncp)
 {
-#if BITS_PER_LONG == 32 && (!defined(CONFIG_SMP) && !defined(CONFIG_PREEMPT_RT))
+#if BITS_PER_LONG==32 && !defined(CONFIG_SMP)
 	preempt_disable();
 #endif
 	return __u64_stats_fetch_begin(syncp);
@@ -189,7 +179,7 @@
 static inline bool __u64_stats_fetch_retry(const struct u64_stats_sync *syncp,
 					 unsigned int start)
 {
-#if BITS_PER_LONG == 32 && (defined(CONFIG_SMP) || defined(CONFIG_PREEMPT_RT))
+#if BITS_PER_LONG==32 && defined(CONFIG_SMP)
 	return read_seqcount_retry(&syncp->seq, start);
 #else
 	return false;
@@ -199,7 +189,7 @@
 static inline bool u64_stats_fetch_retry(const struct u64_stats_sync *syncp,
 					 unsigned int start)
 {
-#if BITS_PER_LONG == 32 && (!defined(CONFIG_SMP) && !defined(CONFIG_PREEMPT_RT))
+#if BITS_PER_LONG==32 && !defined(CONFIG_SMP)
 	preempt_enable();
 #endif
 	return __u64_stats_fetch_retry(syncp, start);
@@ -213,9 +203,7 @@
  */
 static inline unsigned int u64_stats_fetch_begin_irq(const struct u64_stats_sync *syncp)
 {
-#if BITS_PER_LONG == 32 && defined(CONFIG_PREEMPT_RT)
-	preempt_disable();
-#elif BITS_PER_LONG == 32 && !defined(CONFIG_SMP)
+#if BITS_PER_LONG==32 && !defined(CONFIG_SMP)
 	local_irq_disable();
 #endif
 	return __u64_stats_fetch_begin(syncp);
@@ -224,9 +212,7 @@
 static inline bool u64_stats_fetch_retry_irq(const struct u64_stats_sync *syncp,
 					     unsigned int start)
 {
-#if BITS_PER_LONG == 32 && defined(CONFIG_PREEMPT_RT)
-	preempt_enable();
-#elif BITS_PER_LONG == 32 && !defined(CONFIG_SMP)
+#if BITS_PER_LONG==32 && !defined(CONFIG_SMP)
 	local_irq_enable();
 #endif
 	return __u64_stats_fetch_retry(syncp, start);
diff --git a/kernel/include/linux/vmstat.h b/kernel/include/linux/vmstat.h
index 9a3a10e..322dcbf 100644
--- a/kernel/include/linux/vmstat.h
+++ b/kernel/include/linux/vmstat.h
@@ -63,9 +63,7 @@
  */
 static inline void __count_vm_event(enum vm_event_item item)
 {
-	preempt_disable_rt();
 	raw_cpu_inc(vm_event_states.event[item]);
-	preempt_enable_rt();
 }
 
 static inline void count_vm_event(enum vm_event_item item)
@@ -75,9 +73,7 @@
 
 static inline void __count_vm_events(enum vm_event_item item, long delta)
 {
-	preempt_disable_rt();
 	raw_cpu_add(vm_event_states.event[item], delta);
-	preempt_enable_rt();
 }
 
 static inline void count_vm_events(enum vm_event_item item, long delta)
diff --git a/kernel/include/linux/vtime.h b/kernel/include/linux/vtime.h
index 041d652..2cdeca0 100644
--- a/kernel/include/linux/vtime.h
+++ b/kernel/include/linux/vtime.h
@@ -83,46 +83,36 @@
 #endif
 
 #ifdef CONFIG_VIRT_CPU_ACCOUNTING_NATIVE
-extern void vtime_account_irq(struct task_struct *tsk, unsigned int offset);
-extern void vtime_account_softirq(struct task_struct *tsk);
-extern void vtime_account_hardirq(struct task_struct *tsk);
+extern void vtime_account_irq_enter(struct task_struct *tsk);
+static inline void vtime_account_irq_exit(struct task_struct *tsk)
+{
+	/* On hard|softirq exit we always account to hard|softirq cputime */
+	vtime_account_kernel(tsk);
+}
 extern void vtime_flush(struct task_struct *tsk);
 #else /* !CONFIG_VIRT_CPU_ACCOUNTING_NATIVE */
-static inline void vtime_account_irq(struct task_struct *tsk, unsigned int offset) { }
-static inline void vtime_account_softirq(struct task_struct *tsk) { }
-static inline void vtime_account_hardirq(struct task_struct *tsk) { }
+static inline void vtime_account_irq_enter(struct task_struct *tsk) { }
+static inline void vtime_account_irq_exit(struct task_struct *tsk) { }
 static inline void vtime_flush(struct task_struct *tsk) { }
 #endif
 
 
 #ifdef CONFIG_IRQ_TIME_ACCOUNTING
-extern void irqtime_account_irq(struct task_struct *tsk, unsigned int offset);
+extern void irqtime_account_irq(struct task_struct *tsk);
 #else
-static inline void irqtime_account_irq(struct task_struct *tsk, unsigned int offset) { }
+static inline void irqtime_account_irq(struct task_struct *tsk) { }
 #endif
 
-static inline void account_softirq_enter(struct task_struct *tsk)
+static inline void account_irq_enter_time(struct task_struct *tsk)
 {
-	vtime_account_irq(tsk, SOFTIRQ_OFFSET);
-	irqtime_account_irq(tsk, SOFTIRQ_OFFSET);
+	vtime_account_irq_enter(tsk);
+	irqtime_account_irq(tsk);
 }
 
-static inline void account_softirq_exit(struct task_struct *tsk)
+static inline void account_irq_exit_time(struct task_struct *tsk)
 {
-	vtime_account_softirq(tsk);
-	irqtime_account_irq(tsk, 0);
-}
-
-static inline void account_hardirq_enter(struct task_struct *tsk)
-{
-	vtime_account_irq(tsk, HARDIRQ_OFFSET);
-	irqtime_account_irq(tsk, HARDIRQ_OFFSET);
-}
-
-static inline void account_hardirq_exit(struct task_struct *tsk)
-{
-	vtime_account_hardirq(tsk);
-	irqtime_account_irq(tsk, 0);
+	vtime_account_irq_exit(tsk);
+	irqtime_account_irq(tsk);
 }
 
 #endif /* _LINUX_KERNEL_VTIME_H */
diff --git a/kernel/include/linux/wait.h b/kernel/include/linux/wait.h
index 328e2e5..e9966f3 100644
--- a/kernel/include/linux/wait.h
+++ b/kernel/include/linux/wait.h
@@ -10,7 +10,6 @@
 
 #include <asm/current.h>
 #include <uapi/linux/wait.h>
-#include <linux/atomic.h>
 
 typedef struct wait_queue_entry wait_queue_entry_t;
 
diff --git a/kernel/include/linux/ww_mutex.h b/kernel/include/linux/ww_mutex.h
index 3145de5..6ecf2a0 100644
--- a/kernel/include/linux/ww_mutex.h
+++ b/kernel/include/linux/ww_mutex.h
@@ -28,14 +28,6 @@
 	unsigned int is_wait_die;
 };
 
-struct ww_mutex {
-	struct mutex base;
-	struct ww_acquire_ctx *ctx;
-#ifdef CONFIG_DEBUG_MUTEXES
-	struct ww_class *ww_class;
-#endif
-};
-
 struct ww_acquire_ctx {
 	struct task_struct *task;
 	unsigned long stamp;
diff --git a/kernel/include/net/gen_stats.h b/kernel/include/net/gen_stats.h
index 163f841..1424e02 100644
--- a/kernel/include/net/gen_stats.h
+++ b/kernel/include/net/gen_stats.h
@@ -6,7 +6,6 @@
 #include <linux/socket.h>
 #include <linux/rtnetlink.h>
 #include <linux/pkt_sched.h>
-#include <net/net_seq_lock.h>
 
 /* Note: this used to be in include/uapi/linux/gen_stats.h */
 struct gnet_stats_basic_packed {
@@ -43,15 +42,15 @@
 				 spinlock_t *lock, struct gnet_dump *d,
 				 int padattr);
 
-int gnet_stats_copy_basic(net_seqlock_t *running,
+int gnet_stats_copy_basic(const seqcount_t *running,
 			  struct gnet_dump *d,
 			  struct gnet_stats_basic_cpu __percpu *cpu,
 			  struct gnet_stats_basic_packed *b);
-void __gnet_stats_copy_basic(net_seqlock_t *running,
+void __gnet_stats_copy_basic(const seqcount_t *running,
 			     struct gnet_stats_basic_packed *bstats,
 			     struct gnet_stats_basic_cpu __percpu *cpu,
 			     struct gnet_stats_basic_packed *b);
-int gnet_stats_copy_basic_hw(net_seqlock_t *running,
+int gnet_stats_copy_basic_hw(const seqcount_t *running,
 			     struct gnet_dump *d,
 			     struct gnet_stats_basic_cpu __percpu *cpu,
 			     struct gnet_stats_basic_packed *b);
@@ -71,13 +70,13 @@
 		      struct gnet_stats_basic_cpu __percpu *cpu_bstats,
 		      struct net_rate_estimator __rcu **rate_est,
 		      spinlock_t *lock,
-		      net_seqlock_t *running, struct nlattr *opt);
+		      seqcount_t *running, struct nlattr *opt);
 void gen_kill_estimator(struct net_rate_estimator __rcu **ptr);
 int gen_replace_estimator(struct gnet_stats_basic_packed *bstats,
 			  struct gnet_stats_basic_cpu __percpu *cpu_bstats,
 			  struct net_rate_estimator __rcu **ptr,
 			  spinlock_t *lock,
-			  net_seqlock_t *running, struct nlattr *opt);
+			  seqcount_t *running, struct nlattr *opt);
 bool gen_estimator_active(struct net_rate_estimator __rcu **ptr);
 bool gen_estimator_read(struct net_rate_estimator __rcu **ptr,
 			struct gnet_stats_rate_est64 *sample);
diff --git a/kernel/include/net/net_seq_lock.h b/kernel/include/net/net_seq_lock.h
deleted file mode 100644
index 67710ba..0000000
--- a/kernel/include/net/net_seq_lock.h
+++ /dev/null
@@ -1,15 +0,0 @@
-#ifndef __NET_NET_SEQ_LOCK_H__
-#define __NET_NET_SEQ_LOCK_H__
-
-#ifdef CONFIG_PREEMPT_RT
-# define net_seqlock_t			seqlock_t
-# define net_seq_begin(__r)		read_seqbegin(__r)
-# define net_seq_retry(__r, __s)	read_seqretry(__r, __s)
-
-#else
-# define net_seqlock_t			seqcount_t
-# define net_seq_begin(__r)		read_seqcount_begin(__r)
-# define net_seq_retry(__r, __s)	read_seqcount_retry(__r, __s)
-#endif
-
-#endif
diff --git a/kernel/include/net/netns/xfrm.h b/kernel/include/net/netns/xfrm.h
index 344de4b..93d74c6 100644
--- a/kernel/include/net/netns/xfrm.h
+++ b/kernel/include/net/netns/xfrm.h
@@ -74,7 +74,7 @@
 	struct dst_ops		xfrm6_dst_ops;
 #endif
 	spinlock_t		xfrm_state_lock;
-	seqcount_spinlock_t	xfrm_state_hash_generation;
+	seqcount_t		xfrm_state_hash_generation;
 
 	spinlock_t xfrm_policy_lock;
 	struct mutex xfrm_cfg_mutex;
diff --git a/kernel/include/net/sch_generic.h b/kernel/include/net/sch_generic.h
index 18965dd..882d918 100644
--- a/kernel/include/net/sch_generic.h
+++ b/kernel/include/net/sch_generic.h
@@ -10,7 +10,6 @@
 #include <linux/percpu.h>
 #include <linux/dynamic_queue_limits.h>
 #include <linux/list.h>
-#include <net/net_seq_lock.h>
 #include <linux/refcount.h>
 #include <linux/workqueue.h>
 #include <linux/mutex.h>
@@ -103,7 +102,7 @@
 	struct sk_buff_head	gso_skb ____cacheline_aligned_in_smp;
 	struct qdisc_skb_head	q;
 	struct gnet_stats_basic_packed bstats;
-	net_seqlock_t		running;
+	seqcount_t		running;
 	struct gnet_stats_queue	qstats;
 	unsigned long		state;
 	struct Qdisc            *next_sched;
@@ -146,11 +145,7 @@
 {
 	if (qdisc->flags & TCQ_F_NOLOCK)
 		return spin_is_locked(&qdisc->seqlock);
-#ifdef CONFIG_PREEMPT_RT
-	return spin_is_locked(&qdisc->running.lock) ? true : false;
-#else
 	return (raw_read_seqcount(&qdisc->running) & 1) ? true : false;
-#endif
 }
 
 static inline bool qdisc_is_percpu_stats(const struct Qdisc *q)
@@ -191,35 +186,17 @@
 	} else if (qdisc_is_running(qdisc)) {
 		return false;
 	}
-#ifdef CONFIG_PREEMPT_RT
-	if (spin_trylock(&qdisc->running.lock)) {
-		seqcount_t *s = &qdisc->running.seqcount.seqcount;
-		/*
-		 * Variant of write_seqcount_t_begin() telling lockdep that a
-		 * trylock was attempted.
-		 */
-		raw_write_seqcount_t_begin(s);
-		seqcount_acquire(&s->dep_map, 0, 1, _RET_IP_);
-		return true;
-	}
-	return false;
-#else
 	/* Variant of write_seqcount_begin() telling lockdep a trylock
 	 * was attempted.
 	 */
 	raw_write_seqcount_begin(&qdisc->running);
 	seqcount_acquire(&qdisc->running.dep_map, 0, 1, _RET_IP_);
 	return true;
-#endif
 }
 
 static inline void qdisc_run_end(struct Qdisc *qdisc)
 {
-#ifdef CONFIG_PREEMPT_RT
-	write_sequnlock(&qdisc->running);
-#else
 	write_seqcount_end(&qdisc->running);
-#endif
 	if (qdisc->flags & TCQ_F_NOLOCK) {
 		spin_unlock(&qdisc->seqlock);
 
@@ -611,7 +588,7 @@
 	return qdisc_lock(root);
 }
 
-static inline net_seqlock_t *qdisc_root_sleeping_running(const struct Qdisc *qdisc)
+static inline seqcount_t *qdisc_root_sleeping_running(const struct Qdisc *qdisc)
 {
 	struct Qdisc *root = qdisc_root_sleeping(qdisc);
 
diff --git a/kernel/include/net/sock.h b/kernel/include/net/sock.h
index d6cbb2b..c604052 100644
--- a/kernel/include/net/sock.h
+++ b/kernel/include/net/sock.h
@@ -478,7 +478,7 @@
 	u32			sk_ack_backlog;
 	u32			sk_max_ack_backlog;
 	kuid_t			sk_uid;
-#if IS_ENABLED(CONFIG_DEBUG_SPINLOCK) || IS_ENABLED(CONFIG_DEBUG_LOCK_ALLOC) || IS_ENABLED(CONFIG_PREEMPT_RT)
+#if IS_ENABLED(CONFIG_DEBUG_SPINLOCK) || IS_ENABLED(CONFIG_DEBUG_LOCK_ALLOC)
 	spinlock_t		sk_peer_lock;
 #else
 	/* sk_peer_lock is in the ANDROID_KABI_RESERVE(1) field below */
@@ -526,7 +526,7 @@
 #endif
 	struct rcu_head		sk_rcu;
 
-#if IS_ENABLED(CONFIG_DEBUG_SPINLOCK) || IS_ENABLED(CONFIG_DEBUG_LOCK_ALLOC) || IS_ENABLED(CONFIG_PREEMPT_RT)
+#if IS_ENABLED(CONFIG_DEBUG_SPINLOCK) || IS_ENABLED(CONFIG_DEBUG_LOCK_ALLOC)
 	ANDROID_KABI_RESERVE(1);
 #else
 	ANDROID_KABI_USE(1, spinlock_t sk_peer_lock);
diff --git a/kernel/include/soc/rockchip/rockchip_amp.h b/kernel/include/soc/rockchip/rockchip_amp.h
new file mode 100644
index 0000000..1c37e8b
--- /dev/null
+++ b/kernel/include/soc/rockchip/rockchip_amp.h
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Rockchip AMP support.
+ *
+ * Copyright (c) 2023 Rockchip Electronics Co. Ltd.
+ * Author: Tony Xie <tony.xie@rock-chips.com>
+ */
+
+#ifndef _ROCKCHIP_AMP
+#define _ROCKCHIP_AMP
+
+#if IS_REACHABLE(CONFIG_ROCKCHIP_AMP)
+void rockchip_amp_get_gic_info(void);
+int rockchip_amp_check_amp_irq(u32 irq);
+u32 rockchip_amp_get_irq_prio(u32 irq);
+u32 rockchip_amp_get_irq_cpumask(u32 irq);
+#else
+#include <linux/irqchip/arm-gic-common.h>
+
+static inline void rockchip_amp_get_gic_info(void)
+{
+}
+
+static inline int rockchip_amp_check_amp_irq(u32 irq)
+{
+	return 0;
+}
+
+static inline u32 rockchip_amp_get_irq_prio(u32 irq)
+{
+	return GICD_INT_DEF_PRI;
+}
+
+static inline u32 rockchip_amp_get_irq_cpumask(u32 irq)
+{
+	return 0;
+}
+#endif /* CONFIG_ROCKCHIP_AMP */
+#endif /* _ROCKCHIP_AMP */
diff --git a/kernel/include/soc/rockchip/rockchip_dmc.h b/kernel/include/soc/rockchip/rockchip_dmc.h
index 882aa26..44a976b 100644
--- a/kernel/include/soc/rockchip/rockchip_dmc.h
+++ b/kernel/include/soc/rockchip/rockchip_dmc.h
@@ -51,6 +51,7 @@
 	struct freq_map_table *vop_frame_bw_tbl;
 	struct rl_map_table *vop_pn_rl_tbl;
 	struct delayed_work msch_rl_work;
+	unsigned long vop_4k_rate;
 	unsigned long vop_req_rate;
 	unsigned int read_latency;
 	unsigned int auto_freq_en;
@@ -62,6 +63,7 @@
 	unsigned int line_bw_mbyte;
 	unsigned int frame_bw_mbyte;
 	unsigned int plane_num;
+	unsigned int plane_num_4k;
 };
 
 #if IS_REACHABLE(CONFIG_ARM_ROCKCHIP_DMC_DEVFREQ)
diff --git a/kernel/include/soc/rockchip/rockchip_iommu.h b/kernel/include/soc/rockchip/rockchip_iommu.h
index 28af038..191a2fd 100644
--- a/kernel/include/soc/rockchip/rockchip_iommu.h
+++ b/kernel/include/soc/rockchip/rockchip_iommu.h
@@ -7,7 +7,7 @@
 
 struct device;
 
-#if IS_ENABLED(CONFIG_ROCKCHIP_IOMMU)
+#if IS_REACHABLE(CONFIG_ROCKCHIP_IOMMU)
 int rockchip_iommu_enable(struct device *dev);
 int rockchip_iommu_disable(struct device *dev);
 int rockchip_pagefault_done(struct device *master_dev);
diff --git a/kernel/include/soc/rockchip/rockchip_opp_select.h b/kernel/include/soc/rockchip/rockchip_opp_select.h
index 2277fbe..60f01b4 100644
--- a/kernel/include/soc/rockchip/rockchip_opp_select.h
+++ b/kernel/include/soc/rockchip/rockchip_opp_select.h
@@ -88,7 +88,7 @@
 void rockchip_pvtpll_calibrate_opp(struct rockchip_opp_info *info);
 void rockchip_pvtpll_add_length(struct rockchip_opp_info *info);
 void rockchip_of_get_pvtm_sel(struct device *dev, struct device_node *np,
-			      char *reg_name, int process,
+			      char *reg_name, int bin, int process,
 			      int *volt_sel, int *scale_sel);
 void rockchip_of_get_bin_sel(struct device *dev, struct device_node *np,
 			     int bin, int *scale_sel);
@@ -102,11 +102,16 @@
 			       char *porp_name, struct volt_rm_table **table);
 void rockchip_get_opp_data(const struct of_device_id *matches,
 			   struct rockchip_opp_info *info);
+int rockchip_get_soc_info(struct device *dev, struct device_node *np, int *bin,
+			  int *process);
 void rockchip_get_scale_volt_sel(struct device *dev, char *lkg_name,
 				 char *reg_name, int bin, int process,
 				 int *scale, int *volt_sel);
 struct opp_table *rockchip_set_opp_prop_name(struct device *dev, int process,
 					     int volt_sel);
+struct opp_table *rockchip_set_opp_supported_hw(struct device *dev,
+						struct device_node *np,
+						int bin, int volt_sel);
 int rockchip_adjust_power_scale(struct device *dev, int scale);
 int rockchip_get_read_margin(struct device *dev,
 			     struct rockchip_opp_info *opp_info,
@@ -149,7 +154,7 @@
 
 static inline void rockchip_of_get_pvtm_sel(struct device *dev,
 					    struct device_node *np,
-					    char *reg_name, int process,
+					    char *reg_name, int bin, int process,
 					    int *volt_sel, int *scale_sel)
 {
 }
@@ -191,6 +196,12 @@
 					 struct rockchip_opp_info *info)
 {
 }
+static inline int rockchip_get_soc_info(struct device *dev,
+					struct device_node *np, int *bin,
+					int *process)
+{
+	return -EOPNOTSUPP;
+}
 
 static inline void rockchip_get_scale_volt_sel(struct device *dev,
 					       char *lkg_name, char *reg_name,
@@ -206,6 +217,13 @@
 	return ERR_PTR(-EOPNOTSUPP);
 }
 
+static inline struct opp_table *rockchip_set_opp_supported_hw(struct device *dev,
+							      struct device_node *np,
+							      int bin, int volt_sel)
+{
+	return ERR_PTR(-EOPNOTSUPP);
+}
+
 static inline int rockchip_adjust_power_scale(struct device *dev, int scale)
 {
 	return -EOPNOTSUPP;
diff --git a/kernel/include/soc/rockchip/rockchip_rockit.h b/kernel/include/soc/rockchip/rockchip_rockit.h
index 212351b..9d3a34b 100644
--- a/kernel/include/soc/rockchip/rockchip_rockit.h
+++ b/kernel/include/soc/rockchip/rockchip_rockit.h
@@ -14,6 +14,13 @@
 
 #define ROCKIT_VICAP_NUM_MAX	6
 
+enum {
+	RKISP_NORMAL_ONLINE,
+	RKISP_NORMAL_OFFLINE,
+	RKISP_FAST_ONLINE,
+	RKISP_FAST_OFFLINE,
+};
+
 enum function_cmd {
 	ROCKIT_BUF_QUE,
 	ROCKIT_MPIBUF_DONE
@@ -116,6 +123,7 @@
 
 void *rkisp_rockit_function_register(void *function, int cmd);
 int rkisp_rockit_get_ispdev(char **name);
+int rkisp_rockit_get_isp_mode(const char *name);
 int rkisp_rockit_buf_queue(struct rockit_cfg *input_rockit_cfg);
 int rkisp_rockit_pause_stream(struct rockit_cfg *input_rockit_cfg);
 int rkisp_rockit_resume_stream(struct rockit_cfg *input_rockit_cfg);
@@ -137,6 +145,7 @@
 
 static inline void *rkisp_rockit_function_register(void *function, int cmd) { return NULL; }
 static inline int rkisp_rockit_get_ispdev(char **name) { return -EINVAL; }
+static inline int rkisp_rockit_get_isp_mode(const char *name) { return -EINVAL; }
 static inline int rkisp_rockit_buf_queue(struct rockit_cfg *input_rockit_cfg)
 {
 	return -EINVAL;
diff --git a/kernel/include/soc/rockchip/rockchip_sip.h b/kernel/include/soc/rockchip/rockchip_sip.h
index 4afba01..1ae20d8 100644
--- a/kernel/include/soc/rockchip/rockchip_sip.h
+++ b/kernel/include/soc/rockchip/rockchip_sip.h
@@ -20,6 +20,7 @@
 #define ROCKCHIP_SIP_CONFIG_DRAM_SET_MSCH_RL	0x0a
 #define ROCKCHIP_SIP_CONFIG_DRAM_DEBUG		0x0b
 #define ROCKCHIP_SIP_CONFIG_MCU_START		0x0c
+#define ROCKCHIP_SIP_CONFIG_DRAM_ECC		0x0d
 #define ROCKCHIP_SIP_CONFIG_DRAM_GET_FREQ_INFO	0x0e
 #define ROCKCHIP_SIP_CONFIG_DRAM_ADDRMAP_GET	0x10
 
diff --git a/kernel/include/trace/events/sched.h b/kernel/include/trace/events/sched.h
index 361a635..bcd7f1f 100644
--- a/kernel/include/trace/events/sched.h
+++ b/kernel/include/trace/events/sched.h
@@ -677,18 +677,6 @@
 	TP_PROTO(struct rq *rq, int change),
 	TP_ARGS(rq, change));
 
-DECLARE_TRACE(sched_migrate_disable_tp,
-	      TP_PROTO(struct task_struct *p),
-	      TP_ARGS(p));
-
-DECLARE_TRACE(sched_migrate_enable_tp,
-	      TP_PROTO(struct task_struct *p),
-	      TP_ARGS(p));
-
-DECLARE_TRACE(sched_migrate_pull_tp,
-	      TP_PROTO(struct task_struct *p),
-	      TP_ARGS(p));
-
 #endif /* _TRACE_SCHED_H */
 
 /* This part must be outside protection */
diff --git a/kernel/include/trace/hooks/dtask.h b/kernel/include/trace/hooks/dtask.h
index e5ab47b..956e842 100644
--- a/kernel/include/trace/hooks/dtask.h
+++ b/kernel/include/trace/hooks/dtask.h
@@ -56,14 +56,12 @@
 DECLARE_HOOK(android_vh_sched_show_task,
 	TP_PROTO(struct task_struct *task),
 	TP_ARGS(task));
-#ifndef CONFIG_PREEMPT_RT
 DECLARE_HOOK(android_vh_alter_mutex_list_add,
 	TP_PROTO(struct mutex *lock,
 		struct mutex_waiter *waiter,
 		struct list_head *list,
 		bool *already_on_list),
 	TP_ARGS(lock, waiter, list, already_on_list));
-#endif
 DECLARE_HOOK(android_vh_mutex_unlock_slowpath,
 	TP_PROTO(struct mutex *lock),
 	TP_ARGS(lock));
diff --git a/kernel/include/uapi/drm/rockchip_drm.h b/kernel/include/uapi/drm/rockchip_drm.h
index 59f842d..2ac71b4 100644
--- a/kernel/include/uapi/drm/rockchip_drm.h
+++ b/kernel/include/uapi/drm/rockchip_drm.h
@@ -91,6 +91,7 @@
 	ROCKCHIP_DRM_CRTC_FEATURE_ALPHA_SCALE,
 	ROCKCHIP_DRM_CRTC_FEATURE_HDR10,
 	ROCKCHIP_DRM_CRTC_FEATURE_NEXT_HDR,
+	ROCKCHIP_DRM_CRTC_FEATURE_VIVID_HDR,
 };
 
 enum rockchip_plane_feture {
diff --git a/kernel/include/uapi/linux/media-bus-format.h b/kernel/include/uapi/linux/media-bus-format.h
index 096f891..f16dff5 100644
--- a/kernel/include/uapi/linux/media-bus-format.h
+++ b/kernel/include/uapi/linux/media-bus-format.h
@@ -67,6 +67,7 @@
 #define MEDIA_BUS_FMT_BGR888_DUMMY_4X8		0x1020
 #define MEDIA_BUS_FMT_RGB101010_1X7X5_SPWG	0x1022
 #define MEDIA_BUS_FMT_RGB101010_1X7X5_JEIDA	0x1023
+#define MEDIA_BUS_FMT_RGB666_3X6		0x1100
 
 /* YUV (including grey) - next is	0x202e */
 #define MEDIA_BUS_FMT_Y8_1X8			0x2001
diff --git a/kernel/include/uapi/linux/rk-camera-module.h b/kernel/include/uapi/linux/rk-camera-module.h
index fd04161..7a825af 100644
--- a/kernel/include/uapi/linux/rk-camera-module.h
+++ b/kernel/include/uapi/linux/rk-camera-module.h
@@ -58,6 +58,7 @@
 							 RKMODULE_CAMERA_BT656_CHANNEL_3)
 
 #define DPHY_MAX_LANE					4
+#define RKMODULE_MULTI_DEV_NUM				4
 
 #define RKMODULE_GET_MODULE_INFO	\
 	_IOR('V', BASE_VIDIOC_PRIVATE + 0, struct rkmodule_inf)
@@ -175,6 +176,12 @@
 
 #define RKMODULE_SET_GROUP_ID       \
 	_IOW('V', BASE_VIDIOC_PRIVATE + 38, __u32)
+
+#define RKMODULE_GET_CAPTURE_MODE  \
+	_IOR('V', BASE_VIDIOC_PRIVATE + 39, struct rkmodule_capture_info)
+
+#define RKMODULE_SET_CAPTURE_MODE  \
+	_IOW('V', BASE_VIDIOC_PRIVATE + 40, struct rkmodule_capture_info)
 
 struct rkmodule_i2cdev_info {
 	__u8 slave_addr;
@@ -319,6 +326,7 @@
 	__u32 dccmap_height;
 	__u32 dcc_mode;
 	__u32 dcc_dir;
+	__u32 pd_offset;
 	__u16 gainmap[RKMODULE_PADF_GAINMAP_LEN];
 	__u16 dccmap[RKMODULE_PDAF_DCCMAP_LEN];
 } __attribute__ ((packed));
@@ -766,4 +774,39 @@
 	struct rkmodule_sensor_fmt sensor_fmt[RKMODULE_MAX_SENSOR_NUM];
 };
 
+enum rkmodule_capture_mode {
+	RKMODULE_CAPTURE_MODE_NONE = 0,
+	RKMODULE_MULTI_DEV_COMBINE_ONE,
+	RKMODULE_ONE_CH_TO_MULTI_ISP,
+	RKMODULE_MULTI_CH_TO_MULTI_ISP,
+	RKMODULE_MULTI_CH_COMBINE_SQUARE,
+};
+
+struct rkmodule_multi_dev_info {
+	__u32 dev_idx[RKMODULE_MULTI_DEV_NUM];
+	__u32 combine_idx[RKMODULE_MULTI_DEV_NUM];
+	__u32 pixel_offset;
+	__u32 dev_num;
+	__u32 reserved[8];
+};
+
+struct rkmodule_one_to_multi_info {
+	__u32 isp_num;
+	__u32 frame_pattern[RKMODULE_MULTI_DEV_NUM];
+};
+
+struct rkmodule_multi_combine_info {
+	__u32 combine_num;
+	__u32 combine_index[RKMODULE_MULTI_DEV_NUM];
+};
+
+struct rkmodule_capture_info {
+	__u32 mode;
+	union {
+		struct rkmodule_multi_dev_info multi_dev;
+		struct rkmodule_one_to_multi_info one_to_multi;
+		struct rkmodule_multi_combine_info multi_combine_info;
+	};
+};
+
 #endif /* _UAPI_RKMODULE_CAMERA_H */
diff --git a/kernel/include/uapi/linux/rk-isp2-config.h b/kernel/include/uapi/linux/rk-isp2-config.h
index 242fb5a..9de8ac7 100644
--- a/kernel/include/uapi/linux/rk-isp2-config.h
+++ b/kernel/include/uapi/linux/rk-isp2-config.h
@@ -11,7 +11,7 @@
 #include <linux/types.h>
 #include <linux/v4l2-controls.h>
 
-#define RKISP_API_VERSION		KERNEL_VERSION(2, 2, 1)
+#define RKISP_API_VERSION		KERNEL_VERSION(2, 3, 0)
 
 /****************ISP SUBDEV IOCTL*****************************/
 
diff --git a/kernel/include/uapi/linux/rkcif-config.h b/kernel/include/uapi/linux/rkcif-config.h
index eed9473..a65b5b1 100644
--- a/kernel/include/uapi/linux/rkcif-config.h
+++ b/kernel/include/uapi/linux/rkcif-config.h
@@ -9,7 +9,9 @@
 #include <linux/types.h>
 #include <linux/v4l2-controls.h>
 
-#define RKCIF_API_VERSION		KERNEL_VERSION(0, 1, 0xa)
+#define RKCIF_MAX_CSI_NUM		4
+
+#define RKCIF_API_VERSION		KERNEL_VERSION(0, 2, 0)
 
 #define V4L2_EVENT_RESET_DEV		0X1001
 
@@ -32,7 +34,7 @@
 	_IOW('V', BASE_VIDIOC_PRIVATE + 6, int)
 
 #define RKCIF_CMD_SET_CSI_IDX \
-	_IOW('V', BASE_VIDIOC_PRIVATE + 7, unsigned int)
+	_IOW('V', BASE_VIDIOC_PRIVATE + 7, struct rkcif_csi_info)
 
 /* cif memory mode
  * 0: raw12/raw10/raw8 8bit memory compact
@@ -71,4 +73,10 @@
 	int fps;
 };
 
+struct rkcif_csi_info {
+	int csi_num;
+	int csi_idx[RKCIF_MAX_CSI_NUM];
+	int dphy_vendor[RKCIF_MAX_CSI_NUM];
+};
+
 #endif
diff --git a/kernel/init/Kconfig b/kernel/init/Kconfig
index 76865c3..2c92f94 100644
--- a/kernel/init/Kconfig
+++ b/kernel/init/Kconfig
@@ -880,7 +880,7 @@
 	bool "Memory placement aware NUMA scheduler"
 	depends on ARCH_SUPPORTS_NUMA_BALANCING
 	depends on !ARCH_WANT_NUMA_VARIABLE_LOCALITY
-	depends on SMP && NUMA && MIGRATION && !PREEMPT_RT
+	depends on SMP && NUMA && MIGRATION
 	help
 	  This option adds support for automatic NUMA aware memory/task placement.
 	  The mechanism is quite primitive and is based on migrating memory when
@@ -987,7 +987,6 @@
 config RT_GROUP_SCHED
 	bool "Group scheduling for SCHED_RR/FIFO"
 	depends on CGROUP_SCHED
-	depends on !PREEMPT_RT
 	default n
 	help
 	  This feature lets you explicitly allocate real CPU bandwidth
@@ -1961,7 +1960,6 @@
 
 config SLAB
 	bool "SLAB"
-	depends on !PREEMPT_RT
 	select HAVE_HARDENED_USERCOPY_ALLOCATOR
 	help
 	  The regular slab allocator that is established and known to work
@@ -1982,7 +1980,6 @@
 config SLOB
 	depends on EXPERT
 	bool "SLOB (Simple Allocator)"
-	depends on !PREEMPT_RT
 	help
 	   SLOB replaces the stock allocator with a drastically simpler
 	   allocator. SLOB is generally more space efficient but
@@ -2049,7 +2046,7 @@
 
 config SLUB_CPU_PARTIAL
 	default y
-	depends on SLUB && SMP && !PREEMPT_RT
+	depends on SLUB && SMP
 	bool "SLUB per cpu partial cache"
 	help
 	  Per cpu partial caches accelerate objects allocation and freeing
diff --git a/kernel/kernel/Kconfig.locks b/kernel/kernel/Kconfig.locks
index 4198f02..3de8fd1 100644
--- a/kernel/kernel/Kconfig.locks
+++ b/kernel/kernel/Kconfig.locks
@@ -251,7 +251,7 @@
 
 config QUEUED_RWLOCKS
 	def_bool y if ARCH_USE_QUEUED_RWLOCKS
-	depends on SMP && !PREEMPT_RT
+	depends on SMP
 
 config ARCH_HAS_MMIOWB
 	bool
diff --git a/kernel/kernel/Kconfig.preempt b/kernel/kernel/Kconfig.preempt
index b5cd1e2..bf82259 100644
--- a/kernel/kernel/Kconfig.preempt
+++ b/kernel/kernel/Kconfig.preempt
@@ -1,11 +1,5 @@
 # SPDX-License-Identifier: GPL-2.0-only
 
-config HAVE_PREEMPT_LAZY
-	bool
-
-config PREEMPT_LAZY
-	def_bool y if HAVE_PREEMPT_LAZY && PREEMPT_RT
-
 choice
 	prompt "Preemption Model"
 	default PREEMPT_NONE
@@ -65,7 +59,6 @@
 	bool "Fully Preemptible Kernel (Real-Time)"
 	depends on EXPERT && ARCH_SUPPORTS_RT
 	select PREEMPTION
-	select RT_MUTEXES
 	help
 	  This option turns the kernel into a real-time kernel by replacing
 	  various locking primitives (spinlocks, rwlocks, etc.) with
diff --git a/kernel/kernel/cgroup/cpuset.c b/kernel/kernel/cgroup/cpuset.c
index de768d5..6820a0c 100644
--- a/kernel/kernel/cgroup/cpuset.c
+++ b/kernel/kernel/cgroup/cpuset.c
@@ -339,7 +339,7 @@
  */
 
 static DEFINE_MUTEX(cpuset_mutex);
-static DEFINE_RAW_SPINLOCK(callback_lock);
+static DEFINE_SPINLOCK(callback_lock);
 
 static struct workqueue_struct *cpuset_migrate_mm_wq;
 
@@ -1315,7 +1315,7 @@
 	 * Newly added CPUs will be removed from effective_cpus and
 	 * newly deleted ones will be added back to effective_cpus.
 	 */
-	raw_spin_lock_irq(&callback_lock);
+	spin_lock_irq(&callback_lock);
 	if (adding) {
 		cpumask_or(parent->subparts_cpus,
 			   parent->subparts_cpus, tmp->addmask);
@@ -1337,7 +1337,7 @@
 
 	if (cpuset->partition_root_state != new_prs)
 		cpuset->partition_root_state = new_prs;
-	raw_spin_unlock_irq(&callback_lock);
+	spin_unlock_irq(&callback_lock);
 
 	return cmd == partcmd_update;
 }
@@ -1440,7 +1440,7 @@
 			continue;
 		rcu_read_unlock();
 
-		raw_spin_lock_irq(&callback_lock);
+		spin_lock_irq(&callback_lock);
 
 		cpumask_copy(cp->effective_cpus, tmp->new_cpus);
 		if (cp->nr_subparts_cpus && (new_prs != PRS_ENABLED)) {
@@ -1474,7 +1474,7 @@
 		if (new_prs != cp->partition_root_state)
 			cp->partition_root_state = new_prs;
 
-		raw_spin_unlock_irq(&callback_lock);
+		spin_unlock_irq(&callback_lock);
 
 		WARN_ON(!is_in_v2_mode() &&
 			!cpumask_equal(cp->cpus_allowed, cp->effective_cpus));
@@ -1603,7 +1603,7 @@
 			return -EINVAL;
 	}
 
-	raw_spin_lock_irq(&callback_lock);
+	spin_lock_irq(&callback_lock);
 	cpumask_copy(cs->cpus_allowed, trialcs->cpus_allowed);
 	cpumask_copy(cs->cpus_requested, trialcs->cpus_requested);
 
@@ -1614,7 +1614,7 @@
 		cpumask_and(cs->subparts_cpus, cs->subparts_cpus, cs->cpus_allowed);
 		cs->nr_subparts_cpus = cpumask_weight(cs->subparts_cpus);
 	}
-	raw_spin_unlock_irq(&callback_lock);
+	spin_unlock_irq(&callback_lock);
 
 	update_cpumasks_hier(cs, &tmp);
 
@@ -1808,9 +1808,9 @@
 			continue;
 		rcu_read_unlock();
 
-		raw_spin_lock_irq(&callback_lock);
+		spin_lock_irq(&callback_lock);
 		cp->effective_mems = *new_mems;
-		raw_spin_unlock_irq(&callback_lock);
+		spin_unlock_irq(&callback_lock);
 
 		WARN_ON(!is_in_v2_mode() &&
 			!nodes_equal(cp->mems_allowed, cp->effective_mems));
@@ -1878,9 +1878,9 @@
 	if (retval < 0)
 		goto done;
 
-	raw_spin_lock_irq(&callback_lock);
+	spin_lock_irq(&callback_lock);
 	cs->mems_allowed = trialcs->mems_allowed;
-	raw_spin_unlock_irq(&callback_lock);
+	spin_unlock_irq(&callback_lock);
 
 	/* use trialcs->mems_allowed as a temp variable */
 	update_nodemasks_hier(cs, &trialcs->mems_allowed);
@@ -1971,9 +1971,9 @@
 	spread_flag_changed = ((is_spread_slab(cs) != is_spread_slab(trialcs))
 			|| (is_spread_page(cs) != is_spread_page(trialcs)));
 
-	raw_spin_lock_irq(&callback_lock);
+	spin_lock_irq(&callback_lock);
 	cs->flags = trialcs->flags;
-	raw_spin_unlock_irq(&callback_lock);
+	spin_unlock_irq(&callback_lock);
 
 	if (!cpumask_empty(trialcs->cpus_allowed) && balance_flag_changed)
 		rebuild_sched_domains_locked();
@@ -2059,9 +2059,9 @@
 	rebuild_sched_domains_locked();
 out:
 	if (!err) {
-		raw_spin_lock_irq(&callback_lock);
+		spin_lock_irq(&callback_lock);
 		cs->partition_root_state = new_prs;
-		raw_spin_unlock_irq(&callback_lock);
+		spin_unlock_irq(&callback_lock);
 	}
 
 	free_cpumasks(NULL, &tmpmask);
@@ -2476,7 +2476,7 @@
 	cpuset_filetype_t type = seq_cft(sf)->private;
 	int ret = 0;
 
-	raw_spin_lock_irq(&callback_lock);
+	spin_lock_irq(&callback_lock);
 
 	switch (type) {
 	case FILE_CPULIST:
@@ -2498,7 +2498,7 @@
 		ret = -EINVAL;
 	}
 
-	raw_spin_unlock_irq(&callback_lock);
+	spin_unlock_irq(&callback_lock);
 	return ret;
 }
 
@@ -2811,14 +2811,14 @@
 
 	cpuset_inc();
 
-	raw_spin_lock_irq(&callback_lock);
+	spin_lock_irq(&callback_lock);
 	if (is_in_v2_mode()) {
 		cpumask_copy(cs->effective_cpus, parent->effective_cpus);
 		cs->effective_mems = parent->effective_mems;
 		cs->use_parent_ecpus = true;
 		parent->child_ecpus_count++;
 	}
-	raw_spin_unlock_irq(&callback_lock);
+	spin_unlock_irq(&callback_lock);
 
 	if (!test_bit(CGRP_CPUSET_CLONE_CHILDREN, &css->cgroup->flags))
 		goto out_unlock;
@@ -2845,13 +2845,13 @@
 	}
 	rcu_read_unlock();
 
-	raw_spin_lock_irq(&callback_lock);
+	spin_lock_irq(&callback_lock);
 	cs->mems_allowed = parent->mems_allowed;
 	cs->effective_mems = parent->mems_allowed;
 	cpumask_copy(cs->cpus_allowed, parent->cpus_allowed);
 	cpumask_copy(cs->cpus_requested, parent->cpus_requested);
 	cpumask_copy(cs->effective_cpus, parent->cpus_allowed);
-	raw_spin_unlock_irq(&callback_lock);
+	spin_unlock_irq(&callback_lock);
 out_unlock:
 	mutex_unlock(&cpuset_mutex);
 	put_online_cpus();
@@ -2907,7 +2907,7 @@
 static void cpuset_bind(struct cgroup_subsys_state *root_css)
 {
 	mutex_lock(&cpuset_mutex);
-	raw_spin_lock_irq(&callback_lock);
+	spin_lock_irq(&callback_lock);
 
 	if (is_in_v2_mode()) {
 		cpumask_copy(top_cpuset.cpus_allowed, cpu_possible_mask);
@@ -2918,7 +2918,7 @@
 		top_cpuset.mems_allowed = top_cpuset.effective_mems;
 	}
 
-	raw_spin_unlock_irq(&callback_lock);
+	spin_unlock_irq(&callback_lock);
 	mutex_unlock(&cpuset_mutex);
 }
 
@@ -3018,12 +3018,12 @@
 {
 	bool is_empty;
 
-	raw_spin_lock_irq(&callback_lock);
+	spin_lock_irq(&callback_lock);
 	cpumask_copy(cs->cpus_allowed, new_cpus);
 	cpumask_copy(cs->effective_cpus, new_cpus);
 	cs->mems_allowed = *new_mems;
 	cs->effective_mems = *new_mems;
-	raw_spin_unlock_irq(&callback_lock);
+	spin_unlock_irq(&callback_lock);
 
 	/*
 	 * Don't call update_tasks_cpumask() if the cpuset becomes empty,
@@ -3060,10 +3060,10 @@
 	if (nodes_empty(*new_mems))
 		*new_mems = parent_cs(cs)->effective_mems;
 
-	raw_spin_lock_irq(&callback_lock);
+	spin_lock_irq(&callback_lock);
 	cpumask_copy(cs->effective_cpus, new_cpus);
 	cs->effective_mems = *new_mems;
-	raw_spin_unlock_irq(&callback_lock);
+	spin_unlock_irq(&callback_lock);
 
 	if (cpus_updated)
 		update_tasks_cpumask(cs);
@@ -3130,10 +3130,10 @@
 	if (is_partition_root(cs) && (cpumask_empty(&new_cpus) ||
 	   (parent->partition_root_state == PRS_ERROR))) {
 		if (cs->nr_subparts_cpus) {
-			raw_spin_lock_irq(&callback_lock);
+			spin_lock_irq(&callback_lock);
 			cs->nr_subparts_cpus = 0;
 			cpumask_clear(cs->subparts_cpus);
-			raw_spin_unlock_irq(&callback_lock);
+			spin_unlock_irq(&callback_lock);
 			compute_effective_cpumask(&new_cpus, cs, parent);
 		}
 
@@ -3147,9 +3147,9 @@
 		     cpumask_empty(&new_cpus)) {
 			update_parent_subparts_cpumask(cs, partcmd_disable,
 						       NULL, tmp);
-			raw_spin_lock_irq(&callback_lock);
+			spin_lock_irq(&callback_lock);
 			cs->partition_root_state = PRS_ERROR;
-			raw_spin_unlock_irq(&callback_lock);
+			spin_unlock_irq(&callback_lock);
 		}
 		cpuset_force_rebuild();
 	}
@@ -3229,7 +3229,7 @@
 
 	/* synchronize cpus_allowed to cpu_active_mask */
 	if (cpus_updated) {
-		raw_spin_lock_irq(&callback_lock);
+		spin_lock_irq(&callback_lock);
 		if (!on_dfl)
 			cpumask_copy(top_cpuset.cpus_allowed, &new_cpus);
 		/*
@@ -3249,17 +3249,17 @@
 			}
 		}
 		cpumask_copy(top_cpuset.effective_cpus, &new_cpus);
-		raw_spin_unlock_irq(&callback_lock);
+		spin_unlock_irq(&callback_lock);
 		/* we don't mess with cpumasks of tasks in top_cpuset */
 	}
 
 	/* synchronize mems_allowed to N_MEMORY */
 	if (mems_updated) {
-		raw_spin_lock_irq(&callback_lock);
+		spin_lock_irq(&callback_lock);
 		if (!on_dfl)
 			top_cpuset.mems_allowed = new_mems;
 		top_cpuset.effective_mems = new_mems;
-		raw_spin_unlock_irq(&callback_lock);
+		spin_unlock_irq(&callback_lock);
 		update_tasks_nodemask(&top_cpuset);
 	}
 
@@ -3368,11 +3368,11 @@
 {
 	unsigned long flags;
 
-	raw_spin_lock_irqsave(&callback_lock, flags);
+	spin_lock_irqsave(&callback_lock, flags);
 	rcu_read_lock();
 	guarantee_online_cpus(tsk, pmask);
 	rcu_read_unlock();
-	raw_spin_unlock_irqrestore(&callback_lock, flags);
+	spin_unlock_irqrestore(&callback_lock, flags);
 }
 EXPORT_SYMBOL_GPL(cpuset_cpus_allowed);
 /**
@@ -3441,11 +3441,11 @@
 	nodemask_t mask;
 	unsigned long flags;
 
-	raw_spin_lock_irqsave(&callback_lock, flags);
+	spin_lock_irqsave(&callback_lock, flags);
 	rcu_read_lock();
 	guarantee_online_mems(task_cs(tsk), &mask);
 	rcu_read_unlock();
-	raw_spin_unlock_irqrestore(&callback_lock, flags);
+	spin_unlock_irqrestore(&callback_lock, flags);
 
 	return mask;
 }
@@ -3537,14 +3537,14 @@
 		return true;
 
 	/* Not hardwall and node outside mems_allowed: scan up cpusets */
-	raw_spin_lock_irqsave(&callback_lock, flags);
+	spin_lock_irqsave(&callback_lock, flags);
 
 	rcu_read_lock();
 	cs = nearest_hardwall_ancestor(task_cs(current));
 	allowed = node_isset(node, cs->mems_allowed);
 	rcu_read_unlock();
 
-	raw_spin_unlock_irqrestore(&callback_lock, flags);
+	spin_unlock_irqrestore(&callback_lock, flags);
 	return allowed;
 }
 
diff --git a/kernel/kernel/cgroup/rstat.c b/kernel/kernel/cgroup/rstat.c
index 753dc34..89ca9b6 100644
--- a/kernel/kernel/cgroup/rstat.c
+++ b/kernel/kernel/cgroup/rstat.c
@@ -149,9 +149,8 @@
 		raw_spinlock_t *cpu_lock = per_cpu_ptr(&cgroup_rstat_cpu_lock,
 						       cpu);
 		struct cgroup *pos = NULL;
-		unsigned long flags;
 
-		raw_spin_lock_irqsave(cpu_lock, flags);
+		raw_spin_lock(cpu_lock);
 		while ((pos = cgroup_rstat_cpu_pop_updated(pos, cgrp, cpu))) {
 			struct cgroup_subsys_state *css;
 
@@ -163,7 +162,7 @@
 				css->ss->css_rstat_flush(css, cpu);
 			rcu_read_unlock();
 		}
-		raw_spin_unlock_irqrestore(cpu_lock, flags);
+		raw_spin_unlock(cpu_lock);
 
 		/* if @may_sleep, play nice and yield if necessary */
 		if (may_sleep && (need_resched() ||
diff --git a/kernel/kernel/cpu.c b/kernel/kernel/cpu.c
index b561341..d2e4b56 100644
--- a/kernel/kernel/cpu.c
+++ b/kernel/kernel/cpu.c
@@ -1980,7 +1980,7 @@
 		.name			= "ap:online",
 	},
 	/*
-	 * Handled on control processor until the plugged processor manages
+	 * Handled on controll processor until the plugged processor manages
 	 * this itself.
 	 */
 	[CPUHP_TEARDOWN_CPU] = {
@@ -1989,13 +1989,6 @@
 		.teardown.single	= takedown_cpu,
 		.cant_stop		= true,
 	},
-
-	[CPUHP_AP_SCHED_WAIT_EMPTY] = {
-		.name			= "sched:waitempty",
-		.startup.single		= NULL,
-		.teardown.single	= sched_cpu_wait_empty,
-	},
-
 	/* Handle smpboot threads park/unpark */
 	[CPUHP_AP_SMPBOOT_THREADS] = {
 		.name			= "smpboot/threads:online",
diff --git a/kernel/kernel/debug/kdb/kdb_main.c b/kernel/kernel/debug/kdb/kdb_main.c
index 1f5c577..4e09fab 100644
--- a/kernel/kernel/debug/kdb/kdb_main.c
+++ b/kernel/kernel/debug/kdb/kdb_main.c
@@ -2157,7 +2157,7 @@
 	int adjust = 0;
 	int n = 0;
 	int skip = 0;
-	struct kmsg_dumper_iter iter = { .active = 1 };
+	struct kmsg_dumper dumper = { .active = 1 };
 	size_t len;
 	char buf[201];
 
@@ -2182,8 +2182,8 @@
 		kdb_set(2, setargs);
 	}
 
-	kmsg_dump_rewind(&iter);
-	while (kmsg_dump_get_line(&iter, 1, NULL, 0, NULL))
+	kmsg_dump_rewind_nolock(&dumper);
+	while (kmsg_dump_get_line_nolock(&dumper, 1, NULL, 0, NULL))
 		n++;
 
 	if (lines < 0) {
@@ -2215,8 +2215,8 @@
 	if (skip >= n || skip < 0)
 		return 0;
 
-	kmsg_dump_rewind(&iter);
-	while (kmsg_dump_get_line(&iter, 1, buf, sizeof(buf), &len)) {
+	kmsg_dump_rewind_nolock(&dumper);
+	while (kmsg_dump_get_line_nolock(&dumper, 1, buf, sizeof(buf), &len)) {
 		if (skip) {
 			skip--;
 			continue;
diff --git a/kernel/kernel/entry/common.c b/kernel/kernel/entry/common.c
index cdf97ea..09f5885 100644
--- a/kernel/kernel/entry/common.c
+++ b/kernel/kernel/entry/common.c
@@ -2,7 +2,6 @@
 
 #include <linux/context_tracking.h>
 #include <linux/entry-common.h>
-#include <linux/highmem.h>
 #include <linux/livepatch.h>
 #include <linux/audit.h>
 
@@ -157,16 +156,8 @@
 
 		local_irq_enable_exit_to_user(ti_work);
 
-		if (ti_work & _TIF_NEED_RESCHED_MASK)
+		if (ti_work & _TIF_NEED_RESCHED)
 			schedule();
-
-#ifdef ARCH_RT_DELAYS_SIGNAL_SEND
-		if (unlikely(current->forced_info.si_signo)) {
-			struct task_struct *t = current;
-			force_sig_info(&t->forced_info);
-			t->forced_info.si_signo = 0;
-		}
-#endif
 
 		if (ti_work & _TIF_UPROBE)
 			uprobe_notify_resume(regs);
@@ -211,7 +202,6 @@
 
 	/* Ensure that the address limit is intact and no locks are held */
 	addr_limit_user_check();
-	kmap_assert_nomap();
 	lockdep_assert_irqs_disabled();
 	lockdep_sys_exit();
 }
@@ -371,7 +361,7 @@
 		rcu_irq_exit_check_preempt();
 		if (IS_ENABLED(CONFIG_DEBUG_ENTRY))
 			WARN_ON_ONCE(!on_thread_stack());
-		if (should_resched(0))
+		if (need_resched())
 			preempt_schedule_irq();
 	}
 }
diff --git a/kernel/kernel/exit.c b/kernel/kernel/exit.c
index 560e25e..86e4031 100644
--- a/kernel/kernel/exit.c
+++ b/kernel/kernel/exit.c
@@ -153,7 +153,7 @@
 	 * Do this under ->siglock, we can race with another thread
 	 * doing sigqueue_free() if we have SIGQUEUE_PREALLOC signals.
 	 */
-	flush_task_sigqueue(tsk);
+	flush_sigqueue(&tsk->pending);
 	tsk->sighand = NULL;
 	spin_unlock(&sighand->siglock);
 
diff --git a/kernel/kernel/fork.c b/kernel/kernel/fork.c
index be449b7..f73e3e6 100644
--- a/kernel/kernel/fork.c
+++ b/kernel/kernel/fork.c
@@ -42,7 +42,6 @@
 #include <linux/mmu_notifier.h>
 #include <linux/fs.h>
 #include <linux/mm.h>
-#include <linux/kprobes.h>
 #include <linux/vmacache.h>
 #include <linux/nsproxy.h>
 #include <linux/capability.h>
@@ -295,7 +294,7 @@
 			return;
 		}
 
-		vfree(tsk->stack);
+		vfree_atomic(tsk->stack);
 		return;
 	}
 #endif
@@ -724,19 +723,6 @@
 }
 EXPORT_SYMBOL_GPL(__mmdrop);
 
-#ifdef CONFIG_PREEMPT_RT
-/*
- * RCU callback for delayed mm drop. Not strictly rcu, but we don't
- * want another facility to make this work.
- */
-void __mmdrop_delayed(struct rcu_head *rhp)
-{
-	struct mm_struct *mm = container_of(rhp, struct mm_struct, delayed_drop);
-
-	__mmdrop(mm);
-}
-#endif
-
 static void mmdrop_async_fn(struct work_struct *work)
 {
 	struct mm_struct *mm;
@@ -777,15 +763,6 @@
 	WARN_ON(!tsk->exit_state);
 	WARN_ON(refcount_read(&tsk->usage));
 	WARN_ON(tsk == current);
-
-	/*
-	 * Remove function-return probe instances associated with this
-	 * task and put them back on the free list.
-	 */
-	kprobe_flush_task(tsk);
-
-	/* Task is done with its stack. */
-	put_task_stack(tsk);
 
 	io_uring_free(tsk);
 	cgroup_free(tsk);
@@ -984,13 +961,11 @@
 	tsk->splice_pipe = NULL;
 	tsk->task_frag.page = NULL;
 	tsk->wake_q.next = NULL;
-	tsk->wake_q_sleeper.next = NULL;
 	tsk->pf_io_worker = NULL;
 
 	account_kernel_stack(tsk, 1);
 
 	kcov_task_init(tsk);
-	kmap_local_fork(tsk);
 
 #ifdef CONFIG_FAULT_INJECTION
 	tsk->fail_nth = 0;
@@ -2084,7 +2059,6 @@
 	spin_lock_init(&p->alloc_lock);
 
 	init_sigpending(&p->pending);
-	p->sigqueue_cache = NULL;
 
 	p->utime = p->stime = p->gtime = 0;
 #ifdef CONFIG_ARCH_HAS_SCALED_CPUTIME
diff --git a/kernel/kernel/futex.c b/kernel/kernel/futex.c
index eee1cd6..b223cc5 100644
--- a/kernel/kernel/futex.c
+++ b/kernel/kernel/futex.c
@@ -1499,7 +1499,6 @@
 	struct task_struct *new_owner;
 	bool postunlock = false;
 	DEFINE_WAKE_Q(wake_q);
-	DEFINE_WAKE_Q(wake_sleeper_q);
 	int ret = 0;
 
 	new_owner = rt_mutex_next_owner(&pi_state->pi_mutex);
@@ -1549,15 +1548,14 @@
 		 * not fail.
 		 */
 		pi_state_update_owner(pi_state, new_owner);
-		postunlock = __rt_mutex_futex_unlock(&pi_state->pi_mutex, &wake_q,
-						     &wake_sleeper_q);
+		postunlock = __rt_mutex_futex_unlock(&pi_state->pi_mutex, &wake_q);
 	}
 
 out_unlock:
 	raw_spin_unlock_irq(&pi_state->pi_mutex.wait_lock);
 
 	if (postunlock)
-		rt_mutex_postunlock(&wake_q, &wake_sleeper_q);
+		rt_mutex_postunlock(&wake_q);
 
 	return ret;
 }
@@ -2861,7 +2859,7 @@
 		goto no_block;
 	}
 
-	rt_mutex_init_waiter(&rt_waiter, false);
+	rt_mutex_init_waiter(&rt_waiter);
 
 	/*
 	 * On PREEMPT_RT_FULL, when hb->lock becomes an rt_mutex, we must not
@@ -3207,7 +3205,7 @@
 	 * The waiter is allocated on our stack, manipulated by the requeue
 	 * code while we sleep on uaddr.
 	 */
-	rt_mutex_init_waiter(&rt_waiter, false);
+	rt_mutex_init_waiter(&rt_waiter);
 
 	ret = get_futex_key(uaddr2, flags & FLAGS_SHARED, &key2, FUTEX_WRITE);
 	if (unlikely(ret != 0))
diff --git a/kernel/kernel/irq/manage.c b/kernel/kernel/irq/manage.c
index 02ecab8..76da8de 100644
--- a/kernel/kernel/irq/manage.c
+++ b/kernel/kernel/irq/manage.c
@@ -1202,8 +1202,6 @@
 
 	irq_thread_set_ready(desc, action);
 
-	sched_set_fifo(current);
-
 	if (force_irqthreads && test_bit(IRQTF_FORCED_THREAD,
 					&action->thread_flags))
 		handler_fn = irq_forced_thread_fn;
@@ -1368,6 +1366,8 @@
 
 	if (IS_ERR(t))
 		return PTR_ERR(t);
+
+	sched_set_fifo(t);
 
 	/*
 	 * We keep the reference to the task struct even if
@@ -2750,7 +2750,7 @@
  *	This call sets the internal irqchip state of an interrupt,
  *	depending on the value of @which.
  *
- *	This function should be called with migration disabled if the
+ *	This function should be called with preemption disabled if the
  *	interrupt controller has per-cpu registers.
  */
 int irq_set_irqchip_state(unsigned int irq, enum irqchip_irq_state which,
diff --git a/kernel/kernel/irq/spurious.c b/kernel/kernel/irq/spurious.c
index dc7311d..f865e5f 100644
--- a/kernel/kernel/irq/spurious.c
+++ b/kernel/kernel/irq/spurious.c
@@ -443,10 +443,6 @@
 
 static int __init irqfixup_setup(char *str)
 {
-#ifdef CONFIG_PREEMPT_RT
-	pr_warn("irqfixup boot option not supported w/ CONFIG_PREEMPT_RT\n");
-	return 1;
-#endif
 	irqfixup = 1;
 	printk(KERN_WARNING "Misrouted IRQ fixup support enabled.\n");
 	printk(KERN_WARNING "This may impact system performance.\n");
@@ -459,10 +455,6 @@
 
 static int __init irqpoll_setup(char *str)
 {
-#ifdef CONFIG_PREEMPT_RT
-	pr_warn("irqpoll boot option not supported w/ CONFIG_PREEMPT_RT\n");
-	return 1;
-#endif
 	irqfixup = 2;
 	printk(KERN_WARNING "Misrouted IRQ fixup and polling support "
 				"enabled\n");
diff --git a/kernel/kernel/irq_work.c b/kernel/kernel/irq_work.c
index 820798c..e0ed16d 100644
--- a/kernel/kernel/irq_work.c
+++ b/kernel/kernel/irq_work.c
@@ -18,37 +18,11 @@
 #include <linux/cpu.h>
 #include <linux/notifier.h>
 #include <linux/smp.h>
-#include <linux/smpboot.h>
-#include <linux/interrupt.h>
 #include <asm/processor.h>
 
 
 static DEFINE_PER_CPU(struct llist_head, raised_list);
 static DEFINE_PER_CPU(struct llist_head, lazy_list);
-static DEFINE_PER_CPU(struct task_struct *, irq_workd);
-
-static void wake_irq_workd(void)
-{
-	struct task_struct *tsk = __this_cpu_read(irq_workd);
-
-	if (!llist_empty(this_cpu_ptr(&lazy_list)) && tsk)
-		wake_up_process(tsk);
-}
-
-#ifdef CONFIG_SMP
-static void irq_work_wake(struct irq_work *entry)
-{
-	wake_irq_workd();
-}
-
-static DEFINE_PER_CPU(struct irq_work, irq_work_wakeup) =
-	IRQ_WORK_INIT_HARD(irq_work_wake);
-#endif
-
-static int irq_workd_should_run(unsigned int cpu)
-{
-	return !llist_empty(this_cpu_ptr(&lazy_list));
-}
 
 /*
  * Claim the entry so that no one else will poke at it.
@@ -78,29 +52,15 @@
 /* Enqueue on current CPU, work must already be claimed and preempt disabled */
 static void __irq_work_queue_local(struct irq_work *work)
 {
-	struct llist_head *list;
-	bool rt_lazy_work = false;
-	bool lazy_work = false;
-	int work_flags;
-
-	work_flags = atomic_read(&work->flags);
-	if (work_flags & IRQ_WORK_LAZY)
-		lazy_work = true;
-	else if (IS_ENABLED(CONFIG_PREEMPT_RT) &&
-		 !(work_flags & IRQ_WORK_HARD_IRQ))
-		rt_lazy_work = true;
-
-	if (lazy_work || rt_lazy_work)
-		list = this_cpu_ptr(&lazy_list);
-	else
-		list = this_cpu_ptr(&raised_list);
-
-	if (!llist_add(&work->llnode, list))
-		return;
-
 	/* If the work is "lazy", handle it from next tick if any */
-	if (!lazy_work || tick_nohz_tick_stopped())
-		arch_irq_work_raise();
+	if (atomic_read(&work->flags) & IRQ_WORK_LAZY) {
+		if (llist_add(&work->llnode, this_cpu_ptr(&lazy_list)) &&
+		    tick_nohz_tick_stopped())
+			arch_irq_work_raise();
+	} else {
+		if (llist_add(&work->llnode, this_cpu_ptr(&raised_list)))
+			arch_irq_work_raise();
+	}
 }
 
 /* Enqueue the irq work @work on the current CPU */
@@ -142,28 +102,10 @@
 	if (cpu != smp_processor_id()) {
 		/* Arch remote IPI send/receive backend aren't NMI safe */
 		WARN_ON_ONCE(in_nmi());
-
-		/*
-		 * On PREEMPT_RT the items which are not marked as
-		 * IRQ_WORK_HARD_IRQ are added to the lazy list and a HARD work
-		 * item is used on the remote CPU to wake the thread.
-		 */
-		if (IS_ENABLED(CONFIG_PREEMPT_RT) &&
-		    !(atomic_read(&work->flags) & IRQ_WORK_HARD_IRQ)) {
-
-			if (!llist_add(&work->llnode, &per_cpu(lazy_list, cpu)))
-				goto out;
-
-			work = &per_cpu(irq_work_wakeup, cpu);
-			if (!irq_work_claim(work))
-				goto out;
-		}
-
 		__smp_call_single_queue(cpu, &work->llnode);
 	} else {
 		__irq_work_queue_local(work);
 	}
-out:
 	preempt_enable();
 
 	return true;
@@ -178,8 +120,9 @@
 	raised = this_cpu_ptr(&raised_list);
 	lazy = this_cpu_ptr(&lazy_list);
 
-	if (llist_empty(raised) && llist_empty(lazy))
-		return false;
+	if (llist_empty(raised) || arch_irq_work_has_interrupt())
+		if (llist_empty(lazy))
+			return false;
 
 	/* All work should have been flushed before going offline */
 	WARN_ON_ONCE(cpu_is_offline(smp_processor_id()));
@@ -210,10 +153,6 @@
 	 */
 	flags &= ~IRQ_WORK_PENDING;
 	(void)atomic_cmpxchg(&work->flags, flags, flags & ~IRQ_WORK_BUSY);
-
-	if ((IS_ENABLED(CONFIG_PREEMPT_RT) && !irq_work_is_hard(work)) ||
-	    !arch_irq_work_has_interrupt())
-		rcuwait_wake_up(&work->irqwait);
 }
 
 static void irq_work_run_list(struct llist_head *list)
@@ -221,12 +160,7 @@
 	struct irq_work *work, *tmp;
 	struct llist_node *llnode;
 
-	/*
-	 * On PREEMPT_RT IRQ-work which is not marked as HARD will be processed
-	 * in a per-CPU thread in preemptible context. Only the items which are
-	 * marked as IRQ_WORK_HARD_IRQ will be processed in hardirq context.
-	 */
-	BUG_ON(!irqs_disabled() && !IS_ENABLED(CONFIG_PREEMPT_RT));
+	BUG_ON(!irqs_disabled());
 
 	if (llist_empty(list))
 		return;
@@ -243,10 +177,7 @@
 void irq_work_run(void)
 {
 	irq_work_run_list(this_cpu_ptr(&raised_list));
-	if (!IS_ENABLED(CONFIG_PREEMPT_RT))
-		irq_work_run_list(this_cpu_ptr(&lazy_list));
-	else
-		wake_irq_workd();
+	irq_work_run_list(this_cpu_ptr(&lazy_list));
 }
 EXPORT_SYMBOL_GPL(irq_work_run);
 
@@ -256,11 +187,7 @@
 
 	if (!llist_empty(raised) && !arch_irq_work_has_interrupt())
 		irq_work_run_list(raised);
-
-	if (!IS_ENABLED(CONFIG_PREEMPT_RT))
-		irq_work_run_list(this_cpu_ptr(&lazy_list));
-	else
-		wake_irq_workd();
+	irq_work_run_list(this_cpu_ptr(&lazy_list));
 }
 
 /*
@@ -270,42 +197,8 @@
 void irq_work_sync(struct irq_work *work)
 {
 	lockdep_assert_irqs_enabled();
-	might_sleep();
-
-	if ((IS_ENABLED(CONFIG_PREEMPT_RT) && !irq_work_is_hard(work)) ||
-	    !arch_irq_work_has_interrupt()) {
-		rcuwait_wait_event(&work->irqwait, !irq_work_is_busy(work),
-				   TASK_UNINTERRUPTIBLE);
-		return;
-	}
 
 	while (atomic_read(&work->flags) & IRQ_WORK_BUSY)
 		cpu_relax();
 }
 EXPORT_SYMBOL_GPL(irq_work_sync);
-
-static void run_irq_workd(unsigned int cpu)
-{
-	irq_work_run_list(this_cpu_ptr(&lazy_list));
-}
-
-static void irq_workd_setup(unsigned int cpu)
-{
-	sched_set_fifo_low(current);
-}
-
-static struct smp_hotplug_thread irqwork_threads = {
-	.store                  = &irq_workd,
-	.setup			= irq_workd_setup,
-	.thread_should_run      = irq_workd_should_run,
-	.thread_fn              = run_irq_workd,
-	.thread_comm            = "irq_work/%u",
-};
-
-static __init int irq_work_init_threads(void)
-{
-	if (IS_ENABLED(CONFIG_PREEMPT_RT))
-		BUG_ON(smpboot_register_percpu_thread(&irqwork_threads));
-	return 0;
-}
-early_initcall(irq_work_init_threads);
diff --git a/kernel/kernel/kexec_core.c b/kernel/kernel/kexec_core.c
index fb0ca1a..c589c7a 100644
--- a/kernel/kernel/kexec_core.c
+++ b/kernel/kernel/kexec_core.c
@@ -978,6 +978,7 @@
 	old_cpu = atomic_cmpxchg(&panic_cpu, PANIC_CPU_INVALID, this_cpu);
 	if (old_cpu == PANIC_CPU_INVALID) {
 		/* This is the 1st CPU which comes here, so go ahead. */
+		printk_safe_flush_on_panic();
 		__crash_kexec(regs);
 
 		/*
diff --git a/kernel/kernel/ksysfs.c b/kernel/kernel/ksysfs.c
index dfff31e..35859da 100644
--- a/kernel/kernel/ksysfs.c
+++ b/kernel/kernel/ksysfs.c
@@ -138,15 +138,6 @@
 
 #endif /* CONFIG_CRASH_CORE */
 
-#if defined(CONFIG_PREEMPT_RT)
-static ssize_t realtime_show(struct kobject *kobj,
-			     struct kobj_attribute *attr, char *buf)
-{
-	return sprintf(buf, "%d\n", 1);
-}
-KERNEL_ATTR_RO(realtime);
-#endif
-
 /* whether file capabilities are enabled */
 static ssize_t fscaps_show(struct kobject *kobj,
 				  struct kobj_attribute *attr, char *buf)
@@ -237,9 +228,6 @@
 #ifndef CONFIG_TINY_RCU
 	&rcu_expedited_attr.attr,
 	&rcu_normal_attr.attr,
-#endif
-#ifdef CONFIG_PREEMPT_RT
-	&realtime_attr.attr,
 #endif
 	NULL
 };
diff --git a/kernel/kernel/kthread.c b/kernel/kernel/kthread.c
index 49dad4f..9d736f5 100644
--- a/kernel/kernel/kthread.c
+++ b/kernel/kernel/kthread.c
@@ -264,7 +264,6 @@
 
 static int kthread(void *_create)
 {
-	static const struct sched_param param = { .sched_priority = 0 };
 	/* Copy data: it's on kthread's stack */
 	struct kthread_create_info *create = _create;
 	int (*threadfn)(void *data) = create->threadfn;
@@ -294,13 +293,6 @@
 	init_completion(&self->exited);
 	init_completion(&self->parked);
 	current->vfork_done = &self->exited;
-
-	/*
-	 * The new thread inherited kthreadd's priority and CPU mask. Reset
-	 * back to default in case they have been changed.
-	 */
-	sched_setscheduler_nocheck(current, SCHED_NORMAL, &param);
-	set_cpus_allowed_ptr(current, housekeeping_cpumask(HK_FLAG_KTHREAD));
 
 	/* OK, tell user we're spawned, wait for stop or wakeup */
 	__set_current_state(TASK_UNINTERRUPTIBLE);
@@ -399,6 +391,7 @@
 	}
 	task = create->result;
 	if (!IS_ERR(task)) {
+		static const struct sched_param param = { .sched_priority = 0 };
 		char name[TASK_COMM_LEN];
 
 		/*
@@ -407,6 +400,13 @@
 		 */
 		vsnprintf(name, sizeof(name), namefmt, args);
 		set_task_comm(task, name);
+		/*
+		 * root may have changed our (kthreadd's) priority or CPU mask.
+		 * The kernel thread should not inherit these properties.
+		 */
+		sched_setscheduler_nocheck(task, SCHED_NORMAL, &param);
+		set_cpus_allowed_ptr(task,
+				     housekeeping_cpumask(HK_FLAG_KTHREAD));
 	}
 	kfree(create);
 	return task;
diff --git a/kernel/kernel/locking/Makefile b/kernel/kernel/locking/Makefile
index c7fbf73..6d11cfb 100644
--- a/kernel/kernel/locking/Makefile
+++ b/kernel/kernel/locking/Makefile
@@ -3,7 +3,7 @@
 # and is generally not a function of system call inputs.
 KCOV_INSTRUMENT		:= n
 
-obj-y += semaphore.o rwsem.o percpu-rwsem.o
+obj-y += mutex.o semaphore.o rwsem.o percpu-rwsem.o
 
 # Avoid recursion lockdep -> KCSAN -> ... -> lockdep.
 KCSAN_SANITIZE_lockdep.o := n
@@ -15,23 +15,19 @@
 CFLAGS_REMOVE_rtmutex-debug.o = $(CC_FLAGS_FTRACE)
 endif
 
+obj-$(CONFIG_DEBUG_MUTEXES) += mutex-debug.o
 obj-$(CONFIG_LOCKDEP) += lockdep.o
 ifeq ($(CONFIG_PROC_FS),y)
 obj-$(CONFIG_LOCKDEP) += lockdep_proc.o
 endif
 obj-$(CONFIG_SMP) += spinlock.o
+obj-$(CONFIG_LOCK_SPIN_ON_OWNER) += osq_lock.o
 obj-$(CONFIG_PROVE_LOCKING) += spinlock.o
 obj-$(CONFIG_QUEUED_SPINLOCKS) += qspinlock.o
 obj-$(CONFIG_RT_MUTEXES) += rtmutex.o
 obj-$(CONFIG_DEBUG_RT_MUTEXES) += rtmutex-debug.o
 obj-$(CONFIG_DEBUG_SPINLOCK) += spinlock.o
 obj-$(CONFIG_DEBUG_SPINLOCK) += spinlock_debug.o
-ifneq ($(CONFIG_PREEMPT_RT),y)
-obj-y += mutex.o
-obj-$(CONFIG_LOCK_SPIN_ON_OWNER) += osq_lock.o
-obj-$(CONFIG_DEBUG_MUTEXES) += mutex-debug.o
-endif
-obj-$(CONFIG_PREEMPT_RT) += mutex-rt.o rwsem-rt.o rwlock-rt.o
 obj-$(CONFIG_QUEUED_RWLOCKS) += qrwlock.o
 obj-$(CONFIG_LOCK_TORTURE_TEST) += locktorture.o
 obj-$(CONFIG_WW_MUTEX_SELFTEST) += test-ww_mutex.o
diff --git a/kernel/kernel/locking/lockdep.c b/kernel/kernel/locking/lockdep.c
index f2f5def..6cbd2b4 100644
--- a/kernel/kernel/locking/lockdep.c
+++ b/kernel/kernel/locking/lockdep.c
@@ -5413,7 +5413,6 @@
 		}
 	}
 
-#ifndef CONFIG_PREEMPT_RT
 	/*
 	 * We dont accurately track softirq state in e.g.
 	 * hardirq contexts (such as on 4KSTACKS), so only
@@ -5428,7 +5427,6 @@
 			DEBUG_LOCKS_WARN_ON(!current->softirqs_enabled);
 		}
 	}
-#endif
 
 	if (!debug_locks)
 		print_irqtrace_events(current);
diff --git a/kernel/kernel/locking/mutex-rt.c b/kernel/kernel/locking/mutex-rt.c
deleted file mode 100644
index 2b849e6..0000000
--- a/kernel/kernel/locking/mutex-rt.c
+++ /dev/null
@@ -1,224 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Real-Time Preemption Support
- *
- * started by Ingo Molnar:
- *
- *  Copyright (C) 2004-2006 Red Hat, Inc., Ingo Molnar <mingo@redhat.com>
- *  Copyright (C) 2006, Timesys Corp., Thomas Gleixner <tglx@timesys.com>
- *
- * historic credit for proving that Linux spinlocks can be implemented via
- * RT-aware mutexes goes to many people: The Pmutex project (Dirk Grambow
- * and others) who prototyped it on 2.4 and did lots of comparative
- * research and analysis; TimeSys, for proving that you can implement a
- * fully preemptible kernel via the use of IRQ threading and mutexes;
- * Bill Huey for persuasively arguing on lkml that the mutex model is the
- * right one; and to MontaVista, who ported pmutexes to 2.6.
- *
- * This code is a from-scratch implementation and is not based on pmutexes,
- * but the idea of converting spinlocks to mutexes is used here too.
- *
- * lock debugging, locking tree, deadlock detection:
- *
- *  Copyright (C) 2004, LynuxWorks, Inc., Igor Manyilov, Bill Huey
- *  Released under the General Public License (GPL).
- *
- * Includes portions of the generic R/W semaphore implementation from:
- *
- *  Copyright (c) 2001   David Howells (dhowells@redhat.com).
- *  - Derived partially from idea by Andrea Arcangeli <andrea@suse.de>
- *  - Derived also from comments by Linus
- *
- * Pending ownership of locks and ownership stealing:
- *
- *  Copyright (C) 2005, Kihon Technologies Inc., Steven Rostedt
- *
- *   (also by Steven Rostedt)
- *    - Converted single pi_lock to individual task locks.
- *
- * By Esben Nielsen:
- *    Doing priority inheritance with help of the scheduler.
- *
- *  Copyright (C) 2006, Timesys Corp., Thomas Gleixner <tglx@timesys.com>
- *  - major rework based on Esben Nielsens initial patch
- *  - replaced thread_info references by task_struct refs
- *  - removed task->pending_owner dependency
- *  - BKL drop/reacquire for semaphore style locks to avoid deadlocks
- *    in the scheduler return path as discussed with Steven Rostedt
- *
- *  Copyright (C) 2006, Kihon Technologies Inc.
- *    Steven Rostedt <rostedt@goodmis.org>
- *  - debugged and patched Thomas Gleixner's rework.
- *  - added back the cmpxchg to the rework.
- *  - turned atomic require back on for SMP.
- */
-
-#include <linux/spinlock.h>
-#include <linux/rtmutex.h>
-#include <linux/sched.h>
-#include <linux/delay.h>
-#include <linux/module.h>
-#include <linux/kallsyms.h>
-#include <linux/syscalls.h>
-#include <linux/interrupt.h>
-#include <linux/plist.h>
-#include <linux/fs.h>
-#include <linux/futex.h>
-#include <linux/hrtimer.h>
-#include <linux/blkdev.h>
-
-#include "rtmutex_common.h"
-
-/*
- * struct mutex functions
- */
-void __mutex_do_init(struct mutex *mutex, const char *name,
-		     struct lock_class_key *key)
-{
-#ifdef CONFIG_DEBUG_LOCK_ALLOC
-	/*
-	 * Make sure we are not reinitializing a held lock:
-	 */
-	debug_check_no_locks_freed((void *)mutex, sizeof(*mutex));
-	lockdep_init_map(&mutex->dep_map, name, key, 0);
-#endif
-	mutex->lock.save_state = 0;
-}
-EXPORT_SYMBOL(__mutex_do_init);
-
-static int _mutex_lock_blk_flush(struct mutex *lock, int state)
-{
-	/*
-	 * Flush blk before ->pi_blocked_on is set. At schedule() time it is too
-	 * late if one of the callbacks needs to acquire a sleeping lock.
-	 */
-	if (blk_needs_flush_plug(current))
-		blk_schedule_flush_plug(current);
-	return __rt_mutex_lock_state(&lock->lock, state);
-}
-
-void __lockfunc _mutex_lock(struct mutex *lock)
-{
-	mutex_acquire(&lock->dep_map, 0, 0, _RET_IP_);
-	_mutex_lock_blk_flush(lock, TASK_UNINTERRUPTIBLE);
-}
-EXPORT_SYMBOL(_mutex_lock);
-
-void __lockfunc _mutex_lock_io_nested(struct mutex *lock, int subclass)
-{
-	int token;
-
-	token = io_schedule_prepare();
-
-	mutex_acquire_nest(&lock->dep_map, subclass, 0, NULL, _RET_IP_);
-	__rt_mutex_lock_state(&lock->lock, TASK_UNINTERRUPTIBLE);
-
-	io_schedule_finish(token);
-}
-EXPORT_SYMBOL_GPL(_mutex_lock_io_nested);
-
-int __lockfunc _mutex_lock_interruptible(struct mutex *lock)
-{
-	int ret;
-
-	mutex_acquire(&lock->dep_map, 0, 0, _RET_IP_);
-	ret = _mutex_lock_blk_flush(lock, TASK_INTERRUPTIBLE);
-	if (ret)
-		mutex_release(&lock->dep_map, _RET_IP_);
-	return ret;
-}
-EXPORT_SYMBOL(_mutex_lock_interruptible);
-
-int __lockfunc _mutex_lock_killable(struct mutex *lock)
-{
-	int ret;
-
-	mutex_acquire(&lock->dep_map, 0, 0, _RET_IP_);
-	ret = _mutex_lock_blk_flush(lock, TASK_KILLABLE);
-	if (ret)
-		mutex_release(&lock->dep_map, _RET_IP_);
-	return ret;
-}
-EXPORT_SYMBOL(_mutex_lock_killable);
-
-#ifdef CONFIG_DEBUG_LOCK_ALLOC
-void __lockfunc _mutex_lock_nested(struct mutex *lock, int subclass)
-{
-	mutex_acquire_nest(&lock->dep_map, subclass, 0, NULL, _RET_IP_);
-	_mutex_lock_blk_flush(lock, TASK_UNINTERRUPTIBLE);
-}
-EXPORT_SYMBOL(_mutex_lock_nested);
-
-void __lockfunc _mutex_lock_nest_lock(struct mutex *lock, struct lockdep_map *nest)
-{
-	mutex_acquire_nest(&lock->dep_map, 0, 0, nest, _RET_IP_);
-	_mutex_lock_blk_flush(lock, TASK_UNINTERRUPTIBLE);
-}
-EXPORT_SYMBOL(_mutex_lock_nest_lock);
-
-int __lockfunc _mutex_lock_interruptible_nested(struct mutex *lock, int subclass)
-{
-	int ret;
-
-	mutex_acquire_nest(&lock->dep_map, subclass, 0, NULL, _RET_IP_);
-	ret = _mutex_lock_blk_flush(lock, TASK_INTERRUPTIBLE);
-	if (ret)
-		mutex_release(&lock->dep_map, _RET_IP_);
-	return ret;
-}
-EXPORT_SYMBOL(_mutex_lock_interruptible_nested);
-
-int __lockfunc _mutex_lock_killable_nested(struct mutex *lock, int subclass)
-{
-	int ret;
-
-	mutex_acquire(&lock->dep_map, subclass, 0, _RET_IP_);
-	ret = _mutex_lock_blk_flush(lock, TASK_KILLABLE);
-	if (ret)
-		mutex_release(&lock->dep_map, _RET_IP_);
-	return ret;
-}
-EXPORT_SYMBOL(_mutex_lock_killable_nested);
-#endif
-
-int __lockfunc _mutex_trylock(struct mutex *lock)
-{
-	int ret = __rt_mutex_trylock(&lock->lock);
-
-	if (ret)
-		mutex_acquire(&lock->dep_map, 0, 1, _RET_IP_);
-
-	return ret;
-}
-EXPORT_SYMBOL(_mutex_trylock);
-
-void __lockfunc _mutex_unlock(struct mutex *lock)
-{
-	mutex_release(&lock->dep_map, _RET_IP_);
-	__rt_mutex_unlock(&lock->lock);
-}
-EXPORT_SYMBOL(_mutex_unlock);
-
-/**
- * atomic_dec_and_mutex_lock - return holding mutex if we dec to 0
- * @cnt: the atomic which we are to dec
- * @lock: the mutex to return holding if we dec to 0
- *
- * return true and hold lock if we dec to 0, return false otherwise
- */
-int atomic_dec_and_mutex_lock(atomic_t *cnt, struct mutex *lock)
-{
-	/* dec if we can't possibly hit 0 */
-	if (atomic_add_unless(cnt, -1, 1))
-		return 0;
-	/* we might hit 0, so take the lock */
-	mutex_lock(lock);
-	if (!atomic_dec_and_test(cnt)) {
-		/* when we actually did the dec, we didn't hit 0 */
-		mutex_unlock(lock);
-		return 0;
-	}
-	/* we hit 0, and we hold the lock */
-	return 1;
-}
-EXPORT_SYMBOL(atomic_dec_and_mutex_lock);
diff --git a/kernel/kernel/locking/rtmutex-debug.c b/kernel/kernel/locking/rtmutex-debug.c
index fb15010..36e6910 100644
--- a/kernel/kernel/locking/rtmutex-debug.c
+++ b/kernel/kernel/locking/rtmutex-debug.c
@@ -32,10 +32,108 @@
 
 #include "rtmutex_common.h"
 
+static void printk_task(struct task_struct *p)
+{
+	if (p)
+		printk("%16s:%5d [%p, %3d]", p->comm, task_pid_nr(p), p, p->prio);
+	else
+		printk("<none>");
+}
+
+static void printk_lock(struct rt_mutex *lock, int print_owner)
+{
+	if (lock->name)
+		printk(" [%p] {%s}\n",
+			lock, lock->name);
+	else
+		printk(" [%p] {%s:%d}\n",
+			lock, lock->file, lock->line);
+
+	if (print_owner && rt_mutex_owner(lock)) {
+		printk(".. ->owner: %p\n", lock->owner);
+		printk(".. held by:  ");
+		printk_task(rt_mutex_owner(lock));
+		printk("\n");
+	}
+}
+
 void rt_mutex_debug_task_free(struct task_struct *task)
 {
 	DEBUG_LOCKS_WARN_ON(!RB_EMPTY_ROOT(&task->pi_waiters.rb_root));
 	DEBUG_LOCKS_WARN_ON(task->pi_blocked_on);
+}
+
+/*
+ * We fill out the fields in the waiter to store the information about
+ * the deadlock. We print when we return. act_waiter can be NULL in
+ * case of a remove waiter operation.
+ */
+void debug_rt_mutex_deadlock(enum rtmutex_chainwalk chwalk,
+			     struct rt_mutex_waiter *act_waiter,
+			     struct rt_mutex *lock)
+{
+	struct task_struct *task;
+
+	if (!debug_locks || chwalk == RT_MUTEX_FULL_CHAINWALK || !act_waiter)
+		return;
+
+	task = rt_mutex_owner(act_waiter->lock);
+	if (task && task != current) {
+		act_waiter->deadlock_task_pid = get_pid(task_pid(task));
+		act_waiter->deadlock_lock = lock;
+	}
+}
+
+void debug_rt_mutex_print_deadlock(struct rt_mutex_waiter *waiter)
+{
+	struct task_struct *task;
+
+	if (!waiter->deadlock_lock || !debug_locks)
+		return;
+
+	rcu_read_lock();
+	task = pid_task(waiter->deadlock_task_pid, PIDTYPE_PID);
+	if (!task) {
+		rcu_read_unlock();
+		return;
+	}
+
+	if (!debug_locks_off()) {
+		rcu_read_unlock();
+		return;
+	}
+
+	pr_warn("\n");
+	pr_warn("============================================\n");
+	pr_warn("WARNING: circular locking deadlock detected!\n");
+	pr_warn("%s\n", print_tainted());
+	pr_warn("--------------------------------------------\n");
+	printk("%s/%d is deadlocking current task %s/%d\n\n",
+	       task->comm, task_pid_nr(task),
+	       current->comm, task_pid_nr(current));
+
+	printk("\n1) %s/%d is trying to acquire this lock:\n",
+	       current->comm, task_pid_nr(current));
+	printk_lock(waiter->lock, 1);
+
+	printk("\n2) %s/%d is blocked on this lock:\n",
+		task->comm, task_pid_nr(task));
+	printk_lock(waiter->deadlock_lock, 1);
+
+	debug_show_held_locks(current);
+	debug_show_held_locks(task);
+
+	printk("\n%s/%d's [blocked] stackdump:\n\n",
+		task->comm, task_pid_nr(task));
+	show_stack(task, NULL, KERN_DEFAULT);
+	printk("\n%s/%d's [current] stackdump:\n\n",
+		current->comm, task_pid_nr(current));
+	dump_stack();
+	debug_show_all_locks();
+	rcu_read_unlock();
+
+	printk("[ turning off deadlock detection."
+	       "Please report this trace. ]\n\n");
 }
 
 void debug_rt_mutex_lock(struct rt_mutex *lock)
@@ -60,10 +158,12 @@
 void debug_rt_mutex_init_waiter(struct rt_mutex_waiter *waiter)
 {
 	memset(waiter, 0x11, sizeof(*waiter));
+	waiter->deadlock_task_pid = NULL;
 }
 
 void debug_rt_mutex_free_waiter(struct rt_mutex_waiter *waiter)
 {
+	put_pid(waiter->deadlock_task_pid);
 	memset(waiter, 0x22, sizeof(*waiter));
 }
 
@@ -73,8 +173,10 @@
 	 * Make sure we are not reinitializing a held lock:
 	 */
 	debug_check_no_locks_freed((void *)lock, sizeof(*lock));
+	lock->name = name;
 
 #ifdef CONFIG_DEBUG_LOCK_ALLOC
 	lockdep_init_map(&lock->dep_map, name, key, 0);
 #endif
 }
+
diff --git a/kernel/kernel/locking/rtmutex-debug.h b/kernel/kernel/locking/rtmutex-debug.h
index 659e93e..fc54971 100644
--- a/kernel/kernel/locking/rtmutex-debug.h
+++ b/kernel/kernel/locking/rtmutex-debug.h
@@ -18,9 +18,20 @@
 extern void debug_rt_mutex_proxy_lock(struct rt_mutex *lock,
 				      struct task_struct *powner);
 extern void debug_rt_mutex_proxy_unlock(struct rt_mutex *lock);
+extern void debug_rt_mutex_deadlock(enum rtmutex_chainwalk chwalk,
+				    struct rt_mutex_waiter *waiter,
+				    struct rt_mutex *lock);
+extern void debug_rt_mutex_print_deadlock(struct rt_mutex_waiter *waiter);
+# define debug_rt_mutex_reset_waiter(w)			\
+	do { (w)->deadlock_lock = NULL; } while (0)
 
 static inline bool debug_rt_mutex_detect_deadlock(struct rt_mutex_waiter *waiter,
 						  enum rtmutex_chainwalk walk)
 {
 	return (waiter != NULL);
 }
+
+static inline void rt_mutex_print_deadlock(struct rt_mutex_waiter *w)
+{
+	debug_rt_mutex_print_deadlock(w);
+}
diff --git a/kernel/kernel/locking/rtmutex.c b/kernel/kernel/locking/rtmutex.c
index 47d59f9..419cc66 100644
--- a/kernel/kernel/locking/rtmutex.c
+++ b/kernel/kernel/locking/rtmutex.c
@@ -8,11 +8,6 @@
  *  Copyright (C) 2005-2006 Timesys Corp., Thomas Gleixner <tglx@timesys.com>
  *  Copyright (C) 2005 Kihon Technologies Inc., Steven Rostedt
  *  Copyright (C) 2006 Esben Nielsen
- * Adaptive Spinlocks:
- *  Copyright (C) 2008 Novell, Inc., Gregory Haskins, Sven Dietrich,
- *				     and Peter Morreale,
- * Adaptive Spinlocks simplification:
- *  Copyright (C) 2008 Red Hat, Inc., Steven Rostedt <srostedt@redhat.com>
  *
  *  See Documentation/locking/rt-mutex-design.rst for details.
  */
@@ -25,7 +20,6 @@
 #include <linux/sched/debug.h>
 #include <linux/timer.h>
 #include <trace/hooks/dtask.h>
-#include <linux/ww_mutex.h>
 
 #include "rtmutex_common.h"
 
@@ -143,12 +137,6 @@
 		WRITE_ONCE(*p, owner & ~RT_MUTEX_HAS_WAITERS);
 }
 
-static int rt_mutex_real_waiter(struct rt_mutex_waiter *waiter)
-{
-	return waiter && waiter != PI_WAKEUP_INPROGRESS &&
-		waiter != PI_REQUEUE_INPROGRESS;
-}
-
 /*
  * We can speed up the acquire/release, if there's no debugging state to be
  * set up.
@@ -240,7 +228,7 @@
  * Only use with rt_mutex_waiter_{less,equal}()
  */
 #define task_to_waiter(p)	\
-	&(struct rt_mutex_waiter){ .prio = (p)->prio, .deadline = (p)->dl.deadline, .task = (p) }
+	&(struct rt_mutex_waiter){ .prio = (p)->prio, .deadline = (p)->dl.deadline }
 
 static inline int
 rt_mutex_waiter_less(struct rt_mutex_waiter *left,
@@ -278,27 +266,6 @@
 		return left->deadline == right->deadline;
 
 	return 1;
-}
-
-#define STEAL_NORMAL  0
-#define STEAL_LATERAL 1
-
-static inline int
-rt_mutex_steal(struct rt_mutex *lock, struct rt_mutex_waiter *waiter, int mode)
-{
-	struct rt_mutex_waiter *top_waiter = rt_mutex_top_waiter(lock);
-
-	if (waiter == top_waiter || rt_mutex_waiter_less(waiter, top_waiter))
-		return 1;
-
-	/*
-	 * Note that RT tasks are excluded from lateral-steals
-	 * to prevent the introduction of an unbounded latency.
-	 */
-	if (mode == STEAL_NORMAL || rt_task(waiter->task))
-		return 0;
-
-	return rt_mutex_waiter_equal(waiter, top_waiter);
 }
 
 static void
@@ -405,14 +372,6 @@
 	return debug_rt_mutex_detect_deadlock(waiter, chwalk);
 }
 
-static void rt_mutex_wake_waiter(struct rt_mutex_waiter *waiter)
-{
-	if (waiter->savestate)
-		wake_up_lock_sleeper(waiter->task);
-	else
-		wake_up_process(waiter->task);
-}
-
 /*
  * Max number of times we'll walk the boosting chain:
  */
@@ -420,8 +379,7 @@
 
 static inline struct rt_mutex *task_blocked_on_lock(struct task_struct *p)
 {
-	return rt_mutex_real_waiter(p->pi_blocked_on) ?
-		p->pi_blocked_on->lock : NULL;
+	return p->pi_blocked_on ? p->pi_blocked_on->lock : NULL;
 }
 
 /*
@@ -557,7 +515,7 @@
 	 * reached or the state of the chain has changed while we
 	 * dropped the locks.
 	 */
-	if (!rt_mutex_real_waiter(waiter))
+	if (!waiter)
 		goto out_unlock_pi;
 
 	/*
@@ -640,6 +598,7 @@
 	 * walk, we detected a deadlock.
 	 */
 	if (lock == orig_lock || rt_mutex_owner(lock) == top_task) {
+		debug_rt_mutex_deadlock(chwalk, orig_waiter, lock);
 		raw_spin_unlock(&lock->wait_lock);
 		ret = -EDEADLK;
 		goto out_unlock_pi;
@@ -736,16 +695,13 @@
 	 * follow here. This is the end of the chain we are walking.
 	 */
 	if (!rt_mutex_owner(lock)) {
-		struct rt_mutex_waiter *lock_top_waiter;
-
 		/*
 		 * If the requeue [7] above changed the top waiter,
 		 * then we need to wake the new top waiter up to try
 		 * to get the lock.
 		 */
-		lock_top_waiter = rt_mutex_top_waiter(lock);
-		if (prerequeue_top_waiter != lock_top_waiter)
-			rt_mutex_wake_waiter(lock_top_waiter);
+		if (prerequeue_top_waiter != rt_mutex_top_waiter(lock))
+			wake_up_process(rt_mutex_top_waiter(lock)->task);
 		raw_spin_unlock_irq(&lock->wait_lock);
 		return 0;
 	}
@@ -846,11 +802,9 @@
  * @task:   The task which wants to acquire the lock
  * @waiter: The waiter that is queued to the lock's wait tree if the
  *	    callsite called task_blocked_on_lock(), otherwise NULL
- * @mode:   Lock steal mode (STEAL_NORMAL, STEAL_LATERAL)
  */
-static int __try_to_take_rt_mutex(struct rt_mutex *lock,
-				  struct task_struct *task,
-				  struct rt_mutex_waiter *waiter, int mode)
+static int try_to_take_rt_mutex(struct rt_mutex *lock, struct task_struct *task,
+				struct rt_mutex_waiter *waiter)
 {
 	lockdep_assert_held(&lock->wait_lock);
 
@@ -886,11 +840,12 @@
 	 */
 	if (waiter) {
 		/*
-		 * If waiter is not the highest priority waiter of @lock,
-		 * or its peer when lateral steal is allowed, give up.
+		 * If waiter is not the highest priority waiter of
+		 * @lock, give up.
 		 */
-		if (!rt_mutex_steal(lock, waiter, mode))
+		if (waiter != rt_mutex_top_waiter(lock))
 			return 0;
+
 		/*
 		 * We can acquire the lock. Remove the waiter from the
 		 * lock waiters tree.
@@ -908,12 +863,14 @@
 		 */
 		if (rt_mutex_has_waiters(lock)) {
 			/*
-			 * If @task->prio is greater than the top waiter
-			 * priority (kernel view), or equal to it when a
-			 * lateral steal is forbidden, @task lost.
+			 * If @task->prio is greater than or equal to
+			 * the top waiter priority (kernel view),
+			 * @task lost.
 			 */
-			if (!rt_mutex_steal(lock, task_to_waiter(task), mode))
+			if (!rt_mutex_waiter_less(task_to_waiter(task),
+						  rt_mutex_top_waiter(lock)))
 				return 0;
+
 			/*
 			 * The current top waiter stays enqueued. We
 			 * don't have to change anything in the lock
@@ -960,329 +917,6 @@
 	return 1;
 }
 
-#ifdef CONFIG_PREEMPT_RT
-/*
- * preemptible spin_lock functions:
- */
-static inline void rt_spin_lock_fastlock(struct rt_mutex *lock,
-					 void  (*slowfn)(struct rt_mutex *lock))
-{
-	might_sleep_no_state_check();
-
-	if (likely(rt_mutex_cmpxchg_acquire(lock, NULL, current)))
-		return;
-	else
-		slowfn(lock);
-}
-
-static inline void rt_spin_lock_fastunlock(struct rt_mutex *lock,
-					   void  (*slowfn)(struct rt_mutex *lock))
-{
-	if (likely(rt_mutex_cmpxchg_release(lock, current, NULL)))
-		return;
-	else
-		slowfn(lock);
-}
-#ifdef CONFIG_SMP
-/*
- * Note that owner is a speculative pointer and dereferencing relies
- * on rcu_read_lock() and the check against the lock owner.
- */
-static int adaptive_wait(struct rt_mutex *lock,
-			 struct task_struct *owner)
-{
-	int res = 0;
-
-	rcu_read_lock();
-	for (;;) {
-		if (owner != rt_mutex_owner(lock))
-			break;
-		/*
-		 * Ensure that owner->on_cpu is dereferenced _after_
-		 * checking the above to be valid.
-		 */
-		barrier();
-		if (!owner->on_cpu) {
-			res = 1;
-			break;
-		}
-		cpu_relax();
-	}
-	rcu_read_unlock();
-	return res;
-}
-#else
-static int adaptive_wait(struct rt_mutex *lock,
-			 struct task_struct *orig_owner)
-{
-	return 1;
-}
-#endif
-
-static int task_blocks_on_rt_mutex(struct rt_mutex *lock,
-				   struct rt_mutex_waiter *waiter,
-				   struct task_struct *task,
-				   enum rtmutex_chainwalk chwalk);
-/*
- * Slow path lock function spin_lock style: this variant is very
- * careful not to miss any non-lock wakeups.
- *
- * We store the current state under p->pi_lock in p->saved_state and
- * the try_to_wake_up() code handles this accordingly.
- */
-void __sched rt_spin_lock_slowlock_locked(struct rt_mutex *lock,
-					  struct rt_mutex_waiter *waiter,
-					  unsigned long flags)
-{
-	struct task_struct *lock_owner, *self = current;
-	struct rt_mutex_waiter *top_waiter;
-	int ret;
-
-	if (__try_to_take_rt_mutex(lock, self, NULL, STEAL_LATERAL))
-		return;
-
-	BUG_ON(rt_mutex_owner(lock) == self);
-
-	/*
-	 * We save whatever state the task is in and we'll restore it
-	 * after acquiring the lock taking real wakeups into account
-	 * as well. We are serialized via pi_lock against wakeups. See
-	 * try_to_wake_up().
-	 */
-	raw_spin_lock(&self->pi_lock);
-	self->saved_state = self->state;
-	__set_current_state_no_track(TASK_UNINTERRUPTIBLE);
-	raw_spin_unlock(&self->pi_lock);
-
-	ret = task_blocks_on_rt_mutex(lock, waiter, self, RT_MUTEX_MIN_CHAINWALK);
-	BUG_ON(ret);
-
-	for (;;) {
-		/* Try to acquire the lock again. */
-		if (__try_to_take_rt_mutex(lock, self, waiter, STEAL_LATERAL))
-			break;
-
-		top_waiter = rt_mutex_top_waiter(lock);
-		lock_owner = rt_mutex_owner(lock);
-
-		raw_spin_unlock_irqrestore(&lock->wait_lock, flags);
-
-		if (top_waiter != waiter || adaptive_wait(lock, lock_owner))
-			preempt_schedule_lock();
-
-		raw_spin_lock_irqsave(&lock->wait_lock, flags);
-
-		raw_spin_lock(&self->pi_lock);
-		__set_current_state_no_track(TASK_UNINTERRUPTIBLE);
-		raw_spin_unlock(&self->pi_lock);
-	}
-
-	/*
-	 * Restore the task state to current->saved_state. We set it
-	 * to the original state above and the try_to_wake_up() code
-	 * has possibly updated it when a real (non-rtmutex) wakeup
-	 * happened while we were blocked. Clear saved_state so
-	 * try_to_wakeup() does not get confused.
-	 */
-	raw_spin_lock(&self->pi_lock);
-	__set_current_state_no_track(self->saved_state);
-	self->saved_state = TASK_RUNNING;
-	raw_spin_unlock(&self->pi_lock);
-
-	/*
-	 * try_to_take_rt_mutex() sets the waiter bit
-	 * unconditionally. We might have to fix that up:
-	 */
-	fixup_rt_mutex_waiters(lock);
-
-	BUG_ON(rt_mutex_has_waiters(lock) && waiter == rt_mutex_top_waiter(lock));
-	BUG_ON(!RB_EMPTY_NODE(&waiter->tree_entry));
-}
-
-static void noinline __sched rt_spin_lock_slowlock(struct rt_mutex *lock)
-{
-	struct rt_mutex_waiter waiter;
-	unsigned long flags;
-
-	rt_mutex_init_waiter(&waiter, true);
-
-	raw_spin_lock_irqsave(&lock->wait_lock, flags);
-	rt_spin_lock_slowlock_locked(lock, &waiter, flags);
-	raw_spin_unlock_irqrestore(&lock->wait_lock, flags);
-	debug_rt_mutex_free_waiter(&waiter);
-}
-
-static bool __sched __rt_mutex_unlock_common(struct rt_mutex *lock,
-					     struct wake_q_head *wake_q,
-					     struct wake_q_head *wq_sleeper);
-/*
- * Slow path to release a rt_mutex spin_lock style
- */
-void __sched rt_spin_lock_slowunlock(struct rt_mutex *lock)
-{
-	unsigned long flags;
-	DEFINE_WAKE_Q(wake_q);
-	DEFINE_WAKE_Q(wake_sleeper_q);
-	bool postunlock;
-
-	raw_spin_lock_irqsave(&lock->wait_lock, flags);
-	postunlock = __rt_mutex_unlock_common(lock, &wake_q, &wake_sleeper_q);
-	raw_spin_unlock_irqrestore(&lock->wait_lock, flags);
-
-	if (postunlock)
-		rt_mutex_postunlock(&wake_q, &wake_sleeper_q);
-}
-
-void __lockfunc rt_spin_lock(spinlock_t *lock)
-{
-	spin_acquire(&lock->dep_map, 0, 0, _RET_IP_);
-	rt_spin_lock_fastlock(&lock->lock, rt_spin_lock_slowlock);
-	rcu_read_lock();
-	migrate_disable();
-}
-EXPORT_SYMBOL(rt_spin_lock);
-
-void __lockfunc __rt_spin_lock(struct rt_mutex *lock)
-{
-	rt_spin_lock_fastlock(lock, rt_spin_lock_slowlock);
-}
-
-#ifdef CONFIG_DEBUG_LOCK_ALLOC
-void __lockfunc rt_spin_lock_nested(spinlock_t *lock, int subclass)
-{
-	spin_acquire(&lock->dep_map, subclass, 0, _RET_IP_);
-	rt_spin_lock_fastlock(&lock->lock, rt_spin_lock_slowlock);
-	rcu_read_lock();
-	migrate_disable();
-}
-EXPORT_SYMBOL(rt_spin_lock_nested);
-
-void __lockfunc rt_spin_lock_nest_lock(spinlock_t *lock,
-				       struct lockdep_map *nest_lock)
-{
-	spin_acquire_nest(&lock->dep_map, 0, 0, nest_lock, _RET_IP_);
-	rt_spin_lock_fastlock(&lock->lock, rt_spin_lock_slowlock);
-	rcu_read_lock();
-	migrate_disable();
-}
-EXPORT_SYMBOL(rt_spin_lock_nest_lock);
-#endif
-
-void __lockfunc rt_spin_unlock(spinlock_t *lock)
-{
-	/* NOTE: we always pass in '1' for nested, for simplicity */
-	spin_release(&lock->dep_map, _RET_IP_);
-	migrate_enable();
-	rcu_read_unlock();
-	rt_spin_lock_fastunlock(&lock->lock, rt_spin_lock_slowunlock);
-}
-EXPORT_SYMBOL(rt_spin_unlock);
-
-void __lockfunc __rt_spin_unlock(struct rt_mutex *lock)
-{
-	rt_spin_lock_fastunlock(lock, rt_spin_lock_slowunlock);
-}
-EXPORT_SYMBOL(__rt_spin_unlock);
-
-/*
- * Wait for the lock to get unlocked: instead of polling for an unlock
- * (like raw spinlocks do), we lock and unlock, to force the kernel to
- * schedule if there's contention:
- */
-void __lockfunc rt_spin_lock_unlock(spinlock_t *lock)
-{
-	spin_lock(lock);
-	spin_unlock(lock);
-}
-EXPORT_SYMBOL(rt_spin_lock_unlock);
-
-int __lockfunc rt_spin_trylock(spinlock_t *lock)
-{
-	int ret;
-
-	ret = __rt_mutex_trylock(&lock->lock);
-	if (ret) {
-		spin_acquire(&lock->dep_map, 0, 1, _RET_IP_);
-		rcu_read_lock();
-		migrate_disable();
-	}
-	return ret;
-}
-EXPORT_SYMBOL(rt_spin_trylock);
-
-int __lockfunc rt_spin_trylock_bh(spinlock_t *lock)
-{
-	int ret;
-
-	local_bh_disable();
-	ret = __rt_mutex_trylock(&lock->lock);
-	if (ret) {
-		spin_acquire(&lock->dep_map, 0, 1, _RET_IP_);
-		rcu_read_lock();
-		migrate_disable();
-	} else {
-		local_bh_enable();
-	}
-	return ret;
-}
-EXPORT_SYMBOL(rt_spin_trylock_bh);
-
-void
-__rt_spin_lock_init(spinlock_t *lock, const char *name, struct lock_class_key *key)
-{
-#ifdef CONFIG_DEBUG_LOCK_ALLOC
-	/*
-	 * Make sure we are not reinitializing a held lock:
-	 */
-	debug_check_no_locks_freed((void *)lock, sizeof(*lock));
-	lockdep_init_map(&lock->dep_map, name, key, 0);
-#endif
-}
-EXPORT_SYMBOL(__rt_spin_lock_init);
-
-#endif /* PREEMPT_RT */
-
-#ifdef CONFIG_PREEMPT_RT
-	static inline int __sched
-__mutex_lock_check_stamp(struct rt_mutex *lock, struct ww_acquire_ctx *ctx)
-{
-	struct ww_mutex *ww = container_of(lock, struct ww_mutex, base.lock);
-	struct ww_acquire_ctx *hold_ctx = READ_ONCE(ww->ctx);
-
-	if (!hold_ctx)
-		return 0;
-
-	if (unlikely(ctx == hold_ctx))
-		return -EALREADY;
-
-	if (ctx->stamp - hold_ctx->stamp <= LONG_MAX &&
-	    (ctx->stamp != hold_ctx->stamp || ctx > hold_ctx)) {
-#ifdef CONFIG_DEBUG_MUTEXES
-		DEBUG_LOCKS_WARN_ON(ctx->contending_lock);
-		ctx->contending_lock = ww;
-#endif
-		return -EDEADLK;
-	}
-
-	return 0;
-}
-#else
-	static inline int __sched
-__mutex_lock_check_stamp(struct rt_mutex *lock, struct ww_acquire_ctx *ctx)
-{
-	BUG();
-	return 0;
-}
-
-#endif
-
-static inline int
-try_to_take_rt_mutex(struct rt_mutex *lock, struct task_struct *task,
-		     struct rt_mutex_waiter *waiter)
-{
-	return __try_to_take_rt_mutex(lock, task, waiter, STEAL_NORMAL);
-}
-
 /*
  * Task blocks on lock.
  *
@@ -1315,22 +949,6 @@
 		return -EDEADLK;
 
 	raw_spin_lock(&task->pi_lock);
-	/*
-	 * In the case of futex requeue PI, this will be a proxy
-	 * lock. The task will wake unaware that it is enqueueed on
-	 * this lock. Avoid blocking on two locks and corrupting
-	 * pi_blocked_on via the PI_WAKEUP_INPROGRESS
-	 * flag. futex_wait_requeue_pi() sets this when it wakes up
-	 * before requeue (due to a signal or timeout). Do not enqueue
-	 * the task if PI_WAKEUP_INPROGRESS is set.
-	 */
-	if (task != current && task->pi_blocked_on == PI_WAKEUP_INPROGRESS) {
-		raw_spin_unlock(&task->pi_lock);
-		return -EAGAIN;
-	}
-
-       BUG_ON(rt_mutex_real_waiter(task->pi_blocked_on));
-
 	waiter->task = task;
 	waiter->lock = lock;
 	waiter->prio = task->prio;
@@ -1354,7 +972,7 @@
 		rt_mutex_enqueue_pi(owner, waiter);
 
 		rt_mutex_adjust_prio(owner);
-		if (rt_mutex_real_waiter(owner->pi_blocked_on))
+		if (owner->pi_blocked_on)
 			chain_walk = 1;
 	} else if (rt_mutex_cond_detect_deadlock(waiter, chwalk)) {
 		chain_walk = 1;
@@ -1396,7 +1014,6 @@
  * Called with lock->wait_lock held and interrupts disabled.
  */
 static void mark_wakeup_next_waiter(struct wake_q_head *wake_q,
-				    struct wake_q_head *wake_sleeper_q,
 				    struct rt_mutex *lock)
 {
 	struct rt_mutex_waiter *waiter;
@@ -1436,10 +1053,7 @@
 	 * Pairs with preempt_enable() in rt_mutex_postunlock();
 	 */
 	preempt_disable();
-	if (waiter->savestate)
-		wake_q_add_sleeper(wake_sleeper_q, waiter->task);
-	else
-		wake_q_add(wake_q, waiter->task);
+	wake_q_add(wake_q, waiter->task);
 	raw_spin_unlock(&current->pi_lock);
 }
 
@@ -1454,7 +1068,7 @@
 {
 	bool is_top_waiter = (waiter == rt_mutex_top_waiter(lock));
 	struct task_struct *owner = rt_mutex_owner(lock);
-	struct rt_mutex *next_lock = NULL;
+	struct rt_mutex *next_lock;
 
 	lockdep_assert_held(&lock->wait_lock);
 
@@ -1480,8 +1094,7 @@
 	rt_mutex_adjust_prio(owner);
 
 	/* Store the lock on which owner is blocked or NULL */
-	if (rt_mutex_real_waiter(owner->pi_blocked_on))
-		next_lock = task_blocked_on_lock(owner);
+	next_lock = task_blocked_on_lock(owner);
 
 	raw_spin_unlock(&owner->pi_lock);
 
@@ -1517,28 +1130,26 @@
 	raw_spin_lock_irqsave(&task->pi_lock, flags);
 
 	waiter = task->pi_blocked_on;
-	if (!rt_mutex_real_waiter(waiter) ||
-	    rt_mutex_waiter_equal(waiter, task_to_waiter(task))) {
+	if (!waiter || rt_mutex_waiter_equal(waiter, task_to_waiter(task))) {
 		raw_spin_unlock_irqrestore(&task->pi_lock, flags);
 		return;
 	}
 	next_lock = waiter->lock;
+	raw_spin_unlock_irqrestore(&task->pi_lock, flags);
 
 	/* gets dropped in rt_mutex_adjust_prio_chain()! */
 	get_task_struct(task);
 
-	raw_spin_unlock_irqrestore(&task->pi_lock, flags);
 	rt_mutex_adjust_prio_chain(task, RT_MUTEX_MIN_CHAINWALK, NULL,
 				   next_lock, NULL, task);
 }
 
-void rt_mutex_init_waiter(struct rt_mutex_waiter *waiter, bool savestate)
+void rt_mutex_init_waiter(struct rt_mutex_waiter *waiter)
 {
 	debug_rt_mutex_init_waiter(waiter);
 	RB_CLEAR_NODE(&waiter->pi_tree_entry);
 	RB_CLEAR_NODE(&waiter->tree_entry);
 	waiter->task = NULL;
-	waiter->savestate = savestate;
 }
 
 /**
@@ -1554,8 +1165,7 @@
 static int __sched
 __rt_mutex_slowlock(struct rt_mutex *lock, int state,
 		    struct hrtimer_sleeper *timeout,
-		    struct rt_mutex_waiter *waiter,
-		    struct ww_acquire_ctx *ww_ctx)
+		    struct rt_mutex_waiter *waiter)
 {
 	int ret = 0;
 
@@ -1565,22 +1175,23 @@
 		if (try_to_take_rt_mutex(lock, current, waiter))
 			break;
 
-		if (timeout && !timeout->task) {
-			ret = -ETIMEDOUT;
-			break;
-		}
-		if (signal_pending_state(state, current)) {
-			ret = -EINTR;
-			break;
-		}
-
-		if (ww_ctx && ww_ctx->acquired > 0) {
-			ret = __mutex_lock_check_stamp(lock, ww_ctx);
+		/*
+		 * TASK_INTERRUPTIBLE checks for signals and
+		 * timeout. Ignored otherwise.
+		 */
+		if (likely(state == TASK_INTERRUPTIBLE)) {
+			/* Signal pending? */
+			if (signal_pending(current))
+				ret = -EINTR;
+			if (timeout && !timeout->task)
+				ret = -ETIMEDOUT;
 			if (ret)
 				break;
 		}
 
 		raw_spin_unlock_irq(&lock->wait_lock);
+
+		debug_rt_mutex_print_deadlock(waiter);
 
 		schedule();
 
@@ -1603,147 +1214,14 @@
 	if (res != -EDEADLOCK || detect_deadlock)
 		return;
 
+	/*
+	 * Yell lowdly and stop the task right here.
+	 */
+	rt_mutex_print_deadlock(w);
 	while (1) {
 		set_current_state(TASK_INTERRUPTIBLE);
 		schedule();
 	}
-}
-
-static __always_inline void ww_mutex_lock_acquired(struct ww_mutex *ww,
-						   struct ww_acquire_ctx *ww_ctx)
-{
-#ifdef CONFIG_DEBUG_MUTEXES
-	/*
-	 * If this WARN_ON triggers, you used ww_mutex_lock to acquire,
-	 * but released with a normal mutex_unlock in this call.
-	 *
-	 * This should never happen, always use ww_mutex_unlock.
-	 */
-	DEBUG_LOCKS_WARN_ON(ww->ctx);
-
-	/*
-	 * Not quite done after calling ww_acquire_done() ?
-	 */
-	DEBUG_LOCKS_WARN_ON(ww_ctx->done_acquire);
-
-	if (ww_ctx->contending_lock) {
-		/*
-		 * After -EDEADLK you tried to
-		 * acquire a different ww_mutex? Bad!
-		 */
-		DEBUG_LOCKS_WARN_ON(ww_ctx->contending_lock != ww);
-
-		/*
-		 * You called ww_mutex_lock after receiving -EDEADLK,
-		 * but 'forgot' to unlock everything else first?
-		 */
-		DEBUG_LOCKS_WARN_ON(ww_ctx->acquired > 0);
-		ww_ctx->contending_lock = NULL;
-	}
-
-	/*
-	 * Naughty, using a different class will lead to undefined behavior!
-	 */
-	DEBUG_LOCKS_WARN_ON(ww_ctx->ww_class != ww->ww_class);
-#endif
-	ww_ctx->acquired++;
-}
-
-#ifdef CONFIG_PREEMPT_RT
-static void ww_mutex_account_lock(struct rt_mutex *lock,
-				  struct ww_acquire_ctx *ww_ctx)
-{
-	struct ww_mutex *ww = container_of(lock, struct ww_mutex, base.lock);
-	struct rt_mutex_waiter *waiter, *n;
-
-	/*
-	 * This branch gets optimized out for the common case,
-	 * and is only important for ww_mutex_lock.
-	 */
-	ww_mutex_lock_acquired(ww, ww_ctx);
-	ww->ctx = ww_ctx;
-
-	/*
-	 * Give any possible sleeping processes the chance to wake up,
-	 * so they can recheck if they have to back off.
-	 */
-	rbtree_postorder_for_each_entry_safe(waiter, n, &lock->waiters.rb_root,
-					     tree_entry) {
-		/* XXX debug rt mutex waiter wakeup */
-
-		BUG_ON(waiter->lock != lock);
-		rt_mutex_wake_waiter(waiter);
-	}
-}
-
-#else
-
-static void ww_mutex_account_lock(struct rt_mutex *lock,
-				  struct ww_acquire_ctx *ww_ctx)
-{
-	BUG();
-}
-#endif
-
-int __sched rt_mutex_slowlock_locked(struct rt_mutex *lock, int state,
-				     struct hrtimer_sleeper *timeout,
-				     enum rtmutex_chainwalk chwalk,
-				     struct ww_acquire_ctx *ww_ctx,
-				     struct rt_mutex_waiter *waiter)
-{
-	int ret;
-
-#ifdef CONFIG_PREEMPT_RT
-	if (ww_ctx) {
-		struct ww_mutex *ww;
-
-		ww = container_of(lock, struct ww_mutex, base.lock);
-		if (unlikely(ww_ctx == READ_ONCE(ww->ctx)))
-			return -EALREADY;
-	}
-#endif
-
-	/* Try to acquire the lock again: */
-	if (try_to_take_rt_mutex(lock, current, NULL)) {
-		if (ww_ctx)
-			ww_mutex_account_lock(lock, ww_ctx);
-		return 0;
-	}
-
-	set_current_state(state);
-
-	/* Setup the timer, when timeout != NULL */
-	if (unlikely(timeout))
-		hrtimer_start_expires(&timeout->timer, HRTIMER_MODE_ABS);
-
-	ret = task_blocks_on_rt_mutex(lock, waiter, current, chwalk);
-
-	if (likely(!ret)) {
-		/* sleep on the mutex */
-		ret = __rt_mutex_slowlock(lock, state, timeout, waiter,
-					  ww_ctx);
-	} else if (ww_ctx) {
-		/* ww_mutex received EDEADLK, let it become EALREADY */
-		ret = __mutex_lock_check_stamp(lock, ww_ctx);
-		BUG_ON(!ret);
-	}
-
-	if (unlikely(ret)) {
-		__set_current_state(TASK_RUNNING);
-		remove_waiter(lock, waiter);
-		/* ww_mutex wants to report EDEADLK/EALREADY, let it */
-		if (!ww_ctx)
-			rt_mutex_handle_deadlock(ret, chwalk, waiter);
-	} else if (ww_ctx) {
-		ww_mutex_account_lock(lock, ww_ctx);
-	}
-
-	/*
-	 * try_to_take_rt_mutex() sets the waiter bit
-	 * unconditionally. We might have to fix that up.
-	 */
-	fixup_rt_mutex_waiters(lock);
-	return ret;
 }
 
 /*
@@ -1752,14 +1230,13 @@
 static int __sched
 rt_mutex_slowlock(struct rt_mutex *lock, int state,
 		  struct hrtimer_sleeper *timeout,
-		  enum rtmutex_chainwalk chwalk,
-		  struct ww_acquire_ctx *ww_ctx)
+		  enum rtmutex_chainwalk chwalk)
 {
 	struct rt_mutex_waiter waiter;
 	unsigned long flags;
 	int ret = 0;
 
-	rt_mutex_init_waiter(&waiter, false);
+	rt_mutex_init_waiter(&waiter);
 
 	/*
 	 * Technically we could use raw_spin_[un]lock_irq() here, but this can
@@ -1771,8 +1248,35 @@
 	 */
 	raw_spin_lock_irqsave(&lock->wait_lock, flags);
 
-	ret = rt_mutex_slowlock_locked(lock, state, timeout, chwalk, ww_ctx,
-				       &waiter);
+	/* Try to acquire the lock again: */
+	if (try_to_take_rt_mutex(lock, current, NULL)) {
+		raw_spin_unlock_irqrestore(&lock->wait_lock, flags);
+		return 0;
+	}
+
+	set_current_state(state);
+
+	/* Setup the timer, when timeout != NULL */
+	if (unlikely(timeout))
+		hrtimer_start_expires(&timeout->timer, HRTIMER_MODE_ABS);
+
+	ret = task_blocks_on_rt_mutex(lock, &waiter, current, chwalk);
+
+	if (likely(!ret))
+		/* sleep on the mutex */
+		ret = __rt_mutex_slowlock(lock, state, timeout, &waiter);
+
+	if (unlikely(ret)) {
+		__set_current_state(TASK_RUNNING);
+		remove_waiter(lock, &waiter);
+		rt_mutex_handle_deadlock(ret, chwalk, &waiter);
+	}
+
+	/*
+	 * try_to_take_rt_mutex() sets the waiter bit
+	 * unconditionally. We might have to fix that up.
+	 */
+	fixup_rt_mutex_waiters(lock);
 
 	raw_spin_unlock_irqrestore(&lock->wait_lock, flags);
 
@@ -1833,8 +1337,7 @@
  * Return whether the current task needs to call rt_mutex_postunlock().
  */
 static bool __sched rt_mutex_slowunlock(struct rt_mutex *lock,
-					struct wake_q_head *wake_q,
-					struct wake_q_head *wake_sleeper_q)
+					struct wake_q_head *wake_q)
 {
 	unsigned long flags;
 
@@ -1888,7 +1391,7 @@
 	 *
 	 * Queue the next waiter for wakeup once we release the wait_lock.
 	 */
-	mark_wakeup_next_waiter(wake_q, wake_sleeper_q, lock);
+	mark_wakeup_next_waiter(wake_q, lock);
 	raw_spin_unlock_irqrestore(&lock->wait_lock, flags);
 
 	return true; /* call rt_mutex_postunlock() */
@@ -1902,16 +1405,29 @@
  */
 static inline int
 rt_mutex_fastlock(struct rt_mutex *lock, int state,
-		  struct ww_acquire_ctx *ww_ctx,
 		  int (*slowfn)(struct rt_mutex *lock, int state,
 				struct hrtimer_sleeper *timeout,
-				enum rtmutex_chainwalk chwalk,
-				struct ww_acquire_ctx *ww_ctx))
+				enum rtmutex_chainwalk chwalk))
 {
 	if (likely(rt_mutex_cmpxchg_acquire(lock, NULL, current)))
 		return 0;
 
-	return slowfn(lock, state, NULL, RT_MUTEX_MIN_CHAINWALK, ww_ctx);
+	return slowfn(lock, state, NULL, RT_MUTEX_MIN_CHAINWALK);
+}
+
+static inline int
+rt_mutex_timed_fastlock(struct rt_mutex *lock, int state,
+			struct hrtimer_sleeper *timeout,
+			enum rtmutex_chainwalk chwalk,
+			int (*slowfn)(struct rt_mutex *lock, int state,
+				      struct hrtimer_sleeper *timeout,
+				      enum rtmutex_chainwalk chwalk))
+{
+	if (chwalk == RT_MUTEX_MIN_CHAINWALK &&
+	    likely(rt_mutex_cmpxchg_acquire(lock, NULL, current)))
+		return 0;
+
+	return slowfn(lock, state, timeout, chwalk);
 }
 
 static inline int
@@ -1927,11 +1443,9 @@
 /*
  * Performs the wakeup of the top-waiter and re-enables preemption.
  */
-void rt_mutex_postunlock(struct wake_q_head *wake_q,
-			 struct wake_q_head *wake_sleeper_q)
+void rt_mutex_postunlock(struct wake_q_head *wake_q)
 {
 	wake_up_q(wake_q);
-	wake_up_q_sleeper(wake_sleeper_q);
 
 	/* Pairs with preempt_disable() in rt_mutex_slowunlock() */
 	preempt_enable();
@@ -1940,48 +1454,24 @@
 static inline void
 rt_mutex_fastunlock(struct rt_mutex *lock,
 		    bool (*slowfn)(struct rt_mutex *lock,
-				   struct wake_q_head *wqh,
-				   struct wake_q_head *wq_sleeper))
+				   struct wake_q_head *wqh))
 {
 	DEFINE_WAKE_Q(wake_q);
-	DEFINE_WAKE_Q(wake_sleeper_q);
 
 	if (likely(rt_mutex_cmpxchg_release(lock, current, NULL)))
 		return;
 
-	if (slowfn(lock, &wake_q, &wake_sleeper_q))
-		rt_mutex_postunlock(&wake_q, &wake_sleeper_q);
-}
-
-int __sched __rt_mutex_lock_state(struct rt_mutex *lock, int state)
-{
-	might_sleep();
-	return rt_mutex_fastlock(lock, state, NULL, rt_mutex_slowlock);
-}
-
-/**
- * rt_mutex_lock_state - lock a rt_mutex with a given state
- *
- * @lock:      The rt_mutex to be locked
- * @state:     The state to set when blocking on the rt_mutex
- */
-static inline int __sched rt_mutex_lock_state(struct rt_mutex *lock,
-					      unsigned int subclass, int state)
-{
-	int ret;
-
-	mutex_acquire(&lock->dep_map, subclass, 0, _RET_IP_);
-	ret = __rt_mutex_lock_state(lock, state);
-	if (ret)
-		mutex_release(&lock->dep_map, _RET_IP_);
-	trace_android_vh_record_rtmutex_lock_starttime(current, jiffies);
-
-	return ret;
+	if (slowfn(lock, &wake_q))
+		rt_mutex_postunlock(&wake_q);
 }
 
 static inline void __rt_mutex_lock(struct rt_mutex *lock, unsigned int subclass)
 {
-	rt_mutex_lock_state(lock, subclass, TASK_UNINTERRUPTIBLE);
+	might_sleep();
+
+	mutex_acquire(&lock->dep_map, subclass, 0, _RET_IP_);
+	rt_mutex_fastlock(lock, TASK_UNINTERRUPTIBLE, rt_mutex_slowlock);
+	trace_android_vh_record_rtmutex_lock_starttime(current, jiffies);
 }
 
 #ifdef CONFIG_DEBUG_LOCK_ALLOC
@@ -2022,7 +1512,18 @@
  */
 int __sched rt_mutex_lock_interruptible(struct rt_mutex *lock)
 {
-	return rt_mutex_lock_state(lock, 0, TASK_INTERRUPTIBLE);
+	int ret;
+
+	might_sleep();
+
+	mutex_acquire(&lock->dep_map, 0, 0, _RET_IP_);
+	ret = rt_mutex_fastlock(lock, TASK_INTERRUPTIBLE, rt_mutex_slowlock);
+	if (ret)
+		mutex_release(&lock->dep_map, _RET_IP_);
+	else
+		trace_android_vh_record_rtmutex_lock_starttime(current, jiffies);
+
+	return ret;
 }
 EXPORT_SYMBOL_GPL(rt_mutex_lock_interruptible);
 
@@ -2039,17 +1540,38 @@
 	return __rt_mutex_slowtrylock(lock);
 }
 
-int __sched __rt_mutex_trylock(struct rt_mutex *lock)
+/**
+ * rt_mutex_timed_lock - lock a rt_mutex interruptible
+ *			the timeout structure is provided
+ *			by the caller
+ *
+ * @lock:		the rt_mutex to be locked
+ * @timeout:		timeout structure or NULL (no timeout)
+ *
+ * Returns:
+ *  0		on success
+ * -EINTR	when interrupted by a signal
+ * -ETIMEDOUT	when the timeout expired
+ */
+int
+rt_mutex_timed_lock(struct rt_mutex *lock, struct hrtimer_sleeper *timeout)
 {
-#ifdef CONFIG_PREEMPT_RT
-	if (WARN_ON_ONCE(in_irq() || in_nmi()))
-#else
-	if (WARN_ON_ONCE(in_irq() || in_nmi() || in_serving_softirq()))
-#endif
-		return 0;
+	int ret;
 
-	return rt_mutex_fasttrylock(lock, rt_mutex_slowtrylock);
+	might_sleep();
+
+	mutex_acquire(&lock->dep_map, 0, 0, _RET_IP_);
+	ret = rt_mutex_timed_fastlock(lock, TASK_INTERRUPTIBLE, timeout,
+				       RT_MUTEX_MIN_CHAINWALK,
+				       rt_mutex_slowlock);
+	if (ret)
+		mutex_release(&lock->dep_map, _RET_IP_);
+	else
+		trace_android_vh_record_rtmutex_lock_starttime(current, jiffies);
+
+	return ret;
 }
+EXPORT_SYMBOL_GPL(rt_mutex_timed_lock);
 
 /**
  * rt_mutex_trylock - try to lock a rt_mutex
@@ -2066,7 +1588,10 @@
 {
 	int ret;
 
-	ret = __rt_mutex_trylock(lock);
+	if (WARN_ON_ONCE(in_irq() || in_nmi() || in_serving_softirq()))
+		return 0;
+
+	ret = rt_mutex_fasttrylock(lock, rt_mutex_slowtrylock);
 	if (ret)
 		mutex_acquire(&lock->dep_map, 0, 1, _RET_IP_);
 	else
@@ -2074,11 +1599,7 @@
 
 	return ret;
 }
-
-void __sched __rt_mutex_unlock(struct rt_mutex *lock)
-{
-	rt_mutex_fastunlock(lock, rt_mutex_slowunlock);
-}
+EXPORT_SYMBOL_GPL(rt_mutex_trylock);
 
 /**
  * rt_mutex_unlock - unlock a rt_mutex
@@ -2088,14 +1609,17 @@
 void __sched rt_mutex_unlock(struct rt_mutex *lock)
 {
 	mutex_release(&lock->dep_map, _RET_IP_);
-	__rt_mutex_unlock(lock);
+	rt_mutex_fastunlock(lock, rt_mutex_slowunlock);
 	trace_android_vh_record_rtmutex_lock_starttime(current, 0);
 }
 EXPORT_SYMBOL_GPL(rt_mutex_unlock);
 
-static bool __sched __rt_mutex_unlock_common(struct rt_mutex *lock,
-					     struct wake_q_head *wake_q,
-					     struct wake_q_head *wq_sleeper)
+/**
+ * Futex variant, that since futex variants do not use the fast-path, can be
+ * simple and will not need to retry.
+ */
+bool __sched __rt_mutex_futex_unlock(struct rt_mutex *lock,
+				    struct wake_q_head *wake_q)
 {
 	lockdep_assert_held(&lock->wait_lock);
 
@@ -2112,35 +1636,23 @@
 	 * avoid inversion prior to the wakeup.  preempt_disable()
 	 * therein pairs with rt_mutex_postunlock().
 	 */
-	mark_wakeup_next_waiter(wake_q, wq_sleeper, lock);
+	mark_wakeup_next_waiter(wake_q, lock);
 
 	return true; /* call postunlock() */
-}
-
-/**
- * Futex variant, that since futex variants do not use the fast-path, can be
- * simple and will not need to retry.
- */
-bool __sched __rt_mutex_futex_unlock(struct rt_mutex *lock,
-				     struct wake_q_head *wake_q,
-				     struct wake_q_head *wq_sleeper)
-{
-	return __rt_mutex_unlock_common(lock, wake_q, wq_sleeper);
 }
 
 void __sched rt_mutex_futex_unlock(struct rt_mutex *lock)
 {
 	DEFINE_WAKE_Q(wake_q);
-	DEFINE_WAKE_Q(wake_sleeper_q);
 	unsigned long flags;
 	bool postunlock;
 
 	raw_spin_lock_irqsave(&lock->wait_lock, flags);
-	postunlock = __rt_mutex_futex_unlock(lock, &wake_q, &wake_sleeper_q);
+	postunlock = __rt_mutex_futex_unlock(lock, &wake_q);
 	raw_spin_unlock_irqrestore(&lock->wait_lock, flags);
 
 	if (postunlock)
-		rt_mutex_postunlock(&wake_q, &wake_sleeper_q);
+		rt_mutex_postunlock(&wake_q);
 }
 
 /**
@@ -2154,6 +1666,9 @@
 void rt_mutex_destroy(struct rt_mutex *lock)
 {
 	WARN_ON(rt_mutex_is_locked(lock));
+#ifdef CONFIG_DEBUG_RT_MUTEXES
+	lock->magic = NULL;
+#endif
 }
 EXPORT_SYMBOL_GPL(rt_mutex_destroy);
 
@@ -2176,7 +1691,7 @@
 	if (name && key)
 		debug_rt_mutex_init(lock, name, key);
 }
-EXPORT_SYMBOL(__rt_mutex_init);
+EXPORT_SYMBOL_GPL(__rt_mutex_init);
 
 /**
  * rt_mutex_init_proxy_locked - initialize and lock a rt_mutex on behalf of a
@@ -2196,14 +1711,6 @@
 				struct task_struct *proxy_owner)
 {
 	__rt_mutex_init(lock, NULL, NULL);
-#ifdef CONFIG_DEBUG_SPINLOCK
-	/*
-	 * get another key class for the wait_lock. LOCK_PI and UNLOCK_PI is
-	 * holding the ->wait_lock of the proxy_lock while unlocking a sleeping
-	 * lock.
-	 */
-	raw_spin_lock_init(&lock->wait_lock);
-#endif
 	debug_rt_mutex_proxy_lock(lock, proxy_owner);
 	rt_mutex_set_owner(lock, proxy_owner);
 }
@@ -2224,26 +1731,6 @@
 {
 	debug_rt_mutex_proxy_unlock(lock);
 	rt_mutex_set_owner(lock, NULL);
-}
-
-static void fixup_rt_mutex_blocked(struct rt_mutex *lock)
-{
-	struct task_struct *tsk = current;
-	/*
-	 * RT has a problem here when the wait got interrupted by a timeout
-	 * or a signal. task->pi_blocked_on is still set. The task must
-	 * acquire the hash bucket lock when returning from this function.
-	 *
-	 * If the hash bucket lock is contended then the
-	 * BUG_ON(rt_mutex_real_waiter(task->pi_blocked_on)) in
-	 * task_blocks_on_rt_mutex() will trigger. This can be avoided by
-	 * clearing task->pi_blocked_on which removes the task from the
-	 * boosting chain of the rtmutex. That's correct because the task
-	 * is not longer blocked on it.
-	 */
-	raw_spin_lock(&tsk->pi_lock);
-	tsk->pi_blocked_on = NULL;
-	raw_spin_unlock(&tsk->pi_lock);
 }
 
 /**
@@ -2276,34 +1763,6 @@
 	if (try_to_take_rt_mutex(lock, task, NULL))
 		return 1;
 
-#ifdef CONFIG_PREEMPT_RT
-	/*
-	 * In PREEMPT_RT there's an added race.
-	 * If the task, that we are about to requeue, times out,
-	 * it can set the PI_WAKEUP_INPROGRESS. This tells the requeue
-	 * to skip this task. But right after the task sets
-	 * its pi_blocked_on to PI_WAKEUP_INPROGRESS it can then
-	 * block on the spin_lock(&hb->lock), which in RT is an rtmutex.
-	 * This will replace the PI_WAKEUP_INPROGRESS with the actual
-	 * lock that it blocks on. We *must not* place this task
-	 * on this proxy lock in that case.
-	 *
-	 * To prevent this race, we first take the task's pi_lock
-	 * and check if it has updated its pi_blocked_on. If it has,
-	 * we assume that it woke up and we return -EAGAIN.
-	 * Otherwise, we set the task's pi_blocked_on to
-	 * PI_REQUEUE_INPROGRESS, so that if the task is waking up
-	 * it will know that we are in the process of requeuing it.
-	 */
-	raw_spin_lock(&task->pi_lock);
-	if (task->pi_blocked_on) {
-		raw_spin_unlock(&task->pi_lock);
-		return -EAGAIN;
-	}
-	task->pi_blocked_on = PI_REQUEUE_INPROGRESS;
-	raw_spin_unlock(&task->pi_lock);
-#endif
-
 	/* We enforce deadlock detection for futexes */
 	ret = task_blocks_on_rt_mutex(lock, waiter, task,
 				      RT_MUTEX_FULL_CHAINWALK);
@@ -2318,8 +1777,7 @@
 		ret = 0;
 	}
 
-	if (ret)
-		fixup_rt_mutex_blocked(lock);
+	debug_rt_mutex_print_deadlock(waiter);
 
 	return ret;
 }
@@ -2404,15 +1862,12 @@
 	raw_spin_lock_irq(&lock->wait_lock);
 	/* sleep on the mutex */
 	set_current_state(TASK_INTERRUPTIBLE);
-	ret = __rt_mutex_slowlock(lock, TASK_INTERRUPTIBLE, to, waiter, NULL);
+	ret = __rt_mutex_slowlock(lock, TASK_INTERRUPTIBLE, to, waiter);
 	/*
 	 * try_to_take_rt_mutex() sets the waiter bit unconditionally. We might
 	 * have to fix that up.
 	 */
 	fixup_rt_mutex_waiters(lock);
-	if (ret)
-		fixup_rt_mutex_blocked(lock);
-
 	raw_spin_unlock_irq(&lock->wait_lock);
 
 	return ret;
@@ -2474,97 +1929,3 @@
 
 	return cleanup;
 }
-
-static inline int
-ww_mutex_deadlock_injection(struct ww_mutex *lock, struct ww_acquire_ctx *ctx)
-{
-#ifdef CONFIG_DEBUG_WW_MUTEX_SLOWPATH
-	unsigned int tmp;
-
-	if (ctx->deadlock_inject_countdown-- == 0) {
-		tmp = ctx->deadlock_inject_interval;
-		if (tmp > UINT_MAX/4)
-			tmp = UINT_MAX;
-		else
-			tmp = tmp*2 + tmp + tmp/2;
-
-		ctx->deadlock_inject_interval = tmp;
-		ctx->deadlock_inject_countdown = tmp;
-		ctx->contending_lock = lock;
-
-		ww_mutex_unlock(lock);
-
-		return -EDEADLK;
-	}
-#endif
-
-	return 0;
-}
-
-#ifdef CONFIG_PREEMPT_RT
-int __sched
-ww_mutex_lock_interruptible(struct ww_mutex *lock, struct ww_acquire_ctx *ctx)
-{
-	int ret;
-
-	might_sleep();
-
-	mutex_acquire_nest(&lock->base.dep_map, 0, 0,
-			   ctx ? &ctx->dep_map : NULL, _RET_IP_);
-	ret = rt_mutex_slowlock(&lock->base.lock, TASK_INTERRUPTIBLE, NULL, 0,
-				ctx);
-	if (ret)
-		mutex_release(&lock->base.dep_map, _RET_IP_);
-	else if (!ret && ctx && ctx->acquired > 1)
-		return ww_mutex_deadlock_injection(lock, ctx);
-
-	return ret;
-}
-EXPORT_SYMBOL(ww_mutex_lock_interruptible);
-
-int __sched
-ww_mutex_lock(struct ww_mutex *lock, struct ww_acquire_ctx *ctx)
-{
-	int ret;
-
-	might_sleep();
-
-	mutex_acquire_nest(&lock->base.dep_map, 0, 0,
-			   ctx ? &ctx->dep_map : NULL, _RET_IP_);
-	ret = rt_mutex_slowlock(&lock->base.lock, TASK_UNINTERRUPTIBLE, NULL, 0,
-				ctx);
-	if (ret)
-		mutex_release(&lock->base.dep_map, _RET_IP_);
-	else if (!ret && ctx && ctx->acquired > 1)
-		return ww_mutex_deadlock_injection(lock, ctx);
-
-	return ret;
-}
-EXPORT_SYMBOL(ww_mutex_lock);
-
-void __sched ww_mutex_unlock(struct ww_mutex *lock)
-{
-	/*
-	 * The unlocking fastpath is the 0->1 transition from 'locked'
-	 * into 'unlocked' state:
-	 */
-	if (lock->ctx) {
-#ifdef CONFIG_DEBUG_MUTEXES
-		DEBUG_LOCKS_WARN_ON(!lock->ctx->acquired);
-#endif
-		if (lock->ctx->acquired > 0)
-			lock->ctx->acquired--;
-		lock->ctx = NULL;
-	}
-
-	mutex_release(&lock->base.dep_map, _RET_IP_);
-	__rt_mutex_unlock(&lock->base.lock);
-}
-EXPORT_SYMBOL(ww_mutex_unlock);
-
-int __rt_mutex_owner_current(struct rt_mutex *lock)
-{
-	return rt_mutex_owner(lock) == current;
-}
-EXPORT_SYMBOL(__rt_mutex_owner_current);
-#endif
diff --git a/kernel/kernel/locking/rtmutex.h b/kernel/kernel/locking/rtmutex.h
index 338ccd2..732f96a 100644
--- a/kernel/kernel/locking/rtmutex.h
+++ b/kernel/kernel/locking/rtmutex.h
@@ -19,8 +19,15 @@
 #define debug_rt_mutex_proxy_unlock(l)			do { } while (0)
 #define debug_rt_mutex_unlock(l)			do { } while (0)
 #define debug_rt_mutex_init(m, n, k)			do { } while (0)
+#define debug_rt_mutex_deadlock(d, a ,l)		do { } while (0)
+#define debug_rt_mutex_print_deadlock(w)		do { } while (0)
 #define debug_rt_mutex_reset_waiter(w)			do { } while (0)
 
+static inline void rt_mutex_print_deadlock(struct rt_mutex_waiter *w)
+{
+	WARN(1, "rtmutex deadlock detected\n");
+}
+
 static inline bool debug_rt_mutex_detect_deadlock(struct rt_mutex_waiter *w,
 						  enum rtmutex_chainwalk walk)
 {
diff --git a/kernel/kernel/locking/rtmutex_common.h b/kernel/kernel/locking/rtmutex_common.h
index 248a7d9..ca6fb48 100644
--- a/kernel/kernel/locking/rtmutex_common.h
+++ b/kernel/kernel/locking/rtmutex_common.h
@@ -15,7 +15,6 @@
 
 #include <linux/rtmutex.h>
 #include <linux/sched/wake_q.h>
-#include <linux/sched/debug.h>
 
 /*
  * This is the control structure for tasks blocked on a rt_mutex,
@@ -30,8 +29,12 @@
 	struct rb_node          pi_tree_entry;
 	struct task_struct	*task;
 	struct rt_mutex		*lock;
+#ifdef CONFIG_DEBUG_RT_MUTEXES
+	unsigned long		ip;
+	struct pid		*deadlock_task_pid;
+	struct rt_mutex		*deadlock_lock;
+#endif
 	int prio;
-	bool			savestate;
 	u64 deadline;
 };
 
@@ -127,14 +130,11 @@
 /*
  * PI-futex support (proxy locking functions, etc.):
  */
-#define PI_WAKEUP_INPROGRESS	((struct rt_mutex_waiter *) 1)
-#define PI_REQUEUE_INPROGRESS	((struct rt_mutex_waiter *) 2)
-
 extern struct task_struct *rt_mutex_next_owner(struct rt_mutex *lock);
 extern void rt_mutex_init_proxy_locked(struct rt_mutex *lock,
 				       struct task_struct *proxy_owner);
 extern void rt_mutex_proxy_unlock(struct rt_mutex *lock);
-extern void rt_mutex_init_waiter(struct rt_mutex_waiter *waiter, bool savetate);
+extern void rt_mutex_init_waiter(struct rt_mutex_waiter *waiter);
 extern int __rt_mutex_start_proxy_lock(struct rt_mutex *lock,
 				     struct rt_mutex_waiter *waiter,
 				     struct task_struct *task);
@@ -152,27 +152,9 @@
 
 extern void rt_mutex_futex_unlock(struct rt_mutex *lock);
 extern bool __rt_mutex_futex_unlock(struct rt_mutex *lock,
-				 struct wake_q_head *wqh,
-				 struct wake_q_head *wq_sleeper);
+				 struct wake_q_head *wqh);
 
-extern void rt_mutex_postunlock(struct wake_q_head *wake_q,
-				struct wake_q_head *wake_sleeper_q);
-
-/* RW semaphore special interface */
-struct ww_acquire_ctx;
-
-extern int __rt_mutex_lock_state(struct rt_mutex *lock, int state);
-extern int __rt_mutex_trylock(struct rt_mutex *lock);
-extern void __rt_mutex_unlock(struct rt_mutex *lock);
-int __sched rt_mutex_slowlock_locked(struct rt_mutex *lock, int state,
-				     struct hrtimer_sleeper *timeout,
-				     enum rtmutex_chainwalk chwalk,
-				     struct ww_acquire_ctx *ww_ctx,
-				     struct rt_mutex_waiter *waiter);
-void __sched rt_spin_lock_slowlock_locked(struct rt_mutex *lock,
-					  struct rt_mutex_waiter *waiter,
-					  unsigned long flags);
-void __sched rt_spin_lock_slowunlock(struct rt_mutex *lock);
+extern void rt_mutex_postunlock(struct wake_q_head *wake_q);
 
 #ifdef CONFIG_DEBUG_RT_MUTEXES
 # include "rtmutex-debug.h"
diff --git a/kernel/kernel/locking/rwlock-rt.c b/kernel/kernel/locking/rwlock-rt.c
deleted file mode 100644
index 3d2d1f1..0000000
--- a/kernel/kernel/locking/rwlock-rt.c
+++ /dev/null
@@ -1,334 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-#include <linux/sched/debug.h>
-#include <linux/export.h>
-
-#include "rtmutex_common.h"
-#include <linux/rwlock_types_rt.h>
-
-/*
- * RT-specific reader/writer locks
- *
- * write_lock()
- *  1) Lock lock->rtmutex
- *  2) Remove the reader BIAS to force readers into the slow path
- *  3) Wait until all readers have left the critical region
- *  4) Mark it write locked
- *
- * write_unlock()
- *  1) Remove the write locked marker
- *  2) Set the reader BIAS so readers can use the fast path again
- *  3) Unlock lock->rtmutex to release blocked readers
- *
- * read_lock()
- *  1) Try fast path acquisition (reader BIAS is set)
- *  2) Take lock->rtmutex.wait_lock which protects the writelocked flag
- *  3) If !writelocked, acquire it for read
- *  4) If writelocked, block on lock->rtmutex
- *  5) unlock lock->rtmutex, goto 1)
- *
- * read_unlock()
- *  1) Try fast path release (reader count != 1)
- *  2) Wake the writer waiting in write_lock()#3
- *
- * read_lock()#3 has the consequence, that rw locks on RT are not writer
- * fair, but writers, which should be avoided in RT tasks (think tasklist
- * lock), are subject to the rtmutex priority/DL inheritance mechanism.
- *
- * It's possible to make the rw locks writer fair by keeping a list of
- * active readers. A blocked writer would force all newly incoming readers
- * to block on the rtmutex, but the rtmutex would have to be proxy locked
- * for one reader after the other. We can't use multi-reader inheritance
- * because there is no way to support that with
- * SCHED_DEADLINE. Implementing the one by one reader boosting/handover
- * mechanism is a major surgery for a very dubious value.
- *
- * The risk of writer starvation is there, but the pathological use cases
- * which trigger it are not necessarily the typical RT workloads.
- */
-
-void __rwlock_biased_rt_init(struct rt_rw_lock *lock, const char *name,
-			     struct lock_class_key *key)
-{
-#ifdef CONFIG_DEBUG_LOCK_ALLOC
-	/*
-	 * Make sure we are not reinitializing a held semaphore:
-	 */
-	debug_check_no_locks_freed((void *)lock, sizeof(*lock));
-	lockdep_init_map(&lock->dep_map, name, key, 0);
-#endif
-	atomic_set(&lock->readers, READER_BIAS);
-	rt_mutex_init(&lock->rtmutex);
-	lock->rtmutex.save_state = 1;
-}
-
-static int __read_rt_trylock(struct rt_rw_lock *lock)
-{
-	int r, old;
-
-	/*
-	 * Increment reader count, if lock->readers < 0, i.e. READER_BIAS is
-	 * set.
-	 */
-	for (r = atomic_read(&lock->readers); r < 0;) {
-		old = atomic_cmpxchg(&lock->readers, r, r + 1);
-		if (likely(old == r))
-			return 1;
-		r = old;
-	}
-	return 0;
-}
-
-static void __read_rt_lock(struct rt_rw_lock *lock)
-{
-	struct rt_mutex *m = &lock->rtmutex;
-	struct rt_mutex_waiter waiter;
-	unsigned long flags;
-
-	if (__read_rt_trylock(lock))
-		return;
-
-	raw_spin_lock_irqsave(&m->wait_lock, flags);
-	/*
-	 * Allow readers as long as the writer has not completely
-	 * acquired the semaphore for write.
-	 */
-	if (atomic_read(&lock->readers) != WRITER_BIAS) {
-		atomic_inc(&lock->readers);
-		raw_spin_unlock_irqrestore(&m->wait_lock, flags);
-		return;
-	}
-
-	/*
-	 * Call into the slow lock path with the rtmutex->wait_lock
-	 * held, so this can't result in the following race:
-	 *
-	 * Reader1		Reader2		Writer
-	 *			read_lock()
-	 *					write_lock()
-	 *					rtmutex_lock(m)
-	 *					swait()
-	 * read_lock()
-	 * unlock(m->wait_lock)
-	 *			read_unlock()
-	 *			swake()
-	 *					lock(m->wait_lock)
-	 *					lock->writelocked=true
-	 *					unlock(m->wait_lock)
-	 *
-	 *					write_unlock()
-	 *					lock->writelocked=false
-	 *					rtmutex_unlock(m)
-	 *			read_lock()
-	 *					write_lock()
-	 *					rtmutex_lock(m)
-	 *					swait()
-	 * rtmutex_lock(m)
-	 *
-	 * That would put Reader1 behind the writer waiting on
-	 * Reader2 to call read_unlock() which might be unbound.
-	 */
-	rt_mutex_init_waiter(&waiter, true);
-	rt_spin_lock_slowlock_locked(m, &waiter, flags);
-	/*
-	 * The slowlock() above is guaranteed to return with the rtmutex is
-	 * now held, so there can't be a writer active. Increment the reader
-	 * count and immediately drop the rtmutex again.
-	 */
-	atomic_inc(&lock->readers);
-	raw_spin_unlock_irqrestore(&m->wait_lock, flags);
-	rt_spin_lock_slowunlock(m);
-
-	debug_rt_mutex_free_waiter(&waiter);
-}
-
-static void __read_rt_unlock(struct rt_rw_lock *lock)
-{
-	struct rt_mutex *m = &lock->rtmutex;
-	struct task_struct *tsk;
-
-	/*
-	 * sem->readers can only hit 0 when a writer is waiting for the
-	 * active readers to leave the critical region.
-	 */
-	if (!atomic_dec_and_test(&lock->readers))
-		return;
-
-	raw_spin_lock_irq(&m->wait_lock);
-	/*
-	 * Wake the writer, i.e. the rtmutex owner. It might release the
-	 * rtmutex concurrently in the fast path, but to clean up the rw
-	 * lock it needs to acquire m->wait_lock. The worst case which can
-	 * happen is a spurious wakeup.
-	 */
-	tsk = rt_mutex_owner(m);
-	if (tsk)
-		wake_up_process(tsk);
-
-	raw_spin_unlock_irq(&m->wait_lock);
-}
-
-static void __write_unlock_common(struct rt_rw_lock *lock, int bias,
-				  unsigned long flags)
-{
-	struct rt_mutex *m = &lock->rtmutex;
-
-	atomic_add(READER_BIAS - bias, &lock->readers);
-	raw_spin_unlock_irqrestore(&m->wait_lock, flags);
-	rt_spin_lock_slowunlock(m);
-}
-
-static void __write_rt_lock(struct rt_rw_lock *lock)
-{
-	struct rt_mutex *m = &lock->rtmutex;
-	struct task_struct *self = current;
-	unsigned long flags;
-
-	/* Take the rtmutex as a first step */
-	__rt_spin_lock(m);
-
-	/* Force readers into slow path */
-	atomic_sub(READER_BIAS, &lock->readers);
-
-	raw_spin_lock_irqsave(&m->wait_lock, flags);
-
-	raw_spin_lock(&self->pi_lock);
-	self->saved_state = self->state;
-	__set_current_state_no_track(TASK_UNINTERRUPTIBLE);
-	raw_spin_unlock(&self->pi_lock);
-
-	for (;;) {
-		/* Have all readers left the critical region? */
-		if (!atomic_read(&lock->readers)) {
-			atomic_set(&lock->readers, WRITER_BIAS);
-			raw_spin_lock(&self->pi_lock);
-			__set_current_state_no_track(self->saved_state);
-			self->saved_state = TASK_RUNNING;
-			raw_spin_unlock(&self->pi_lock);
-			raw_spin_unlock_irqrestore(&m->wait_lock, flags);
-			return;
-		}
-
-		raw_spin_unlock_irqrestore(&m->wait_lock, flags);
-
-		if (atomic_read(&lock->readers) != 0)
-			preempt_schedule_lock();
-
-		raw_spin_lock_irqsave(&m->wait_lock, flags);
-
-		raw_spin_lock(&self->pi_lock);
-		__set_current_state_no_track(TASK_UNINTERRUPTIBLE);
-		raw_spin_unlock(&self->pi_lock);
-	}
-}
-
-static int __write_rt_trylock(struct rt_rw_lock *lock)
-{
-	struct rt_mutex *m = &lock->rtmutex;
-	unsigned long flags;
-
-	if (!__rt_mutex_trylock(m))
-		return 0;
-
-	atomic_sub(READER_BIAS, &lock->readers);
-
-	raw_spin_lock_irqsave(&m->wait_lock, flags);
-	if (!atomic_read(&lock->readers)) {
-		atomic_set(&lock->readers, WRITER_BIAS);
-		raw_spin_unlock_irqrestore(&m->wait_lock, flags);
-		return 1;
-	}
-	__write_unlock_common(lock, 0, flags);
-	return 0;
-}
-
-static void __write_rt_unlock(struct rt_rw_lock *lock)
-{
-	struct rt_mutex *m = &lock->rtmutex;
-	unsigned long flags;
-
-	raw_spin_lock_irqsave(&m->wait_lock, flags);
-	__write_unlock_common(lock, WRITER_BIAS, flags);
-}
-
-int __lockfunc rt_read_can_lock(rwlock_t *rwlock)
-{
-	return  atomic_read(&rwlock->readers) < 0;
-}
-
-int __lockfunc rt_write_can_lock(rwlock_t *rwlock)
-{
-	return atomic_read(&rwlock->readers) == READER_BIAS;
-}
-
-/*
- * The common functions which get wrapped into the rwlock API.
- */
-int __lockfunc rt_read_trylock(rwlock_t *rwlock)
-{
-	int ret;
-
-	ret = __read_rt_trylock(rwlock);
-	if (ret) {
-		rwlock_acquire_read(&rwlock->dep_map, 0, 1, _RET_IP_);
-		rcu_read_lock();
-		migrate_disable();
-	}
-	return ret;
-}
-EXPORT_SYMBOL(rt_read_trylock);
-
-int __lockfunc rt_write_trylock(rwlock_t *rwlock)
-{
-	int ret;
-
-	ret = __write_rt_trylock(rwlock);
-	if (ret) {
-		rwlock_acquire(&rwlock->dep_map, 0, 1, _RET_IP_);
-		rcu_read_lock();
-		migrate_disable();
-	}
-	return ret;
-}
-EXPORT_SYMBOL(rt_write_trylock);
-
-void __lockfunc rt_read_lock(rwlock_t *rwlock)
-{
-	rwlock_acquire_read(&rwlock->dep_map, 0, 0, _RET_IP_);
-	__read_rt_lock(rwlock);
-	rcu_read_lock();
-	migrate_disable();
-}
-EXPORT_SYMBOL(rt_read_lock);
-
-void __lockfunc rt_write_lock(rwlock_t *rwlock)
-{
-	rwlock_acquire(&rwlock->dep_map, 0, 0, _RET_IP_);
-	__write_rt_lock(rwlock);
-	rcu_read_lock();
-	migrate_disable();
-}
-EXPORT_SYMBOL(rt_write_lock);
-
-void __lockfunc rt_read_unlock(rwlock_t *rwlock)
-{
-	rwlock_release(&rwlock->dep_map, _RET_IP_);
-	migrate_enable();
-	rcu_read_unlock();
-	__read_rt_unlock(rwlock);
-}
-EXPORT_SYMBOL(rt_read_unlock);
-
-void __lockfunc rt_write_unlock(rwlock_t *rwlock)
-{
-	rwlock_release(&rwlock->dep_map, _RET_IP_);
-	migrate_enable();
-	rcu_read_unlock();
-	__write_rt_unlock(rwlock);
-}
-EXPORT_SYMBOL(rt_write_unlock);
-
-void __rt_rwlock_init(rwlock_t *rwlock, char *name, struct lock_class_key *key)
-{
-	__rwlock_biased_rt_init(rwlock, name, key);
-}
-EXPORT_SYMBOL(__rt_rwlock_init);
diff --git a/kernel/kernel/locking/rwsem-rt.c b/kernel/kernel/locking/rwsem-rt.c
deleted file mode 100644
index b61edc4..0000000
--- a/kernel/kernel/locking/rwsem-rt.c
+++ /dev/null
@@ -1,317 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-#include <linux/rwsem.h>
-#include <linux/sched/debug.h>
-#include <linux/sched/signal.h>
-#include <linux/export.h>
-#include <linux/blkdev.h>
-
-#include "rtmutex_common.h"
-
-/*
- * RT-specific reader/writer semaphores
- *
- * down_write()
- *  1) Lock sem->rtmutex
- *  2) Remove the reader BIAS to force readers into the slow path
- *  3) Wait until all readers have left the critical region
- *  4) Mark it write locked
- *
- * up_write()
- *  1) Remove the write locked marker
- *  2) Set the reader BIAS so readers can use the fast path again
- *  3) Unlock sem->rtmutex to release blocked readers
- *
- * down_read()
- *  1) Try fast path acquisition (reader BIAS is set)
- *  2) Take sem->rtmutex.wait_lock which protects the writelocked flag
- *  3) If !writelocked, acquire it for read
- *  4) If writelocked, block on sem->rtmutex
- *  5) unlock sem->rtmutex, goto 1)
- *
- * up_read()
- *  1) Try fast path release (reader count != 1)
- *  2) Wake the writer waiting in down_write()#3
- *
- * down_read()#3 has the consequence, that rw semaphores on RT are not writer
- * fair, but writers, which should be avoided in RT tasks (think mmap_sem),
- * are subject to the rtmutex priority/DL inheritance mechanism.
- *
- * It's possible to make the rw semaphores writer fair by keeping a list of
- * active readers. A blocked writer would force all newly incoming readers to
- * block on the rtmutex, but the rtmutex would have to be proxy locked for one
- * reader after the other. We can't use multi-reader inheritance because there
- * is no way to support that with SCHED_DEADLINE. Implementing the one by one
- * reader boosting/handover mechanism is a major surgery for a very dubious
- * value.
- *
- * The risk of writer starvation is there, but the pathological use cases
- * which trigger it are not necessarily the typical RT workloads.
- */
-
-void __rwsem_init(struct rw_semaphore *sem, const char *name,
-		  struct lock_class_key *key)
-{
-#ifdef CONFIG_DEBUG_LOCK_ALLOC
-	/*
-	 * Make sure we are not reinitializing a held semaphore:
-	 */
-	debug_check_no_locks_freed((void *)sem, sizeof(*sem));
-	lockdep_init_map(&sem->dep_map, name, key, 0);
-#endif
-	atomic_set(&sem->readers, READER_BIAS);
-}
-EXPORT_SYMBOL(__rwsem_init);
-
-int __down_read_trylock(struct rw_semaphore *sem)
-{
-	int r, old;
-
-	/*
-	 * Increment reader count, if sem->readers < 0, i.e. READER_BIAS is
-	 * set.
-	 */
-	for (r = atomic_read(&sem->readers); r < 0;) {
-		old = atomic_cmpxchg(&sem->readers, r, r + 1);
-		if (likely(old == r))
-			return 1;
-		r = old;
-	}
-	return 0;
-}
-
-static int __sched __down_read_common(struct rw_semaphore *sem, int state)
-{
-	struct rt_mutex *m = &sem->rtmutex;
-	struct rt_mutex_waiter waiter;
-	int ret;
-
-	if (__down_read_trylock(sem))
-		return 0;
-
-	/*
-	 * Flush blk before ->pi_blocked_on is set. At schedule() time it is too
-	 * late if one of the callbacks needs to acquire a sleeping lock.
-	 */
-	if (blk_needs_flush_plug(current))
-		blk_schedule_flush_plug(current);
-
-	might_sleep();
-	raw_spin_lock_irq(&m->wait_lock);
-	/*
-	 * Allow readers as long as the writer has not completely
-	 * acquired the semaphore for write.
-	 */
-	if (atomic_read(&sem->readers) != WRITER_BIAS) {
-		atomic_inc(&sem->readers);
-		raw_spin_unlock_irq(&m->wait_lock);
-		return 0;
-	}
-
-	/*
-	 * Call into the slow lock path with the rtmutex->wait_lock
-	 * held, so this can't result in the following race:
-	 *
-	 * Reader1		Reader2		Writer
-	 *			down_read()
-	 *					down_write()
-	 *					rtmutex_lock(m)
-	 *					swait()
-	 * down_read()
-	 * unlock(m->wait_lock)
-	 *			up_read()
-	 *			swake()
-	 *					lock(m->wait_lock)
-	 *					sem->writelocked=true
-	 *					unlock(m->wait_lock)
-	 *
-	 *					up_write()
-	 *					sem->writelocked=false
-	 *					rtmutex_unlock(m)
-	 *			down_read()
-	 *					down_write()
-	 *					rtmutex_lock(m)
-	 *					swait()
-	 * rtmutex_lock(m)
-	 *
-	 * That would put Reader1 behind the writer waiting on
-	 * Reader2 to call up_read() which might be unbound.
-	 */
-	rt_mutex_init_waiter(&waiter, false);
-	ret = rt_mutex_slowlock_locked(m, state, NULL, RT_MUTEX_MIN_CHAINWALK,
-				       NULL, &waiter);
-	/*
-	 * The slowlock() above is guaranteed to return with the rtmutex (for
-	 * ret = 0) is now held, so there can't be a writer active. Increment
-	 * the reader count and immediately drop the rtmutex again.
-	 * For ret != 0 we don't hold the rtmutex and need unlock the wait_lock.
-	 * We don't own the lock then.
-	 */
-	if (!ret)
-		atomic_inc(&sem->readers);
-	raw_spin_unlock_irq(&m->wait_lock);
-	if (!ret)
-		__rt_mutex_unlock(m);
-
-	debug_rt_mutex_free_waiter(&waiter);
-	return ret;
-}
-
-void __down_read(struct rw_semaphore *sem)
-{
-	int ret;
-
-	ret = __down_read_common(sem, TASK_UNINTERRUPTIBLE);
-	WARN_ON_ONCE(ret);
-}
-
-int __down_read_interruptible(struct rw_semaphore *sem)
-{
-	int ret;
-
-	ret = __down_read_common(sem, TASK_INTERRUPTIBLE);
-	if (likely(!ret))
-		return ret;
-	WARN_ONCE(ret != -EINTR, "Unexpected state: %d\n", ret);
-	return -EINTR;
-}
-
-int __down_read_killable(struct rw_semaphore *sem)
-{
-	int ret;
-
-	ret = __down_read_common(sem, TASK_KILLABLE);
-	if (likely(!ret))
-		return ret;
-	WARN_ONCE(ret != -EINTR, "Unexpected state: %d\n", ret);
-	return -EINTR;
-}
-
-void __up_read(struct rw_semaphore *sem)
-{
-	struct rt_mutex *m = &sem->rtmutex;
-	struct task_struct *tsk;
-
-	/*
-	 * sem->readers can only hit 0 when a writer is waiting for the
-	 * active readers to leave the critical region.
-	 */
-	if (!atomic_dec_and_test(&sem->readers))
-		return;
-
-	raw_spin_lock_irq(&m->wait_lock);
-	/*
-	 * Wake the writer, i.e. the rtmutex owner. It might release the
-	 * rtmutex concurrently in the fast path (due to a signal), but to
-	 * clean up the rwsem it needs to acquire m->wait_lock. The worst
-	 * case which can happen is a spurious wakeup.
-	 */
-	tsk = rt_mutex_owner(m);
-	if (tsk)
-		wake_up_process(tsk);
-
-	raw_spin_unlock_irq(&m->wait_lock);
-}
-
-static void __up_write_unlock(struct rw_semaphore *sem, int bias,
-			      unsigned long flags)
-{
-	struct rt_mutex *m = &sem->rtmutex;
-
-	atomic_add(READER_BIAS - bias, &sem->readers);
-	raw_spin_unlock_irqrestore(&m->wait_lock, flags);
-	__rt_mutex_unlock(m);
-}
-
-static int __sched __down_write_common(struct rw_semaphore *sem, int state)
-{
-	struct rt_mutex *m = &sem->rtmutex;
-	unsigned long flags;
-
-	/*
-	 * Flush blk before ->pi_blocked_on is set. At schedule() time it is too
-	 * late if one of the callbacks needs to acquire a sleeping lock.
-	 */
-	if (blk_needs_flush_plug(current))
-		blk_schedule_flush_plug(current);
-
-	/* Take the rtmutex as a first step */
-	if (__rt_mutex_lock_state(m, state))
-		return -EINTR;
-
-	/* Force readers into slow path */
-	atomic_sub(READER_BIAS, &sem->readers);
-	might_sleep();
-
-	set_current_state(state);
-	for (;;) {
-		raw_spin_lock_irqsave(&m->wait_lock, flags);
-		/* Have all readers left the critical region? */
-		if (!atomic_read(&sem->readers)) {
-			atomic_set(&sem->readers, WRITER_BIAS);
-			__set_current_state(TASK_RUNNING);
-			raw_spin_unlock_irqrestore(&m->wait_lock, flags);
-			return 0;
-		}
-
-		if (signal_pending_state(state, current)) {
-			__set_current_state(TASK_RUNNING);
-			__up_write_unlock(sem, 0, flags);
-			return -EINTR;
-		}
-		raw_spin_unlock_irqrestore(&m->wait_lock, flags);
-
-		if (atomic_read(&sem->readers) != 0) {
-			schedule();
-			set_current_state(state);
-		}
-	}
-}
-
-void __sched __down_write(struct rw_semaphore *sem)
-{
-	__down_write_common(sem, TASK_UNINTERRUPTIBLE);
-}
-
-int __sched __down_write_killable(struct rw_semaphore *sem)
-{
-	return __down_write_common(sem, TASK_KILLABLE);
-}
-
-int __down_write_trylock(struct rw_semaphore *sem)
-{
-	struct rt_mutex *m = &sem->rtmutex;
-	unsigned long flags;
-
-	if (!__rt_mutex_trylock(m))
-		return 0;
-
-	atomic_sub(READER_BIAS, &sem->readers);
-
-	raw_spin_lock_irqsave(&m->wait_lock, flags);
-	if (!atomic_read(&sem->readers)) {
-		atomic_set(&sem->readers, WRITER_BIAS);
-		raw_spin_unlock_irqrestore(&m->wait_lock, flags);
-		return 1;
-	}
-	__up_write_unlock(sem, 0, flags);
-	return 0;
-}
-
-void __up_write(struct rw_semaphore *sem)
-{
-	struct rt_mutex *m = &sem->rtmutex;
-	unsigned long flags;
-
-	raw_spin_lock_irqsave(&m->wait_lock, flags);
-	__up_write_unlock(sem, WRITER_BIAS, flags);
-}
-
-void __downgrade_write(struct rw_semaphore *sem)
-{
-	struct rt_mutex *m = &sem->rtmutex;
-	unsigned long flags;
-
-	raw_spin_lock_irqsave(&m->wait_lock, flags);
-	/* Release it and account current as reader */
-	__up_write_unlock(sem, WRITER_BIAS - 1, flags);
-}
diff --git a/kernel/kernel/locking/rwsem.c b/kernel/kernel/locking/rwsem.c
index b55d945..d368d1a 100644
--- a/kernel/kernel/locking/rwsem.c
+++ b/kernel/kernel/locking/rwsem.c
@@ -28,7 +28,6 @@
 #include <linux/rwsem.h>
 #include <linux/atomic.h>
 
-#ifndef CONFIG_PREEMPT_RT
 #include "lock_events.h"
 #include <trace/hooks/rwsem.h>
 #include <trace/hooks/dtask.h>
@@ -1525,7 +1524,6 @@
 	if (tmp & RWSEM_FLAG_WAITERS)
 		rwsem_downgrade_wake(sem);
 }
-#endif
 
 /*
  * lock for reading
@@ -1639,6 +1637,7 @@
 void up_write(struct rw_semaphore *sem)
 {
 	rwsem_release(&sem->dep_map, _RET_IP_);
+	trace_android_vh_rwsem_write_finished(sem);
 	__up_write(sem);
 }
 EXPORT_SYMBOL(up_write);
@@ -1649,6 +1648,7 @@
 void downgrade_write(struct rw_semaphore *sem)
 {
 	lock_downgrade(&sem->dep_map, _RET_IP_);
+	trace_android_vh_rwsem_write_finished(sem);
 	__downgrade_write(sem);
 }
 EXPORT_SYMBOL(downgrade_write);
@@ -1689,9 +1689,7 @@
 {
 	might_sleep();
 	__down_read(sem);
-#ifndef CONFIG_PREEMPT_RT
 	__rwsem_set_reader_owned(sem, NULL);
-#endif
 }
 EXPORT_SYMBOL(down_read_non_owner);
 
@@ -1720,9 +1718,7 @@
 
 void up_read_non_owner(struct rw_semaphore *sem)
 {
-#ifndef CONFIG_PREEMPT_RT
 	DEBUG_RWSEMS_WARN_ON(!is_rwsem_reader_owned(sem), sem);
-#endif
 	__up_read(sem);
 }
 EXPORT_SYMBOL(up_read_non_owner);
diff --git a/kernel/kernel/locking/spinlock.c b/kernel/kernel/locking/spinlock.c
index 45445a2..0ff0838 100644
--- a/kernel/kernel/locking/spinlock.c
+++ b/kernel/kernel/locking/spinlock.c
@@ -124,11 +124,8 @@
  *         __[spin|read|write]_lock_bh()
  */
 BUILD_LOCK_OPS(spin, raw_spinlock);
-
-#ifndef CONFIG_PREEMPT_RT
 BUILD_LOCK_OPS(read, rwlock);
 BUILD_LOCK_OPS(write, rwlock);
-#endif
 
 #endif
 
@@ -211,8 +208,6 @@
 }
 EXPORT_SYMBOL(_raw_spin_unlock_bh);
 #endif
-
-#ifndef CONFIG_PREEMPT_RT
 
 #ifndef CONFIG_INLINE_READ_TRYLOCK
 int __lockfunc _raw_read_trylock(rwlock_t *lock)
@@ -357,8 +352,6 @@
 }
 EXPORT_SYMBOL(_raw_write_unlock_bh);
 #endif
-
-#endif /* !PREEMPT_RT */
 
 #ifdef CONFIG_DEBUG_LOCK_ALLOC
 
diff --git a/kernel/kernel/locking/spinlock_debug.c b/kernel/kernel/locking/spinlock_debug.c
index 72e306e..b9d9308 100644
--- a/kernel/kernel/locking/spinlock_debug.c
+++ b/kernel/kernel/locking/spinlock_debug.c
@@ -31,7 +31,6 @@
 
 EXPORT_SYMBOL(__raw_spin_lock_init);
 
-#ifndef CONFIG_PREEMPT_RT
 void __rwlock_init(rwlock_t *lock, const char *name,
 		   struct lock_class_key *key)
 {
@@ -49,7 +48,6 @@
 }
 
 EXPORT_SYMBOL(__rwlock_init);
-#endif
 
 static void spin_dump(raw_spinlock_t *lock, const char *msg)
 {
@@ -141,7 +139,6 @@
 	arch_spin_unlock(&lock->raw_lock);
 }
 
-#ifndef CONFIG_PREEMPT_RT
 static void rwlock_bug(rwlock_t *lock, const char *msg)
 {
 	if (!debug_locks_off())
@@ -231,5 +228,3 @@
 	debug_write_unlock(lock);
 	arch_write_unlock(&lock->raw_lock);
 }
-
-#endif
diff --git a/kernel/kernel/notifier.c b/kernel/kernel/notifier.c
index c20782f..1b019cb 100644
--- a/kernel/kernel/notifier.c
+++ b/kernel/kernel/notifier.c
@@ -142,9 +142,9 @@
 	unsigned long flags;
 	int ret;
 
-	raw_spin_lock_irqsave(&nh->lock, flags);
+	spin_lock_irqsave(&nh->lock, flags);
 	ret = notifier_chain_register(&nh->head, n);
-	raw_spin_unlock_irqrestore(&nh->lock, flags);
+	spin_unlock_irqrestore(&nh->lock, flags);
 	return ret;
 }
 EXPORT_SYMBOL_GPL(atomic_notifier_chain_register);
@@ -164,9 +164,9 @@
 	unsigned long flags;
 	int ret;
 
-	raw_spin_lock_irqsave(&nh->lock, flags);
+	spin_lock_irqsave(&nh->lock, flags);
 	ret = notifier_chain_unregister(&nh->head, n);
-	raw_spin_unlock_irqrestore(&nh->lock, flags);
+	spin_unlock_irqrestore(&nh->lock, flags);
 	synchronize_rcu();
 	return ret;
 }
@@ -182,9 +182,9 @@
 	 * Musn't use RCU; because then the notifier list can
 	 * change between the up and down traversal.
 	 */
-	raw_spin_lock_irqsave(&nh->lock, flags);
+	spin_lock_irqsave(&nh->lock, flags);
 	ret = notifier_call_chain_robust(&nh->head, val_up, val_down, v);
-	raw_spin_unlock_irqrestore(&nh->lock, flags);
+	spin_unlock_irqrestore(&nh->lock, flags);
 
 	return ret;
 }
diff --git a/kernel/kernel/panic.c b/kernel/kernel/panic.c
index 873fedb..332736a 100644
--- a/kernel/kernel/panic.c
+++ b/kernel/kernel/panic.c
@@ -177,7 +177,6 @@
 void panic(const char *fmt, ...)
 {
 	static char buf[1024];
-	va_list args2;
 	va_list args;
 	long i, i_next = 0, len;
 	int state = 0;
@@ -192,21 +191,6 @@
 	 */
 	local_irq_disable();
 	preempt_disable_notrace();
-
-	console_verbose();
-	pr_emerg("Kernel panic - not syncing:\n");
-	va_start(args2, fmt);
-	va_copy(args, args2);
-	vprintk(fmt, args2);
-	va_end(args2);
-#ifdef CONFIG_DEBUG_BUGVERBOSE
-	/*
-	 * Avoid nested stack-dumping if a panic occurs during oops processing
-	 */
-	if (!test_taint(TAINT_DIE) && oops_in_progress <= 1)
-		dump_stack();
-#endif
-	pr_flush(1000, true);
 
 	/*
 	 * It's possible to come here directly from a panic-assertion and
@@ -229,12 +213,23 @@
 	if (old_cpu != PANIC_CPU_INVALID && old_cpu != this_cpu)
 		panic_smp_self_stop();
 
+	console_verbose();
 	bust_spinlocks(1);
+	va_start(args, fmt);
 	len = vscnprintf(buf, sizeof(buf), fmt, args);
 	va_end(args);
 
 	if (len && buf[len - 1] == '\n')
 		buf[len - 1] = '\0';
+
+	pr_emerg("Kernel panic - not syncing: %s\n", buf);
+#ifdef CONFIG_DEBUG_BUGVERBOSE
+	/*
+	 * Avoid nested stack-dumping if a panic occurs during oops processing
+	 */
+	if (!test_taint(TAINT_DIE) && oops_in_progress <= 1)
+		dump_stack();
+#endif
 
 	/*
 	 * If kgdb is enabled, give it a chance to run before we stop all
@@ -252,6 +247,7 @@
 	 * Bypass the panic_cpu check and call __crash_kexec directly.
 	 */
 	if (!_crash_kexec_post_notifiers) {
+		printk_safe_flush_on_panic();
 		__crash_kexec(NULL);
 
 		/*
@@ -275,6 +271,8 @@
 	 */
 	atomic_notifier_call_chain(&panic_notifier_list, 0, buf);
 
+	/* Call flush even twice. It tries harder with a single online CPU */
+	printk_safe_flush_on_panic();
 	kmsg_dump(KMSG_DUMP_PANIC);
 
 	/*
@@ -544,11 +542,9 @@
 
 static int init_oops_id(void)
 {
-#ifndef CONFIG_PREEMPT_RT
 	if (!oops_id)
 		get_random_bytes(&oops_id, sizeof(oops_id));
 	else
-#endif
 		oops_id++;
 
 	return 0;
@@ -559,7 +555,6 @@
 {
 	init_oops_id();
 	pr_warn("---[ end trace %016llx ]---\n", (unsigned long long)oops_id);
-	pr_flush(1000, true);
 }
 
 /*
diff --git a/kernel/kernel/printk/Makefile b/kernel/kernel/printk/Makefile
index 59cb24e..eee3dc9 100644
--- a/kernel/kernel/printk/Makefile
+++ b/kernel/kernel/printk/Makefile
@@ -1,4 +1,5 @@
 # SPDX-License-Identifier: GPL-2.0-only
 obj-y	= printk.o
+obj-$(CONFIG_PRINTK)	+= printk_safe.o
 obj-$(CONFIG_A11Y_BRAILLE_CONSOLE)	+= braille.o
 obj-$(CONFIG_PRINTK)	+= printk_ringbuffer.o
diff --git a/kernel/kernel/printk/internal.h b/kernel/kernel/printk/internal.h
new file mode 100644
index 0000000..3a8fd49
--- /dev/null
+++ b/kernel/kernel/printk/internal.h
@@ -0,0 +1,74 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * internal.h - printk internal definitions
+ */
+#include <linux/percpu.h>
+
+#ifdef CONFIG_PRINTK
+
+#define PRINTK_SAFE_CONTEXT_MASK	0x007ffffff
+#define PRINTK_NMI_DIRECT_CONTEXT_MASK	0x008000000
+#define PRINTK_NMI_CONTEXT_MASK		0xff0000000
+
+#define PRINTK_NMI_CONTEXT_OFFSET	0x010000000
+
+extern raw_spinlock_t logbuf_lock;
+
+__printf(4, 0)
+int vprintk_store(int facility, int level,
+		  const struct dev_printk_info *dev_info,
+		  const char *fmt, va_list args);
+
+__printf(1, 0) int vprintk_default(const char *fmt, va_list args);
+__printf(1, 0) int vprintk_deferred(const char *fmt, va_list args);
+__printf(1, 0) int vprintk_func(const char *fmt, va_list args);
+void __printk_safe_enter(void);
+void __printk_safe_exit(void);
+
+void printk_safe_init(void);
+bool printk_percpu_data_ready(void);
+
+#define printk_safe_enter_irqsave(flags)	\
+	do {					\
+		local_irq_save(flags);		\
+		__printk_safe_enter();		\
+	} while (0)
+
+#define printk_safe_exit_irqrestore(flags)	\
+	do {					\
+		__printk_safe_exit();		\
+		local_irq_restore(flags);	\
+	} while (0)
+
+#define printk_safe_enter_irq()		\
+	do {					\
+		local_irq_disable();		\
+		__printk_safe_enter();		\
+	} while (0)
+
+#define printk_safe_exit_irq()			\
+	do {					\
+		__printk_safe_exit();		\
+		local_irq_enable();		\
+	} while (0)
+
+void defer_console_output(void);
+
+#else
+
+__printf(1, 0) int vprintk_func(const char *fmt, va_list args) { return 0; }
+
+/*
+ * In !PRINTK builds we still export logbuf_lock spin_lock, console_sem
+ * semaphore and some of console functions (console_unlock()/etc.), so
+ * printk-safe must preserve the existing local IRQ guarantees.
+ */
+#define printk_safe_enter_irqsave(flags) local_irq_save(flags)
+#define printk_safe_exit_irqrestore(flags) local_irq_restore(flags)
+
+#define printk_safe_enter_irq() local_irq_disable()
+#define printk_safe_exit_irq() local_irq_enable()
+
+static inline void printk_safe_init(void) { }
+static inline bool printk_percpu_data_ready(void) { return false; }
+#endif /* CONFIG_PRINTK */
diff --git a/kernel/kernel/printk/printk.c b/kernel/kernel/printk/printk.c
index 13cf2d7..e253475 100644
--- a/kernel/kernel/printk/printk.c
+++ b/kernel/kernel/printk/printk.c
@@ -44,9 +44,6 @@
 #include <linux/irq_work.h>
 #include <linux/ctype.h>
 #include <linux/uio.h>
-#include <linux/kthread.h>
-#include <linux/kdb.h>
-#include <linux/clocksource.h>
 #include <linux/sched/clock.h>
 #include <linux/sched/debug.h>
 #include <linux/sched/task_stack.h>
@@ -64,6 +61,25 @@
 #include "printk_ringbuffer.h"
 #include "console_cmdline.h"
 #include "braille.h"
+#include "internal.h"
+
+#ifdef CONFIG_PRINTK_TIME_FROM_ARM_ARCH_TIMER
+#include <clocksource/arm_arch_timer.h>
+static u64 get_local_clock(void)
+{
+	u64 ns;
+
+	ns = arch_timer_read_counter() * 1000;
+	do_div(ns, 24);
+
+	return ns;
+}
+#else
+static inline u64 get_local_clock(void)
+{
+	return local_clock();
+}
+#endif
 
 int console_printk[4] = {
 	CONSOLE_LOGLEVEL_DEFAULT,	/* console_loglevel */
@@ -232,7 +248,19 @@
 
 static int __down_trylock_console_sem(unsigned long ip)
 {
-	if (down_trylock(&console_sem))
+	int lock_failed;
+	unsigned long flags;
+
+	/*
+	 * Here and in __up_console_sem() we need to be in safe mode,
+	 * because spindump/WARN/etc from under console ->lock will
+	 * deadlock in printk()->down_trylock_console_sem() otherwise.
+	 */
+	printk_safe_enter_irqsave(flags);
+	lock_failed = down_trylock(&console_sem);
+	printk_safe_exit_irqrestore(flags);
+
+	if (lock_failed)
 		return 1;
 	mutex_acquire(&console_lock_dep_map, 0, 1, ip);
 	return 0;
@@ -241,9 +269,13 @@
 
 static void __up_console_sem(unsigned long ip)
 {
+	unsigned long flags;
+
 	mutex_release(&console_lock_dep_map, ip);
 
+	printk_safe_enter_irqsave(flags);
 	up(&console_sem);
+	printk_safe_exit_irqrestore(flags);
 }
 #define up_console_sem() __up_console_sem(_RET_IP_)
 
@@ -256,6 +288,11 @@
  * locked without the console sempahore held).
  */
 static int console_locked, console_suspended;
+
+/*
+ * If exclusive_console is non-NULL then only this console is to be printed to.
+ */
+static struct console *exclusive_console;
 
 /*
  *	Array of consoles built from command line options (console=)
@@ -341,43 +378,61 @@
 	LOG_CONT	= 8,	/* text is a fragment of a continuation line */
 };
 
+/*
+ * The logbuf_lock protects kmsg buffer, indices, counters.  This can be taken
+ * within the scheduler's rq lock. It must be released before calling
+ * console_unlock() or anything else that might wake up a process.
+ */
+DEFINE_RAW_SPINLOCK(logbuf_lock);
+
+/*
+ * Helper macros to lock/unlock logbuf_lock and switch between
+ * printk-safe/unsafe modes.
+ */
+#define logbuf_lock_irq()				\
+	do {						\
+		printk_safe_enter_irq();		\
+		raw_spin_lock(&logbuf_lock);		\
+	} while (0)
+
+#define logbuf_unlock_irq()				\
+	do {						\
+		raw_spin_unlock(&logbuf_lock);		\
+		printk_safe_exit_irq();			\
+	} while (0)
+
+#define logbuf_lock_irqsave(flags)			\
+	do {						\
+		printk_safe_enter_irqsave(flags);	\
+		raw_spin_lock(&logbuf_lock);		\
+	} while (0)
+
+#define logbuf_unlock_irqrestore(flags)		\
+	do {						\
+		raw_spin_unlock(&logbuf_lock);		\
+		printk_safe_exit_irqrestore(flags);	\
+	} while (0)
+
 #ifdef CONFIG_PRINTK
-/* syslog_lock protects syslog_* variables and write access to clear_seq. */
-static DEFINE_SPINLOCK(syslog_lock);
-
-/* Set to enable sync mode. Once set, it is never cleared. */
-static bool sync_mode;
-
 DECLARE_WAIT_QUEUE_HEAD(log_wait);
-/* All 3 protected by @syslog_lock. */
 /* the next printk record to read by syslog(READ) or /proc/kmsg */
 static u64 syslog_seq;
 static size_t syslog_partial;
 static bool syslog_time;
 
-struct latched_seq {
-	seqcount_latch_t	latch;
-	u64			val[2];
-};
+/* the next printk record to write to the console */
+static u64 console_seq;
+static u64 exclusive_console_stop_seq;
+static unsigned long console_dropped;
 
-/*
- * The next printk record to read after the last 'clear' command. There are
- * two copies (updated with seqcount_latch) so that reads can locklessly
- * access a valid value. Writers are synchronized by @syslog_lock.
- */
-static struct latched_seq clear_seq = {
-	.latch		= SEQCNT_LATCH_ZERO(clear_seq.latch),
-	.val[0]		= 0,
-	.val[1]		= 0,
-};
+/* the next printk record to read after the last 'clear' command */
+static u64 clear_seq;
 
 #ifdef CONFIG_PRINTK_CALLER
 #define PREFIX_MAX		48
 #else
 #define PREFIX_MAX		32
 #endif
-
-/* the maximum size allowed to be reserved for a record */
 #define LOG_LINE_MAX		(1024 - PREFIX_MAX)
 
 #define LOG_LEVEL(v)		((v) & 0x07)
@@ -415,34 +470,9 @@
  */
 static bool __printk_percpu_data_ready __read_mostly;
 
-static bool printk_percpu_data_ready(void)
+bool printk_percpu_data_ready(void)
 {
 	return __printk_percpu_data_ready;
-}
-
-/* Must be called under syslog_lock. */
-static void latched_seq_write(struct latched_seq *ls, u64 val)
-{
-	raw_write_seqcount_latch(&ls->latch);
-	ls->val[0] = val;
-	raw_write_seqcount_latch(&ls->latch);
-	ls->val[1] = val;
-}
-
-/* Can be called from any context. */
-static u64 latched_seq_read_nolock(struct latched_seq *ls)
-{
-	unsigned int seq;
-	unsigned int idx;
-	u64 val;
-
-	do {
-		seq = raw_read_seqcount_latch(&ls->latch);
-		idx = seq & 0x1;
-		val = ls->val[idx];
-	} while (read_seqcount_latch_retry(&ls->latch, seq));
-
-	return val;
 }
 
 /* Return log buffer address */
@@ -484,6 +514,54 @@
 		*text_len -= *trunc_msg_len;
 	else
 		*trunc_msg_len = 0;
+}
+
+/* insert record into the buffer, discard old ones, update heads */
+static int log_store(u32 caller_id, int facility, int level,
+		     enum log_flags flags, u64 ts_nsec,
+		     const struct dev_printk_info *dev_info,
+		     const char *text, u16 text_len)
+{
+	struct prb_reserved_entry e;
+	struct printk_record r;
+	u16 trunc_msg_len = 0;
+
+	prb_rec_init_wr(&r, text_len);
+
+	if (!prb_reserve(&e, prb, &r)) {
+		/* truncate the message if it is too long for empty buffer */
+		truncate_msg(&text_len, &trunc_msg_len);
+		prb_rec_init_wr(&r, text_len + trunc_msg_len);
+		/* survive when the log buffer is too small for trunc_msg */
+		if (!prb_reserve(&e, prb, &r))
+			return 0;
+	}
+
+	/* fill message */
+	memcpy(&r.text_buf[0], text, text_len);
+	if (trunc_msg_len)
+		memcpy(&r.text_buf[text_len], trunc_msg, trunc_msg_len);
+	r.info->text_len = text_len + trunc_msg_len;
+	r.info->facility = facility;
+	r.info->level = level & 7;
+	r.info->flags = flags & 0x1f;
+	if (ts_nsec > 0)
+		r.info->ts_nsec = ts_nsec;
+	else
+		r.info->ts_nsec = get_local_clock();
+	r.info->caller_id = caller_id;
+	if (dev_info)
+		memcpy(&r.info->dev_info, dev_info, sizeof(r.info->dev_info));
+
+	/* A message without a trailing newline can be continued. */
+	if (!(flags & LOG_NEWLINE))
+		prb_commit(&e);
+	else
+		prb_final_commit(&e);
+
+	trace_android_vh_logbuf(prb, &r);
+
+	return (text_len + trunc_msg_len);
 }
 
 int dmesg_restrict = IS_ENABLED(CONFIG_SECURITY_DMESG_RESTRICT);
@@ -614,7 +692,7 @@
 
 /* /dev/kmsg - userspace message inject/listen interface */
 struct devkmsg_user {
-	atomic64_t seq;
+	u64 seq;
 	struct ratelimit_state rs;
 	struct mutex lock;
 	char buf[CONSOLE_EXT_LOG_MAX];
@@ -715,22 +793,27 @@
 	if (ret)
 		return ret;
 
-	if (!prb_read_valid(prb, atomic64_read(&user->seq), r)) {
+	logbuf_lock_irq();
+	if (!prb_read_valid(prb, user->seq, r)) {
 		if (file->f_flags & O_NONBLOCK) {
 			ret = -EAGAIN;
+			logbuf_unlock_irq();
 			goto out;
 		}
 
+		logbuf_unlock_irq();
 		ret = wait_event_interruptible(log_wait,
-				prb_read_valid(prb, atomic64_read(&user->seq), r));
+					prb_read_valid(prb, user->seq, r));
 		if (ret)
 			goto out;
+		logbuf_lock_irq();
 	}
 
-	if (r->info->seq != atomic64_read(&user->seq)) {
+	if (r->info->seq != user->seq) {
 		/* our last seen message is gone, return error and reset */
-		atomic64_set(&user->seq, r->info->seq);
+		user->seq = r->info->seq;
 		ret = -EPIPE;
+		logbuf_unlock_irq();
 		goto out;
 	}
 
@@ -739,7 +822,8 @@
 				  &r->text_buf[0], r->info->text_len,
 				  &r->info->dev_info);
 
-	atomic64_set(&user->seq, r->info->seq + 1);
+	user->seq = r->info->seq + 1;
+	logbuf_unlock_irq();
 
 	if (len > count) {
 		ret = -EINVAL;
@@ -774,10 +858,11 @@
 	if (offset)
 		return -ESPIPE;
 
+	logbuf_lock_irq();
 	switch (whence) {
 	case SEEK_SET:
 		/* the first record */
-		atomic64_set(&user->seq, prb_first_valid_seq(prb));
+		user->seq = prb_first_valid_seq(prb);
 		break;
 	case SEEK_DATA:
 		/*
@@ -785,15 +870,16 @@
 		 * like issued by 'dmesg -c'. Reading /dev/kmsg itself
 		 * changes no global state, and does not clear anything.
 		 */
-		atomic64_set(&user->seq, latched_seq_read_nolock(&clear_seq));
+		user->seq = clear_seq;
 		break;
 	case SEEK_END:
 		/* after the last record */
-		atomic64_set(&user->seq, prb_next_seq(prb));
+		user->seq = prb_next_seq(prb);
 		break;
 	default:
 		ret = -EINVAL;
 	}
+	logbuf_unlock_irq();
 	return ret;
 }
 
@@ -808,13 +894,15 @@
 
 	poll_wait(file, &log_wait, wait);
 
-	if (prb_read_valid_info(prb, atomic64_read(&user->seq), &info, NULL)) {
+	logbuf_lock_irq();
+	if (prb_read_valid_info(prb, user->seq, &info, NULL)) {
 		/* return error when data has vanished underneath us */
-		if (info.seq != atomic64_read(&user->seq))
+		if (info.seq != user->seq)
 			ret = EPOLLIN|EPOLLRDNORM|EPOLLERR|EPOLLPRI;
 		else
 			ret = EPOLLIN|EPOLLRDNORM;
 	}
+	logbuf_unlock_irq();
 
 	return ret;
 }
@@ -847,7 +935,9 @@
 	prb_rec_init_rd(&user->record, &user->info,
 			&user->text_buf[0], sizeof(user->text_buf));
 
-	atomic64_set(&user->seq, prb_first_valid_seq(prb));
+	logbuf_lock_irq();
+	user->seq = prb_first_valid_seq(prb);
+	logbuf_unlock_irq();
 
 	file->private_data = user;
 	return 0;
@@ -939,9 +1029,6 @@
 
 	VMCOREINFO_SIZE(atomic_long_t);
 	VMCOREINFO_TYPE_OFFSET(atomic_long_t, counter);
-
-	VMCOREINFO_STRUCT_SIZE(latched_seq);
-	VMCOREINFO_OFFSET(latched_seq, val);
 }
 #endif
 
@@ -1013,6 +1100,9 @@
 
 static void __init set_percpu_data_ready(void)
 {
+	printk_safe_init();
+	/* Make sure we set this flag only after printk_safe() init is done */
+	barrier();
 	__printk_percpu_data_ready = true;
 }
 
@@ -1052,6 +1142,7 @@
 	struct printk_record r;
 	size_t new_descs_size;
 	size_t new_infos_size;
+	unsigned long flags;
 	char *new_log_buf;
 	unsigned int free;
 	u64 seq;
@@ -1109,6 +1200,8 @@
 		 new_descs, ilog2(new_descs_count),
 		 new_infos);
 
+	logbuf_lock_irqsave(flags);
+
 	log_buf_len = new_log_buf_len;
 	log_buf = new_log_buf;
 	new_log_buf_len = 0;
@@ -1123,6 +1216,8 @@
 	 * appear during the transition to the dynamic buffer.
 	 */
 	prb = &printk_rb_dynamic;
+
+	logbuf_unlock_irqrestore(flags);
 
 	if (seq != prb_next_seq(&printk_rb_static)) {
 		pr_err("dropped %llu messages\n",
@@ -1400,50 +1495,6 @@
 	return ((prefix_len * line_count) + info->text_len + 1);
 }
 
-/*
- * Beginning with @start_seq, find the first record where it and all following
- * records up to (but not including) @max_seq fit into @size.
- *
- * @max_seq is simply an upper bound and does not need to exist. If the caller
- * does not require an upper bound, -1 can be used for @max_seq.
- */
-static u64 find_first_fitting_seq(u64 start_seq, u64 max_seq, size_t size,
-				  bool syslog, bool time)
-{
-	struct printk_info info;
-	unsigned int line_count;
-	size_t len = 0;
-	u64 seq;
-
-	/* Determine the size of the records up to @max_seq. */
-	prb_for_each_info(start_seq, prb, seq, &info, &line_count) {
-		if (info.seq >= max_seq)
-			break;
-		len += get_record_print_text_size(&info, line_count, syslog, time);
-	}
-
-	/*
-	 * Adjust the upper bound for the next loop to avoid subtracting
-	 * lengths that were never added.
-	 */
-	if (seq < max_seq)
-		max_seq = seq;
-
-	/*
-	 * Move first record forward until length fits into the buffer. Ignore
-	 * newest messages that were not counted in the above cycle. Messages
-	 * might appear and get lost in the meantime. This is a best effort
-	 * that prevents an infinite loop that could occur with a retry.
-	 */
-	prb_for_each_info(start_seq, prb, seq, &info, &line_count) {
-		if (len <= size || info.seq >= max_seq)
-			break;
-		len -= get_record_print_text_size(&info, line_count, syslog, time);
-	}
-
-	return seq;
-}
-
 static int syslog_print(char __user *buf, int size)
 {
 	struct printk_info info;
@@ -1451,19 +1502,19 @@
 	char *text;
 	int len = 0;
 
-	text = kmalloc(CONSOLE_LOG_MAX, GFP_KERNEL);
+	text = kmalloc(LOG_LINE_MAX + PREFIX_MAX, GFP_KERNEL);
 	if (!text)
 		return -ENOMEM;
 
-	prb_rec_init_rd(&r, &info, text, CONSOLE_LOG_MAX);
+	prb_rec_init_rd(&r, &info, text, LOG_LINE_MAX + PREFIX_MAX);
 
 	while (size > 0) {
 		size_t n;
 		size_t skip;
 
-		spin_lock_irq(&syslog_lock);
+		logbuf_lock_irq();
 		if (!prb_read_valid(prb, syslog_seq, &r)) {
-			spin_unlock_irq(&syslog_lock);
+			logbuf_unlock_irq();
 			break;
 		}
 		if (r.info->seq != syslog_seq) {
@@ -1492,7 +1543,7 @@
 			syslog_partial += n;
 		} else
 			n = 0;
-		spin_unlock_irq(&syslog_lock);
+		logbuf_unlock_irq();
 
 		if (!n)
 			break;
@@ -1515,25 +1566,34 @@
 static int syslog_print_all(char __user *buf, int size, bool clear)
 {
 	struct printk_info info;
+	unsigned int line_count;
 	struct printk_record r;
 	char *text;
 	int len = 0;
 	u64 seq;
 	bool time;
 
-	text = kmalloc(CONSOLE_LOG_MAX, GFP_KERNEL);
+	text = kmalloc(LOG_LINE_MAX + PREFIX_MAX, GFP_KERNEL);
 	if (!text)
 		return -ENOMEM;
 
 	time = printk_time;
+	logbuf_lock_irq();
 	/*
 	 * Find first record that fits, including all following records,
 	 * into the user-provided buffer for this dump.
 	 */
-	seq = find_first_fitting_seq(latched_seq_read_nolock(&clear_seq), -1,
-				     size, true, time);
+	prb_for_each_info(clear_seq, prb, seq, &info, &line_count)
+		len += get_record_print_text_size(&info, line_count, true, time);
 
-	prb_rec_init_rd(&r, &info, text, CONSOLE_LOG_MAX);
+	/* move first record forward until length fits into the buffer */
+	prb_for_each_info(clear_seq, prb, seq, &info, &line_count) {
+		if (len <= size)
+			break;
+		len -= get_record_print_text_size(&info, line_count, true, time);
+	}
+
+	prb_rec_init_rd(&r, &info, text, LOG_LINE_MAX + PREFIX_MAX);
 
 	len = 0;
 	prb_for_each_record(seq, prb, seq, &r) {
@@ -1546,20 +1606,20 @@
 			break;
 		}
 
+		logbuf_unlock_irq();
 		if (copy_to_user(buf + len, text, textlen))
 			len = -EFAULT;
 		else
 			len += textlen;
+		logbuf_lock_irq();
 
 		if (len < 0)
 			break;
 	}
 
-	if (clear) {
-		spin_lock_irq(&syslog_lock);
-		latched_seq_write(&clear_seq, seq);
-		spin_unlock_irq(&syslog_lock);
-	}
+	if (clear)
+		clear_seq = seq;
+	logbuf_unlock_irq();
 
 	kfree(text);
 	return len;
@@ -1567,21 +1627,9 @@
 
 static void syslog_clear(void)
 {
-	spin_lock_irq(&syslog_lock);
-	latched_seq_write(&clear_seq, prb_next_seq(prb));
-	spin_unlock_irq(&syslog_lock);
-}
-
-/* Return a consistent copy of @syslog_seq. */
-static u64 read_syslog_seq_irq(void)
-{
-	u64 seq;
-
-	spin_lock_irq(&syslog_lock);
-	seq = syslog_seq;
-	spin_unlock_irq(&syslog_lock);
-
-	return seq;
+	logbuf_lock_irq();
+	clear_seq = prb_next_seq(prb);
+	logbuf_unlock_irq();
 }
 
 int do_syslog(int type, char __user *buf, int len, int source)
@@ -1607,9 +1655,8 @@
 			return 0;
 		if (!access_ok(buf, len))
 			return -EFAULT;
-
 		error = wait_event_interruptible(log_wait,
-				prb_read_valid(prb, read_syslog_seq_irq(), NULL));
+				prb_read_valid(prb, syslog_seq, NULL));
 		if (error)
 			return error;
 		error = syslog_print(buf, len);
@@ -1657,10 +1704,10 @@
 		break;
 	/* Number of chars in the log buffer */
 	case SYSLOG_ACTION_SIZE_UNREAD:
-		spin_lock_irq(&syslog_lock);
+		logbuf_lock_irq();
 		if (!prb_read_valid_info(prb, syslog_seq, &info, NULL)) {
 			/* No unread messages. */
-			spin_unlock_irq(&syslog_lock);
+			logbuf_unlock_irq();
 			return 0;
 		}
 		if (info.seq != syslog_seq) {
@@ -1688,7 +1735,7 @@
 			}
 			error -= syslog_partial;
 		}
-		spin_unlock_irq(&syslog_lock);
+		logbuf_unlock_irq();
 		break;
 	/* Size of the log buffer */
 	case SYSLOG_ACTION_SIZE_BUFFER:
@@ -1707,12 +1754,194 @@
 	return do_syslog(type, buf, len, SYSLOG_FROM_READER);
 }
 
+/*
+ * Special console_lock variants that help to reduce the risk of soft-lockups.
+ * They allow to pass console_lock to another printk() call using a busy wait.
+ */
+
+#ifdef CONFIG_LOCKDEP
+static struct lockdep_map console_owner_dep_map = {
+	.name = "console_owner"
+};
+#endif
+
+static DEFINE_RAW_SPINLOCK(console_owner_lock);
+static struct task_struct *console_owner;
+static bool console_waiter;
+
+/**
+ * console_lock_spinning_enable - mark beginning of code where another
+ *	thread might safely busy wait
+ *
+ * This basically converts console_lock into a spinlock. This marks
+ * the section where the console_lock owner can not sleep, because
+ * there may be a waiter spinning (like a spinlock). Also it must be
+ * ready to hand over the lock at the end of the section.
+ */
+static void console_lock_spinning_enable(void)
+{
+	raw_spin_lock(&console_owner_lock);
+	console_owner = current;
+	raw_spin_unlock(&console_owner_lock);
+
+	/* The waiter may spin on us after setting console_owner */
+	spin_acquire(&console_owner_dep_map, 0, 0, _THIS_IP_);
+}
+
+/**
+ * console_lock_spinning_disable_and_check - mark end of code where another
+ *	thread was able to busy wait and check if there is a waiter
+ *
+ * This is called at the end of the section where spinning is allowed.
+ * It has two functions. First, it is a signal that it is no longer
+ * safe to start busy waiting for the lock. Second, it checks if
+ * there is a busy waiter and passes the lock rights to her.
+ *
+ * Important: Callers lose the lock if there was a busy waiter.
+ *	They must not touch items synchronized by console_lock
+ *	in this case.
+ *
+ * Return: 1 if the lock rights were passed, 0 otherwise.
+ */
+static int console_lock_spinning_disable_and_check(void)
+{
+	int waiter;
+
+	raw_spin_lock(&console_owner_lock);
+	waiter = READ_ONCE(console_waiter);
+	console_owner = NULL;
+	raw_spin_unlock(&console_owner_lock);
+
+	if (!waiter) {
+		spin_release(&console_owner_dep_map, _THIS_IP_);
+		return 0;
+	}
+
+	/* The waiter is now free to continue */
+	WRITE_ONCE(console_waiter, false);
+
+	spin_release(&console_owner_dep_map, _THIS_IP_);
+
+	/*
+	 * Hand off console_lock to waiter. The waiter will perform
+	 * the up(). After this, the waiter is the console_lock owner.
+	 */
+	mutex_release(&console_lock_dep_map, _THIS_IP_);
+	return 1;
+}
+
+/**
+ * console_trylock_spinning - try to get console_lock by busy waiting
+ *
+ * This allows to busy wait for the console_lock when the current
+ * owner is running in specially marked sections. It means that
+ * the current owner is running and cannot reschedule until it
+ * is ready to lose the lock.
+ *
+ * Return: 1 if we got the lock, 0 othrewise
+ */
+static int console_trylock_spinning(void)
+{
+	struct task_struct *owner = NULL;
+	bool waiter;
+	bool spin = false;
+	unsigned long flags;
+
+	if (console_trylock())
+		return 1;
+
+	printk_safe_enter_irqsave(flags);
+
+	raw_spin_lock(&console_owner_lock);
+	owner = READ_ONCE(console_owner);
+	waiter = READ_ONCE(console_waiter);
+	if (!waiter && owner && owner != current) {
+		WRITE_ONCE(console_waiter, true);
+		spin = true;
+	}
+	raw_spin_unlock(&console_owner_lock);
+
+	/*
+	 * If there is an active printk() writing to the
+	 * consoles, instead of having it write our data too,
+	 * see if we can offload that load from the active
+	 * printer, and do some printing ourselves.
+	 * Go into a spin only if there isn't already a waiter
+	 * spinning, and there is an active printer, and
+	 * that active printer isn't us (recursive printk?).
+	 */
+	if (!spin) {
+		printk_safe_exit_irqrestore(flags);
+		return 0;
+	}
+
+	/* We spin waiting for the owner to release us */
+	spin_acquire(&console_owner_dep_map, 0, 0, _THIS_IP_);
+	/* Owner will clear console_waiter on hand off */
+	while (READ_ONCE(console_waiter))
+		cpu_relax();
+	spin_release(&console_owner_dep_map, _THIS_IP_);
+
+	printk_safe_exit_irqrestore(flags);
+	/*
+	 * The owner passed the console lock to us.
+	 * Since we did not spin on console lock, annotate
+	 * this as a trylock. Otherwise lockdep will
+	 * complain.
+	 */
+	mutex_acquire(&console_lock_dep_map, 0, 1, _THIS_IP_);
+
+	return 1;
+}
+
+/*
+ * Call the console drivers, asking them to write out
+ * log_buf[start] to log_buf[end - 1].
+ * The console_lock must be held.
+ */
+static void call_console_drivers(const char *ext_text, size_t ext_len,
+				 const char *text, size_t len)
+{
+	static char dropped_text[64];
+	size_t dropped_len = 0;
+	struct console *con;
+
+	trace_console_rcuidle(text, len);
+
+	if (!console_drivers)
+		return;
+
+	if (console_dropped) {
+		dropped_len = snprintf(dropped_text, sizeof(dropped_text),
+				       "** %lu printk messages dropped **\n",
+				       console_dropped);
+		console_dropped = 0;
+	}
+
+	for_each_console(con) {
+		if (exclusive_console && con != exclusive_console)
+			continue;
+		if (!(con->flags & CON_ENABLED))
+			continue;
+		if (!con->write)
+			continue;
+		if (!cpu_online(smp_processor_id()) &&
+		    !(con->flags & CON_ANYTIME))
+			continue;
+		if (con->flags & CON_EXTENDED)
+			con->write(con, ext_text, ext_len);
+		else {
+			if (dropped_len)
+				con->write(con, dropped_text, dropped_len);
+			con->write(con, text, len);
+		}
+	}
+}
+
 int printk_delay_msec __read_mostly;
 
-static inline void printk_delay(int level)
+static inline void printk_delay(void)
 {
-	boot_delay_msec(level);
-
 	if (unlikely(printk_delay_msec)) {
 		int m = printk_delay_msec;
 
@@ -1723,282 +1952,83 @@
 	}
 }
 
-static bool kernel_sync_mode(void)
-{
-	return (oops_in_progress || sync_mode);
-}
-
-static bool console_can_sync(struct console *con)
-{
-	if (!(con->flags & CON_ENABLED))
-		return false;
-	if (con->write_atomic && kernel_sync_mode())
-		return true;
-	if (con->write_atomic && (con->flags & CON_HANDOVER) && !con->thread)
-		return true;
-	if (con->write && (con->flags & CON_BOOT) && !con->thread)
-		return true;
-	return false;
-}
-
-static bool call_sync_console_driver(struct console *con, const char *text, size_t text_len)
-{
-	if (!(con->flags & CON_ENABLED))
-		return false;
-	if (con->write_atomic && kernel_sync_mode())
-		con->write_atomic(con, text, text_len);
-	else if (con->write_atomic && (con->flags & CON_HANDOVER) && !con->thread)
-		con->write_atomic(con, text, text_len);
-	else if (con->write && (con->flags & CON_BOOT) && !con->thread)
-		con->write(con, text, text_len);
-	else
-		return false;
-
-	return true;
-}
-
-static bool have_atomic_console(void)
-{
-	struct console *con;
-
-	for_each_console(con) {
-		if (!(con->flags & CON_ENABLED))
-			continue;
-		if (con->write_atomic)
-			return true;
-	}
-	return false;
-}
-
-static bool print_sync(struct console *con, u64 *seq)
-{
-	struct printk_info info;
-	struct printk_record r;
-	size_t text_len;
-
-	prb_rec_init_rd(&r, &info, &con->sync_buf[0], sizeof(con->sync_buf));
-
-	if (!prb_read_valid(prb, *seq, &r))
-		return false;
-
-	text_len = record_print_text(&r, console_msg_format & MSG_FORMAT_SYSLOG, printk_time);
-
-	if (!call_sync_console_driver(con, &con->sync_buf[0], text_len))
-		return false;
-
-	*seq = r.info->seq;
-
-	touch_softlockup_watchdog_sync();
-	clocksource_touch_watchdog();
-	rcu_cpu_stall_reset();
-	touch_nmi_watchdog();
-
-	if (text_len)
-		printk_delay(r.info->level);
-
-	return true;
-}
-
-static void print_sync_until(struct console *con, u64 seq)
-{
-	unsigned int flags;
-	u64 printk_seq;
-
-	console_atomic_lock(&flags);
-	for (;;) {
-		printk_seq = atomic64_read(&con->printk_seq);
-		if (printk_seq >= seq)
-			break;
-		if (!print_sync(con, &printk_seq))
-			break;
-		atomic64_set(&con->printk_seq, printk_seq + 1);
-	}
-	console_atomic_unlock(flags);
-}
-
-#ifdef CONFIG_PRINTK_NMI
-#define NUM_RECURSION_CTX 2
-#else
-#define NUM_RECURSION_CTX 1
-#endif
-
-struct printk_recursion {
-	char	count[NUM_RECURSION_CTX];
-};
-
-static DEFINE_PER_CPU(struct printk_recursion, percpu_printk_recursion);
-static char printk_recursion_count[NUM_RECURSION_CTX];
-
-static char *printk_recursion_counter(void)
-{
-	struct printk_recursion *rec;
-	char *count;
-
-	if (!printk_percpu_data_ready()) {
-		count = &printk_recursion_count[0];
-	} else {
-		rec = this_cpu_ptr(&percpu_printk_recursion);
-
-		count = &rec->count[0];
-	}
-
-#ifdef CONFIG_PRINTK_NMI
-	if (in_nmi())
-		count++;
-#endif
-
-	return count;
-}
-
-static bool printk_enter_irqsave(unsigned long *flags)
-{
-	char *count;
-
-	local_irq_save(*flags);
-	count = printk_recursion_counter();
-	/* Only 1 level of recursion allowed. */
-	if (*count > 1) {
-		local_irq_restore(*flags);
-		return false;
-	}
-	(*count)++;
-
-	return true;
-}
-
-static void printk_exit_irqrestore(unsigned long flags)
-{
-	char *count;
-
-	count = printk_recursion_counter();
-	(*count)--;
-	local_irq_restore(flags);
-}
-
 static inline u32 printk_caller_id(void)
 {
 	return in_task() ? task_pid_nr(current) :
 		0x80000000 + raw_smp_processor_id();
 }
 
-/**
- * parse_prefix - Parse level and control flags.
- *
- * @text:     The terminated text message.
- * @level:    A pointer to the current level value, will be updated.
- * @lflags:   A pointer to the current log flags, will be updated.
- *
- * @level may be NULL if the caller is not interested in the parsed value.
- * Otherwise the variable pointed to by @level must be set to
- * LOGLEVEL_DEFAULT in order to be updated with the parsed value.
- *
- * @lflags may be NULL if the caller is not interested in the parsed value.
- * Otherwise the variable pointed to by @lflags will be OR'd with the parsed
- * value.
- *
- * Return: The length of the parsed level and control flags.
- */
-static u16 parse_prefix(char *text, int *level, enum log_flags *lflags)
-{
-	u16 prefix_len = 0;
-	int kern_level;
-
-	while (*text) {
-		kern_level = printk_get_level(text);
-		if (!kern_level)
-			break;
-
-		switch (kern_level) {
-		case '0' ... '7':
-			if (level && *level == LOGLEVEL_DEFAULT)
-				*level = kern_level - '0';
-			break;
-		case 'c':	/* KERN_CONT */
-			if (lflags)
-				*lflags |= LOG_CONT;
-		}
-
-		prefix_len += 2;
-		text += 2;
-	}
-
-	return prefix_len;
-}
-
-static u16 printk_sprint(char *text, u16 size, int facility, enum log_flags *lflags,
-			 const char *fmt, va_list args)
-{
-	u16 text_len;
-
-	text_len = vscnprintf(text, size, fmt, args);
-
-	/* Mark and strip a trailing newline. */
-	if (text_len && text[text_len - 1] == '\n') {
-		text_len--;
-		*lflags |= LOG_NEWLINE;
-	}
-
-	/* Strip log level and control flags. */
-	if (facility == 0) {
-		u16 prefix_len;
-
-		prefix_len = parse_prefix(text, NULL, NULL);
-		if (prefix_len) {
-			text_len -= prefix_len;
-			memmove(text, text + prefix_len, text_len);
-		}
-	}
-
-	return text_len;
-}
-
-__printf(4, 0)
-static int vprintk_store(int facility, int level,
+static size_t log_output(int facility, int level, enum log_flags lflags,
 			 const struct dev_printk_info *dev_info,
-			 const char *fmt, va_list args)
+			 char *text, size_t text_len)
 {
 	const u32 caller_id = printk_caller_id();
-	struct prb_reserved_entry e;
+
+	if (lflags & LOG_CONT) {
+		struct prb_reserved_entry e;
+		struct printk_record r;
+
+		prb_rec_init_wr(&r, text_len);
+		if (prb_reserve_in_last(&e, prb, &r, caller_id, LOG_LINE_MAX)) {
+			memcpy(&r.text_buf[r.info->text_len], text, text_len);
+			r.info->text_len += text_len;
+			if (lflags & LOG_NEWLINE) {
+				r.info->flags |= LOG_NEWLINE;
+				prb_final_commit(&e);
+			} else {
+				prb_commit(&e);
+			}
+
+			trace_android_vh_logbuf_pr_cont(&r, text_len);
+			return text_len;
+		}
+	}
+
+	/* Store it in the record log */
+	return log_store(caller_id, facility, level, lflags, 0,
+			 dev_info, text, text_len);
+}
+
+/* Must be called under logbuf_lock. */
+int vprintk_store(int facility, int level,
+		  const struct dev_printk_info *dev_info,
+		  const char *fmt, va_list args)
+{
+	static char textbuf[LOG_LINE_MAX];
+	char *text = textbuf;
+	size_t text_len;
 	enum log_flags lflags = 0;
-	bool final_commit = false;
-	struct printk_record r;
-	unsigned long irqflags;
-	u16 trunc_msg_len = 0;
-	char prefix_buf[8];
-	u16 reserve_size;
-	va_list args2;
-	u16 text_len;
-	int ret = 0;
-	u64 ts_nsec;
-	u64 seq;
 
 	/*
-	 * Since the duration of printk() can vary depending on the message
-	 * and state of the ringbuffer, grab the timestamp now so that it is
-	 * close to the call of printk(). This provides a more deterministic
-	 * timestamp with respect to the caller.
+	 * The printf needs to come first; we need the syslog
+	 * prefix which might be passed-in as a parameter.
 	 */
-	ts_nsec = local_clock();
+	text_len = vscnprintf(text, sizeof(textbuf), fmt, args);
 
-	if (!printk_enter_irqsave(&irqflags))
-		return 0;
+	/* mark and strip a trailing newline */
+	if (text_len && text[text_len-1] == '\n') {
+		text_len--;
+		lflags |= LOG_NEWLINE;
+	}
 
-	/*
-	 * The sprintf needs to come first since the syslog prefix might be
-	 * passed in as a parameter. An extra byte must be reserved so that
-	 * later the vscnprintf() into the reserved buffer has room for the
-	 * terminating '\0', which is not counted by vsnprintf().
-	 */
-	va_copy(args2, args);
-	reserve_size = vsnprintf(&prefix_buf[0], sizeof(prefix_buf), fmt, args2) + 1;
-	va_end(args2);
+	/* strip kernel syslog prefix and extract log level or control flags */
+	if (facility == 0) {
+		int kern_level;
 
-	if (reserve_size > LOG_LINE_MAX)
-		reserve_size = LOG_LINE_MAX;
+		while ((kern_level = printk_get_level(text)) != 0) {
+			switch (kern_level) {
+			case '0' ... '7':
+				if (level == LOGLEVEL_DEFAULT)
+					level = kern_level - '0';
+				break;
+			case 'c':	/* KERN_CONT */
+				lflags |= LOG_CONT;
+			}
 
-	/* Extract log level or control flags. */
-	if (facility == 0)
-		parse_prefix(&prefix_buf[0], &level, &lflags);
+			text_len -= 2;
+			text += 2;
+		}
+	}
 
 	if (level == LOGLEVEL_DEFAULT)
 		level = default_message_loglevel;
@@ -2006,79 +2036,7 @@
 	if (dev_info)
 		lflags |= LOG_NEWLINE;
 
-	if (lflags & LOG_CONT) {
-		prb_rec_init_wr(&r, reserve_size);
-		if (prb_reserve_in_last(&e, prb, &r, caller_id, LOG_LINE_MAX)) {
-			seq = r.info->seq;
-			text_len = printk_sprint(&r.text_buf[r.info->text_len], reserve_size,
-						 facility, &lflags, fmt, args);
-			r.info->text_len += text_len;
-
-			if (lflags & LOG_NEWLINE) {
-				r.info->flags |= LOG_NEWLINE;
-				prb_final_commit(&e);
-				final_commit = true;
-			} else {
-				prb_commit(&e);
-			}
-
-			ret = text_len;
-			goto out;
-		}
-	}
-
-	/*
-	 * Explicitly initialize the record before every prb_reserve() call.
-	 * prb_reserve_in_last() and prb_reserve() purposely invalidate the
-	 * structure when they fail.
-	 */
-	prb_rec_init_wr(&r, reserve_size);
-	if (!prb_reserve(&e, prb, &r)) {
-		/* truncate the message if it is too long for empty buffer */
-		truncate_msg(&reserve_size, &trunc_msg_len);
-
-		prb_rec_init_wr(&r, reserve_size + trunc_msg_len);
-		if (!prb_reserve(&e, prb, &r))
-			goto out;
-	}
-
-	seq = r.info->seq;
-
-	/* fill message */
-	text_len = printk_sprint(&r.text_buf[0], reserve_size, facility, &lflags, fmt, args);
-	if (trunc_msg_len)
-		memcpy(&r.text_buf[text_len], trunc_msg, trunc_msg_len);
-	r.info->text_len = text_len + trunc_msg_len;
-	r.info->facility = facility;
-	r.info->level = level & 7;
-	r.info->flags = lflags & 0x1f;
-	r.info->ts_nsec = ts_nsec;
-	r.info->caller_id = caller_id;
-	if (dev_info)
-		memcpy(&r.info->dev_info, dev_info, sizeof(r.info->dev_info));
-
-	/* A message without a trailing newline can be continued. */
-	if (!(lflags & LOG_NEWLINE)) {
-		prb_commit(&e);
-	} else {
-		prb_final_commit(&e);
-		final_commit = true;
-	}
-
-	ret = text_len + trunc_msg_len;
-out:
-	/* only the kernel may perform synchronous printing */
-	if (facility == 0 && final_commit) {
-		struct console *con;
-
-		for_each_console(con) {
-			if (console_can_sync(con))
-				print_sync_until(con, seq + 1);
-		}
-	}
-
-	printk_exit_irqrestore(irqflags);
-	return ret;
+	return log_output(facility, level, lflags, dev_info, text, text_len);
 }
 
 asmlinkage int vprintk_emit(int facility, int level,
@@ -2086,42 +2044,60 @@
 			    const char *fmt, va_list args)
 {
 	int printed_len;
+	bool in_sched = false;
+	unsigned long flags;
 
 	/* Suppress unimportant messages after panic happens */
 	if (unlikely(suppress_printk))
 		return 0;
 
-	if (level == LOGLEVEL_SCHED)
+	if (level == LOGLEVEL_SCHED) {
 		level = LOGLEVEL_DEFAULT;
+		in_sched = true;
+	}
 
+	boot_delay_msec(level);
+	printk_delay();
+
+	/* This stops the holder of console_sem just where we want him */
+	logbuf_lock_irqsave(flags);
 	printed_len = vprintk_store(facility, level, dev_info, fmt, args);
+	logbuf_unlock_irqrestore(flags);
+
+	/* If called from the scheduler, we can not call up(). */
+	if (!in_sched) {
+		/*
+		 * Disable preemption to avoid being preempted while holding
+		 * console_sem which would prevent anyone from printing to
+		 * console
+		 */
+		preempt_disable();
+		/*
+		 * Try to acquire and then immediately release the console
+		 * semaphore.  The release will print out buffers and wake up
+		 * /dev/kmsg and syslog() users.
+		 */
+		if (console_trylock_spinning())
+			console_unlock();
+		preempt_enable();
+	}
 
 	wake_up_klogd();
 	return printed_len;
 }
 EXPORT_SYMBOL(vprintk_emit);
 
-__printf(1, 0)
-static int vprintk_default(const char *fmt, va_list args)
-{
-	return vprintk_emit(0, LOGLEVEL_DEFAULT, NULL, fmt, args);
-}
-__printf(1, 0)
-static int vprintk_func(const char *fmt, va_list args)
-{
-#ifdef CONFIG_KGDB_KDB
-	/* Allow to pass printk() to kdb but avoid a recursion. */
-	if (unlikely(kdb_trap_printk && kdb_printf_cpu < 0))
-		return vkdb_printf(KDB_MSGSRC_PRINTK, fmt, args);
-#endif
-	return vprintk_default(fmt, args);
-}
-
 asmlinkage int vprintk(const char *fmt, va_list args)
 {
 	return vprintk_func(fmt, args);
 }
 EXPORT_SYMBOL(vprintk);
+
+int vprintk_default(const char *fmt, va_list args)
+{
+	return vprintk_emit(0, LOGLEVEL_DEFAULT, NULL, fmt, args);
+}
+EXPORT_SYMBOL_GPL(vprintk_default);
 
 /**
  * printk - print a kernel message
@@ -2157,162 +2133,38 @@
 }
 EXPORT_SYMBOL(printk);
 
-static int printk_kthread_func(void *data)
-{
-	struct console *con = data;
-	unsigned long dropped = 0;
-	char *dropped_text = NULL;
-	struct printk_info info;
-	struct printk_record r;
-	char *ext_text = NULL;
-	size_t dropped_len;
-	int ret = -ENOMEM;
-	char *text = NULL;
-	char *write_text;
-	u64 printk_seq;
-	size_t len;
-	int error;
-	u64 seq;
-
-	if (con->flags & CON_EXTENDED) {
-		ext_text = kmalloc(CONSOLE_EXT_LOG_MAX, GFP_KERNEL);
-		if (!ext_text)
-			goto out;
-	}
-	text = kmalloc(LOG_LINE_MAX + PREFIX_MAX, GFP_KERNEL);
-	dropped_text = kmalloc(64, GFP_KERNEL);
-	if (!text || !dropped_text)
-		goto out;
-
-	if (con->flags & CON_EXTENDED)
-		write_text = ext_text;
-	else
-		write_text = text;
-
-	seq = atomic64_read(&con->printk_seq);
-
-	prb_rec_init_rd(&r, &info, text, LOG_LINE_MAX + PREFIX_MAX);
-
-	for (;;) {
-		error = wait_event_interruptible(log_wait,
-				prb_read_valid(prb, seq, &r) || kthread_should_stop());
-
-		if (kthread_should_stop())
-			break;
-
-		if (error)
-			continue;
-
-		if (seq != r.info->seq) {
-			dropped += r.info->seq - seq;
-			seq = r.info->seq;
-		}
-
-		seq++;
-
-		if (!(con->flags & CON_ENABLED))
-			continue;
-
-		if (suppress_message_printing(r.info->level))
-			continue;
-
-		if (con->flags & CON_EXTENDED) {
-			len = info_print_ext_header(ext_text,
-				CONSOLE_EXT_LOG_MAX,
-				r.info);
-			len += msg_print_ext_body(ext_text + len,
-				CONSOLE_EXT_LOG_MAX - len,
-				&r.text_buf[0], r.info->text_len,
-				&r.info->dev_info);
-		} else {
-			len = record_print_text(&r,
-				console_msg_format & MSG_FORMAT_SYSLOG,
-				printk_time);
-		}
-
-		printk_seq = atomic64_read(&con->printk_seq);
-
-		console_lock();
-		console_may_schedule = 0;
-
-		if (kernel_sync_mode() && con->write_atomic) {
-			console_unlock();
-			break;
-		}
-
-		if (!(con->flags & CON_EXTENDED) && dropped) {
-			dropped_len = snprintf(dropped_text, 64,
-					       "** %lu printk messages dropped **\n",
-					       dropped);
-			dropped = 0;
-
-			con->write(con, dropped_text, dropped_len);
-			printk_delay(r.info->level);
-		}
-
-		con->write(con, write_text, len);
-		if (len)
-			printk_delay(r.info->level);
-
-		atomic64_cmpxchg_relaxed(&con->printk_seq, printk_seq, seq);
-
-		console_unlock();
-	}
-out:
-	kfree(dropped_text);
-	kfree(text);
-	kfree(ext_text);
-	pr_info("%sconsole [%s%d]: printing thread stopped\n",
-		(con->flags & CON_BOOT) ? "boot" : "",
-		con->name, con->index);
-	return ret;
-}
-
-/* Must be called within console_lock(). */
-static void start_printk_kthread(struct console *con)
-{
-	/* No need to start a printing thread if the console cannot print. */
-	if (!con->write)
-		return;
-
-	con->thread = kthread_run(printk_kthread_func, con,
-				  "pr/%s%d", con->name, con->index);
-	if (IS_ERR(con->thread)) {
-		pr_err("%sconsole [%s%d]: unable to start printing thread\n",
-			(con->flags & CON_BOOT) ? "boot" : "",
-			con->name, con->index);
-		return;
-	}
-	pr_info("%sconsole [%s%d]: printing thread started\n",
-		(con->flags & CON_BOOT) ? "boot" : "",
-		con->name, con->index);
-}
-
-/* protected by console_lock */
-static bool kthreads_started;
-
-/* Must be called within console_lock(). */
-static void console_try_thread(struct console *con)
-{
-	if (kthreads_started) {
-		start_printk_kthread(con);
-		return;
-	}
-
-	/*
-	 * The printing threads have not been started yet. If this console
-	 * can print synchronously, print all unprinted messages.
-	 */
-	if (console_can_sync(con))
-		print_sync_until(con, prb_next_seq(prb));
-}
-
 #else /* CONFIG_PRINTK */
 
-#define prb_first_valid_seq(rb)		0
-#define prb_next_seq(rb)		0
+#define LOG_LINE_MAX		0
+#define PREFIX_MAX		0
+#define printk_time		false
 
-#define console_try_thread(con)
+#define prb_read_valid(rb, seq, r)	false
+#define prb_first_valid_seq(rb)		0
+
+static u64 syslog_seq;
+static u64 console_seq;
+static u64 exclusive_console_stop_seq;
+static unsigned long console_dropped;
+
+static size_t record_print_text(const struct printk_record *r,
+				bool syslog, bool time)
+{
+	return 0;
+}
+static ssize_t info_print_ext_header(char *buf, size_t size,
+				     struct printk_info *info)
+{
+	return 0;
+}
+static ssize_t msg_print_ext_body(char *buf, size_t size,
+				  char *text, size_t text_len,
+				  struct dev_printk_info *dev_info) { return 0; }
+static void console_lock_spinning_enable(void) { }
+static int console_lock_spinning_disable_and_check(void) { return 0; }
+static void call_console_drivers(const char *ext_text, size_t ext_len,
+				 const char *text, size_t len) {}
+static bool suppress_message_printing(int level) { return false; }
 
 #endif /* CONFIG_PRINTK */
 
@@ -2563,6 +2415,34 @@
 }
 EXPORT_SYMBOL(is_console_locked);
 
+/*
+ * Check if we have any console that is capable of printing while cpu is
+ * booting or shutting down. Requires console_sem.
+ */
+static int have_callable_console(void)
+{
+	struct console *con;
+
+	for_each_console(con)
+		if ((con->flags & CON_ENABLED) &&
+				(con->flags & CON_ANYTIME))
+			return 1;
+
+	return 0;
+}
+
+/*
+ * Can we actually use the console at this time on this cpu?
+ *
+ * Console drivers may assume that per-cpu resources have been allocated. So
+ * unless they're explicitly marked as being able to cope (CON_ANYTIME) don't
+ * call them until this CPU is officially up.
+ */
+static inline int can_use_console(void)
+{
+	return cpu_online(raw_smp_processor_id()) || have_callable_console();
+}
+
 /**
  * console_unlock - unlock the console system
  *
@@ -2579,14 +2459,142 @@
  */
 void console_unlock(void)
 {
+	static char ext_text[CONSOLE_EXT_LOG_MAX];
+	static char text[LOG_LINE_MAX + PREFIX_MAX];
+	unsigned long flags;
+	bool do_cond_resched, retry;
+	struct printk_info info;
+	struct printk_record r;
+
 	if (console_suspended) {
 		up_console_sem();
 		return;
 	}
 
+	prb_rec_init_rd(&r, &info, text, sizeof(text));
+
+	/*
+	 * Console drivers are called with interrupts disabled, so
+	 * @console_may_schedule should be cleared before; however, we may
+	 * end up dumping a lot of lines, for example, if called from
+	 * console registration path, and should invoke cond_resched()
+	 * between lines if allowable.  Not doing so can cause a very long
+	 * scheduling stall on a slow console leading to RCU stall and
+	 * softlockup warnings which exacerbate the issue with more
+	 * messages practically incapacitating the system.
+	 *
+	 * console_trylock() is not able to detect the preemptive
+	 * context reliably. Therefore the value must be stored before
+	 * and cleared after the "again" goto label.
+	 */
+	do_cond_resched = console_may_schedule;
+again:
+	console_may_schedule = 0;
+
+	/*
+	 * We released the console_sem lock, so we need to recheck if
+	 * cpu is online and (if not) is there at least one CON_ANYTIME
+	 * console.
+	 */
+	if (!can_use_console()) {
+		console_locked = 0;
+		up_console_sem();
+		return;
+	}
+
+	for (;;) {
+		size_t ext_len = 0;
+		size_t len;
+
+		printk_safe_enter_irqsave(flags);
+		raw_spin_lock(&logbuf_lock);
+skip:
+		if (!prb_read_valid(prb, console_seq, &r))
+			break;
+
+		if (console_seq != r.info->seq) {
+			console_dropped += r.info->seq - console_seq;
+			console_seq = r.info->seq;
+		}
+
+		if (suppress_message_printing(r.info->level)) {
+			/*
+			 * Skip record we have buffered and already printed
+			 * directly to the console when we received it, and
+			 * record that has level above the console loglevel.
+			 */
+			console_seq++;
+			goto skip;
+		}
+
+		/* Output to all consoles once old messages replayed. */
+		if (unlikely(exclusive_console &&
+			     console_seq >= exclusive_console_stop_seq)) {
+			exclusive_console = NULL;
+		}
+
+		/*
+		 * Handle extended console text first because later
+		 * record_print_text() will modify the record buffer in-place.
+		 */
+		if (nr_ext_console_drivers) {
+			ext_len = info_print_ext_header(ext_text,
+						sizeof(ext_text),
+						r.info);
+			ext_len += msg_print_ext_body(ext_text + ext_len,
+						sizeof(ext_text) - ext_len,
+						&r.text_buf[0],
+						r.info->text_len,
+						&r.info->dev_info);
+		}
+		len = record_print_text(&r,
+				console_msg_format & MSG_FORMAT_SYSLOG,
+				printk_time);
+		console_seq++;
+		raw_spin_unlock(&logbuf_lock);
+
+		/*
+		 * While actively printing out messages, if another printk()
+		 * were to occur on another CPU, it may wait for this one to
+		 * finish. This task can not be preempted if there is a
+		 * waiter waiting to take over.
+		 */
+		console_lock_spinning_enable();
+
+		stop_critical_timings();	/* don't trace print latency */
+		call_console_drivers(ext_text, ext_len, text, len);
+		start_critical_timings();
+
+		if (console_lock_spinning_disable_and_check()) {
+			printk_safe_exit_irqrestore(flags);
+			return;
+		}
+
+		printk_safe_exit_irqrestore(flags);
+
+		if (do_cond_resched)
+			cond_resched();
+	}
+
 	console_locked = 0;
 
+	raw_spin_unlock(&logbuf_lock);
+
 	up_console_sem();
+
+	/*
+	 * Someone could have filled up the buffer again, so re-check if there's
+	 * something to flush. In case we cannot trylock the console_sem again,
+	 * there's a new owner and the console_unlock() from them will do the
+	 * flush, no worries.
+	 */
+	raw_spin_lock(&logbuf_lock);
+	retry = prb_read_valid(prb, console_seq, NULL);
+	raw_spin_unlock(&logbuf_lock);
+	printk_safe_exit_irqrestore(flags);
+
+	if (retry && console_trylock())
+		goto again;
 }
 EXPORT_SYMBOL(console_unlock);
 
@@ -2636,20 +2644,23 @@
  */
 void console_flush_on_panic(enum con_flush_mode mode)
 {
-	struct console *c;
-	u64 seq;
-
-	if (!console_trylock())
-		return;
-
+	/*
+	 * If someone else is holding the console lock, trylock will fail
+	 * and may_schedule may be set.  Ignore and proceed to unlock so
+	 * that messages are flushed out.  As this can be called from any
+	 * context and we don't want to get preempted while flushing,
+	 * ensure may_schedule is cleared.
+	 */
+	console_trylock();
 	console_may_schedule = 0;
 
 	if (mode == CONSOLE_REPLAY_ALL) {
-		seq = prb_first_valid_seq(prb);
-		for_each_console(c)
-			atomic64_set(&c->printk_seq, seq);
-	}
+		unsigned long flags;
 
+		logbuf_lock_irqsave(flags);
+		console_seq = prb_first_valid_seq(prb);
+		logbuf_unlock_irqrestore(flags);
+	}
 	console_unlock();
 }
 
@@ -2784,6 +2795,7 @@
  */
 void register_console(struct console *newcon)
 {
+	unsigned long flags;
 	struct console *bcon = NULL;
 	int err;
 
@@ -2806,8 +2818,6 @@
 			}
 		}
 	}
-
-	newcon->thread = NULL;
 
 	if (console_drivers && console_drivers->flags & CON_BOOT)
 		bcon = console_drivers;
@@ -2850,10 +2860,8 @@
 	 * the real console are the same physical device, it's annoying to
 	 * see the beginning boot messages twice
 	 */
-	if (bcon && ((newcon->flags & (CON_CONSDEV | CON_BOOT)) == CON_CONSDEV)) {
+	if (bcon && ((newcon->flags & (CON_CONSDEV | CON_BOOT)) == CON_CONSDEV))
 		newcon->flags &= ~CON_PRINTBUFFER;
-		newcon->flags |= CON_HANDOVER;
-	}
 
 	/*
 	 *	Put this console in the list - keep the
@@ -2875,12 +2883,26 @@
 	if (newcon->flags & CON_EXTENDED)
 		nr_ext_console_drivers++;
 
-	if (newcon->flags & CON_PRINTBUFFER)
-		atomic64_set(&newcon->printk_seq, 0);
-	else
-		atomic64_set(&newcon->printk_seq, prb_next_seq(prb));
-
-	console_try_thread(newcon);
+	if (newcon->flags & CON_PRINTBUFFER) {
+		/*
+		 * console_unlock(); will print out the buffered messages
+		 * for us.
+		 */
+		logbuf_lock_irqsave(flags);
+		/*
+		 * We're about to replay the log buffer.  Only do this to the
+		 * just-registered console to avoid excessive message spam to
+		 * the already-registered consoles.
+		 *
+		 * Set exclusive_console with disabled interrupts to reduce
+		 * race window with eventual console_flush_on_panic() that
+		 * ignores console_lock.
+		 */
+		exclusive_console = newcon;
+		exclusive_console_stop_seq = console_seq;
+		console_seq = syslog_seq;
+		logbuf_unlock_irqrestore(flags);
+	}
 	console_unlock();
 	console_sysfs_notify();
 
@@ -2953,9 +2975,6 @@
 	console->flags &= ~CON_ENABLED;
 	console_unlock();
 	console_sysfs_notify();
-
-	if (console->thread && !IS_ERR(console->thread))
-		kthread_stop(console->thread);
 
 	if (console->exit)
 		res = console->exit(console);
@@ -3039,15 +3058,6 @@
 			unregister_console(con);
 		}
 	}
-
-#ifdef CONFIG_PRINTK
-	console_lock();
-	for_each_console(con)
-		start_printk_kthread(con);
-	kthreads_started = true;
-	console_unlock();
-#endif
-
 	ret = cpuhp_setup_state_nocalls(CPUHP_PRINTK_DEAD, "printk:dead", NULL,
 					console_cpu_notify);
 	WARN_ON(ret < 0);
@@ -3063,6 +3073,7 @@
  * Delayed printk version, for scheduler-internal messages:
  */
 #define PRINTK_PENDING_WAKEUP	0x01
+#define PRINTK_PENDING_OUTPUT	0x02
 
 static DEFINE_PER_CPU(int, printk_pending);
 
@@ -3070,8 +3081,14 @@
 {
 	int pending = __this_cpu_xchg(printk_pending, 0);
 
+	if (pending & PRINTK_PENDING_OUTPUT) {
+		/* If trylock fails, someone else is doing the printing */
+		if (console_trylock())
+			console_unlock();
+	}
+
 	if (pending & PRINTK_PENDING_WAKEUP)
-		wake_up_interruptible_all(&log_wait);
+		wake_up_interruptible(&log_wait);
 }
 
 static DEFINE_PER_CPU(struct irq_work, wake_up_klogd_work) = {
@@ -3092,10 +3109,25 @@
 	preempt_enable();
 }
 
-__printf(1, 0)
-static int vprintk_deferred(const char *fmt, va_list args)
+void defer_console_output(void)
 {
-	return vprintk_emit(0, LOGLEVEL_DEFAULT, NULL, fmt, args);
+	if (!printk_percpu_data_ready())
+		return;
+
+	preempt_disable();
+	__this_cpu_or(printk_pending, PRINTK_PENDING_OUTPUT);
+	irq_work_queue(this_cpu_ptr(&wake_up_klogd_work));
+	preempt_enable();
+}
+
+int vprintk_deferred(const char *fmt, va_list args)
+{
+	int r;
+
+	r = vprintk_emit(0, LOGLEVEL_SCHED, NULL, fmt, args);
+	defer_console_output();
+
+	return r;
 }
 
 int printk_deferred(const char *fmt, ...)
@@ -3235,26 +3267,8 @@
  */
 void kmsg_dump(enum kmsg_dump_reason reason)
 {
-	struct kmsg_dumper_iter iter;
 	struct kmsg_dumper *dumper;
-
-	if (!oops_in_progress) {
-		/*
-		 * If atomic consoles are available, activate kernel sync mode
-		 * to make sure any final messages are visible. The trailing
-		 * printk message is important to flush any pending messages.
-		 */
-		if (have_atomic_console()) {
-			sync_mode = true;
-			pr_info("enabled sync mode\n");
-		}
-
-		/*
-		 * Give the printing threads time to flush, allowing up to
-		 * 1s of no printing forward progress before giving up.
-		 */
-		pr_flush(1000, true);
-	}
+	unsigned long flags;
 
 	rcu_read_lock();
 	list_for_each_entry_rcu(dumper, &dump_list, list) {
@@ -3272,18 +3286,81 @@
 			continue;
 
 		/* initialize iterator with data about the stored records */
-		iter.active = true;
-		kmsg_dump_rewind(&iter);
+		dumper->active = true;
+
+		logbuf_lock_irqsave(flags);
+		dumper->cur_seq = clear_seq;
+		dumper->next_seq = prb_next_seq(prb);
+		logbuf_unlock_irqrestore(flags);
 
 		/* invoke dumper which will iterate over records */
-		dumper->dump(dumper, reason, &iter);
+		dumper->dump(dumper, reason);
+
+		/* reset iterator */
+		dumper->active = false;
 	}
 	rcu_read_unlock();
 }
 
 /**
+ * kmsg_dump_get_line_nolock - retrieve one kmsg log line (unlocked version)
+ * @dumper: registered kmsg dumper
+ * @syslog: include the "<4>" prefixes
+ * @line: buffer to copy the line to
+ * @size: maximum size of the buffer
+ * @len: length of line placed into buffer
+ *
+ * Start at the beginning of the kmsg buffer, with the oldest kmsg
+ * record, and copy one record into the provided buffer.
+ *
+ * Consecutive calls will return the next available record moving
+ * towards the end of the buffer with the youngest messages.
+ *
+ * A return value of FALSE indicates that there are no more records to
+ * read.
+ *
+ * The function is similar to kmsg_dump_get_line(), but grabs no locks.
+ */
+bool kmsg_dump_get_line_nolock(struct kmsg_dumper *dumper, bool syslog,
+			       char *line, size_t size, size_t *len)
+{
+	struct printk_info info;
+	unsigned int line_count;
+	struct printk_record r;
+	size_t l = 0;
+	bool ret = false;
+
+	prb_rec_init_rd(&r, &info, line, size);
+
+	if (!dumper->active)
+		goto out;
+
+	/* Read text or count text lines? */
+	if (line) {
+		if (!prb_read_valid(prb, dumper->cur_seq, &r))
+			goto out;
+		l = record_print_text(&r, syslog, printk_time);
+	} else {
+		if (!prb_read_valid_info(prb, dumper->cur_seq,
+					 &info, &line_count)) {
+			goto out;
+		}
+		l = get_record_print_text_size(&info, line_count, syslog,
+					       printk_time);
+
+	}
+
+	dumper->cur_seq = r.info->seq + 1;
+	ret = true;
+out:
+	if (len)
+		*len = l;
+	return ret;
+}
+
+/**
  * kmsg_dump_get_line - retrieve one kmsg log line
- * @iter: kmsg dumper iterator
+ * @dumper: registered kmsg dumper
  * @syslog: include the "<4>" prefixes
  * @line: buffer to copy the line to
  * @size: maximum size of the buffer
@@ -3298,47 +3375,23 @@
  * A return value of FALSE indicates that there are no more records to
  * read.
  */
-bool kmsg_dump_get_line(struct kmsg_dumper_iter *iter, bool syslog,
+bool kmsg_dump_get_line(struct kmsg_dumper *dumper, bool syslog,
 			char *line, size_t size, size_t *len)
 {
-	struct printk_info info;
-	unsigned int line_count;
-	struct printk_record r;
-	size_t l = 0;
-	bool ret = false;
+	unsigned long flags;
+	bool ret;
 
-	prb_rec_init_rd(&r, &info, line, size);
+	logbuf_lock_irqsave(flags);
+	ret = kmsg_dump_get_line_nolock(dumper, syslog, line, size, len);
+	logbuf_unlock_irqrestore(flags);
 
-	if (!iter->active)
-		goto out;
-
-	/* Read text or count text lines? */
-	if (line) {
-		if (!prb_read_valid(prb, iter->cur_seq, &r))
-			goto out;
-		l = record_print_text(&r, syslog, printk_time);
-	} else {
-		if (!prb_read_valid_info(prb, iter->cur_seq,
-					 &info, &line_count)) {
-			goto out;
-		}
-		l = get_record_print_text_size(&info, line_count, syslog,
-					       printk_time);
-
-	}
-
-	iter->cur_seq = r.info->seq + 1;
-	ret = true;
-out:
-	if (len)
-		*len = l;
 	return ret;
 }
 EXPORT_SYMBOL_GPL(kmsg_dump_get_line);
 
 /**
  * kmsg_dump_get_buffer - copy kmsg log lines
- * @iter: kmsg dumper iterator
+ * @dumper: registered kmsg dumper
  * @syslog: include the "<4>" prefixes
  * @buf: buffer to copy the line to
  * @size: maximum size of the buffer
@@ -3355,258 +3408,116 @@
  * A return value of FALSE indicates that there are no more records to
  * read.
  */
-bool kmsg_dump_get_buffer(struct kmsg_dumper_iter *iter, bool syslog,
-			  char *buf, size_t size, size_t *len_out)
+bool kmsg_dump_get_buffer(struct kmsg_dumper *dumper, bool syslog,
+			  char *buf, size_t size, size_t *len)
 {
 	struct printk_info info;
+	unsigned int line_count;
 	struct printk_record r;
+	unsigned long flags;
 	u64 seq;
 	u64 next_seq;
-	size_t len = 0;
+	size_t l = 0;
 	bool ret = false;
 	bool time = printk_time;
 
-	if (!iter->active || !buf || !size)
+	prb_rec_init_rd(&r, &info, buf, size);
+
+	if (!dumper->active || !buf || !size)
 		goto out;
 
-	if (prb_read_valid_info(prb, iter->cur_seq, &info, NULL)) {
-		if (info.seq != iter->cur_seq) {
+	logbuf_lock_irqsave(flags);
+	if (prb_read_valid_info(prb, dumper->cur_seq, &info, NULL)) {
+		if (info.seq != dumper->cur_seq) {
 			/* messages are gone, move to first available one */
-			iter->cur_seq = info.seq;
+			dumper->cur_seq = info.seq;
 		}
 	}
 
 	/* last entry */
-	if (iter->cur_seq >= iter->next_seq)
+	if (dumper->cur_seq >= dumper->next_seq) {
+		logbuf_unlock_irqrestore(flags);
 		goto out;
-
-	/*
-	 * Find first record that fits, including all following records,
-	 * into the user-provided buffer for this dump. Pass in size-1
-	 * because this function (by way of record_print_text()) will
-	 * not write more than size-1 bytes of text into @buf.
-	 */
-	seq = find_first_fitting_seq(iter->cur_seq, iter->next_seq,
-				     size - 1, syslog, time);
-
-	/*
-	 * Next kmsg_dump_get_buffer() invocation will dump block of
-	 * older records stored right before this one.
-	 */
-	next_seq = seq;
-
-	prb_rec_init_rd(&r, &info, buf, size);
-
-	len = 0;
-	prb_for_each_record(seq, prb, seq, &r) {
-		if (r.info->seq >= iter->next_seq)
-			break;
-
-		len += record_print_text(&r, syslog, time);
-
-		/* Adjust record to store to remaining buffer space. */
-		prb_rec_init_rd(&r, &info, buf + len, size - len);
 	}
 
-	iter->next_seq = next_seq;
+	/* calculate length of entire buffer */
+	seq = dumper->cur_seq;
+	while (prb_read_valid_info(prb, seq, &info, &line_count)) {
+		if (r.info->seq >= dumper->next_seq)
+			break;
+		l += get_record_print_text_size(&info, line_count, syslog, time);
+		seq = r.info->seq + 1;
+	}
+
+	/* move first record forward until length fits into the buffer */
+	seq = dumper->cur_seq;
+	while (l >= size && prb_read_valid_info(prb, seq,
+						&info, &line_count)) {
+		if (r.info->seq >= dumper->next_seq)
+			break;
+		l -= get_record_print_text_size(&info, line_count, syslog, time);
+		seq = r.info->seq + 1;
+	}
+
+	/* last message in next interation */
+	next_seq = seq;
+
+	/* actually read text into the buffer now */
+	l = 0;
+	while (prb_read_valid(prb, seq, &r)) {
+		if (r.info->seq >= dumper->next_seq)
+			break;
+
+		l += record_print_text(&r, syslog, time);
+
+		/* adjust record to store to remaining buffer space */
+		prb_rec_init_rd(&r, &info, buf + l, size - l);
+
+		seq = r.info->seq + 1;
+	}
+
+	dumper->next_seq = next_seq;
 	ret = true;
+	logbuf_unlock_irqrestore(flags);
 out:
-	if (len_out)
-		*len_out = len;
+	if (len)
+		*len = l;
 	return ret;
 }
 EXPORT_SYMBOL_GPL(kmsg_dump_get_buffer);
 
 /**
+ * kmsg_dump_rewind_nolock - reset the iterator (unlocked version)
+ * @dumper: registered kmsg dumper
+ *
+ * Reset the dumper's iterator so that kmsg_dump_get_line() and
+ * kmsg_dump_get_buffer() can be called again and used multiple
+ * times within the same dumper.dump() callback.
+ *
+ * The function is similar to kmsg_dump_rewind(), but grabs no locks.
+ */
+void kmsg_dump_rewind_nolock(struct kmsg_dumper *dumper)
+{
+	dumper->cur_seq = clear_seq;
+	dumper->next_seq = prb_next_seq(prb);
+}
+
+/**
  * kmsg_dump_rewind - reset the iterator
- * @iter: kmsg dumper iterator
+ * @dumper: registered kmsg dumper
  *
  * Reset the dumper's iterator so that kmsg_dump_get_line() and
  * kmsg_dump_get_buffer() can be called again and used multiple
  * times within the same dumper.dump() callback.
  */
-void kmsg_dump_rewind(struct kmsg_dumper_iter *iter)
+void kmsg_dump_rewind(struct kmsg_dumper *dumper)
 {
-	iter->cur_seq = latched_seq_read_nolock(&clear_seq);
-	iter->next_seq = prb_next_seq(prb);
+	unsigned long flags;
+
+	logbuf_lock_irqsave(flags);
+	kmsg_dump_rewind_nolock(dumper);
+	logbuf_unlock_irqrestore(flags);
 }
 EXPORT_SYMBOL_GPL(kmsg_dump_rewind);
 
 #endif
-
-struct prb_cpulock {
-	atomic_t owner;
-	unsigned long __percpu *irqflags;
-};
-
-#define DECLARE_STATIC_PRINTKRB_CPULOCK(name)				\
-static DEFINE_PER_CPU(unsigned long, _##name##_percpu_irqflags);	\
-static struct prb_cpulock name = {					\
-	.owner = ATOMIC_INIT(-1),					\
-	.irqflags = &_##name##_percpu_irqflags,				\
-}
-
-static bool __prb_trylock(struct prb_cpulock *cpu_lock,
-			  unsigned int *cpu_store)
-{
-	unsigned long *flags;
-	unsigned int cpu;
-
-	cpu = get_cpu();
-
-	*cpu_store = atomic_read(&cpu_lock->owner);
-	/* memory barrier to ensure the current lock owner is visible */
-	smp_rmb();
-	if (*cpu_store == -1) {
-		flags = per_cpu_ptr(cpu_lock->irqflags, cpu);
-		local_irq_save(*flags);
-		if (atomic_try_cmpxchg_acquire(&cpu_lock->owner,
-					       cpu_store, cpu)) {
-			return true;
-		}
-		local_irq_restore(*flags);
-	} else if (*cpu_store == cpu) {
-		return true;
-	}
-
-	put_cpu();
-	return false;
-}
-
-/*
- * prb_lock: Perform a processor-reentrant spin lock.
- * @cpu_lock: A pointer to the lock object.
- * @cpu_store: A "flags" pointer to store lock status information.
- *
- * If no processor has the lock, the calling processor takes the lock and
- * becomes the owner. If the calling processor is already the owner of the
- * lock, this function succeeds immediately. If lock is locked by another
- * processor, this function spins until the calling processor becomes the
- * owner.
- *
- * It is safe to call this function from any context and state.
- */
-static void prb_lock(struct prb_cpulock *cpu_lock, unsigned int *cpu_store)
-{
-	for (;;) {
-		if (__prb_trylock(cpu_lock, cpu_store))
-			break;
-		cpu_relax();
-	}
-}
-
-/*
- * prb_unlock: Perform a processor-reentrant spin unlock.
- * @cpu_lock: A pointer to the lock object.
- * @cpu_store: A "flags" object storing lock status information.
- *
- * Release the lock. The calling processor must be the owner of the lock.
- *
- * It is safe to call this function from any context and state.
- */
-static void prb_unlock(struct prb_cpulock *cpu_lock, unsigned int cpu_store)
-{
-	unsigned long *flags;
-	unsigned int cpu;
-
-	cpu = atomic_read(&cpu_lock->owner);
-	atomic_set_release(&cpu_lock->owner, cpu_store);
-
-	if (cpu_store == -1) {
-		flags = per_cpu_ptr(cpu_lock->irqflags, cpu);
-		local_irq_restore(*flags);
-	}
-
-	put_cpu();
-}
-
-DECLARE_STATIC_PRINTKRB_CPULOCK(printk_cpulock);
-
-void console_atomic_lock(unsigned int *flags)
-{
-	prb_lock(&printk_cpulock, flags);
-}
-EXPORT_SYMBOL(console_atomic_lock);
-
-void console_atomic_unlock(unsigned int flags)
-{
-	prb_unlock(&printk_cpulock, flags);
-}
-EXPORT_SYMBOL(console_atomic_unlock);
-
-static void pr_msleep(bool may_sleep, int ms)
-{
-	if (may_sleep) {
-		msleep(ms);
-	} else {
-		while (ms--)
-			udelay(1000);
-	}
-}
-
-/**
- * pr_flush() - Wait for printing threads to catch up.
- *
- * @timeout_ms:        The maximum time (in ms) to wait.
- * @reset_on_progress: Reset the timeout if forward progress is seen.
- *
- * A value of 0 for @timeout_ms means no waiting will occur. A value of -1
- * represents infinite waiting.
- *
- * If @reset_on_progress is true, the timeout will be reset whenever any
- * printer has been seen to make some forward progress.
- *
- * Context: Any context.
- * Return: true if all enabled printers are caught up.
- */
-bool pr_flush(int timeout_ms, bool reset_on_progress)
-{
-	int remaining = timeout_ms;
-	struct console *con;
-	u64 last_diff = 0;
-	bool may_sleep;
-	u64 printk_seq;
-	u64 diff;
-	u64 seq;
-
-	may_sleep = (preemptible() &&
-		     !in_softirq() &&
-		     system_state >= SYSTEM_RUNNING);
-
-	seq = prb_next_seq(prb);
-
-	for (;;) {
-		diff = 0;
-
-		for_each_console(con) {
-			if (!(con->flags & CON_ENABLED))
-				continue;
-			if (!con->write && !con->write_atomic)
-				continue;
-			printk_seq = atomic64_read(&con->printk_seq);
-			if (printk_seq < seq)
-				diff += seq - printk_seq;
-		}
-
-		if (diff != last_diff && reset_on_progress)
-			remaining = timeout_ms;
-
-		if (!diff || remaining == 0)
-			break;
-
-		if (remaining < 0) {
-			pr_msleep(may_sleep, 100);
-		} else if (remaining < 100) {
-			pr_msleep(may_sleep, remaining);
-			remaining = 0;
-		} else {
-			pr_msleep(may_sleep, 100);
-			remaining -= 100;
-		}
-
-		last_diff = diff;
-	}
-
-	return (diff == 0);
-}
-EXPORT_SYMBOL(pr_flush);
diff --git a/kernel/kernel/printk/printk_safe.c b/kernel/kernel/printk/printk_safe.c
new file mode 100644
index 0000000..2e9e3ed
--- /dev/null
+++ b/kernel/kernel/printk/printk_safe.c
@@ -0,0 +1,422 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * printk_safe.c - Safe printk for printk-deadlock-prone contexts
+ */
+
+#include <linux/preempt.h>
+#include <linux/spinlock.h>
+#include <linux/debug_locks.h>
+#include <linux/kdb.h>
+#include <linux/smp.h>
+#include <linux/cpumask.h>
+#include <linux/irq_work.h>
+#include <linux/printk.h>
+#include <linux/kprobes.h>
+
+#include "internal.h"
+
+/*
+ * printk() could not take logbuf_lock in NMI context. Instead,
+ * it uses an alternative implementation that temporary stores
+ * the strings into a per-CPU buffer. The content of the buffer
+ * is later flushed into the main ring buffer via IRQ work.
+ *
+ * The alternative implementation is chosen transparently
+ * by examining current printk() context mask stored in @printk_context
+ * per-CPU variable.
+ *
+ * The implementation allows to flush the strings also from another CPU.
+ * There are situations when we want to make sure that all buffers
+ * were handled or when IRQs are blocked.
+ */
+
+#define SAFE_LOG_BUF_LEN ((1 << CONFIG_PRINTK_SAFE_LOG_BUF_SHIFT) -	\
+				sizeof(atomic_t) -			\
+				sizeof(atomic_t) -			\
+				sizeof(struct irq_work))
+
+struct printk_safe_seq_buf {
+	atomic_t		len;	/* length of written data */
+	atomic_t		message_lost;
+	struct irq_work		work;	/* IRQ work that flushes the buffer */
+	unsigned char		buffer[SAFE_LOG_BUF_LEN];
+};
+
+static DEFINE_PER_CPU(struct printk_safe_seq_buf, safe_print_seq);
+static DEFINE_PER_CPU(int, printk_context);
+
+static DEFINE_RAW_SPINLOCK(safe_read_lock);
+
+#ifdef CONFIG_PRINTK_NMI
+static DEFINE_PER_CPU(struct printk_safe_seq_buf, nmi_print_seq);
+#endif
+
+/* Get flushed in a more safe context. */
+static void queue_flush_work(struct printk_safe_seq_buf *s)
+{
+	if (printk_percpu_data_ready())
+		irq_work_queue(&s->work);
+}
+
+/*
+ * Add a message to per-CPU context-dependent buffer. NMI and printk-safe
+ * have dedicated buffers, because otherwise printk-safe preempted by
+ * NMI-printk would have overwritten the NMI messages.
+ *
+ * The messages are flushed from irq work (or from panic()), possibly,
+ * from other CPU, concurrently with printk_safe_log_store(). Should this
+ * happen, printk_safe_log_store() will notice the buffer->len mismatch
+ * and repeat the write.
+ */
+static __printf(2, 0) int printk_safe_log_store(struct printk_safe_seq_buf *s,
+						const char *fmt, va_list args)
+{
+	int add;
+	size_t len;
+	va_list ap;
+
+again:
+	len = atomic_read(&s->len);
+
+	/* The trailing '\0' is not counted into len. */
+	if (len >= sizeof(s->buffer) - 1) {
+		atomic_inc(&s->message_lost);
+		queue_flush_work(s);
+		return 0;
+	}
+
+	/*
+	 * Make sure that all old data have been read before the buffer
+	 * was reset. This is not needed when we just append data.
+	 */
+	if (!len)
+		smp_rmb();
+
+	va_copy(ap, args);
+	add = vscnprintf(s->buffer + len, sizeof(s->buffer) - len, fmt, ap);
+	va_end(ap);
+	if (!add)
+		return 0;
+
+	/*
+	 * Do it once again if the buffer has been flushed in the meantime.
+	 * Note that atomic_cmpxchg() is an implicit memory barrier that
+	 * makes sure that the data were written before updating s->len.
+	 */
+	if (atomic_cmpxchg(&s->len, len, len + add) != len)
+		goto again;
+
+	queue_flush_work(s);
+	return add;
+}
+
+static inline void printk_safe_flush_line(const char *text, int len)
+{
+	/*
+	 * Avoid any console drivers calls from here, because we may be
+	 * in NMI or printk_safe context (when in panic). The messages
+	 * must go only into the ring buffer at this stage.  Consoles will
+	 * get explicitly called later when a crashdump is not generated.
+	 */
+	printk_deferred("%.*s", len, text);
+}
+
+/* printk part of the temporary buffer line by line */
+static int printk_safe_flush_buffer(const char *start, size_t len)
+{
+	const char *c, *end;
+	bool header;
+
+	c = start;
+	end = start + len;
+	header = true;
+
+	/* Print line by line. */
+	while (c < end) {
+		if (*c == '\n') {
+			printk_safe_flush_line(start, c - start + 1);
+			start = ++c;
+			header = true;
+			continue;
+		}
+
+		/* Handle continuous lines or missing new line. */
+		if ((c + 1 < end) && printk_get_level(c)) {
+			if (header) {
+				c = printk_skip_level(c);
+				continue;
+			}
+
+			printk_safe_flush_line(start, c - start);
+			start = c++;
+			header = true;
+			continue;
+		}
+
+		header = false;
+		c++;
+	}
+
+	/* Check if there was a partial line. Ignore pure header. */
+	if (start < end && !header) {
+		static const char newline[] = KERN_CONT "\n";
+
+		printk_safe_flush_line(start, end - start);
+		printk_safe_flush_line(newline, strlen(newline));
+	}
+
+	return len;
+}
+
+static void report_message_lost(struct printk_safe_seq_buf *s)
+{
+	int lost = atomic_xchg(&s->message_lost, 0);
+
+	if (lost)
+		printk_deferred("Lost %d message(s)!\n", lost);
+}
+
+/*
+ * Flush data from the associated per-CPU buffer. The function
+ * can be called either via IRQ work or independently.
+ */
+static void __printk_safe_flush(struct irq_work *work)
+{
+	struct printk_safe_seq_buf *s =
+		container_of(work, struct printk_safe_seq_buf, work);
+	unsigned long flags;
+	size_t len;
+	int i;
+
+	/*
+	 * The lock has two functions. First, one reader has to flush all
+	 * available message to make the lockless synchronization with
+	 * writers easier. Second, we do not want to mix messages from
+	 * different CPUs. This is especially important when printing
+	 * a backtrace.
+	 */
+	raw_spin_lock_irqsave(&safe_read_lock, flags);
+
+	i = 0;
+more:
+	len = atomic_read(&s->len);
+
+	/*
+	 * This is just a paranoid check that nobody has manipulated
+	 * the buffer an unexpected way. If we printed something then
+	 * @len must only increase. Also it should never overflow the
+	 * buffer size.
+	 */
+	if ((i && i >= len) || len > sizeof(s->buffer)) {
+		const char *msg = "printk_safe_flush: internal error\n";
+
+		printk_safe_flush_line(msg, strlen(msg));
+		len = 0;
+	}
+
+	if (!len)
+		goto out; /* Someone else has already flushed the buffer. */
+
+	/* Make sure that data has been written up to the @len */
+	smp_rmb();
+	i += printk_safe_flush_buffer(s->buffer + i, len - i);
+
+	/*
+	 * Check that nothing has got added in the meantime and truncate
+	 * the buffer. Note that atomic_cmpxchg() is an implicit memory
+	 * barrier that makes sure that the data were copied before
+	 * updating s->len.
+	 */
+	if (atomic_cmpxchg(&s->len, len, 0) != len)
+		goto more;
+
+out:
+	report_message_lost(s);
+	raw_spin_unlock_irqrestore(&safe_read_lock, flags);
+}
+
+/**
+ * printk_safe_flush - flush all per-cpu nmi buffers.
+ *
+ * The buffers are flushed automatically via IRQ work. This function
+ * is useful only when someone wants to be sure that all buffers have
+ * been flushed at some point.
+ */
+void printk_safe_flush(void)
+{
+	int cpu;
+
+	for_each_possible_cpu(cpu) {
+#ifdef CONFIG_PRINTK_NMI
+		__printk_safe_flush(&per_cpu(nmi_print_seq, cpu).work);
+#endif
+		__printk_safe_flush(&per_cpu(safe_print_seq, cpu).work);
+	}
+}
+
+/**
+ * printk_safe_flush_on_panic - flush all per-cpu nmi buffers when the system
+ *	goes down.
+ *
+ * Similar to printk_safe_flush() but it can be called even in NMI context when
+ * the system goes down. It does the best effort to get NMI messages into
+ * the main ring buffer.
+ *
+ * Note that it could try harder when there is only one CPU online.
+ */
+void printk_safe_flush_on_panic(void)
+{
+	/*
+	 * Make sure that we could access the main ring buffer.
+	 * Do not risk a double release when more CPUs are up.
+	 */
+	if (raw_spin_is_locked(&logbuf_lock)) {
+		if (num_online_cpus() > 1)
+			return;
+
+		debug_locks_off();
+		raw_spin_lock_init(&logbuf_lock);
+	}
+
+	if (raw_spin_is_locked(&safe_read_lock)) {
+		if (num_online_cpus() > 1)
+			return;
+
+		debug_locks_off();
+		raw_spin_lock_init(&safe_read_lock);
+	}
+
+	printk_safe_flush();
+}
+
+#ifdef CONFIG_PRINTK_NMI
+/*
+ * Safe printk() for NMI context. It uses a per-CPU buffer to
+ * store the message. NMIs are not nested, so there is always only
+ * one writer running. But the buffer might get flushed from another
+ * CPU, so we need to be careful.
+ */
+static __printf(1, 0) int vprintk_nmi(const char *fmt, va_list args)
+{
+	struct printk_safe_seq_buf *s = this_cpu_ptr(&nmi_print_seq);
+
+	return printk_safe_log_store(s, fmt, args);
+}
+
+void noinstr printk_nmi_enter(void)
+{
+	this_cpu_add(printk_context, PRINTK_NMI_CONTEXT_OFFSET);
+}
+
+void noinstr printk_nmi_exit(void)
+{
+	this_cpu_sub(printk_context, PRINTK_NMI_CONTEXT_OFFSET);
+}
+
+/*
+ * Marks a code that might produce many messages in NMI context
+ * and the risk of losing them is more critical than eventual
+ * reordering.
+ *
+ * It has effect only when called in NMI context. Then printk()
+ * will try to store the messages into the main logbuf directly
+ * and use the per-CPU buffers only as a fallback when the lock
+ * is not available.
+ */
+void printk_nmi_direct_enter(void)
+{
+	if (this_cpu_read(printk_context) & PRINTK_NMI_CONTEXT_MASK)
+		this_cpu_or(printk_context, PRINTK_NMI_DIRECT_CONTEXT_MASK);
+}
+
+void printk_nmi_direct_exit(void)
+{
+	this_cpu_and(printk_context, ~PRINTK_NMI_DIRECT_CONTEXT_MASK);
+}
+
+#else
+
+static __printf(1, 0) int vprintk_nmi(const char *fmt, va_list args)
+{
+	return 0;
+}
+
+#endif /* CONFIG_PRINTK_NMI */
+
+/*
+ * Lock-less printk(), to avoid deadlocks should the printk() recurse
+ * into itself. It uses a per-CPU buffer to store the message, just like
+ * NMI.
+ */
+static __printf(1, 0) int vprintk_safe(const char *fmt, va_list args)
+{
+	struct printk_safe_seq_buf *s = this_cpu_ptr(&safe_print_seq);
+
+	return printk_safe_log_store(s, fmt, args);
+}
+
+/* Can be preempted by NMI. */
+void __printk_safe_enter(void)
+{
+	this_cpu_inc(printk_context);
+}
+
+/* Can be preempted by NMI. */
+void __printk_safe_exit(void)
+{
+	this_cpu_dec(printk_context);
+}
+
+__printf(1, 0) int vprintk_func(const char *fmt, va_list args)
+{
+#ifdef CONFIG_KGDB_KDB
+	/* Allow to pass printk() to kdb but avoid a recursion. */
+	if (unlikely(kdb_trap_printk && kdb_printf_cpu < 0))
+		return vkdb_printf(KDB_MSGSRC_PRINTK, fmt, args);
+#endif
+
+	/*
+	 * Try to use the main logbuf even in NMI. But avoid calling console
+	 * drivers that might have their own locks.
+	 */
+	if ((this_cpu_read(printk_context) & PRINTK_NMI_DIRECT_CONTEXT_MASK) &&
+	    raw_spin_trylock(&logbuf_lock)) {
+		int len;
+
+		len = vprintk_store(0, LOGLEVEL_DEFAULT, NULL, fmt, args);
+		raw_spin_unlock(&logbuf_lock);
+		defer_console_output();
+		return len;
+	}
+
+	/* Use extra buffer in NMI when logbuf_lock is taken or in safe mode. */
+	if (this_cpu_read(printk_context) & PRINTK_NMI_CONTEXT_MASK)
+		return vprintk_nmi(fmt, args);
+
+	/* Use extra buffer to prevent a recursion deadlock in safe mode. */
+	if (this_cpu_read(printk_context) & PRINTK_SAFE_CONTEXT_MASK)
+		return vprintk_safe(fmt, args);
+
+	/* No obstacles. */
+	return vprintk_default(fmt, args);
+}
+
+void __init printk_safe_init(void)
+{
+	int cpu;
+
+	for_each_possible_cpu(cpu) {
+		struct printk_safe_seq_buf *s;
+
+		s = &per_cpu(safe_print_seq, cpu);
+		init_irq_work(&s->work, __printk_safe_flush);
+
+#ifdef CONFIG_PRINTK_NMI
+		s = &per_cpu(nmi_print_seq, cpu);
+		init_irq_work(&s->work, __printk_safe_flush);
+#endif
+	}
+
+	/* Flush pending messages that did not have scheduled IRQ works. */
+	printk_safe_flush();
+}
diff --git a/kernel/kernel/ptrace.c b/kernel/kernel/ptrace.c
index fb5d1a1..aab480e 100644
--- a/kernel/kernel/ptrace.c
+++ b/kernel/kernel/ptrace.c
@@ -196,14 +196,7 @@
 	spin_lock_irq(&task->sighand->siglock);
 	if (task_is_traced(task) && !looks_like_a_spurious_pid(task) &&
 	    !__fatal_signal_pending(task)) {
-		unsigned long flags;
-
-		raw_spin_lock_irqsave(&task->pi_lock, flags);
-		if (task->state & __TASK_TRACED)
-			task->state = __TASK_TRACED;
-		else
-			task->saved_state = __TASK_TRACED;
-		raw_spin_unlock_irqrestore(&task->pi_lock, flags);
+		task->state = __TASK_TRACED;
 		ret = true;
 	}
 	spin_unlock_irq(&task->sighand->siglock);
@@ -213,8 +206,8 @@
 
 static void ptrace_unfreeze_traced(struct task_struct *task)
 {
-	unsigned long flags;
-	bool frozen = true;
+	if (task->state != __TASK_TRACED)
+		return;
 
 	WARN_ON(!task->ptrace || task->parent != current);
 
@@ -223,19 +216,12 @@
 	 * Recheck state under the lock to close this race.
 	 */
 	spin_lock_irq(&task->sighand->siglock);
-
-	raw_spin_lock_irqsave(&task->pi_lock, flags);
-	if (task->state == __TASK_TRACED)
-		task->state = TASK_TRACED;
-	else if (task->saved_state == __TASK_TRACED)
-		task->saved_state = TASK_TRACED;
-	else
-		frozen = false;
-	raw_spin_unlock_irqrestore(&task->pi_lock, flags);
-
-	if (frozen && __fatal_signal_pending(task))
-		wake_up_state(task, __TASK_TRACED);
-
+	if (task->state == __TASK_TRACED) {
+		if (__fatal_signal_pending(task))
+			wake_up_state(task, __TASK_TRACED);
+		else
+			task->state = TASK_TRACED;
+	}
 	spin_unlock_irq(&task->sighand->siglock);
 }
 
diff --git a/kernel/kernel/rcu/Kconfig b/kernel/kernel/rcu/Kconfig
index 0c71197..cd6e114 100644
--- a/kernel/kernel/rcu/Kconfig
+++ b/kernel/kernel/rcu/Kconfig
@@ -189,8 +189,8 @@
 
 config RCU_BOOST
 	bool "Enable RCU priority boosting"
-	depends on (RT_MUTEXES && PREEMPT_RCU && RCU_EXPERT) || PREEMPT_RT
-	default y if PREEMPT_RT
+	depends on RT_MUTEXES && PREEMPT_RCU && RCU_EXPERT
+	default n
 	help
 	  This option boosts the priority of preempted RCU readers that
 	  block the current preemptible RCU grace period for too long.
diff --git a/kernel/kernel/rcu/tree.c b/kernel/kernel/rcu/tree.c
index f48cf60..b10d6bc 100644
--- a/kernel/kernel/rcu/tree.c
+++ b/kernel/kernel/rcu/tree.c
@@ -100,10 +100,8 @@
 static bool dump_tree;
 module_param(dump_tree, bool, 0444);
 /* By default, use RCU_SOFTIRQ instead of rcuc kthreads. */
-static bool use_softirq = !IS_ENABLED(CONFIG_PREEMPT_RT);
-#ifndef CONFIG_PREEMPT_RT
+static bool use_softirq = true;
 module_param(use_softirq, bool, 0444);
-#endif
 /* Control rcu_node-tree auto-balancing at boot time. */
 static bool rcu_fanout_exact;
 module_param(rcu_fanout_exact, bool, 0444);
diff --git a/kernel/kernel/rcu/update.c b/kernel/kernel/rcu/update.c
index dd94a60..849f0aa9 100644
--- a/kernel/kernel/rcu/update.c
+++ b/kernel/kernel/rcu/update.c
@@ -56,10 +56,8 @@
 #ifndef CONFIG_TINY_RCU
 module_param(rcu_expedited, int, 0);
 module_param(rcu_normal, int, 0);
-static int rcu_normal_after_boot = IS_ENABLED(CONFIG_PREEMPT_RT);
-#ifndef CONFIG_PREEMPT_RT
+static int rcu_normal_after_boot;
 module_param(rcu_normal_after_boot, int, 0);
-#endif
 #endif /* #ifndef CONFIG_TINY_RCU */
 
 #ifdef CONFIG_DEBUG_LOCK_ALLOC
diff --git a/kernel/kernel/sched/core.c b/kernel/kernel/sched/core.c
index e00ae06..7359375 100644
--- a/kernel/kernel/sched/core.c
+++ b/kernel/kernel/sched/core.c
@@ -78,11 +78,7 @@
  * Number of tasks to iterate in a single balance run.
  * Limited because this is done with IRQs disabled.
  */
-#ifdef CONFIG_PREEMPT_RT
-const_debug unsigned int sysctl_sched_nr_migrate = 8;
-#else
 const_debug unsigned int sysctl_sched_nr_migrate = 32;
-#endif
 
 /*
  * period over which we measure -rt task CPU usage in us.
@@ -531,15 +527,9 @@
 #endif
 #endif
 
-static bool __wake_q_add(struct wake_q_head *head, struct task_struct *task,
-			 bool sleeper)
+static bool __wake_q_add(struct wake_q_head *head, struct task_struct *task)
 {
-	struct wake_q_node *node;
-
-	if (sleeper)
-		node = &task->wake_q_sleeper;
-	else
-		node = &task->wake_q;
+	struct wake_q_node *node = &task->wake_q;
 
 	/*
 	 * Atomically grab the task, if ->wake_q is !nil already it means
@@ -576,13 +566,7 @@
  */
 void wake_q_add(struct wake_q_head *head, struct task_struct *task)
 {
-	if (__wake_q_add(head, task, false))
-		get_task_struct(task);
-}
-
-void wake_q_add_sleeper(struct wake_q_head *head, struct task_struct *task)
-{
-	if (__wake_q_add(head, task, true))
+	if (__wake_q_add(head, task))
 		get_task_struct(task);
 }
 
@@ -605,40 +589,29 @@
  */
 void wake_q_add_safe(struct wake_q_head *head, struct task_struct *task)
 {
-	if (!__wake_q_add(head, task, false))
+	if (!__wake_q_add(head, task))
 		put_task_struct(task);
 }
 
-void __wake_up_q(struct wake_q_head *head, bool sleeper)
+void wake_up_q(struct wake_q_head *head)
 {
 	struct wake_q_node *node = head->first;
 
 	while (node != WAKE_Q_TAIL) {
 		struct task_struct *task;
 
-		if (sleeper)
-			task = container_of(node, struct task_struct, wake_q_sleeper);
-		else
-			task = container_of(node, struct task_struct, wake_q);
-
+		task = container_of(node, struct task_struct, wake_q);
 		BUG_ON(!task);
 		/* Task can safely be re-inserted now: */
 		node = node->next;
+		task->wake_q.next = NULL;
 		task->wake_q_count = head->count;
-		if (sleeper)
-			task->wake_q_sleeper.next = NULL;
-		else
-			task->wake_q.next = NULL;
 
 		/*
 		 * wake_up_process() executes a full barrier, which pairs with
 		 * the queueing in wake_q_add() so as not to miss wakeups.
 		 */
-		if (sleeper)
-			wake_up_lock_sleeper(task);
-		else
-			wake_up_process(task);
-
+		wake_up_process(task);
 		task->wake_q_count = 0;
 		put_task_struct(task);
 	}
@@ -675,48 +648,6 @@
 		trace_sched_wake_idle_without_ipi(cpu);
 }
 EXPORT_SYMBOL_GPL(resched_curr);
-
-#ifdef CONFIG_PREEMPT_LAZY
-
-static int tsk_is_polling(struct task_struct *p)
-{
-#ifdef TIF_POLLING_NRFLAG
-	return test_tsk_thread_flag(p, TIF_POLLING_NRFLAG);
-#else
-	return 0;
-#endif
-}
-
-void resched_curr_lazy(struct rq *rq)
-{
-	struct task_struct *curr = rq->curr;
-	int cpu;
-
-	if (!sched_feat(PREEMPT_LAZY)) {
-		resched_curr(rq);
-		return;
-	}
-
-	lockdep_assert_held(&rq->lock);
-
-	if (test_tsk_need_resched(curr))
-		return;
-
-	if (test_tsk_need_resched_lazy(curr))
-		return;
-
-	set_tsk_need_resched_lazy(curr);
-
-	cpu = cpu_of(rq);
-	if (cpu == smp_processor_id())
-		return;
-
-	/* NEED_RESCHED_LAZY must be visible before we test polling */
-	smp_mb();
-	if (!tsk_is_polling(curr))
-		smp_send_reschedule(cpu);
-}
-#endif
 
 void resched_cpu(int cpu)
 {
@@ -1870,82 +1801,6 @@
 
 #ifdef CONFIG_SMP
 
-static void
-__do_set_cpus_allowed(struct task_struct *p, const struct cpumask *new_mask, u32 flags);
-
-static int __set_cpus_allowed_ptr(struct task_struct *p,
-				  const struct cpumask *new_mask,
-				  u32 flags);
-
-static void migrate_disable_switch(struct rq *rq, struct task_struct *p)
-{
-	if (likely(!p->migration_disabled))
-		return;
-
-	if (p->cpus_ptr != &p->cpus_mask)
-		return;
-
-	/*
-	 * Violates locking rules! see comment in __do_set_cpus_allowed().
-	 */
-	__do_set_cpus_allowed(p, cpumask_of(rq->cpu), SCA_MIGRATE_DISABLE);
-}
-
-void migrate_disable(void)
-{
-	struct task_struct *p = current;
-
-	if (p->migration_disabled) {
-		p->migration_disabled++;
-		return;
-	}
-
-	trace_sched_migrate_disable_tp(p);
-
-	preempt_disable();
-	this_rq()->nr_pinned++;
-	p->migration_disabled = 1;
-	preempt_lazy_disable();
-	preempt_enable();
-}
-EXPORT_SYMBOL_GPL(migrate_disable);
-
-void migrate_enable(void)
-{
-	struct task_struct *p = current;
-
-	if (p->migration_disabled > 1) {
-		p->migration_disabled--;
-		return;
-	}
-
-	/*
-	 * Ensure stop_task runs either before or after this, and that
-	 * __set_cpus_allowed_ptr(SCA_MIGRATE_ENABLE) doesn't schedule().
-	 */
-	preempt_disable();
-	if (p->cpus_ptr != &p->cpus_mask)
-		__set_cpus_allowed_ptr(p, &p->cpus_mask, SCA_MIGRATE_ENABLE);
-	/*
-	 * Mustn't clear migration_disabled() until cpus_ptr points back at the
-	 * regular cpus_mask, otherwise things that race (eg.
-	 * select_fallback_rq) get confused.
-	 */
-	barrier();
-	p->migration_disabled = 0;
-	this_rq()->nr_pinned--;
-	preempt_lazy_enable();
-	preempt_enable();
-
-	trace_sched_migrate_enable_tp(p);
-}
-EXPORT_SYMBOL_GPL(migrate_enable);
-
-static inline bool rq_has_pinned_tasks(struct rq *rq)
-{
-	return rq->nr_pinned;
-}
-
 /*
  * Per-CPU kthreads are allowed to run on !active && online CPUs, see
  * __set_cpus_allowed_ptr() and select_fallback_rq().
@@ -1955,7 +1810,7 @@
 	if (!cpumask_test_cpu(cpu, p->cpus_ptr))
 		return false;
 
-	if (is_per_cpu_kthread(p) || is_migration_disabled(p))
+	if (is_per_cpu_kthread(p))
 		return cpu_online(cpu);
 
 	if (!cpu_active(cpu))
@@ -2015,21 +1870,8 @@
 }
 
 struct migration_arg {
-	struct task_struct		*task;
-	int				dest_cpu;
-	struct set_affinity_pending	*pending;
-};
-
-/*
- * @refs: number of wait_for_completion()
- * @stop_pending: is @stop_work in use
- */
-struct set_affinity_pending {
-	refcount_t		refs;
-	unsigned int		stop_pending;
-	struct completion	done;
-	struct cpu_stop_work	stop_work;
-	struct migration_arg	arg;
+	struct task_struct *task;
+	int dest_cpu;
 };
 
 /*
@@ -2062,17 +1904,15 @@
 static int migration_cpu_stop(void *data)
 {
 	struct migration_arg *arg = data;
-	struct set_affinity_pending *pending = arg->pending;
 	struct task_struct *p = arg->task;
 	struct rq *rq = this_rq();
-	bool complete = false;
 	struct rq_flags rf;
 
 	/*
 	 * The original target CPU might have gone down and we might
 	 * be on another CPU but it doesn't matter.
 	 */
-	local_irq_save(rf.flags);
+	local_irq_disable();
 	/*
 	 * We need to explicitly wake pending tasks before running
 	 * __migrate_task() such that we will not miss enforcing cpus_ptr
@@ -2082,121 +1922,21 @@
 
 	raw_spin_lock(&p->pi_lock);
 	rq_lock(rq, &rf);
-
 	/*
 	 * If task_rq(p) != rq, it cannot be migrated here, because we're
 	 * holding rq->lock, if p->on_rq == 0 it cannot get enqueued because
 	 * we're holding p->pi_lock.
 	 */
 	if (task_rq(p) == rq) {
-		if (is_migration_disabled(p))
-			goto out;
-
-		if (pending) {
-			if (p->migration_pending == pending)
-				p->migration_pending = NULL;
-			complete = true;
-
-			if (cpumask_test_cpu(task_cpu(p), &p->cpus_mask))
-				goto out;
-		}
-
 		if (task_on_rq_queued(p))
 			rq = __migrate_task(rq, &rf, p, arg->dest_cpu);
 		else
 			p->wake_cpu = arg->dest_cpu;
-
-		/*
-		 * XXX __migrate_task() can fail, at which point we might end
-		 * up running on a dodgy CPU, AFAICT this can only happen
-		 * during CPU hotplug, at which point we'll get pushed out
-		 * anyway, so it's probably not a big deal.
-		 */
-
-	} else if (pending) {
-		/*
-		 * This happens when we get migrated between migrate_enable()'s
-		 * preempt_enable() and scheduling the stopper task. At that
-		 * point we're a regular task again and not current anymore.
-		 *
-		 * A !PREEMPT kernel has a giant hole here, which makes it far
-		 * more likely.
-		 */
-
-		/*
-		 * The task moved before the stopper got to run. We're holding
-		 * ->pi_lock, so the allowed mask is stable - if it got
-		 * somewhere allowed, we're done.
-		 */
-		if (cpumask_test_cpu(task_cpu(p), p->cpus_ptr)) {
-			if (p->migration_pending == pending)
-				p->migration_pending = NULL;
-			complete = true;
-			goto out;
-		}
-
-		/*
-		 * When migrate_enable() hits a rq mis-match we can't reliably
-		 * determine is_migration_disabled() and so have to chase after
-		 * it.
-		 */
-		WARN_ON_ONCE(!pending->stop_pending);
-		task_rq_unlock(rq, p, &rf);
-		stop_one_cpu_nowait(task_cpu(p), migration_cpu_stop,
-				    &pending->arg, &pending->stop_work);
-		return 0;
 	}
-out:
-	if (pending)
-		pending->stop_pending = false;
-	task_rq_unlock(rq, p, &rf);
+	rq_unlock(rq, &rf);
+	raw_spin_unlock(&p->pi_lock);
 
-	if (complete)
-		complete_all(&pending->done);
-
-	return 0;
-}
-
-int push_cpu_stop(void *arg)
-{
-	struct rq *lowest_rq = NULL, *rq = this_rq();
-	struct task_struct *p = arg;
-
-	raw_spin_lock_irq(&p->pi_lock);
-	raw_spin_lock(&rq->lock);
-
-	if (task_rq(p) != rq)
-		goto out_unlock;
-
-	if (is_migration_disabled(p)) {
-		p->migration_flags |= MDF_PUSH;
-		goto out_unlock;
-	}
-
-	p->migration_flags &= ~MDF_PUSH;
-
-	if (p->sched_class->find_lock_rq)
-		lowest_rq = p->sched_class->find_lock_rq(p, rq);
-
-	if (!lowest_rq)
-		goto out_unlock;
-
-	// XXX validate p is still the highest prio task
-	if (task_rq(p) == rq) {
-		deactivate_task(rq, p, 0);
-		set_task_cpu(p, lowest_rq->cpu);
-		activate_task(lowest_rq, p, 0);
-		resched_curr(lowest_rq);
-	}
-
-	double_unlock_balance(rq, lowest_rq);
-
-out_unlock:
-	rq->push_busy = false;
-	raw_spin_unlock(&rq->lock);
-	raw_spin_unlock_irq(&p->pi_lock);
-
-	put_task_struct(p);
+	local_irq_enable();
 	return 0;
 }
 
@@ -2204,40 +1944,19 @@
  * sched_class::set_cpus_allowed must do the below, but is not required to
  * actually call this function.
  */
-void set_cpus_allowed_common(struct task_struct *p, const struct cpumask *new_mask, u32 flags)
+void set_cpus_allowed_common(struct task_struct *p, const struct cpumask *new_mask)
 {
-	if (flags & (SCA_MIGRATE_ENABLE | SCA_MIGRATE_DISABLE)) {
-		p->cpus_ptr = new_mask;
-		return;
-	}
-
 	cpumask_copy(&p->cpus_mask, new_mask);
 	p->nr_cpus_allowed = cpumask_weight(new_mask);
 	trace_android_rvh_set_cpus_allowed_comm(p, new_mask);
 }
 
-static void
-__do_set_cpus_allowed(struct task_struct *p, const struct cpumask *new_mask, u32 flags)
+void do_set_cpus_allowed(struct task_struct *p, const struct cpumask *new_mask)
 {
 	struct rq *rq = task_rq(p);
 	bool queued, running;
 
-	/*
-	 * This here violates the locking rules for affinity, since we're only
-	 * supposed to change these variables while holding both rq->lock and
-	 * p->pi_lock.
-	 *
-	 * HOWEVER, it magically works, because ttwu() is the only code that
-	 * accesses these variables under p->pi_lock and only does so after
-	 * smp_cond_load_acquire(&p->on_cpu, !VAL), and we're in __schedule()
-	 * before finish_task().
-	 *
-	 * XXX do further audits, this smells like something putrid.
-	 */
-	if (flags & SCA_MIGRATE_DISABLE)
-		SCHED_WARN_ON(!p->on_cpu);
-	else
-		lockdep_assert_held(&p->pi_lock);
+	lockdep_assert_held(&p->pi_lock);
 
 	queued = task_on_rq_queued(p);
 	running = task_current(rq, p);
@@ -2253,7 +1972,7 @@
 	if (running)
 		put_prev_task(rq, p);
 
-	p->sched_class->set_cpus_allowed(p, new_mask, flags);
+	p->sched_class->set_cpus_allowed(p, new_mask);
 
 	if (queued)
 		enqueue_task(rq, p, ENQUEUE_RESTORE | ENQUEUE_NOCLOCK);
@@ -2261,14 +1980,12 @@
 		set_next_task(rq, p);
 }
 
-static int affine_move_task(struct rq *rq, struct task_struct *p, struct rq_flags *rf,
-			    int dest_cpu, unsigned int flags);
 /*
  * Called with both p->pi_lock and rq->lock held; drops both before returning.
  */
 static int __set_cpus_allowed_ptr_locked(struct task_struct *p,
 					 const struct cpumask *new_mask,
-					 u32 flags,
+					 bool check,
 					 struct rq *rq,
 					 struct rq_flags *rf)
 {
@@ -2279,14 +1996,9 @@
 
 	update_rq_clock(rq);
 
-	if (p->flags & PF_KTHREAD || is_migration_disabled(p)) {
+	if (p->flags & PF_KTHREAD) {
 		/*
-		 * Kernel threads are allowed on online && !active CPUs.
-		 *
-		 * Specifically, migration_disabled() tasks must not fail the
-		 * cpumask_any_and_distribute() pick below, esp. so on
-		 * SCA_MIGRATE_ENABLE, otherwise we'll not call
-		 * set_cpus_allowed_common() and actually reset p->cpus_ptr.
+		 * Kernel threads are allowed on online && !active CPUs
 		 */
 		cpu_valid_mask = cpu_online_mask;
 	} else if (!cpumask_subset(new_mask, cpu_allowed_mask)) {
@@ -2298,22 +2010,13 @@
 	 * Must re-check here, to close a race against __kthread_bind(),
 	 * sched_setaffinity() is not guaranteed to observe the flag.
 	 */
-	if ((flags & SCA_CHECK) && (p->flags & PF_NO_SETAFFINITY)) {
+	if (check && (p->flags & PF_NO_SETAFFINITY)) {
 		ret = -EINVAL;
 		goto out;
 	}
 
-	if (!(flags & SCA_MIGRATE_ENABLE)) {
-		if (cpumask_equal(&p->cpus_mask, new_mask))
-			goto out;
-
-		if (WARN_ON_ONCE(p == current &&
-				 is_migration_disabled(p) &&
-				 !cpumask_test_cpu(task_cpu(p), new_mask))) {
-			ret = -EBUSY;
-			goto out;
-		}
-	}
+	if (cpumask_equal(&p->cpus_mask, new_mask))
+		goto out;
 
 	/*
 	 * Picking a ~random cpu helps in cases where we are changing affinity
@@ -2326,7 +2029,7 @@
 		goto out;
 	}
 
-	__do_set_cpus_allowed(p, new_mask, flags);
+	do_set_cpus_allowed(p, new_mask);
 
 	if (p->flags & PF_KTHREAD) {
 		/*
@@ -2338,227 +2041,27 @@
 			p->nr_cpus_allowed != 1);
 	}
 
-	return affine_move_task(rq, p, rf, dest_cpu, flags);
+	/* Can the task run on the task's current CPU? If so, we're done */
+	if (cpumask_test_cpu(task_cpu(p), new_mask))
+		goto out;
+
+	if (task_running(rq, p) || p->state == TASK_WAKING) {
+		struct migration_arg arg = { p, dest_cpu };
+		/* Need help from migration thread: drop lock and wait. */
+		task_rq_unlock(rq, p, rf);
+		stop_one_cpu(cpu_of(rq), migration_cpu_stop, &arg);
+		return 0;
+	} else if (task_on_rq_queued(p)) {
+		/*
+		 * OK, since we're going to drop the lock immediately
+		 * afterwards anyway.
+		 */
+		rq = move_queued_task(rq, rf, p, dest_cpu);
+	}
 out:
 	task_rq_unlock(rq, p, rf);
 
 	return ret;
-}
-
-void do_set_cpus_allowed(struct task_struct *p, const struct cpumask *new_mask)
-{
-	__do_set_cpus_allowed(p, new_mask, 0);
-}
-
-/*
- * This function is wildly self concurrent; here be dragons.
- *
- *
- * When given a valid mask, __set_cpus_allowed_ptr() must block until the
- * designated task is enqueued on an allowed CPU. If that task is currently
- * running, we have to kick it out using the CPU stopper.
- *
- * Migrate-Disable comes along and tramples all over our nice sandcastle.
- * Consider:
- *
- *     Initial conditions: P0->cpus_mask = [0, 1]
- *
- *     P0@CPU0                  P1
- *
- *     migrate_disable();
- *     <preempted>
- *                              set_cpus_allowed_ptr(P0, [1]);
- *
- * P1 *cannot* return from this set_cpus_allowed_ptr() call until P0 executes
- * its outermost migrate_enable() (i.e. it exits its Migrate-Disable region).
- * This means we need the following scheme:
- *
- *     P0@CPU0                  P1
- *
- *     migrate_disable();
- *     <preempted>
- *                              set_cpus_allowed_ptr(P0, [1]);
- *                                <blocks>
- *     <resumes>
- *     migrate_enable();
- *       __set_cpus_allowed_ptr();
- *       <wakes local stopper>
- *                         `--> <woken on migration completion>
- *
- * Now the fun stuff: there may be several P1-like tasks, i.e. multiple
- * concurrent set_cpus_allowed_ptr(P0, [*]) calls. CPU affinity changes of any
- * task p are serialized by p->pi_lock, which we can leverage: the one that
- * should come into effect at the end of the Migrate-Disable region is the last
- * one. This means we only need to track a single cpumask (i.e. p->cpus_mask),
- * but we still need to properly signal those waiting tasks at the appropriate
- * moment.
- *
- * This is implemented using struct set_affinity_pending. The first
- * __set_cpus_allowed_ptr() caller within a given Migrate-Disable region will
- * setup an instance of that struct and install it on the targeted task_struct.
- * Any and all further callers will reuse that instance. Those then wait for
- * a completion signaled at the tail of the CPU stopper callback (1), triggered
- * on the end of the Migrate-Disable region (i.e. outermost migrate_enable()).
- *
- *
- * (1) In the cases covered above. There is one more where the completion is
- * signaled within affine_move_task() itself: when a subsequent affinity request
- * cancels the need for an active migration. Consider:
- *
- *     Initial conditions: P0->cpus_mask = [0, 1]
- *
- *     P0@CPU0            P1                             P2
- *
- *     migrate_disable();
- *     <preempted>
- *                        set_cpus_allowed_ptr(P0, [1]);
- *                          <blocks>
- *                                                       set_cpus_allowed_ptr(P0, [0, 1]);
- *                                                         <signal completion>
- *                          <awakes>
- *
- * Note that the above is safe vs a concurrent migrate_enable(), as any
- * pending affinity completion is preceded an uninstallion of
- * p->migration_pending done with p->pi_lock held.
- */
-static int affine_move_task(struct rq *rq, struct task_struct *p, struct rq_flags *rf,
-			    int dest_cpu, unsigned int flags)
-{
-	struct set_affinity_pending my_pending = { }, *pending = NULL;
-	bool stop_pending, complete = false;
-
-	/* Can the task run on the task's current CPU? If so, we're done */
-	if (cpumask_test_cpu(task_cpu(p), &p->cpus_mask)) {
-		struct task_struct *push_task = NULL;
-
-		if ((flags & SCA_MIGRATE_ENABLE) &&
-		    (p->migration_flags & MDF_PUSH) && !rq->push_busy) {
-			rq->push_busy = true;
-			push_task = get_task_struct(p);
-		}
-
-		/*
-		 * If there are pending waiters, but no pending stop_work,
-		 * then complete now.
-		 */
-		pending = p->migration_pending;
-		if (pending && !pending->stop_pending) {
-			p->migration_pending = NULL;
-			complete = true;
-		}
-
-		task_rq_unlock(rq, p, rf);
-
-		if (push_task) {
-			stop_one_cpu_nowait(rq->cpu, push_cpu_stop,
-					    p, &rq->push_work);
-		}
-
-		if (complete)
-			complete_all(&pending->done);
-
-		return 0;
-	}
-
-	if (!(flags & SCA_MIGRATE_ENABLE)) {
-		/* serialized by p->pi_lock */
-		if (!p->migration_pending) {
-			/* Install the request */
-			refcount_set(&my_pending.refs, 1);
-			init_completion(&my_pending.done);
-			my_pending.arg = (struct migration_arg) {
-				.task = p,
-				.dest_cpu = dest_cpu,
-				.pending = &my_pending,
-			};
-
-			p->migration_pending = &my_pending;
-		} else {
-			pending = p->migration_pending;
-			refcount_inc(&pending->refs);
-			/*
-			 * Affinity has changed, but we've already installed a
-			 * pending. migration_cpu_stop() *must* see this, else
-			 * we risk a completion of the pending despite having a
-			 * task on a disallowed CPU.
-			 *
-			 * Serialized by p->pi_lock, so this is safe.
-			 */
-			pending->arg.dest_cpu = dest_cpu;
-		}
-	}
-	pending = p->migration_pending;
-	/*
-	 * - !MIGRATE_ENABLE:
-	 *   we'll have installed a pending if there wasn't one already.
-	 *
-	 * - MIGRATE_ENABLE:
-	 *   we're here because the current CPU isn't matching anymore,
-	 *   the only way that can happen is because of a concurrent
-	 *   set_cpus_allowed_ptr() call, which should then still be
-	 *   pending completion.
-	 *
-	 * Either way, we really should have a @pending here.
-	 */
-	if (WARN_ON_ONCE(!pending)) {
-		task_rq_unlock(rq, p, rf);
-		return -EINVAL;
-	}
-
-	if (task_running(rq, p) || p->state == TASK_WAKING) {
-		/*
-		 * MIGRATE_ENABLE gets here because 'p == current', but for
-		 * anything else we cannot do is_migration_disabled(), punt
-		 * and have the stopper function handle it all race-free.
-		 */
-		stop_pending = pending->stop_pending;
-		if (!stop_pending)
-			pending->stop_pending = true;
-
-		if (flags & SCA_MIGRATE_ENABLE)
-			p->migration_flags &= ~MDF_PUSH;
-
-		task_rq_unlock(rq, p, rf);
-
-		if (!stop_pending) {
-			stop_one_cpu_nowait(cpu_of(rq), migration_cpu_stop,
-					    &pending->arg, &pending->stop_work);
-		}
-
-		if (flags & SCA_MIGRATE_ENABLE)
-			return 0;
-	} else {
-
-		if (!is_migration_disabled(p)) {
-			if (task_on_rq_queued(p))
-				rq = move_queued_task(rq, rf, p, dest_cpu);
-
-			if (!pending->stop_pending) {
-				p->migration_pending = NULL;
-				complete = true;
-			}
-		}
-		task_rq_unlock(rq, p, rf);
-
-		if (complete)
-			complete_all(&pending->done);
-	}
-
-	wait_for_completion(&pending->done);
-
-	if (refcount_dec_and_test(&pending->refs))
-		wake_up_var(&pending->refs); /* No UaF, just an address */
-
-	/*
-	 * Block the original owner of &pending until all subsequent callers
-	 * have seen the completion and decremented the refcount
-	 */
-	wait_var_event(&my_pending.refs, !refcount_read(&my_pending.refs));
-
-	/* ARGH */
-	WARN_ON_ONCE(my_pending.stop_pending);
-
-	return 0;
 }
 
 /*
@@ -2571,19 +2074,18 @@
  * call is not atomic; no spinlocks may be held.
  */
 static int __set_cpus_allowed_ptr(struct task_struct *p,
-				  const struct cpumask *new_mask,
-				  u32 flags)
+				  const struct cpumask *new_mask, bool check)
 {
 	struct rq_flags rf;
 	struct rq *rq;
 
 	rq = task_rq_lock(p, &rf);
-	return __set_cpus_allowed_ptr_locked(p, new_mask, flags, rq, &rf);
+	return __set_cpus_allowed_ptr_locked(p, new_mask, check, rq, &rf);
 }
 
 int set_cpus_allowed_ptr(struct task_struct *p, const struct cpumask *new_mask)
 {
-	return __set_cpus_allowed_ptr(p, new_mask, 0);
+	return __set_cpus_allowed_ptr(p, new_mask, false);
 }
 EXPORT_SYMBOL_GPL(set_cpus_allowed_ptr);
 
@@ -2692,8 +2194,6 @@
 	 * Clearly, migrating tasks to offline CPUs is a fairly daft thing.
 	 */
 	WARN_ON_ONCE(!cpu_online(new_cpu));
-
-	WARN_ON_ONCE(is_migration_disabled(p));
 #endif
 
 	trace_sched_migrate_task(p, new_cpu);
@@ -2827,18 +2327,6 @@
 }
 EXPORT_SYMBOL_GPL(migrate_swap);
 
-static bool check_task_state(struct task_struct *p, long match_state)
-{
-	bool match = false;
-
-	raw_spin_lock_irq(&p->pi_lock);
-	if (p->state == match_state || p->saved_state == match_state)
-		match = true;
-	raw_spin_unlock_irq(&p->pi_lock);
-
-	return match;
-}
-
 /*
  * wait_task_inactive - wait for a thread to unschedule.
  *
@@ -2883,7 +2371,7 @@
 		 * is actually now running somewhere else!
 		 */
 		while (task_running(rq, p)) {
-			if (match_state && !check_task_state(p, match_state))
+			if (match_state && unlikely(p->state != match_state))
 				return 0;
 			cpu_relax();
 		}
@@ -2898,8 +2386,7 @@
 		running = task_running(rq, p);
 		queued = task_on_rq_queued(p);
 		ncsw = 0;
-		if (!match_state || p->state == match_state ||
-		    p->saved_state == match_state)
+		if (!match_state || p->state == match_state)
 			ncsw = p->nvcsw | LONG_MIN; /* sets MSB */
 		task_rq_unlock(rq, p, &rf);
 
@@ -2933,7 +2420,7 @@
 			ktime_t to = NSEC_PER_SEC / HZ;
 
 			set_current_state(TASK_UNINTERRUPTIBLE);
-			schedule_hrtimeout(&to, HRTIMER_MODE_REL_HARD);
+			schedule_hrtimeout(&to, HRTIMER_MODE_REL);
 			continue;
 		}
 
@@ -3040,12 +2527,6 @@
 			}
 			fallthrough;
 		case possible:
-			/*
-			 * XXX When called from select_task_rq() we only
-			 * hold p->pi_lock and again violate locking order.
-			 *
-			 * More yuck to audit.
-			 */
 			do_set_cpus_allowed(p, task_cpu_possible_mask(p));
 			state = fail;
 			break;
@@ -3079,7 +2560,7 @@
 {
 	lockdep_assert_held(&p->pi_lock);
 
-	if (p->nr_cpus_allowed > 1 && !is_migration_disabled(p))
+	if (p->nr_cpus_allowed > 1)
 		cpu = p->sched_class->select_task_rq(p, cpu, sd_flags, wake_flags);
 	else
 		cpu = cpumask_any(p->cpus_ptr);
@@ -3102,7 +2583,6 @@
 
 void sched_set_stop_task(int cpu, struct task_struct *stop)
 {
-	static struct lock_class_key stop_pi_lock;
 	struct sched_param param = { .sched_priority = MAX_RT_PRIO - 1 };
 	struct task_struct *old_stop = cpu_rq(cpu)->stop;
 
@@ -3118,20 +2598,6 @@
 		sched_setscheduler_nocheck(stop, SCHED_FIFO, &param);
 
 		stop->sched_class = &stop_sched_class;
-
-		/*
-		 * The PI code calls rt_mutex_setprio() with ->pi_lock held to
-		 * adjust the effective priority of a task. As a result,
-		 * rt_mutex_setprio() can trigger (RT) balancing operations,
-		 * which can then trigger wakeups of the stop thread to push
-		 * around the current task.
-		 *
-		 * The stop task itself will never be part of the PI-chain, it
-		 * never blocks, therefore that ->pi_lock recursion is safe.
-		 * Tell lockdep about this by placing the stop->pi_lock in its
-		 * own class.
-		 */
-		lockdep_set_class(&stop->pi_lock, &stop_pi_lock);
 	}
 
 	cpu_rq(cpu)->stop = stop;
@@ -3145,23 +2611,15 @@
 	}
 }
 
-#else /* CONFIG_SMP */
+#else
 
 static inline int __set_cpus_allowed_ptr(struct task_struct *p,
-					 const struct cpumask *new_mask,
-					 u32 flags)
+					 const struct cpumask *new_mask, bool check)
 {
 	return set_cpus_allowed_ptr(p, new_mask);
 }
 
-static inline void migrate_disable_switch(struct rq *rq, struct task_struct *p) { }
-
-static inline bool rq_has_pinned_tasks(struct rq *rq)
-{
-	return false;
-}
-
-#endif /* !CONFIG_SMP */
+#endif /* CONFIG_SMP */
 
 static void
 ttwu_stat(struct task_struct *p, int cpu, int wake_flags)
@@ -3595,7 +3053,7 @@
 	int cpu, success = 0;
 
 	preempt_disable();
-	if (!IS_ENABLED(CONFIG_PREEMPT_RT) && p == current) {
+	if (p == current) {
 		/*
 		 * We're waking current, this means 'p->on_rq' and 'task_cpu(p)
 		 * == smp_processor_id()'. Together this means we can special
@@ -3625,26 +3083,8 @@
 	 */
 	raw_spin_lock_irqsave(&p->pi_lock, flags);
 	smp_mb__after_spinlock();
-	if (!(p->state & state)) {
-		/*
-		 * The task might be running due to a spinlock sleeper
-		 * wakeup. Check the saved state and set it to running
-		 * if the wakeup condition is true.
-		 */
-		if (!(wake_flags & WF_LOCK_SLEEPER)) {
-			if (p->saved_state & state) {
-				p->saved_state = TASK_RUNNING;
-				success = 1;
-			}
-		}
+	if (!(p->state & state))
 		goto unlock;
-	}
-	/*
-	 * If this is a regular wakeup, then we can unconditionally
-	 * clear the saved state of a "lock sleeper".
-	 */
-	if (!(wake_flags & WF_LOCK_SLEEPER))
-		p->saved_state = TASK_RUNNING;
 
 #ifdef CONFIG_FREEZER
 	/*
@@ -3853,18 +3293,6 @@
 }
 EXPORT_SYMBOL(wake_up_process);
 
-/**
- * wake_up_lock_sleeper - Wake up a specific process blocked on a "sleeping lock"
- * @p: The process to be woken up.
- *
- * Same as wake_up_process() above, but wake_flags=WF_LOCK_SLEEPER to indicate
- * the nature of the wakeup.
- */
-int wake_up_lock_sleeper(struct task_struct *p)
-{
-	return try_to_wake_up(p, TASK_UNINTERRUPTIBLE, WF_LOCK_SLEEPER);
-}
-
 int wake_up_state(struct task_struct *p, unsigned int state)
 {
 	return try_to_wake_up(p, state, 0);
@@ -3920,7 +3348,6 @@
 	init_numa_balancing(clone_flags, p);
 #ifdef CONFIG_SMP
 	p->wake_entry.u_flags = CSD_TYPE_TTWU;
-	p->migration_pending = NULL;
 #endif
 }
 
@@ -4099,9 +3526,6 @@
 	p->on_cpu = 0;
 #endif
 	init_task_preempt_count(p);
-#ifdef CONFIG_HAVE_PREEMPT_LAZY
-	task_thread_info(p)->preempt_lazy_count = 0;
-#endif
 #ifdef CONFIG_SMP
 	plist_node_init(&p->pushable_tasks, MAX_PRIO);
 	RB_CLEAR_NODE(&p->pushable_dl_tasks);
@@ -4329,90 +3753,6 @@
 #endif
 }
 
-#ifdef CONFIG_SMP
-
-static void do_balance_callbacks(struct rq *rq, struct callback_head *head)
-{
-	void (*func)(struct rq *rq);
-	struct callback_head *next;
-
-	lockdep_assert_held(&rq->lock);
-
-	while (head) {
-		func = (void (*)(struct rq *))head->func;
-		next = head->next;
-		head->next = NULL;
-		head = next;
-
-		func(rq);
-	}
-}
-
-static inline struct callback_head *splice_balance_callbacks(struct rq *rq)
-{
-	struct callback_head *head = rq->balance_callback;
-
-	lockdep_assert_held(&rq->lock);
-	if (head) {
-		rq->balance_callback = NULL;
-		rq->balance_flags &= ~BALANCE_WORK;
-	}
-
-	return head;
-}
-
-static void __balance_callbacks(struct rq *rq)
-{
-	do_balance_callbacks(rq, splice_balance_callbacks(rq));
-}
-
-static inline void balance_callbacks(struct rq *rq, struct callback_head *head)
-{
-	unsigned long flags;
-
-	if (unlikely(head)) {
-		raw_spin_lock_irqsave(&rq->lock, flags);
-		do_balance_callbacks(rq, head);
-		raw_spin_unlock_irqrestore(&rq->lock, flags);
-	}
-}
-
-static void balance_push(struct rq *rq);
-
-static inline void balance_switch(struct rq *rq)
-{
-	if (likely(!rq->balance_flags))
-		return;
-
-	if (rq->balance_flags & BALANCE_PUSH) {
-		balance_push(rq);
-		return;
-	}
-
-	__balance_callbacks(rq);
-}
-
-#else
-
-static inline void __balance_callbacks(struct rq *rq)
-{
-}
-
-static inline struct callback_head *splice_balance_callbacks(struct rq *rq)
-{
-	return NULL;
-}
-
-static inline void balance_callbacks(struct rq *rq, struct callback_head *head)
-{
-}
-
-static inline void balance_switch(struct rq *rq)
-{
-}
-
-#endif
-
 static inline void
 prepare_lock_switch(struct rq *rq, struct task_struct *next, struct rq_flags *rf)
 {
@@ -4438,7 +3778,6 @@
 	 * prev into current:
 	 */
 	spin_acquire(&rq->lock.dep_map, 0, 0, _THIS_IP_);
-	balance_switch(rq);
 	raw_spin_unlock_irq(&rq->lock);
 }
 
@@ -4453,22 +3792,6 @@
 #ifndef finish_arch_post_lock_switch
 # define finish_arch_post_lock_switch()	do { } while (0)
 #endif
-
-static inline void kmap_local_sched_out(void)
-{
-#ifdef CONFIG_KMAP_LOCAL
-	if (unlikely(current->kmap_ctrl.idx))
-		__kmap_local_sched_out();
-#endif
-}
-
-static inline void kmap_local_sched_in(void)
-{
-#ifdef CONFIG_KMAP_LOCAL
-	if (unlikely(current->kmap_ctrl.idx))
-		__kmap_local_sched_in();
-#endif
-}
 
 /**
  * prepare_task_switch - prepare to switch tasks
@@ -4492,7 +3815,6 @@
 	perf_event_task_sched_out(prev, next);
 	rseq_preempt(prev);
 	fire_sched_out_preempt_notifiers(prev, next);
-	kmap_local_sched_out();
 	prepare_task(next);
 	prepare_arch_switch(next);
 }
@@ -4559,7 +3881,6 @@
 	finish_lock_switch(rq);
 	finish_arch_post_lock_switch();
 	kcov_finish_switch(current);
-	kmap_local_sched_in();
 
 	fire_sched_in_preempt_notifiers(current);
 	/*
@@ -4574,17 +3895,23 @@
 	 *   provided by mmdrop(),
 	 * - a sync_core for SYNC_CORE.
 	 */
-	/*
-	 * We use mmdrop_delayed() here so we don't have to do the
-	 * full __mmdrop() when we are the last user.
-	 */
 	if (mm) {
 		membarrier_mm_sync_core_before_usermode(mm);
-		mmdrop_delayed(mm);
+		mmdrop(mm);
 	}
 	if (unlikely(prev_state == TASK_DEAD)) {
 		if (prev->sched_class->task_dead)
 			prev->sched_class->task_dead(prev);
+
+		/*
+		 * Remove function-return probe instances associated with this
+		 * task and put them back on the free list.
+		 */
+		kprobe_flush_task(prev);
+		trace_android_rvh_flush_task(prev);
+
+		/* Task is done with its stack. */
+		put_task_stack(prev);
 
 		put_task_struct_rcu_user(prev);
 	}
@@ -4592,6 +3919,43 @@
 	tick_nohz_task_switch();
 	return rq;
 }
+
+#ifdef CONFIG_SMP
+
+/* rq->lock is NOT held, but preemption is disabled */
+static void __balance_callback(struct rq *rq)
+{
+	struct callback_head *head, *next;
+	void (*func)(struct rq *rq);
+	unsigned long flags;
+
+	raw_spin_lock_irqsave(&rq->lock, flags);
+	head = rq->balance_callback;
+	rq->balance_callback = NULL;
+	while (head) {
+		func = (void (*)(struct rq *))head->func;
+		next = head->next;
+		head->next = NULL;
+		head = next;
+
+		func(rq);
+	}
+	raw_spin_unlock_irqrestore(&rq->lock, flags);
+}
+
+static inline void balance_callback(struct rq *rq)
+{
+	if (unlikely(rq->balance_callback))
+		__balance_callback(rq);
+}
+
+#else
+
+static inline void balance_callback(struct rq *rq)
+{
+}
+
+#endif
 
 /**
  * schedule_tail - first thing a freshly forked thread must call.
@@ -4612,6 +3976,7 @@
 	 */
 
 	rq = finish_task_switch(prev);
+	balance_callback(rq);
 	preempt_enable();
 
 	if (current->set_child_tid)
@@ -5317,7 +4682,7 @@
  *
  * WARNING: must be called with preemption disabled!
  */
-static void __sched notrace __schedule(bool preempt, bool spinning_lock)
+static void __sched notrace __schedule(bool preempt)
 {
 	struct task_struct *prev, *next;
 	unsigned long *switch_count;
@@ -5370,7 +4735,7 @@
 	 *  - ptrace_{,un}freeze_traced() can change ->state underneath us.
 	 */
 	prev_state = prev->state;
-	if ((!preempt || spinning_lock) && prev_state) {
+	if (!preempt && prev_state) {
 		if (signal_pending_state(prev_state, prev)) {
 			prev->state = TASK_RUNNING;
 		} else {
@@ -5405,7 +4770,6 @@
 
 	next = pick_next_task(rq, prev, &rf);
 	clear_tsk_need_resched(prev);
-	clear_tsk_need_resched_lazy(prev);
 	clear_preempt_need_resched();
 
 	trace_android_rvh_schedule(prev, next, rq);
@@ -5432,7 +4796,6 @@
 		 */
 		++*switch_count;
 
-		migrate_disable_switch(rq, prev);
 		psi_sched_switch(prev, next, !task_on_rq_queued(prev));
 
 		trace_sched_switch(preempt, prev, next);
@@ -5441,11 +4804,10 @@
 		rq = context_switch(rq, prev, next, &rf);
 	} else {
 		rq->clock_update_flags &= ~(RQCF_ACT_SKIP|RQCF_REQ_SKIP);
-
-		rq_unpin_lock(rq, &rf);
-		__balance_callbacks(rq);
-		raw_spin_unlock_irq(&rq->lock);
+		rq_unlock_irq(rq, &rf);
 	}
+
+	balance_callback(rq);
 }
 
 void __noreturn do_task_dead(void)
@@ -5456,7 +4818,7 @@
 	/* Tell freezer to ignore us: */
 	current->flags |= PF_NOFREEZE;
 
-	__schedule(false, false);
+	__schedule(false);
 	BUG();
 
 	/* Avoid "noreturn function does return" - but don't continue if BUG() is a NOP: */
@@ -5489,6 +4851,9 @@
 		preempt_enable_no_resched();
 	}
 
+	if (tsk_is_pi_blocked(tsk))
+		return;
+
 	/*
 	 * If we are going to sleep and we have plugged IO queued,
 	 * make sure to submit it to avoid deadlocks.
@@ -5514,7 +4879,7 @@
 	sched_submit_work(tsk);
 	do {
 		preempt_disable();
-		__schedule(false, false);
+		__schedule(false);
 		sched_preempt_enable_no_resched();
 	} while (need_resched());
 	sched_update_worker(tsk);
@@ -5542,7 +4907,7 @@
 	 */
 	WARN_ON_ONCE(current->state);
 	do {
-		__schedule(false, false);
+		__schedule(false);
 	} while (need_resched());
 }
 
@@ -5595,7 +4960,7 @@
 		 */
 		preempt_disable_notrace();
 		preempt_latency_start(1);
-		__schedule(true, false);
+		__schedule(true);
 		preempt_latency_stop(1);
 		preempt_enable_no_resched_notrace();
 
@@ -5605,30 +4970,6 @@
 		 */
 	} while (need_resched());
 }
-
-#ifdef CONFIG_PREEMPT_LAZY
-/*
- * If TIF_NEED_RESCHED is then we allow to be scheduled away since this is
- * set by a RT task. Oterwise we try to avoid beeing scheduled out as long as
- * preempt_lazy_count counter >0.
- */
-static __always_inline int preemptible_lazy(void)
-{
-	if (test_thread_flag(TIF_NEED_RESCHED))
-		return 1;
-	if (current_thread_info()->preempt_lazy_count)
-		return 0;
-	return 1;
-}
-
-#else
-
-static inline int preemptible_lazy(void)
-{
-	return 1;
-}
-
-#endif
 
 #ifdef CONFIG_PREEMPTION
 /*
@@ -5643,25 +4984,11 @@
 	 */
 	if (likely(!preemptible()))
 		return;
-	if (!preemptible_lazy())
-		return;
+
 	preempt_schedule_common();
 }
 NOKPROBE_SYMBOL(preempt_schedule);
 EXPORT_SYMBOL(preempt_schedule);
-
-#ifdef CONFIG_PREEMPT_RT
-void __sched notrace preempt_schedule_lock(void)
-{
-	do {
-		preempt_disable();
-		__schedule(true, true);
-		sched_preempt_enable_no_resched();
-	} while (need_resched());
-}
-NOKPROBE_SYMBOL(preempt_schedule_lock);
-EXPORT_SYMBOL(preempt_schedule_lock);
-#endif
 
 /**
  * preempt_schedule_notrace - preempt_schedule called by tracing
@@ -5682,9 +5009,6 @@
 	enum ctx_state prev_ctx;
 
 	if (likely(!preemptible()))
-		return;
-
-	if (!preemptible_lazy())
 		return;
 
 	do {
@@ -5709,7 +5033,7 @@
 		 * an infinite recursion.
 		 */
 		prev_ctx = exception_enter();
-		__schedule(true, false);
+		__schedule(true);
 		exception_exit(prev_ctx);
 
 		preempt_latency_stop(1);
@@ -5738,7 +5062,7 @@
 	do {
 		preempt_disable();
 		local_irq_enable();
-		__schedule(true, false);
+		__schedule(true);
 		local_irq_disable();
 		sched_preempt_enable_no_resched();
 	} while (need_resched());
@@ -5905,11 +5229,9 @@
 out_unlock:
 	/* Avoid rq from going away on us: */
 	preempt_disable();
+	__task_rq_unlock(rq, &rf);
 
-	rq_unpin_lock(rq, &rf);
-	__balance_callbacks(rq);
-	raw_spin_unlock(&rq->lock);
-
+	balance_callback(rq);
 	preempt_enable();
 }
 #else
@@ -6154,7 +5476,6 @@
 	int oldpolicy = -1, policy = attr->sched_policy;
 	int retval, oldprio, newprio, queued, running;
 	const struct sched_class *prev_class;
-	struct callback_head *head;
 	struct rq_flags rf;
 	int reset_on_fork;
 	int queue_flags = DEQUEUE_SAVE | DEQUEUE_MOVE | DEQUEUE_NOCLOCK;
@@ -6397,14 +5718,13 @@
 
 	/* Avoid rq from going away on us: */
 	preempt_disable();
-	head = splice_balance_callbacks(rq);
 	task_rq_unlock(rq, p, &rf);
 
 	if (pi)
 		rt_mutex_adjust_pi(p);
 
 	/* Run balance callbacks after we've adjusted the PI chain: */
-	balance_callbacks(rq, head);
+	balance_callback(rq);
 	preempt_enable();
 
 	return 0;
@@ -6916,7 +6236,7 @@
 	}
 #endif
 again:
-	retval = __set_cpus_allowed_ptr(p, new_mask, SCA_CHECK);
+	retval = __set_cpus_allowed_ptr(p, new_mask, true);
 
 	if (!retval) {
 		cpuset_cpus_allowed(p, cpus_allowed);
@@ -7498,7 +6818,7 @@
 	 *
 	 * And since this is boot we can forgo the serialization.
 	 */
-	set_cpus_allowed_common(idle, cpumask_of(cpu), 0);
+	set_cpus_allowed_common(idle, cpumask_of(cpu));
 #endif
 	/*
 	 * We're having a chicken and egg problem, even though we are
@@ -7525,9 +6845,7 @@
 
 	/* Set the preempt count _outside_ the spinlocks! */
 	init_idle_preempt_count(idle, cpu);
-#ifdef CONFIG_HAVE_PREEMPT_LAZY
-	task_thread_info(idle)->preempt_lazy_count = 0;
-#endif
+
 	/*
 	 * The idle tasks have their own, simple scheduling class:
 	 */
@@ -7637,7 +6955,6 @@
 #endif /* CONFIG_NUMA_BALANCING */
 
 #ifdef CONFIG_HOTPLUG_CPU
-
 /*
  * Ensure that the idle task is using init_mm right before its CPU goes
  * offline.
@@ -7657,124 +6974,166 @@
 	/* finish_cpu(), as ran on the BP, will clean up the active_mm state */
 }
 
-static int __balance_push_cpu_stop(void *arg)
+/*
+ * Since this CPU is going 'away' for a while, fold any nr_active delta
+ * we might have. Assumes we're called after migrate_tasks() so that the
+ * nr_active count is stable. We need to take the teardown thread which
+ * is calling this into account, so we hand in adjust = 1 to the load
+ * calculation.
+ *
+ * Also see the comment "Global load-average calculations".
+ */
+static void calc_load_migrate(struct rq *rq)
 {
-	struct task_struct *p = arg;
-	struct rq *rq = this_rq();
-	struct rq_flags rf;
-	int cpu;
+	long delta = calc_load_fold_active(rq, 1);
+	if (delta)
+		atomic_long_add(delta, &calc_load_tasks);
+}
 
-	raw_spin_lock_irq(&p->pi_lock);
-	rq_lock(rq, &rf);
+static struct task_struct *__pick_migrate_task(struct rq *rq)
+{
+	const struct sched_class *class;
+	struct task_struct *next;
 
+	for_each_class(class) {
+		next = class->pick_next_task(rq);
+		if (next) {
+			next->sched_class->put_prev_task(rq, next);
+			return next;
+		}
+	}
+
+	/* The idle class should always have a runnable task */
+	BUG();
+}
+
+/*
+ * Migrate all tasks from the rq, sleeping tasks will be migrated by
+ * try_to_wake_up()->select_task_rq().
+ *
+ * Called with rq->lock held even though we'er in stop_machine() and
+ * there's no concurrency possible, we hold the required locks anyway
+ * because of lock validation efforts.
+ *
+ * force: if false, the function will skip CPU pinned kthreads.
+ */
+static void migrate_tasks(struct rq *dead_rq, struct rq_flags *rf, bool force)
+{
+	struct rq *rq = dead_rq;
+	struct task_struct *next, *tmp, *stop = rq->stop;
+	LIST_HEAD(percpu_kthreads);
+	struct rq_flags orf = *rf;
+	int dest_cpu;
+
+	/*
+	 * Fudge the rq selection such that the below task selection loop
+	 * doesn't get stuck on the currently eligible stop task.
+	 *
+	 * We're currently inside stop_machine() and the rq is either stuck
+	 * in the stop_machine_cpu_stop() loop, or we're executing this code,
+	 * either way we should never end up calling schedule() until we're
+	 * done here.
+	 */
+	rq->stop = NULL;
+
+	/*
+	 * put_prev_task() and pick_next_task() sched
+	 * class method both need to have an up-to-date
+	 * value of rq->clock[_task]
+	 */
 	update_rq_clock(rq);
 
-	if (task_rq(p) == rq && task_on_rq_queued(p)) {
-		cpu = select_fallback_rq(rq->cpu, p);
-		rq = __migrate_task(rq, &rf, p, cpu);
-	}
+#ifdef CONFIG_SCHED_DEBUG
+	/* note the clock update in orf */
+	orf.clock_update_flags |= RQCF_UPDATED;
+#endif
 
-	rq_unlock(rq, &rf);
-	raw_spin_unlock_irq(&p->pi_lock);
-
-	put_task_struct(p);
-
-	return 0;
-}
-
-static DEFINE_PER_CPU(struct cpu_stop_work, push_work);
-
-/*
- * Ensure we only run per-cpu kthreads once the CPU goes !active.
- */
-
-
-static void balance_push(struct rq *rq)
-{
-	struct task_struct *push_task = rq->curr;
-
-	lockdep_assert_held(&rq->lock);
-	SCHED_WARN_ON(rq->cpu != smp_processor_id());
-
-	/*
-	 * Both the cpu-hotplug and stop task are in this case and are
-	 * required to complete the hotplug process.
-	 */
-	if (is_per_cpu_kthread(push_task) || is_migration_disabled(push_task)) {
+	for (;;) {
 		/*
-		 * If this is the idle task on the outgoing CPU try to wake
-		 * up the hotplug control thread which might wait for the
-		 * last task to vanish. The rcuwait_active() check is
-		 * accurate here because the waiter is pinned on this CPU
-		 * and can't obviously be running in parallel.
-		 *
-		 * On RT kernels this also has to check whether there are
-		 * pinned and scheduled out tasks on the runqueue. They
-		 * need to leave the migrate disabled section first.
+		 * There's this thread running, bail when that's the only
+		 * remaining thread:
 		 */
-		if (!rq->nr_running && !rq_has_pinned_tasks(rq) &&
-		    rcuwait_active(&rq->hotplug_wait)) {
-			raw_spin_unlock(&rq->lock);
-			rcuwait_wake_up(&rq->hotplug_wait);
-			raw_spin_lock(&rq->lock);
+		if (rq->nr_running == 1)
+			break;
+
+		next = __pick_migrate_task(rq);
+
+		/*
+		 * Argh ... no iterator for tasks, we need to remove the
+		 * kthread from the run-queue to continue.
+		 */
+		if (!force && is_per_cpu_kthread(next)) {
+			INIT_LIST_HEAD(&next->percpu_kthread_node);
+			list_add(&next->percpu_kthread_node, &percpu_kthreads);
+
+			/* DEQUEUE_SAVE not used due to move_entity in rt */
+			deactivate_task(rq, next,
+					DEQUEUE_NOCLOCK);
+			continue;
 		}
-		return;
+
+		/*
+		 * Rules for changing task_struct::cpus_mask are holding
+		 * both pi_lock and rq->lock, such that holding either
+		 * stabilizes the mask.
+		 *
+		 * Drop rq->lock is not quite as disastrous as it usually is
+		 * because !cpu_active at this point, which means load-balance
+		 * will not interfere. Also, stop-machine.
+		 */
+		rq_unlock(rq, rf);
+		raw_spin_lock(&next->pi_lock);
+		rq_relock(rq, rf);
+
+		/*
+		 * Since we're inside stop-machine, _nothing_ should have
+		 * changed the task, WARN if weird stuff happened, because in
+		 * that case the above rq->lock drop is a fail too.
+		 */
+		if (task_rq(next) != rq || !task_on_rq_queued(next)) {
+			/*
+			 * In the !force case, there is a hole between
+			 * rq_unlock() and rq_relock(), where another CPU might
+			 * not observe an up to date cpu_active_mask and try to
+			 * move tasks around.
+			 */
+			WARN_ON(force);
+			raw_spin_unlock(&next->pi_lock);
+			continue;
+		}
+
+		/* Find suitable destination for @next, with force if needed. */
+		dest_cpu = select_fallback_rq(dead_rq->cpu, next);
+		rq = __migrate_task(rq, rf, next, dest_cpu);
+		if (rq != dead_rq) {
+			rq_unlock(rq, rf);
+			rq = dead_rq;
+			*rf = orf;
+			rq_relock(rq, rf);
+		}
+		raw_spin_unlock(&next->pi_lock);
 	}
 
-	get_task_struct(push_task);
-	/*
-	 * Temporarily drop rq->lock such that we can wake-up the stop task.
-	 * Both preemption and IRQs are still disabled.
-	 */
-	raw_spin_unlock(&rq->lock);
-	stop_one_cpu_nowait(rq->cpu, __balance_push_cpu_stop, push_task,
-			    this_cpu_ptr(&push_work));
-	/*
-	 * At this point need_resched() is true and we'll take the loop in
-	 * schedule(). The next pick is obviously going to be the stop task
-	 * which is_per_cpu_kthread() and will push this task away.
-	 */
-	raw_spin_lock(&rq->lock);
-}
+	list_for_each_entry_safe(next, tmp, &percpu_kthreads,
+				 percpu_kthread_node) {
 
-static void balance_push_set(int cpu, bool on)
-{
-	struct rq *rq = cpu_rq(cpu);
-	struct rq_flags rf;
+		/* ENQUEUE_RESTORE not used due to move_entity in rt */
+		activate_task(rq, next, ENQUEUE_NOCLOCK);
+		list_del(&next->percpu_kthread_node);
+	}
 
-	rq_lock_irqsave(rq, &rf);
-	if (on)
-		rq->balance_flags |= BALANCE_PUSH;
-	else
-		rq->balance_flags &= ~BALANCE_PUSH;
-	rq_unlock_irqrestore(rq, &rf);
-}
-
-/*
- * Invoked from a CPUs hotplug control thread after the CPU has been marked
- * inactive. All tasks which are not per CPU kernel threads are either
- * pushed off this CPU now via balance_push() or placed on a different CPU
- * during wakeup. Wait until the CPU is quiescent.
- */
-static void balance_hotplug_wait(void)
-{
-	struct rq *rq = this_rq();
-
-	rcuwait_wait_event(&rq->hotplug_wait,
-			   rq->nr_running == 1 && !rq_has_pinned_tasks(rq),
-			   TASK_UNINTERRUPTIBLE);
+	rq->stop = stop;
 }
 
 static int drain_rq_cpu_stop(void *data)
 {
-#ifndef CONFIG_PREEMPT_RT
 	struct rq *rq = this_rq();
 	struct rq_flags rf;
 
 	rq_lock_irqsave(rq, &rf);
 	migrate_tasks(rq, &rf, false);
 	rq_unlock_irqrestore(rq, &rf);
-#endif
+
 	return 0;
 }
 
@@ -7799,21 +7158,6 @@
 	if (rq_drain->done)
 		cpu_stop_work_wait(rq_drain);
 }
-
-#else
-
-static inline void balance_push(struct rq *rq)
-{
-}
-
-static inline void balance_push_set(int cpu, bool on)
-{
-}
-
-static inline void balance_hotplug_wait(void)
-{
-}
-
 #endif /* CONFIG_HOTPLUG_CPU */
 
 void set_rq_online(struct rq *rq)
@@ -7901,8 +7245,6 @@
 	struct rq *rq = cpu_rq(cpu);
 	struct rq_flags rf;
 
-	balance_push_set(cpu, false);
-
 #ifdef CONFIG_SCHED_SMT
 	/*
 	 * When going up, increment the number of cores with SMT present.
@@ -7956,21 +7298,9 @@
 
 int _sched_cpu_deactivate(unsigned int cpu)
 {
-	struct rq *rq = cpu_rq(cpu);
-	struct rq_flags rf;
 	int ret;
 
 	set_cpu_active(cpu, false);
-
-	balance_push_set(cpu, true);
-
-	rq_lock_irqsave(rq, &rf);
-	if (rq->rd) {
-		update_rq_clock(rq);
-		BUG_ON(!cpumask_test_cpu(cpu, rq->rd->span));
-		set_rq_offline(rq);
-	}
-	rq_unlock_irqrestore(rq, &rf);
 
 #ifdef CONFIG_SCHED_SMT
 	/*
@@ -7985,7 +7315,6 @@
 
 	ret = cpuset_cpu_inactive(cpu);
 	if (ret) {
-		balance_push_set(cpu, false);
 		set_cpu_active(cpu, true);
 		return ret;
 	}
@@ -8049,41 +7378,6 @@
 }
 
 #ifdef CONFIG_HOTPLUG_CPU
-
-/*
- * Invoked immediately before the stopper thread is invoked to bring the
- * CPU down completely. At this point all per CPU kthreads except the
- * hotplug thread (current) and the stopper thread (inactive) have been
- * either parked or have been unbound from the outgoing CPU. Ensure that
- * any of those which might be on the way out are gone.
- *
- * If after this point a bound task is being woken on this CPU then the
- * responsible hotplug callback has failed to do it's job.
- * sched_cpu_dying() will catch it with the appropriate fireworks.
- */
-int sched_cpu_wait_empty(unsigned int cpu)
-{
-	balance_hotplug_wait();
-	return 0;
-}
-
-/*
- * Since this CPU is going 'away' for a while, fold any nr_active delta we
- * might have. Called from the CPU stopper task after ensuring that the
- * stopper is the last running task on the CPU, so nr_active count is
- * stable. We need to take the teardown thread which is calling this into
- * account, so we hand in adjust = 1 to the load calculation.
- *
- * Also see the comment "Global load-average calculations".
- */
-static void calc_load_migrate(struct rq *rq)
-{
-	long delta = calc_load_fold_active(rq, 1);
-
-	if (delta)
-		atomic_long_add(delta, &calc_load_tasks);
-}
-
 int sched_cpu_dying(unsigned int cpu)
 {
 	struct rq *rq = cpu_rq(cpu);
@@ -8093,7 +7387,12 @@
 	sched_tick_stop(cpu);
 
 	rq_lock_irqsave(rq, &rf);
-	BUG_ON(rq->nr_running != 1 || rq_has_pinned_tasks(rq));
+	if (rq->rd) {
+		BUG_ON(!cpumask_test_cpu(cpu, rq->rd->span));
+		set_rq_offline(rq);
+	}
+	migrate_tasks(rq, &rf, true);
+	BUG_ON(rq->nr_running != 1);
 	rq_unlock_irqrestore(rq, &rf);
 
 	trace_android_rvh_sched_cpu_dying(cpu);
@@ -8304,9 +7603,6 @@
 
 		rq_csd_init(rq, &rq->nohz_csd, nohz_csd_func);
 #endif
-#ifdef CONFIG_HOTPLUG_CPU
-		rcuwait_init(&rq->hotplug_wait);
-#endif
 #endif /* CONFIG_SMP */
 		hrtick_rq_init(rq);
 		atomic_set(&rq->nr_iowait, 0);
@@ -8347,7 +7643,7 @@
 #ifdef CONFIG_DEBUG_ATOMIC_SLEEP
 static inline int preempt_count_equals(int preempt_offset)
 {
-	int nested = preempt_count() + sched_rcu_preempt_depth();
+	int nested = preempt_count() + rcu_preempt_depth();
 
 	return (nested == preempt_offset);
 }
@@ -8447,39 +7743,6 @@
 	add_taint(TAINT_WARN, LOCKDEP_STILL_OK);
 }
 EXPORT_SYMBOL_GPL(__cant_sleep);
-
-#ifdef CONFIG_SMP
-void __cant_migrate(const char *file, int line)
-{
-	static unsigned long prev_jiffy;
-
-	if (irqs_disabled())
-		return;
-
-	if (is_migration_disabled(current))
-		return;
-
-	if (!IS_ENABLED(CONFIG_PREEMPT_COUNT))
-		return;
-
-	if (preempt_count() > 0)
-		return;
-
-	if (time_before(jiffies, prev_jiffy + HZ) && prev_jiffy)
-		return;
-	prev_jiffy = jiffies;
-
-	pr_err("BUG: assuming non migratable context at %s:%d\n", file, line);
-	pr_err("in_atomic(): %d, irqs_disabled(): %d, migration_disabled() %u pid: %d, name: %s\n",
-	       in_atomic(), irqs_disabled(), is_migration_disabled(current),
-	       current->pid, current->comm);
-
-	debug_show_held_locks(current);
-	dump_stack();
-	add_taint(TAINT_WARN, LOCKDEP_STILL_OK);
-}
-EXPORT_SYMBOL_GPL(__cant_migrate);
-#endif
 #endif
 
 #ifdef CONFIG_MAGIC_SYSRQ
diff --git a/kernel/kernel/sched/cpudeadline.c b/kernel/kernel/sched/cpudeadline.c
index ceb03d7..8cb06c8 100644
--- a/kernel/kernel/sched/cpudeadline.c
+++ b/kernel/kernel/sched/cpudeadline.c
@@ -120,7 +120,7 @@
 	const struct sched_dl_entity *dl_se = &p->dl;
 
 	if (later_mask &&
-	    cpumask_and(later_mask, cp->free_cpus, &p->cpus_mask)) {
+	    cpumask_and(later_mask, cp->free_cpus, p->cpus_ptr)) {
 		unsigned long cap, max_cap = 0;
 		int cpu, max_cpu = -1;
 
@@ -151,7 +151,7 @@
 
 		WARN_ON(best_cpu != -1 && !cpu_present(best_cpu));
 
-		if (cpumask_test_cpu(best_cpu, &p->cpus_mask) &&
+		if (cpumask_test_cpu(best_cpu, p->cpus_ptr) &&
 		    dl_time_before(dl_se->deadline, cp->elements[0].dl)) {
 			if (later_mask)
 				cpumask_set_cpu(best_cpu, later_mask);
diff --git a/kernel/kernel/sched/cpupri.c b/kernel/kernel/sched/cpupri.c
index 1d06c0d..cb11531 100644
--- a/kernel/kernel/sched/cpupri.c
+++ b/kernel/kernel/sched/cpupri.c
@@ -94,11 +94,11 @@
 	if (skip)
 		return 0;
 
-	if (cpumask_any_and(&p->cpus_mask, vec->mask) >= nr_cpu_ids)
+	if (cpumask_any_and(p->cpus_ptr, vec->mask) >= nr_cpu_ids)
 		return 0;
 
 	if (lowest_mask) {
-		cpumask_and(lowest_mask, &p->cpus_mask, vec->mask);
+		cpumask_and(lowest_mask, p->cpus_ptr, vec->mask);
 		cpumask_and(lowest_mask, lowest_mask, cpu_active_mask);
 
 #ifdef CONFIG_RT_SOFTINT_OPTIMIZATION
diff --git a/kernel/kernel/sched/cputime.c b/kernel/kernel/sched/cputime.c
index 411a7f7..7ffe3e2 100644
--- a/kernel/kernel/sched/cputime.c
+++ b/kernel/kernel/sched/cputime.c
@@ -47,13 +47,12 @@
 }
 
 /*
- * Called after incrementing preempt_count on {soft,}irq_enter
+ * Called before incrementing preempt_count on {soft,}irq_enter
  * and before decrementing preempt_count on {soft,}irq_exit.
  */
-void irqtime_account_irq(struct task_struct *curr, unsigned int offset)
+void irqtime_account_irq(struct task_struct *curr)
 {
 	struct irqtime *irqtime = this_cpu_ptr(&cpu_irqtime);
-	unsigned int pc;
 	s64 delta;
 	int cpu;
 
@@ -63,7 +62,6 @@
 	cpu = smp_processor_id();
 	delta = sched_clock_cpu(cpu) - irqtime->irq_start_time;
 	irqtime->irq_start_time += delta;
-	pc = irq_count() - offset;
 
 	/*
 	 * We do not account for softirq time from ksoftirqd here.
@@ -71,9 +69,9 @@
 	 * in that case, so as not to confuse scheduler with a special task
 	 * that do not consume any time, but still wants to run.
 	 */
-	if (pc & HARDIRQ_MASK)
+	if (hardirq_count())
 		irqtime_account_delta(irqtime, delta, CPUTIME_IRQ);
-	else if ((pc & SOFTIRQ_OFFSET) && curr != this_cpu_ksoftirqd())
+	else if (in_serving_softirq() && curr != this_cpu_ksoftirqd())
 		irqtime_account_delta(irqtime, delta, CPUTIME_SOFTIRQ);
 
 	trace_android_rvh_account_irq(curr, cpu, delta);
@@ -432,21 +430,24 @@
 }
 # endif
 
-void vtime_account_irq(struct task_struct *tsk, unsigned int offset)
+/*
+ * Archs that account the whole time spent in the idle task
+ * (outside irq) as idle time can rely on this and just implement
+ * vtime_account_kernel() and vtime_account_idle(). Archs that
+ * have other meaning of the idle time (s390 only includes the
+ * time spent by the CPU when it's in low power mode) must override
+ * vtime_account().
+ */
+#ifndef __ARCH_HAS_VTIME_ACCOUNT
+void vtime_account_irq_enter(struct task_struct *tsk)
 {
-	unsigned int pc = irq_count() - offset;
-
-	if (pc & HARDIRQ_OFFSET) {
-		vtime_account_hardirq(tsk);
-	} else if (pc & SOFTIRQ_OFFSET) {
-		vtime_account_softirq(tsk);
-	} else if (!IS_ENABLED(CONFIG_HAVE_VIRT_CPU_ACCOUNTING_IDLE) &&
-		   is_idle_task(tsk)) {
+	if (!in_interrupt() && is_idle_task(tsk))
 		vtime_account_idle(tsk);
-	} else {
+	else
 		vtime_account_kernel(tsk);
-	}
 }
+EXPORT_SYMBOL_GPL(vtime_account_irq_enter);
+#endif /* __ARCH_HAS_VTIME_ACCOUNT */
 
 void cputime_adjust(struct task_cputime *curr, struct prev_cputime *prev,
 		    u64 *ut, u64 *st)
diff --git a/kernel/kernel/sched/deadline.c b/kernel/kernel/sched/deadline.c
index 61d3c34..fa92656 100644
--- a/kernel/kernel/sched/deadline.c
+++ b/kernel/kernel/sched/deadline.c
@@ -565,7 +565,7 @@
 
 static inline bool need_pull_dl_task(struct rq *rq, struct task_struct *prev)
 {
-	return rq->online && dl_task(prev);
+	return dl_task(prev);
 }
 
 static DEFINE_PER_CPU(struct callback_head, dl_push_head);
@@ -1922,7 +1922,7 @@
 static int pick_dl_task(struct rq *rq, struct task_struct *p, int cpu)
 {
 	if (!task_running(rq, p) &&
-	    cpumask_test_cpu(cpu, &p->cpus_mask))
+	    cpumask_test_cpu(cpu, p->cpus_ptr))
 		return 1;
 	return 0;
 }
@@ -2012,8 +2012,8 @@
 				return this_cpu;
 			}
 
-			best_cpu = cpumask_any_and_distribute(later_mask,
-							      sched_domain_span(sd));
+			best_cpu = cpumask_first_and(later_mask,
+							sched_domain_span(sd));
 			/*
 			 * Last chance: if a CPU being in both later_mask
 			 * and current sd span is valid, that becomes our
@@ -2035,7 +2035,7 @@
 	if (this_cpu != -1)
 		return this_cpu;
 
-	cpu = cpumask_any_distribute(later_mask);
+	cpu = cpumask_any(later_mask);
 	if (cpu < nr_cpu_ids)
 		return cpu;
 
@@ -2072,7 +2072,7 @@
 		/* Retry if something changed. */
 		if (double_lock_balance(rq, later_rq)) {
 			if (unlikely(task_rq(task) != rq ||
-				     !cpumask_test_cpu(later_rq->cpu, &task->cpus_mask) ||
+				     !cpumask_test_cpu(later_rq->cpu, task->cpus_ptr) ||
 				     task_running(rq, task) ||
 				     !dl_task(task) ||
 				     !task_on_rq_queued(task))) {
@@ -2139,9 +2139,6 @@
 		return 0;
 
 retry:
-	if (is_migration_disabled(next_task))
-		return 0;
-
 	if (WARN_ON(next_task == rq->curr))
 		return 0;
 
@@ -2219,7 +2216,7 @@
 static void pull_dl_task(struct rq *this_rq)
 {
 	int this_cpu = this_rq->cpu, cpu;
-	struct task_struct *p, *push_task;
+	struct task_struct *p;
 	bool resched = false;
 	struct rq *src_rq;
 	u64 dmin = LONG_MAX;
@@ -2249,7 +2246,6 @@
 			continue;
 
 		/* Might drop this_rq->lock */
-		push_task = NULL;
 		double_lock_balance(this_rq, src_rq);
 
 		/*
@@ -2281,28 +2277,17 @@
 					   src_rq->curr->dl.deadline))
 				goto skip;
 
-			if (is_migration_disabled(p)) {
-				trace_sched_migrate_pull_tp(p);
-				push_task = get_push_task(src_rq);
-			} else {
-				deactivate_task(src_rq, p, 0);
-				set_task_cpu(p, this_cpu);
-				activate_task(this_rq, p, 0);
-				dmin = p->dl.deadline;
-				resched = true;
-			}
+			resched = true;
+
+			deactivate_task(src_rq, p, 0);
+			set_task_cpu(p, this_cpu);
+			activate_task(this_rq, p, 0);
+			dmin = p->dl.deadline;
 
 			/* Is there any other task even earlier? */
 		}
 skip:
 		double_unlock_balance(this_rq, src_rq);
-
-		if (push_task) {
-			raw_spin_unlock(&this_rq->lock);
-			stop_one_cpu_nowait(src_rq->cpu, push_cpu_stop,
-					    push_task, &src_rq->push_work);
-			raw_spin_lock(&this_rq->lock);
-		}
 	}
 
 	if (resched)
@@ -2326,8 +2311,7 @@
 }
 
 static void set_cpus_allowed_dl(struct task_struct *p,
-				const struct cpumask *new_mask,
-				u32 flags)
+				const struct cpumask *new_mask)
 {
 	struct root_domain *src_rd;
 	struct rq *rq;
@@ -2356,7 +2340,7 @@
 		raw_spin_unlock(&src_dl_b->lock);
 	}
 
-	set_cpus_allowed_common(p, new_mask, flags);
+	set_cpus_allowed_common(p, new_mask);
 }
 
 /* Assumes rq->lock is held */
@@ -2554,7 +2538,6 @@
 	.rq_online              = rq_online_dl,
 	.rq_offline             = rq_offline_dl,
 	.task_woken		= task_woken_dl,
-	.find_lock_rq		= find_lock_later_rq,
 #endif
 
 	.task_tick		= task_tick_dl,
diff --git a/kernel/kernel/sched/fair.c b/kernel/kernel/sched/fair.c
index 97f52cf..8ef2f84 100644
--- a/kernel/kernel/sched/fair.c
+++ b/kernel/kernel/sched/fair.c
@@ -4431,7 +4431,7 @@
 	if (skip_preempt)
 		return;
 	if (delta_exec > ideal_runtime) {
-		resched_curr_lazy(rq_of(cfs_rq));
+		resched_curr(rq_of(cfs_rq));
 		/*
 		 * The current task ran long enough, ensure it doesn't get
 		 * re-elected due to buddy favours.
@@ -4455,7 +4455,7 @@
 		return;
 
 	if (delta > ideal_runtime)
-		resched_curr_lazy(rq_of(cfs_rq));
+		resched_curr(rq_of(cfs_rq));
 }
 
 void set_next_entity(struct cfs_rq *cfs_rq, struct sched_entity *se)
@@ -4604,7 +4604,7 @@
 	 * validating it and just reschedule.
 	 */
 	if (queued) {
-		resched_curr_lazy(rq_of(cfs_rq));
+		resched_curr(rq_of(cfs_rq));
 		return;
 	}
 	/*
@@ -4741,7 +4741,7 @@
 	 * hierarchy can be throttled
 	 */
 	if (!assign_cfs_rq_runtime(cfs_rq) && likely(cfs_rq->curr))
-		resched_curr_lazy(rq_of(cfs_rq));
+		resched_curr(rq_of(cfs_rq));
 }
 
 static __always_inline
@@ -5476,7 +5476,7 @@
 
 		if (delta < 0) {
 			if (rq->curr == p)
-				resched_curr_lazy(rq);
+				resched_curr(rq);
 			return;
 		}
 		hrtick_start(rq, delta);
@@ -7211,7 +7211,7 @@
 	return;
 
 preempt:
-	resched_curr_lazy(rq);
+	resched_curr(rq);
 	/*
 	 * Only set the backward buddy when the current task is still
 	 * on the rq. This can happen when a wakeup gets interleaved
@@ -11060,7 +11060,7 @@
 		 * 'current' within the tree based on its new key value.
 		 */
 		swap(curr->vruntime, se->vruntime);
-		resched_curr_lazy(rq);
+		resched_curr(rq);
 	}
 
 	se->vruntime -= cfs_rq->min_vruntime;
@@ -11087,7 +11087,7 @@
 	 */
 	if (rq->curr == p) {
 		if (p->prio > oldprio)
-			resched_curr_lazy(rq);
+			resched_curr(rq);
 	} else
 		check_preempt_curr(rq, p, 0);
 }
diff --git a/kernel/kernel/sched/features.h b/kernel/kernel/sched/features.h
index bc2466a..f1bf5e1 100644
--- a/kernel/kernel/sched/features.h
+++ b/kernel/kernel/sched/features.h
@@ -45,19 +45,11 @@
  */
 SCHED_FEAT(NONTASK_CAPACITY, true)
 
-#ifdef CONFIG_PREEMPT_RT
-SCHED_FEAT(TTWU_QUEUE, false)
-# ifdef CONFIG_PREEMPT_LAZY
-SCHED_FEAT(PREEMPT_LAZY, true)
-# endif
-#else
-
 /*
  * Queue remote wakeups on the target CPU and process them
  * using the scheduler IPI. Reduces rq->lock contention/bounces.
  */
 SCHED_FEAT(TTWU_QUEUE, true)
-#endif
 
 /*
  * When doing wakeups, attempt to limit superfluous scans of the LLC domain.
diff --git a/kernel/kernel/sched/rt.c b/kernel/kernel/sched/rt.c
index 15ac20b..651f578 100644
--- a/kernel/kernel/sched/rt.c
+++ b/kernel/kernel/sched/rt.c
@@ -272,7 +272,7 @@
 static inline bool need_pull_rt_task(struct rq *rq, struct task_struct *prev)
 {
 	/* Try to pull RT tasks here if we lower this rq's prio */
-	return rq->online && rq->rt.highest_prio.curr > prev->prio;
+	return rq->rt.highest_prio.curr > prev->prio;
 }
 
 static inline int rt_overloaded(struct rq *rq)
@@ -1761,7 +1761,7 @@
 static int pick_rt_task(struct rq *rq, struct task_struct *p, int cpu)
 {
 	if (!task_running(rq, p) &&
-	    cpumask_test_cpu(cpu, &p->cpus_mask))
+	    cpumask_test_cpu(cpu, p->cpus_ptr))
 		return 1;
 
 	return 0;
@@ -1864,8 +1864,8 @@
 				return this_cpu;
 			}
 
-			best_cpu = cpumask_any_and_distribute(lowest_mask,
-							      sched_domain_span(sd));
+			best_cpu = cpumask_first_and(lowest_mask,
+						     sched_domain_span(sd));
 			if (best_cpu < nr_cpu_ids) {
 				rcu_read_unlock();
 				return best_cpu;
@@ -1882,7 +1882,7 @@
 	if (this_cpu != -1)
 		return this_cpu;
 
-	cpu = cpumask_any_distribute(lowest_mask);
+	cpu = cpumask_any(lowest_mask);
 	if (cpu < nr_cpu_ids)
 		return cpu;
 
@@ -1923,7 +1923,7 @@
 			 * Also make sure that it wasn't scheduled on its rq.
 			 */
 			if (unlikely(task_rq(task) != rq ||
-				     !cpumask_test_cpu(lowest_rq->cpu, &task->cpus_mask) ||
+				     !cpumask_test_cpu(lowest_rq->cpu, task->cpus_ptr) ||
 				     task_running(rq, task) ||
 				     !rt_task(task) ||
 				     !task_on_rq_queued(task))) {
@@ -1971,7 +1971,7 @@
  * running task can migrate over to a CPU that is running a task
  * of lesser priority.
  */
-static int push_rt_task(struct rq *rq, bool pull)
+static int push_rt_task(struct rq *rq)
 {
 	struct task_struct *next_task;
 	struct rq *lowest_rq;
@@ -1985,39 +1985,6 @@
 		return 0;
 
 retry:
-	if (is_migration_disabled(next_task)) {
-		struct task_struct *push_task = NULL;
-		int cpu;
-
-		if (!pull)
-			return 0;
-
-		trace_sched_migrate_pull_tp(next_task);
-
-		if (rq->push_busy)
-			return 0;
-
-		cpu = find_lowest_rq(rq->curr);
-		if (cpu == -1 || cpu == rq->cpu)
-			return 0;
-
-		/*
-		 * Given we found a CPU with lower priority than @next_task,
-		 * therefore it should be running. However we cannot migrate it
-		 * to this other CPU, instead attempt to push the current
-		 * running task on this CPU away.
-		 */
-		push_task = get_push_task(rq);
-		if (push_task) {
-			raw_spin_unlock(&rq->lock);
-			stop_one_cpu_nowait(rq->cpu, push_cpu_stop,
-					    push_task, &rq->push_work);
-			raw_spin_lock(&rq->lock);
-		}
-
-		return 0;
-	}
-
 	if (WARN_ON(next_task == rq->curr))
 		return 0;
 
@@ -2072,10 +2039,12 @@
 	deactivate_task(rq, next_task, 0);
 	set_task_cpu(next_task, lowest_rq->cpu);
 	activate_task(lowest_rq, next_task, 0);
-	resched_curr(lowest_rq);
 	ret = 1;
 
+	resched_curr(lowest_rq);
+
 	double_unlock_balance(rq, lowest_rq);
+
 out:
 	put_task_struct(next_task);
 
@@ -2085,7 +2054,7 @@
 static void push_rt_tasks(struct rq *rq)
 {
 	/* push_rt_task will return true if it moved an RT */
-	while (push_rt_task(rq, false))
+	while (push_rt_task(rq))
 		;
 }
 
@@ -2238,8 +2207,7 @@
 	 */
 	if (has_pushable_tasks(rq)) {
 		raw_spin_lock(&rq->lock);
-		while (push_rt_task(rq, true))
-			;
+		push_rt_tasks(rq);
 		raw_spin_unlock(&rq->lock);
 	}
 
@@ -2264,7 +2232,7 @@
 {
 	int this_cpu = this_rq->cpu, cpu;
 	bool resched = false;
-	struct task_struct *p, *push_task;
+	struct task_struct *p;
 	struct rq *src_rq;
 	int rt_overload_count = rt_overloaded(this_rq);
 
@@ -2311,7 +2279,6 @@
 		 * double_lock_balance, and another CPU could
 		 * alter this_rq
 		 */
-		push_task = NULL;
 		double_lock_balance(this_rq, src_rq);
 
 		/*
@@ -2339,15 +2306,11 @@
 			if (p->prio < src_rq->curr->prio)
 				goto skip;
 
-			if (is_migration_disabled(p)) {
-				trace_sched_migrate_pull_tp(p);
-				push_task = get_push_task(src_rq);
-			} else {
-				deactivate_task(src_rq, p, 0);
-				set_task_cpu(p, this_cpu);
-				activate_task(this_rq, p, 0);
-				resched = true;
-			}
+			resched = true;
+
+			deactivate_task(src_rq, p, 0);
+			set_task_cpu(p, this_cpu);
+			activate_task(this_rq, p, 0);
 			/*
 			 * We continue with the search, just in
 			 * case there's an even higher prio task
@@ -2357,13 +2320,6 @@
 		}
 skip:
 		double_unlock_balance(this_rq, src_rq);
-
-		if (push_task) {
-			raw_spin_unlock(&this_rq->lock);
-			stop_one_cpu_nowait(src_rq->cpu, push_cpu_stop,
-					    push_task, &src_rq->push_work);
-			raw_spin_lock(&this_rq->lock);
-		}
 	}
 
 	if (resched)
@@ -2612,7 +2568,6 @@
 	.rq_offline             = rq_offline_rt,
 	.task_woken		= task_woken_rt,
 	.switched_from		= switched_from_rt,
-	.find_lock_rq		= find_lock_lowest_rq,
 #endif
 
 	.task_tick		= task_tick_rt,
diff --git a/kernel/kernel/sched/sched.h b/kernel/kernel/sched/sched.h
index 61e804c..9e798c5 100644
--- a/kernel/kernel/sched/sched.h
+++ b/kernel/kernel/sched/sched.h
@@ -996,7 +996,6 @@
 	unsigned long		cpu_capacity_orig;
 
 	struct callback_head	*balance_callback;
-	unsigned char		balance_flags;
 
 	unsigned char		nohz_idle_balance;
 	unsigned char		idle_balance;
@@ -1027,10 +1026,6 @@
 
 	/* This is used to determine avg_idle's max value */
 	u64			max_idle_balance_cost;
-
-#ifdef CONFIG_HOTPLUG_CPU
-	struct rcuwait		hotplug_wait;
-#endif
 #endif /* CONFIG_SMP */
 
 #ifdef CONFIG_IRQ_TIME_ACCOUNTING
@@ -1082,12 +1077,6 @@
 	/* Must be inspected within a rcu lock section */
 	struct cpuidle_state	*idle_state;
 #endif
-
-#ifdef CONFIG_SMP
-	unsigned int		nr_pinned;
-#endif
-	unsigned int		push_busy;
-	struct cpu_stop_work	push_work;
 
 	ANDROID_VENDOR_DATA_ARRAY(1, 96);
 	ANDROID_OEM_DATA_ARRAY(1, 16);
@@ -1286,9 +1275,6 @@
 	rq->clock_update_flags &= (RQCF_REQ_SKIP|RQCF_ACT_SKIP);
 	rf->clock_update_flags = 0;
 #endif
-#ifdef CONFIG_SMP
-	SCHED_WARN_ON(rq->balance_callback);
-#endif
 }
 
 static inline void rq_unpin_lock(struct rq *rq, struct rq_flags *rf)
@@ -1448,9 +1434,6 @@
 
 #ifdef CONFIG_SMP
 
-#define BALANCE_WORK	0x01
-#define BALANCE_PUSH	0x02
-
 extern int migrate_swap(struct task_struct *p, struct task_struct *t,
 			int cpu, int scpu);
 static inline void
@@ -1460,13 +1443,12 @@
 {
 	lockdep_assert_held(&rq->lock);
 
-	if (unlikely(head->next || (rq->balance_flags & BALANCE_PUSH)))
+	if (unlikely(head->next))
 		return;
 
 	head->func = (void (*)(struct callback_head *))func;
 	head->next = rq->balance_callback;
 	rq->balance_callback = head;
-	rq->balance_flags |= BALANCE_WORK;
 }
 
 #define rcu_dereference_check_sched_domain(p) \
@@ -1795,7 +1777,6 @@
 #define WF_FORK			0x02		/* Child wakeup after fork */
 #define WF_MIGRATED		0x04		/* Internal use, task got migrated */
 #define WF_ON_CPU		0x08		/* Wakee is on_cpu */
-#define WF_LOCK_SLEEPER		0x10		/* Wakeup spinlock "sleeper" */
 #define WF_ANDROID_VENDOR	0x1000		/* Vendor specific for Android */
 
 /*
@@ -1880,13 +1861,10 @@
 	void (*task_woken)(struct rq *this_rq, struct task_struct *task);
 
 	void (*set_cpus_allowed)(struct task_struct *p,
-				 const struct cpumask *newmask,
-				 u32 flags);
+				 const struct cpumask *newmask);
 
 	void (*rq_online)(struct rq *rq);
 	void (*rq_offline)(struct rq *rq);
-
-	struct rq *(*find_lock_rq)(struct task_struct *p, struct rq *rq);
 #endif
 
 	void (*task_tick)(struct rq *rq, struct task_struct *p, int queued);
@@ -1970,38 +1948,13 @@
 extern struct task_struct *pick_next_task_fair(struct rq *rq, struct task_struct *prev, struct rq_flags *rf);
 extern struct task_struct *pick_next_task_idle(struct rq *rq);
 
-#define SCA_CHECK		0x01
-#define SCA_MIGRATE_DISABLE	0x02
-#define SCA_MIGRATE_ENABLE	0x04
-
 #ifdef CONFIG_SMP
 
 extern void update_group_capacity(struct sched_domain *sd, int cpu);
 
 extern void trigger_load_balance(struct rq *rq);
 
-extern void set_cpus_allowed_common(struct task_struct *p, const struct cpumask *new_mask, u32 flags);
-
-static inline struct task_struct *get_push_task(struct rq *rq)
-{
-	struct task_struct *p = rq->curr;
-
-	lockdep_assert_held(&rq->lock);
-
-	if (rq->push_busy)
-		return NULL;
-
-	if (p->nr_cpus_allowed == 1)
-		return NULL;
-
-	if (p->migration_disabled)
-		return NULL;
-
-	rq->push_busy = true;
-	return get_task_struct(p);
-}
-
-extern int push_cpu_stop(void *arg);
+extern void set_cpus_allowed_common(struct task_struct *p, const struct cpumask *new_mask);
 
 extern unsigned long __read_mostly max_load_balance_interval;
 #endif
@@ -2045,15 +1998,6 @@
 
 extern void resched_curr(struct rq *rq);
 extern void resched_cpu(int cpu);
-
-#ifdef CONFIG_PREEMPT_LAZY
-extern void resched_curr_lazy(struct rq *rq);
-#else
-static inline void resched_curr_lazy(struct rq *rq)
-{
-	resched_curr(rq);
-}
-#endif
 
 extern struct rt_bandwidth def_rt_bandwidth;
 extern void init_rt_bandwidth(struct rt_bandwidth *rt_b, u64 period, u64 runtime);
@@ -2417,16 +2361,6 @@
 static inline void nohz_balance_exit_idle(struct rq *rq) { }
 #endif
 
-#define MDF_PUSH	0x01
-
-static inline bool is_migration_disabled(struct task_struct *p)
-{
-#ifdef CONFIG_SMP
-	return p->migration_disabled;
-#else
-	return false;
-#endif
-}
 
 #ifdef CONFIG_SMP
 static inline
diff --git a/kernel/kernel/sched/swait.c b/kernel/kernel/sched/swait.c
index f230b1a..e1c655f 100644
--- a/kernel/kernel/sched/swait.c
+++ b/kernel/kernel/sched/swait.c
@@ -64,7 +64,6 @@
 	struct swait_queue *curr;
 	LIST_HEAD(tmp);
 
-	WARN_ON(irqs_disabled());
 	raw_spin_lock_irq(&q->lock);
 	list_splice_init(&q->task_list, &tmp);
 	while (!list_empty(&tmp)) {
diff --git a/kernel/kernel/sched/topology.c b/kernel/kernel/sched/topology.c
index a6c7dda..9227887 100644
--- a/kernel/kernel/sched/topology.c
+++ b/kernel/kernel/sched/topology.c
@@ -507,7 +507,6 @@
 	rd->rto_cpu = -1;
 	raw_spin_lock_init(&rd->rto_lock);
 	init_irq_work(&rd->rto_push_work, rto_push_irq_work_func);
-	atomic_or(IRQ_WORK_HARD_IRQ, &rd->rto_push_work.flags);
 #endif
 
 	init_dl_bw(&rd->dl_bw);
diff --git a/kernel/kernel/signal.c b/kernel/kernel/signal.c
index e302702..f6ecd01 100644
--- a/kernel/kernel/signal.c
+++ b/kernel/kernel/signal.c
@@ -20,7 +20,6 @@
 #include <linux/sched/task.h>
 #include <linux/sched/task_stack.h>
 #include <linux/sched/cputime.h>
-#include <linux/sched/rt.h>
 #include <linux/file.h>
 #include <linux/fs.h>
 #include <linux/proc_fs.h>
@@ -408,30 +407,13 @@
 	task_set_jobctl_pending(task, mask | JOBCTL_STOP_PENDING);
 }
 
-static inline struct sigqueue *get_task_cache(struct task_struct *t)
-{
-	struct sigqueue *q = t->sigqueue_cache;
-
-	if (cmpxchg(&t->sigqueue_cache, q, NULL) != q)
-		return NULL;
-	return q;
-}
-
-static inline int put_task_cache(struct task_struct *t, struct sigqueue *q)
-{
-	if (cmpxchg(&t->sigqueue_cache, NULL, q) == NULL)
-		return 0;
-	return 1;
-}
-
 /*
  * allocate a new signal queue record
  * - this may be called without locks if and only if t == current, otherwise an
  *   appropriate lock must be held to stop the target task from exiting
  */
 static struct sigqueue *
-__sigqueue_do_alloc(int sig, struct task_struct *t, gfp_t flags,
-		    int override_rlimit, int fromslab)
+__sigqueue_alloc(int sig, struct task_struct *t, gfp_t flags, int override_rlimit)
 {
 	struct sigqueue *q = NULL;
 	struct user_struct *user;
@@ -453,10 +435,7 @@
 	rcu_read_unlock();
 
 	if (override_rlimit || likely(sigpending <= task_rlimit(t, RLIMIT_SIGPENDING))) {
-		if (!fromslab)
-			q = get_task_cache(t);
-		if (!q)
-			q = kmem_cache_alloc(sigqueue_cachep, flags);
+		q = kmem_cache_alloc(sigqueue_cachep, flags);
 	} else {
 		print_dropped_signal(sig);
 	}
@@ -473,13 +452,6 @@
 	return q;
 }
 
-static struct sigqueue *
-__sigqueue_alloc(int sig, struct task_struct *t, gfp_t flags,
-		 int override_rlimit)
-{
-	return __sigqueue_do_alloc(sig, t, flags, override_rlimit, 0);
-}
-
 static void __sigqueue_free(struct sigqueue *q)
 {
 	if (q->flags & SIGQUEUE_PREALLOC)
@@ -487,21 +459,6 @@
 	if (atomic_dec_and_test(&q->user->sigpending))
 		free_uid(q->user);
 	kmem_cache_free(sigqueue_cachep, q);
-}
-
-static void sigqueue_free_current(struct sigqueue *q)
-{
-	struct user_struct *up;
-
-	if (q->flags & SIGQUEUE_PREALLOC)
-		return;
-
-	up = q->user;
-	if (rt_prio(current->normal_prio) && !put_task_cache(current, q)) {
-		if (atomic_dec_and_test(&up->sigpending))
-			free_uid(up);
-	} else
-		  __sigqueue_free(q);
 }
 
 void flush_sigqueue(struct sigpending *queue)
@@ -514,21 +471,6 @@
 		list_del_init(&q->list);
 		__sigqueue_free(q);
 	}
-}
-
-/*
- * Called from __exit_signal. Flush tsk->pending and
- * tsk->sigqueue_cache
- */
-void flush_task_sigqueue(struct task_struct *tsk)
-{
-	struct sigqueue *q;
-
-	flush_sigqueue(&tsk->pending);
-
-	q = get_task_cache(tsk);
-	if (q)
-		kmem_cache_free(sigqueue_cachep, q);
 }
 
 /*
@@ -655,7 +597,7 @@
 			(info->si_code == SI_TIMER) &&
 			(info->si_sys_private);
 
-		sigqueue_free_current(first);
+		__sigqueue_free(first);
 	} else {
 		/*
 		 * Ok, it wasn't in the queue.  This must be
@@ -691,8 +633,6 @@
 {
 	bool resched_timer = false;
 	int signr;
-
-	WARN_ON_ONCE(tsk != current);
 
 	/* We only dequeue private signals from ourselves, we don't let
 	 * signalfd steal them
@@ -1377,34 +1317,6 @@
 	struct k_sigaction *action;
 	int sig = info->si_signo;
 
-	/*
-	 * On some archs, PREEMPT_RT has to delay sending a signal from a trap
-	 * since it can not enable preemption, and the signal code's spin_locks
-	 * turn into mutexes. Instead, it must set TIF_NOTIFY_RESUME which will
-	 * send the signal on exit of the trap.
-	 */
-#ifdef ARCH_RT_DELAYS_SIGNAL_SEND
-	if (in_atomic()) {
-		struct task_struct *t = current;
-
-		if (WARN_ON_ONCE(t->forced_info.si_signo))
-			return 0;
-
-		if (is_si_special(info)) {
-			WARN_ON_ONCE(info != SEND_SIG_PRIV);
-			t->forced_info.si_signo = info->si_signo;
-			t->forced_info.si_errno = 0;
-			t->forced_info.si_code = SI_KERNEL;
-			t->forced_info.si_pid = 0;
-			t->forced_info.si_uid = 0;
-		} else {
-			t->forced_info = *info;
-		}
-
-		set_tsk_thread_flag(t, TIF_NOTIFY_RESUME);
-		return 0;
-	}
-#endif
 	spin_lock_irqsave(&t->sighand->siglock, flags);
 	action = &t->sighand->action[sig-1];
 	ignored = action->sa.sa_handler == SIG_IGN;
@@ -1907,8 +1819,7 @@
  */
 struct sigqueue *sigqueue_alloc(void)
 {
-	/* Preallocated sigqueue objects always from the slabcache ! */
-	struct sigqueue *q = __sigqueue_do_alloc(-1, current, GFP_KERNEL, 0, 1);
+	struct sigqueue *q = __sigqueue_alloc(-1, current, GFP_KERNEL, 0);
 
 	if (q)
 		q->flags |= SIGQUEUE_PREALLOC;
@@ -2294,8 +2205,16 @@
 		if (gstop_done && ptrace_reparented(current))
 			do_notify_parent_cldstop(current, false, why);
 
+		/*
+		 * Don't want to allow preemption here, because
+		 * sys_ptrace() needs this task to be inactive.
+		 *
+		 * XXX: implement read_unlock_no_resched().
+		 */
+		preempt_disable();
 		read_unlock(&tasklist_lock);
 		cgroup_enter_frozen();
+		preempt_enable_no_resched();
 		freezable_schedule();
 		cgroup_leave_frozen(true);
 	} else {
diff --git a/kernel/kernel/smp.c b/kernel/kernel/smp.c
index 641826e..d5b2697 100644
--- a/kernel/kernel/smp.c
+++ b/kernel/kernel/smp.c
@@ -451,18 +451,8 @@
 
 	local_irq_save(flags);
 	flush_smp_call_function_queue(true);
-
-	if (local_softirq_pending()) {
-
-		if (!IS_ENABLED(CONFIG_PREEMPT_RT)) {
-			do_softirq();
-		} else {
-			struct task_struct *ksoftirqd = this_cpu_ksoftirqd();
-
-			if (ksoftirqd && ksoftirqd->state != TASK_RUNNING)
-				wake_up_process(ksoftirqd);
-		}
-	}
+	if (local_softirq_pending())
+		do_softirq();
 
 	local_irq_restore(flags);
 }
diff --git a/kernel/kernel/softirq.c b/kernel/kernel/softirq.c
index 97e32f8..d59412b 100644
--- a/kernel/kernel/softirq.c
+++ b/kernel/kernel/softirq.c
@@ -13,7 +13,6 @@
 #include <linux/kernel_stat.h>
 #include <linux/interrupt.h>
 #include <linux/init.h>
-#include <linux/local_lock.h>
 #include <linux/mm.h>
 #include <linux/notifier.h>
 #include <linux/percpu.h>
@@ -26,7 +25,6 @@
 #include <linux/smpboot.h>
 #include <linux/tick.h>
 #include <linux/irq.h>
-#include <linux/wait_bit.h>
 
 #define CREATE_TRACE_POINTS
 #include <trace/events/irq.h>
@@ -90,227 +88,26 @@
 }
 
 /*
- * If ksoftirqd is scheduled, we do not want to process pending softirqs
- * right now. Let ksoftirqd handle this at its own rate, to get fairness,
- * unless we're doing some of the synchronous softirqs.
+ * preempt_count and SOFTIRQ_OFFSET usage:
+ * - preempt_count is changed by SOFTIRQ_OFFSET on entering or leaving
+ *   softirq processing.
+ * - preempt_count is changed by SOFTIRQ_DISABLE_OFFSET (= 2 * SOFTIRQ_OFFSET)
+ *   on local_bh_disable or local_bh_enable.
+ * This lets us distinguish between whether we are currently processing
+ * softirq and whether we just have bh disabled.
  */
-#define SOFTIRQ_NOW_MASK ((1 << HI_SOFTIRQ) | (1 << TASKLET_SOFTIRQ))
-static bool ksoftirqd_running(unsigned long pending)
-{
-	struct task_struct *tsk = __this_cpu_read(ksoftirqd);
 
-	if (pending & SOFTIRQ_NOW_MASK)
-		return false;
-	return tsk && (tsk->state == TASK_RUNNING) &&
-		!__kthread_should_park(tsk);
-}
-
+/*
+ * This one is for softirq.c-internal use,
+ * where hardirqs are disabled legitimately:
+ */
 #ifdef CONFIG_TRACE_IRQFLAGS
+
 DEFINE_PER_CPU(int, hardirqs_enabled);
 DEFINE_PER_CPU(int, hardirq_context);
 EXPORT_PER_CPU_SYMBOL_GPL(hardirqs_enabled);
 EXPORT_PER_CPU_SYMBOL_GPL(hardirq_context);
-#endif
 
-/*
- * SOFTIRQ_OFFSET usage:
- *
- * On !RT kernels 'count' is the preempt counter, on RT kernels this applies
- * to a per CPU counter and to task::softirqs_disabled_cnt.
- *
- * - count is changed by SOFTIRQ_OFFSET on entering or leaving softirq
- *   processing.
- *
- * - count is changed by SOFTIRQ_DISABLE_OFFSET (= 2 * SOFTIRQ_OFFSET)
- *   on local_bh_disable or local_bh_enable.
- *
- * This lets us distinguish between whether we are currently processing
- * softirq and whether we just have bh disabled.
- */
-#ifdef CONFIG_PREEMPT_RT
-
-/*
- * RT accounts for BH disabled sections in task::softirqs_disabled_cnt and
- * also in per CPU softirq_ctrl::cnt. This is necessary to allow tasks in a
- * softirq disabled section to be preempted.
- *
- * The per task counter is used for softirq_count(), in_softirq() and
- * in_serving_softirqs() because these counts are only valid when the task
- * holding softirq_ctrl::lock is running.
- *
- * The per CPU counter prevents pointless wakeups of ksoftirqd in case that
- * the task which is in a softirq disabled section is preempted or blocks.
- */
-struct softirq_ctrl {
-	local_lock_t	lock;
-	int		cnt;
-};
-
-static DEFINE_PER_CPU(struct softirq_ctrl, softirq_ctrl) = {
-	.lock	= INIT_LOCAL_LOCK(softirq_ctrl.lock),
-};
-
-/**
- * local_bh_blocked() - Check for idle whether BH processing is blocked
- *
- * Returns false if the per CPU softirq::cnt is 0 otherwise true.
- *
- * This is invoked from the idle task to guard against false positive
- * softirq pending warnings, which would happen when the task which holds
- * softirq_ctrl::lock was the only running task on the CPU and blocks on
- * some other lock.
- */
-bool local_bh_blocked(void)
-{
-	return __this_cpu_read(softirq_ctrl.cnt) != 0;
-}
-
-void __local_bh_disable_ip(unsigned long ip, unsigned int cnt)
-{
-	unsigned long flags;
-	int newcnt;
-
-	WARN_ON_ONCE(in_hardirq());
-
-	/* First entry of a task into a BH disabled section? */
-	if (!current->softirq_disable_cnt) {
-		if (preemptible()) {
-			local_lock(&softirq_ctrl.lock);
-			/* Required to meet the RCU bottomhalf requirements. */
-			rcu_read_lock();
-		} else {
-			DEBUG_LOCKS_WARN_ON(this_cpu_read(softirq_ctrl.cnt));
-		}
-	}
-
-	/*
-	 * Track the per CPU softirq disabled state. On RT this is per CPU
-	 * state to allow preemption of bottom half disabled sections.
-	 */
-	newcnt = __this_cpu_add_return(softirq_ctrl.cnt, cnt);
-	/*
-	 * Reflect the result in the task state to prevent recursion on the
-	 * local lock and to make softirq_count() & al work.
-	 */
-	current->softirq_disable_cnt = newcnt;
-
-	if (IS_ENABLED(CONFIG_TRACE_IRQFLAGS) && newcnt == cnt) {
-		raw_local_irq_save(flags);
-		lockdep_softirqs_off(ip);
-		raw_local_irq_restore(flags);
-	}
-}
-EXPORT_SYMBOL(__local_bh_disable_ip);
-
-static void __local_bh_enable(unsigned int cnt, bool unlock)
-{
-	unsigned long flags;
-	int newcnt;
-
-	DEBUG_LOCKS_WARN_ON(current->softirq_disable_cnt !=
-			    this_cpu_read(softirq_ctrl.cnt));
-
-	if (IS_ENABLED(CONFIG_TRACE_IRQFLAGS) && softirq_count() == cnt) {
-		raw_local_irq_save(flags);
-		lockdep_softirqs_on(_RET_IP_);
-		raw_local_irq_restore(flags);
-	}
-
-	newcnt = __this_cpu_sub_return(softirq_ctrl.cnt, cnt);
-	current->softirq_disable_cnt = newcnt;
-
-	if (!newcnt && unlock) {
-		rcu_read_unlock();
-		local_unlock(&softirq_ctrl.lock);
-	}
-}
-
-void __local_bh_enable_ip(unsigned long ip, unsigned int cnt)
-{
-	bool preempt_on = preemptible();
-	unsigned long flags;
-	u32 pending;
-	int curcnt;
-
-	WARN_ON_ONCE(in_irq());
-	lockdep_assert_irqs_enabled();
-
-	local_irq_save(flags);
-	curcnt = __this_cpu_read(softirq_ctrl.cnt);
-
-	/*
-	 * If this is not reenabling soft interrupts, no point in trying to
-	 * run pending ones.
-	 */
-	if (curcnt != cnt)
-		goto out;
-
-	pending = local_softirq_pending();
-	if (!pending || ksoftirqd_running(pending))
-		goto out;
-
-	/*
-	 * If this was called from non preemptible context, wake up the
-	 * softirq daemon.
-	 */
-	if (!preempt_on) {
-		wakeup_softirqd();
-		goto out;
-	}
-
-	/*
-	 * Adjust softirq count to SOFTIRQ_OFFSET which makes
-	 * in_serving_softirq() become true.
-	 */
-	cnt = SOFTIRQ_OFFSET;
-	__local_bh_enable(cnt, false);
-	__do_softirq();
-
-out:
-	__local_bh_enable(cnt, preempt_on);
-	local_irq_restore(flags);
-}
-EXPORT_SYMBOL(__local_bh_enable_ip);
-
-/*
- * Invoked from ksoftirqd_run() outside of the interrupt disabled section
- * to acquire the per CPU local lock for reentrancy protection.
- */
-static inline void ksoftirqd_run_begin(void)
-{
-	__local_bh_disable_ip(_RET_IP_, SOFTIRQ_OFFSET);
-	local_irq_disable();
-}
-
-/* Counterpart to ksoftirqd_run_begin() */
-static inline void ksoftirqd_run_end(void)
-{
-	__local_bh_enable(SOFTIRQ_OFFSET, true);
-	WARN_ON_ONCE(in_interrupt());
-	local_irq_enable();
-}
-
-static inline void softirq_handle_begin(void) { }
-static inline void softirq_handle_end(void) { }
-
-static inline bool should_wake_ksoftirqd(void)
-{
-	return !this_cpu_read(softirq_ctrl.cnt);
-}
-
-static inline void invoke_softirq(void)
-{
-	if (should_wake_ksoftirqd())
-		wakeup_softirqd();
-}
-
-#else /* CONFIG_PREEMPT_RT */
-
-/*
- * This one is for softirq.c-internal use, where hardirqs are disabled
- * legitimately:
- */
-#ifdef CONFIG_TRACE_IRQFLAGS
 void __local_bh_disable_ip(unsigned long ip, unsigned int cnt)
 {
 	unsigned long flags;
@@ -401,78 +198,6 @@
 }
 EXPORT_SYMBOL(__local_bh_enable_ip);
 
-static inline void softirq_handle_begin(void)
-{
-	__local_bh_disable_ip(_RET_IP_, SOFTIRQ_OFFSET);
-}
-
-static inline void softirq_handle_end(void)
-{
-	__local_bh_enable(SOFTIRQ_OFFSET);
-	WARN_ON_ONCE(in_interrupt());
-}
-
-static inline void ksoftirqd_run_begin(void)
-{
-	local_irq_disable();
-}
-
-static inline void ksoftirqd_run_end(void)
-{
-	local_irq_enable();
-}
-
-static inline bool should_wake_ksoftirqd(void)
-{
-	return true;
-}
-
-static inline void invoke_softirq(void)
-{
-	if (ksoftirqd_running(local_softirq_pending()))
-		return;
-
-	if (!force_irqthreads) {
-#ifdef CONFIG_HAVE_IRQ_EXIT_ON_IRQ_STACK
-		/*
-		 * We can safely execute softirq on the current stack if
-		 * it is the irq stack, because it should be near empty
-		 * at this stage.
-		 */
-		__do_softirq();
-#else
-		/*
-		 * Otherwise, irq_exit() is called on the task stack that can
-		 * be potentially deep already. So call softirq in its own stack
-		 * to prevent from any overrun.
-		 */
-		do_softirq_own_stack();
-#endif
-	} else {
-		wakeup_softirqd();
-	}
-}
-
-asmlinkage __visible void do_softirq(void)
-{
-	__u32 pending;
-	unsigned long flags;
-
-	if (in_interrupt())
-		return;
-
-	local_irq_save(flags);
-
-	pending = local_softirq_pending();
-
-	if (pending && !ksoftirqd_running(pending))
-		do_softirq_own_stack();
-
-	local_irq_restore(flags);
-}
-
-#endif /* !CONFIG_PREEMPT_RT */
-
 /*
  * We restart softirq processing for at most MAX_SOFTIRQ_RESTART times,
  * but break the loop if need_resched() is set or after 2 ms.
@@ -552,9 +277,9 @@
 
 	pending = local_softirq_pending();
 	deferred = softirq_deferred_for_rt(pending);
-	softirq_handle_begin();
+	account_irq_enter_time(current);
+	__local_bh_disable_ip(_RET_IP_, SOFTIRQ_OFFSET);
 	in_hardirq = lockdep_softirq_start();
-	account_softirq_enter(current);
 
 restart:
 	/* Reset the pending bitmask before enabling irqs */
@@ -590,10 +315,8 @@
 	}
 
 	__this_cpu_write(active_softirqs, 0);
-	if (!IS_ENABLED(CONFIG_PREEMPT_RT) &&
-	    __this_cpu_read(ksoftirqd) == current)
+	if (__this_cpu_read(ksoftirqd) == current)
 		rcu_softirq_qs();
-
 	local_irq_disable();
 
 	pending = local_softirq_pending();
@@ -613,10 +336,29 @@
 	if (pending | deferred)
 		wakeup_softirqd();
 #endif
-	account_softirq_exit(current);
 	lockdep_softirq_end(in_hardirq);
-	softirq_handle_end();
+	account_irq_exit_time(current);
+	__local_bh_enable(SOFTIRQ_OFFSET);
+	WARN_ON_ONCE(in_interrupt());
 	current_restore_flags(old_flags, PF_MEMALLOC);
+}
+
+asmlinkage __visible void do_softirq(void)
+{
+	__u32 pending;
+	unsigned long flags;
+
+	if (in_interrupt())
+		return;
+
+	local_irq_save(flags);
+
+	pending = local_softirq_pending();
+
+	if (pending)
+		do_softirq_own_stack();
+
+	local_irq_restore(flags);
 }
 
 /**
@@ -624,12 +366,16 @@
  */
 void irq_enter_rcu(void)
 {
-	__irq_enter_raw();
-
-	if (is_idle_task(current) && (irq_count() == HARDIRQ_OFFSET))
- 		tick_irq_enter();
-
-	account_hardirq_enter(current);
+	if (is_idle_task(current) && !in_interrupt()) {
+		/*
+		 * Prevent raise_softirq from needlessly waking up ksoftirqd
+		 * here, as softirq will be serviced on return from interrupt.
+		 */
+		local_bh_disable();
+		tick_irq_enter();
+		_local_bh_enable();
+	}
+	__irq_enter();
 }
 
 /**
@@ -639,6 +385,29 @@
 {
 	rcu_irq_enter();
 	irq_enter_rcu();
+}
+
+static inline void invoke_softirq(void)
+{
+	if (!force_irqthreads) {
+#ifdef CONFIG_HAVE_IRQ_EXIT_ON_IRQ_STACK
+		/*
+		 * We can safely execute softirq on the current stack if
+		 * it is the irq stack, because it should be near empty
+		 * at this stage.
+		 */
+		__do_softirq();
+#else
+		/*
+		 * Otherwise, irq_exit() is called on the task stack that can
+		 * be potentially deep already. So call softirq in its own stack
+		 * to prevent from any overrun.
+		 */
+		do_softirq_own_stack();
+#endif
+	} else {
+		wakeup_softirqd();
+	}
 }
 
 static inline void tick_irq_exit(void)
@@ -661,7 +430,7 @@
 #else
 	lockdep_assert_irqs_disabled();
 #endif
-	account_hardirq_exit(current);
+	account_irq_exit_time(current);
 	preempt_count_sub(HARDIRQ_OFFSET);
 	if (!in_interrupt() && local_softirq_pending())
 		invoke_softirq();
@@ -710,7 +479,7 @@
 	 * Otherwise we wake up ksoftirqd to make sure we
 	 * schedule the softirq soon.
 	 */
-	if (!in_interrupt() && should_wake_ksoftirqd())
+	if (!in_interrupt())
 		wakeup_softirqd();
 }
 
@@ -776,16 +545,6 @@
 }
 EXPORT_SYMBOL(__tasklet_hi_schedule);
 
-static inline bool tasklet_clear_sched(struct tasklet_struct *t)
-{
-	if (test_and_clear_bit(TASKLET_STATE_SCHED, &t->state)) {
-		wake_up_var(&t->state);
-		return true;
-	}
-
-	return false;
-}
-
 static void tasklet_action_common(struct softirq_action *a,
 				  struct tasklet_head *tl_head,
 				  unsigned int softirq_nr)
@@ -805,7 +564,8 @@
 
 		if (tasklet_trylock(t)) {
 			if (!atomic_read(&t->count)) {
-				if (!tasklet_clear_sched(t))
+				if (!test_and_clear_bit(TASKLET_STATE_SCHED,
+							&t->state))
 					BUG();
 				if (t->use_callback) {
 					trace_tasklet_entry(t->callback);
@@ -865,61 +625,20 @@
 }
 EXPORT_SYMBOL(tasklet_init);
 
-#if defined(CONFIG_SMP) || defined(CONFIG_PREEMPT_RT)
-/*
- * Do not use in new code. Waiting for tasklets from atomic contexts is
- * error prone and should be avoided.
- */
-void tasklet_unlock_spin_wait(struct tasklet_struct *t)
-{
-	while (test_bit(TASKLET_STATE_RUN, &(t)->state)) {
-		if (IS_ENABLED(CONFIG_PREEMPT_RT)) {
-			/*
-			 * Prevent a live lock when current preempted soft
-			 * interrupt processing or prevents ksoftirqd from
-			 * running. If the tasklet runs on a different CPU
-			 * then this has no effect other than doing the BH
-			 * disable/enable dance for nothing.
-			 */
-			local_bh_disable();
-			local_bh_enable();
-		} else {
-			cpu_relax();
-		}
-	}
-}
-EXPORT_SYMBOL(tasklet_unlock_spin_wait);
-#endif
-
 void tasklet_kill(struct tasklet_struct *t)
 {
 	if (in_interrupt())
 		pr_notice("Attempt to kill tasklet from interrupt\n");
 
-	while (test_and_set_bit(TASKLET_STATE_SCHED, &t->state))
-		wait_var_event(&t->state, !test_bit(TASKLET_STATE_SCHED, &t->state));
-
+	while (test_and_set_bit(TASKLET_STATE_SCHED, &t->state)) {
+		do {
+			yield();
+		} while (test_bit(TASKLET_STATE_SCHED, &t->state));
+	}
 	tasklet_unlock_wait(t);
-	tasklet_clear_sched(t);
+	clear_bit(TASKLET_STATE_SCHED, &t->state);
 }
 EXPORT_SYMBOL(tasklet_kill);
-
-#if defined(CONFIG_SMP) || defined(CONFIG_PREEMPT_RT)
-void tasklet_unlock(struct tasklet_struct *t)
-{
-	smp_mb__before_atomic();
-	clear_bit(TASKLET_STATE_RUN, &t->state);
-	smp_mb__after_atomic();
-	wake_up_var(&t->state);
-}
-EXPORT_SYMBOL_GPL(tasklet_unlock);
-
-void tasklet_unlock_wait(struct tasklet_struct *t)
-{
-	wait_var_event(&t->state, !test_bit(TASKLET_STATE_RUN, &t->state));
-}
-EXPORT_SYMBOL_GPL(tasklet_unlock_wait);
-#endif
 
 void __init softirq_init(void)
 {
@@ -943,18 +662,18 @@
 
 static void run_ksoftirqd(unsigned int cpu)
 {
-	ksoftirqd_run_begin();
+	local_irq_disable();
 	if (local_softirq_pending()) {
 		/*
 		 * We can safely run softirq on inline stack, as we are not deep
 		 * in the task stack here.
 		 */
 		__do_softirq();
-		ksoftirqd_run_end();
+		local_irq_enable();
 		cond_resched();
 		return;
 	}
-	ksoftirqd_run_end();
+	local_irq_enable();
 }
 
 #ifdef CONFIG_HOTPLUG_CPU
diff --git a/kernel/kernel/stop_machine.c b/kernel/kernel/stop_machine.c
index 30395a6..c65cfb7 100644
--- a/kernel/kernel/stop_machine.c
+++ b/kernel/kernel/stop_machine.c
@@ -33,26 +33,10 @@
 	struct list_head	works;		/* list of pending works */
 
 	struct cpu_stop_work	stop_work;	/* for stop_cpus */
-	unsigned long		caller;
-	cpu_stop_fn_t		fn;
 };
 
 static DEFINE_PER_CPU(struct cpu_stopper, cpu_stopper);
 static bool stop_machine_initialized = false;
-
-void print_stop_info(const char *log_lvl, struct task_struct *task)
-{
-	/*
-	 * If @task is a stopper task, it cannot migrate and task_cpu() is
-	 * stable.
-	 */
-	struct cpu_stopper *stopper = per_cpu_ptr(&cpu_stopper, task_cpu(task));
-
-	if (task != stopper->thread)
-		return;
-
-	printk("%sStopper: %pS <- %pS\n", log_lvl, stopper->fn, (void *)stopper->caller);
-}
 
 /* static data for stop_cpus */
 static DEFINE_MUTEX(stop_cpus_mutex);
@@ -130,7 +114,7 @@
 int stop_one_cpu(unsigned int cpu, cpu_stop_fn_t fn, void *arg)
 {
 	struct cpu_stop_done done;
-	struct cpu_stop_work work = { .fn = fn, .arg = arg, .done = &done, .caller = _RET_IP_ };
+	struct cpu_stop_work work = { .fn = fn, .arg = arg, .done = &done };
 
 	cpu_stop_init_done(&done, 1);
 	if (!cpu_stop_queue_work(cpu, &work))
@@ -338,8 +322,7 @@
 	work1 = work2 = (struct cpu_stop_work){
 		.fn = multi_cpu_stop,
 		.arg = &msdata,
-		.done = &done,
-		.caller = _RET_IP_,
+		.done = &done
 	};
 
 	cpu_stop_init_done(&done, 2);
@@ -375,7 +358,7 @@
 bool stop_one_cpu_nowait(unsigned int cpu, cpu_stop_fn_t fn, void *arg,
 			struct cpu_stop_work *work_buf)
 {
-	*work_buf = (struct cpu_stop_work){ .fn = fn, .arg = arg, .caller = _RET_IP_, };
+	*work_buf = (struct cpu_stop_work){ .fn = fn, .arg = arg, };
 	return cpu_stop_queue_work(cpu, work_buf);
 }
 EXPORT_SYMBOL_GPL(stop_one_cpu_nowait);
@@ -544,8 +527,6 @@
 		int ret;
 
 		/* cpu stop callbacks must not sleep, make in_atomic() == T */
-		stopper->caller = work->caller;
-		stopper->fn = fn;
 		preempt_count_inc();
 		ret = fn(arg);
 		if (done) {
@@ -554,8 +535,6 @@
 			cpu_stop_signal_done(done);
 		}
 		preempt_count_dec();
-		stopper->fn = NULL;
-		stopper->caller = 0;
 		WARN_ONCE(preempt_count(),
 			  "cpu_stop: %ps(%p) leaked preempt count\n", fn, arg);
 		goto repeat;
diff --git a/kernel/kernel/time/hrtimer.c b/kernel/kernel/time/hrtimer.c
index 3db616a..544ce87 100644
--- a/kernel/kernel/time/hrtimer.c
+++ b/kernel/kernel/time/hrtimer.c
@@ -2052,36 +2052,6 @@
 }
 #endif
 
-#ifdef CONFIG_PREEMPT_RT
-/*
- * Sleep for 1 ms in hope whoever holds what we want will let it go.
- */
-void cpu_chill(void)
-{
-	unsigned int freeze_flag = current->flags & PF_NOFREEZE;
-	struct task_struct *self = current;
-	ktime_t chill_time;
-
-	raw_spin_lock_irq(&self->pi_lock);
-	self->saved_state = self->state;
-	__set_current_state_no_track(TASK_UNINTERRUPTIBLE);
-	raw_spin_unlock_irq(&self->pi_lock);
-
-	chill_time = ktime_set(0, NSEC_PER_MSEC);
-
-	current->flags |= PF_NOFREEZE;
-	schedule_hrtimeout(&chill_time, HRTIMER_MODE_REL_HARD);
-	if (!freeze_flag)
-		current->flags &= ~PF_NOFREEZE;
-
-	raw_spin_lock_irq(&self->pi_lock);
-	__set_current_state_no_track(self->saved_state);
-	self->saved_state = TASK_RUNNING;
-	raw_spin_unlock_irq(&self->pi_lock);
-}
-EXPORT_SYMBOL(cpu_chill);
-#endif
-
 /*
  * Functions related to boot-time initialization:
  */
diff --git a/kernel/kernel/time/tick-sched.c b/kernel/kernel/time/tick-sched.c
index f3431ac..07969fa 100644
--- a/kernel/kernel/time/tick-sched.c
+++ b/kernel/kernel/time/tick-sched.c
@@ -927,7 +927,7 @@
 	if (unlikely(local_softirq_pending())) {
 		static int ratelimit;
 
-		if (ratelimit < 10 && !local_bh_blocked() &&
+		if (ratelimit < 10 &&
 		    (local_softirq_pending() & SOFTIRQ_STOP_IDLE_MASK)) {
 			pr_warn("NOHZ tick-stop error: Non-RCU local softirq work is pending, handler #%02x!!!\n",
 				(unsigned int) local_softirq_pending());
diff --git a/kernel/kernel/time/timer.c b/kernel/kernel/time/timer.c
index 72a3988..f5147bd 100644
--- a/kernel/kernel/time/timer.c
+++ b/kernel/kernel/time/timer.c
@@ -1293,7 +1293,7 @@
 	u32 tf;
 
 	tf = READ_ONCE(timer->flags);
-	if (!(tf & (TIMER_MIGRATING | TIMER_IRQSAFE))) {
+	if (!(tf & TIMER_MIGRATING)) {
 		struct timer_base *base = get_timer_base(tf);
 
 		/*
@@ -1376,13 +1376,6 @@
 	 * could lead to deadlock.
 	 */
 	WARN_ON(in_irq() && !(timer->flags & TIMER_IRQSAFE));
-
-	/*
-	 * Must be able to sleep on PREEMPT_RT because of the slowpath in
-	 * del_timer_wait_running().
-	 */
-	if (IS_ENABLED(CONFIG_PREEMPT_RT) && !(timer->flags & TIMER_IRQSAFE))
-		lockdep_assert_preemption_enabled();
 
 	do {
 		ret = try_to_del_timer_sync(timer);
diff --git a/kernel/kernel/trace/blktrace.c b/kernel/kernel/trace/blktrace.c
index b3afd10..b89ff18 100644
--- a/kernel/kernel/trace/blktrace.c
+++ b/kernel/kernel/trace/blktrace.c
@@ -72,17 +72,17 @@
 	struct blk_io_trace *t;
 	struct ring_buffer_event *event = NULL;
 	struct trace_buffer *buffer = NULL;
-	unsigned int trace_ctx = 0;
+	int pc = 0;
 	int cpu = smp_processor_id();
 	bool blk_tracer = blk_tracer_enabled;
 	ssize_t cgid_len = cgid ? sizeof(cgid) : 0;
 
 	if (blk_tracer) {
 		buffer = blk_tr->array_buffer.buffer;
-		trace_ctx = tracing_gen_ctx_flags(0);
+		pc = preempt_count();
 		event = trace_buffer_lock_reserve(buffer, TRACE_BLK,
 						  sizeof(*t) + len + cgid_len,
-						  trace_ctx);
+						  0, pc);
 		if (!event)
 			return;
 		t = ring_buffer_event_data(event);
@@ -107,7 +107,7 @@
 		memcpy((void *) t + sizeof(*t) + cgid_len, data, len);
 
 		if (blk_tracer)
-			trace_buffer_unlock_commit(blk_tr, buffer, event, trace_ctx);
+			trace_buffer_unlock_commit(blk_tr, buffer, event, 0, pc);
 	}
 }
 
@@ -222,9 +222,8 @@
 	struct blk_io_trace *t;
 	unsigned long flags = 0;
 	unsigned long *sequence;
-	unsigned int trace_ctx = 0;
 	pid_t pid;
-	int cpu;
+	int cpu, pc = 0;
 	bool blk_tracer = blk_tracer_enabled;
 	ssize_t cgid_len = cgid ? sizeof(cgid) : 0;
 
@@ -253,10 +252,10 @@
 		tracing_record_cmdline(current);
 
 		buffer = blk_tr->array_buffer.buffer;
-		trace_ctx = tracing_gen_ctx_flags(0);
+		pc = preempt_count();
 		event = trace_buffer_lock_reserve(buffer, TRACE_BLK,
 						  sizeof(*t) + pdu_len + cgid_len,
-						  trace_ctx);
+						  0, pc);
 		if (!event)
 			return;
 		t = ring_buffer_event_data(event);
@@ -302,7 +301,7 @@
 			memcpy((void *)t + sizeof(*t) + cgid_len, pdu_data, pdu_len);
 
 		if (blk_tracer) {
-			trace_buffer_unlock_commit(blk_tr, buffer, event, trace_ctx);
+			trace_buffer_unlock_commit(blk_tr, buffer, event, 0, pc);
 			return;
 		}
 	}
diff --git a/kernel/kernel/trace/trace.c b/kernel/kernel/trace/trace.c
index c4cce26..8b1f74e 100644
--- a/kernel/kernel/trace/trace.c
+++ b/kernel/kernel/trace/trace.c
@@ -177,7 +177,7 @@
 int tracing_set_tracer(struct trace_array *tr, const char *buf);
 static void ftrace_trace_userstack(struct trace_array *tr,
 				   struct trace_buffer *buffer,
-				   unsigned int trace_ctx);
+				   unsigned long flags, int pc);
 
 #define MAX_TRACER_SIZE		100
 static char bootup_tracer_buf[MAX_TRACER_SIZE] __initdata;
@@ -910,23 +910,23 @@
 
 #ifdef CONFIG_STACKTRACE
 static void __ftrace_trace_stack(struct trace_buffer *buffer,
-				 unsigned int trace_ctx,
-				 int skip, struct pt_regs *regs);
+				 unsigned long flags,
+				 int skip, int pc, struct pt_regs *regs);
 static inline void ftrace_trace_stack(struct trace_array *tr,
 				      struct trace_buffer *buffer,
-				      unsigned int trace_ctx,
-				      int skip, struct pt_regs *regs);
+				      unsigned long flags,
+				      int skip, int pc, struct pt_regs *regs);
 
 #else
 static inline void __ftrace_trace_stack(struct trace_buffer *buffer,
-					unsigned int trace_ctx,
-					int skip, struct pt_regs *regs)
+					unsigned long flags,
+					int skip, int pc, struct pt_regs *regs)
 {
 }
 static inline void ftrace_trace_stack(struct trace_array *tr,
 				      struct trace_buffer *buffer,
-				      unsigned long trace_ctx,
-				      int skip, struct pt_regs *regs)
+				      unsigned long flags,
+				      int skip, int pc, struct pt_regs *regs)
 {
 }
 
@@ -934,24 +934,24 @@
 
 static __always_inline void
 trace_event_setup(struct ring_buffer_event *event,
-		  int type, unsigned int trace_ctx)
+		  int type, unsigned long flags, int pc)
 {
 	struct trace_entry *ent = ring_buffer_event_data(event);
 
-	tracing_generic_entry_update(ent, type, trace_ctx);
+	tracing_generic_entry_update(ent, type, flags, pc);
 }
 
 static __always_inline struct ring_buffer_event *
 __trace_buffer_lock_reserve(struct trace_buffer *buffer,
 			  int type,
 			  unsigned long len,
-			  unsigned int trace_ctx)
+			  unsigned long flags, int pc)
 {
 	struct ring_buffer_event *event;
 
 	event = ring_buffer_lock_reserve(buffer, len);
 	if (event != NULL)
-		trace_event_setup(event, type, trace_ctx);
+		trace_event_setup(event, type, flags, pc);
 
 	return event;
 }
@@ -1012,22 +1012,25 @@
 	struct ring_buffer_event *event;
 	struct trace_buffer *buffer;
 	struct print_entry *entry;
-	unsigned int trace_ctx;
+	unsigned long irq_flags;
 	int alloc;
+	int pc;
 
 	if (!(global_trace.trace_flags & TRACE_ITER_PRINTK))
 		return 0;
+
+	pc = preempt_count();
 
 	if (unlikely(tracing_selftest_running || tracing_disabled))
 		return 0;
 
 	alloc = sizeof(*entry) + size + 2; /* possible \n added */
 
-	trace_ctx = tracing_gen_ctx();
+	local_save_flags(irq_flags);
 	buffer = global_trace.array_buffer.buffer;
 	ring_buffer_nest_start(buffer);
-	event = __trace_buffer_lock_reserve(buffer, TRACE_PRINT, alloc,
-					    trace_ctx);
+	event = __trace_buffer_lock_reserve(buffer, TRACE_PRINT, alloc, 
+					    irq_flags, pc);
 	if (!event) {
 		size = 0;
 		goto out;
@@ -1046,7 +1049,7 @@
 		entry->buf[size] = '\0';
 
 	__buffer_unlock_commit(buffer, event);
-	ftrace_trace_stack(&global_trace, buffer, trace_ctx, 4, NULL);
+	ftrace_trace_stack(&global_trace, buffer, irq_flags, 4, pc, NULL);
  out:
 	ring_buffer_nest_end(buffer);
 	return size;
@@ -1063,22 +1066,25 @@
 	struct ring_buffer_event *event;
 	struct trace_buffer *buffer;
 	struct bputs_entry *entry;
-	unsigned int trace_ctx;
+	unsigned long irq_flags;
 	int size = sizeof(struct bputs_entry);
 	int ret = 0;
+	int pc;
 
 	if (!(global_trace.trace_flags & TRACE_ITER_PRINTK))
 		return 0;
 
+	pc = preempt_count();
+
 	if (unlikely(tracing_selftest_running || tracing_disabled))
 		return 0;
 
-	trace_ctx = tracing_gen_ctx();
+	local_save_flags(irq_flags);
 	buffer = global_trace.array_buffer.buffer;
 
 	ring_buffer_nest_start(buffer);
 	event = __trace_buffer_lock_reserve(buffer, TRACE_BPUTS, size,
-					    trace_ctx);
+					    irq_flags, pc);
 	if (!event)
 		goto out;
 
@@ -1087,7 +1093,7 @@
 	entry->str			= str;
 
 	__buffer_unlock_commit(buffer, event);
-	ftrace_trace_stack(&global_trace, buffer, trace_ctx, 4, NULL);
+	ftrace_trace_stack(&global_trace, buffer, irq_flags, 4, pc, NULL);
 
 	ret = 1;
  out:
@@ -2608,52 +2614,36 @@
 }
 EXPORT_SYMBOL_GPL(trace_handle_return);
 
-static unsigned short migration_disable_value(void)
+void
+tracing_generic_entry_update(struct trace_entry *entry, unsigned short type,
+			     unsigned long flags, int pc)
 {
-#if defined(CONFIG_SMP) && defined(CONFIG_PREEMPT_RT)
-	return current->migration_disabled;
+	struct task_struct *tsk = current;
+
+	entry->preempt_count		= pc & 0xff;
+	entry->pid			= (tsk) ? tsk->pid : 0;
+	entry->type			= type;
+	entry->flags =
+#ifdef CONFIG_TRACE_IRQFLAGS_SUPPORT
+		(irqs_disabled_flags(flags) ? TRACE_FLAG_IRQS_OFF : 0) |
 #else
-	return 0;
+		TRACE_FLAG_IRQS_NOSUPPORT |
 #endif
+		((pc & NMI_MASK    ) ? TRACE_FLAG_NMI     : 0) |
+		((pc & HARDIRQ_MASK) ? TRACE_FLAG_HARDIRQ : 0) |
+		((pc & SOFTIRQ_OFFSET) ? TRACE_FLAG_SOFTIRQ : 0) |
+		(tif_need_resched() ? TRACE_FLAG_NEED_RESCHED : 0) |
+		(test_preempt_need_resched() ? TRACE_FLAG_PREEMPT_RESCHED : 0);
 }
-
-unsigned int tracing_gen_ctx_irq_test(unsigned int irqs_status)
-{
-	unsigned int trace_flags = irqs_status;
-	unsigned int pc;
-
-	pc = preempt_count();
-
-	if (pc & NMI_MASK)
-		trace_flags |= TRACE_FLAG_NMI;
-	if (pc & HARDIRQ_MASK)
-		trace_flags |= TRACE_FLAG_HARDIRQ;
-	if (in_serving_softirq())
-		trace_flags |= TRACE_FLAG_SOFTIRQ;
-
-	if (tif_need_resched())
-		trace_flags |= TRACE_FLAG_NEED_RESCHED;
-	if (test_preempt_need_resched())
-		trace_flags |= TRACE_FLAG_PREEMPT_RESCHED;
-
-#ifdef CONFIG_PREEMPT_LAZY
-	if (need_resched_lazy())
-		trace_flags |= TRACE_FLAG_NEED_RESCHED_LAZY;
-#endif
-
-	return (pc & 0xff) |
-		(migration_disable_value() & 0xff) << 8 |
-		(preempt_lazy_count() & 0xff) << 16 |
-		(trace_flags << 24);
-}
+EXPORT_SYMBOL_GPL(tracing_generic_entry_update);
 
 struct ring_buffer_event *
 trace_buffer_lock_reserve(struct trace_buffer *buffer,
 			  int type,
 			  unsigned long len,
-			  unsigned int trace_ctx)
+			  unsigned long flags, int pc)
 {
-	return __trace_buffer_lock_reserve(buffer, type, len, trace_ctx);
+	return __trace_buffer_lock_reserve(buffer, type, len, flags, pc);
 }
 
 DEFINE_PER_CPU(struct ring_buffer_event *, trace_buffered_event);
@@ -2773,7 +2763,7 @@
 trace_event_buffer_lock_reserve(struct trace_buffer **current_rb,
 			  struct trace_event_file *trace_file,
 			  int type, unsigned long len,
-			  unsigned int trace_ctx)
+			  unsigned long flags, int pc)
 {
 	struct ring_buffer_event *entry;
 	int val;
@@ -2786,7 +2776,7 @@
 		/* Try to use the per cpu buffer first */
 		val = this_cpu_inc_return(trace_buffered_event_cnt);
 		if ((len < (PAGE_SIZE - sizeof(*entry) - sizeof(entry->array[0]))) && val == 1) {
-			trace_event_setup(entry, type, trace_ctx);
+			trace_event_setup(entry, type, flags, pc);
 			entry->array[0] = len;
 			return entry;
 		}
@@ -2794,7 +2784,7 @@
 	}
 
 	entry = __trace_buffer_lock_reserve(*current_rb,
-					    type, len, trace_ctx);
+					    type, len, flags, pc);
 	/*
 	 * If tracing is off, but we have triggers enabled
 	 * we still need to look at the event data. Use the temp_buffer
@@ -2803,8 +2793,8 @@
 	 */
 	if (!entry && trace_file->flags & EVENT_FILE_FL_TRIGGER_COND) {
 		*current_rb = temp_buffer;
-		entry = __trace_buffer_lock_reserve(*current_rb, type, len,
-						    trace_ctx);
+		entry = __trace_buffer_lock_reserve(*current_rb,
+						    type, len, flags, pc);
 	}
 	return entry;
 }
@@ -2890,7 +2880,7 @@
 		ftrace_exports(fbuffer->event, TRACE_EXPORT_EVENT);
 	event_trigger_unlock_commit_regs(fbuffer->trace_file, fbuffer->buffer,
 				    fbuffer->event, fbuffer->entry,
-				    fbuffer->trace_ctx, fbuffer->regs);
+				    fbuffer->flags, fbuffer->pc, fbuffer->regs);
 }
 EXPORT_SYMBOL_GPL(trace_event_buffer_commit);
 
@@ -2906,7 +2896,7 @@
 void trace_buffer_unlock_commit_regs(struct trace_array *tr,
 				     struct trace_buffer *buffer,
 				     struct ring_buffer_event *event,
-				     unsigned int trace_ctx,
+				     unsigned long flags, int pc,
 				     struct pt_regs *regs)
 {
 	__buffer_unlock_commit(buffer, event);
@@ -2917,8 +2907,8 @@
 	 * and mmiotrace, but that's ok if they lose a function or
 	 * two. They are not that meaningful.
 	 */
-	ftrace_trace_stack(tr, buffer, trace_ctx, regs ? 0 : STACK_SKIP, regs);
-	ftrace_trace_userstack(tr, buffer, trace_ctx);
+	ftrace_trace_stack(tr, buffer, flags, regs ? 0 : STACK_SKIP, pc, regs);
+	ftrace_trace_userstack(tr, buffer, flags, pc);
 }
 
 /*
@@ -2932,8 +2922,9 @@
 }
 
 void
-trace_function(struct trace_array *tr, unsigned long ip, unsigned long
-	       parent_ip, unsigned int trace_ctx)
+trace_function(struct trace_array *tr,
+	       unsigned long ip, unsigned long parent_ip, unsigned long flags,
+	       int pc)
 {
 	struct trace_event_call *call = &event_function;
 	struct trace_buffer *buffer = tr->array_buffer.buffer;
@@ -2941,7 +2932,7 @@
 	struct ftrace_entry *entry;
 
 	event = __trace_buffer_lock_reserve(buffer, TRACE_FN, sizeof(*entry),
-					    trace_ctx);
+					    flags, pc);
 	if (!event)
 		return;
 	entry	= ring_buffer_event_data(event);
@@ -2975,8 +2966,8 @@
 static DEFINE_PER_CPU(int, ftrace_stack_reserve);
 
 static void __ftrace_trace_stack(struct trace_buffer *buffer,
-				 unsigned int trace_ctx,
-				 int skip, struct pt_regs *regs)
+				 unsigned long flags,
+				 int skip, int pc, struct pt_regs *regs)
 {
 	struct trace_event_call *call = &event_kernel_stack;
 	struct ring_buffer_event *event;
@@ -3024,7 +3015,7 @@
 	size = nr_entries * sizeof(unsigned long);
 	event = __trace_buffer_lock_reserve(buffer, TRACE_STACK,
 				    (sizeof(*entry) - sizeof(entry->caller)) + size,
-				    trace_ctx);
+				    flags, pc);
 	if (!event)
 		goto out;
 	entry = ring_buffer_event_data(event);
@@ -3045,22 +3036,22 @@
 
 static inline void ftrace_trace_stack(struct trace_array *tr,
 				      struct trace_buffer *buffer,
-				      unsigned int trace_ctx,
-				      int skip, struct pt_regs *regs)
+				      unsigned long flags,
+				      int skip, int pc, struct pt_regs *regs)
 {
 	if (!(tr->trace_flags & TRACE_ITER_STACKTRACE))
 		return;
 
-	__ftrace_trace_stack(buffer, trace_ctx, skip, regs);
+	__ftrace_trace_stack(buffer, flags, skip, pc, regs);
 }
 
-void __trace_stack(struct trace_array *tr, unsigned int trace_ctx,
-		   int skip)
+void __trace_stack(struct trace_array *tr, unsigned long flags, int skip,
+		   int pc)
 {
 	struct trace_buffer *buffer = tr->array_buffer.buffer;
 
 	if (rcu_is_watching()) {
-		__ftrace_trace_stack(buffer, trace_ctx, skip, NULL);
+		__ftrace_trace_stack(buffer, flags, skip, pc, NULL);
 		return;
 	}
 
@@ -3074,7 +3065,7 @@
 		return;
 
 	rcu_irq_enter_irqson();
-	__ftrace_trace_stack(buffer, trace_ctx, skip, NULL);
+	__ftrace_trace_stack(buffer, flags, skip, pc, NULL);
 	rcu_irq_exit_irqson();
 }
 
@@ -3084,15 +3075,19 @@
  */
 void trace_dump_stack(int skip)
 {
+	unsigned long flags;
+
 	if (tracing_disabled || tracing_selftest_running)
 		return;
+
+	local_save_flags(flags);
 
 #ifndef CONFIG_UNWINDER_ORC
 	/* Skip 1 to skip this function. */
 	skip++;
 #endif
 	__ftrace_trace_stack(global_trace.array_buffer.buffer,
-			     tracing_gen_ctx(), skip, NULL);
+			     flags, skip, preempt_count(), NULL);
 }
 EXPORT_SYMBOL_GPL(trace_dump_stack);
 
@@ -3101,7 +3096,7 @@
 
 static void
 ftrace_trace_userstack(struct trace_array *tr,
-		       struct trace_buffer *buffer, unsigned int trace_ctx)
+		       struct trace_buffer *buffer, unsigned long flags, int pc)
 {
 	struct trace_event_call *call = &event_user_stack;
 	struct ring_buffer_event *event;
@@ -3128,7 +3123,7 @@
 	__this_cpu_inc(user_stack_count);
 
 	event = __trace_buffer_lock_reserve(buffer, TRACE_USER_STACK,
-					    sizeof(*entry), trace_ctx);
+					    sizeof(*entry), flags, pc);
 	if (!event)
 		goto out_drop_count;
 	entry	= ring_buffer_event_data(event);
@@ -3148,7 +3143,7 @@
 #else /* CONFIG_USER_STACKTRACE_SUPPORT */
 static void ftrace_trace_userstack(struct trace_array *tr,
 				   struct trace_buffer *buffer,
-				   unsigned int trace_ctx)
+				   unsigned long flags, int pc)
 {
 }
 #endif /* !CONFIG_USER_STACKTRACE_SUPPORT */
@@ -3278,9 +3273,9 @@
 	struct trace_buffer *buffer;
 	struct trace_array *tr = &global_trace;
 	struct bprint_entry *entry;
-	unsigned int trace_ctx;
+	unsigned long flags;
 	char *tbuffer;
-	int len = 0, size;
+	int len = 0, size, pc;
 
 	if (unlikely(tracing_selftest_running || tracing_disabled))
 		return 0;
@@ -3288,7 +3283,7 @@
 	/* Don't pollute graph traces with trace_vprintk internals */
 	pause_graph_tracing();
 
-	trace_ctx = tracing_gen_ctx();
+	pc = preempt_count();
 	preempt_disable_notrace();
 
 	tbuffer = get_trace_buf();
@@ -3302,11 +3297,12 @@
 	if (len > TRACE_BUF_SIZE/sizeof(int) || len < 0)
 		goto out_put;
 
+	local_save_flags(flags);
 	size = sizeof(*entry) + sizeof(u32) * len;
 	buffer = tr->array_buffer.buffer;
 	ring_buffer_nest_start(buffer);
 	event = __trace_buffer_lock_reserve(buffer, TRACE_BPRINT, size,
-					    trace_ctx);
+					    flags, pc);
 	if (!event)
 		goto out;
 	entry = ring_buffer_event_data(event);
@@ -3316,7 +3312,7 @@
 	memcpy(entry->buf, tbuffer, sizeof(u32) * len);
 	if (!call_filter_check_discard(call, entry, buffer, event)) {
 		__buffer_unlock_commit(buffer, event);
-		ftrace_trace_stack(tr, buffer, trace_ctx, 6, NULL);
+		ftrace_trace_stack(tr, buffer, flags, 6, pc, NULL);
 	}
 
 out:
@@ -3339,9 +3335,9 @@
 {
 	struct trace_event_call *call = &event_print;
 	struct ring_buffer_event *event;
-	int len = 0, size;
+	int len = 0, size, pc;
 	struct print_entry *entry;
-	unsigned int trace_ctx;
+	unsigned long flags;
 	char *tbuffer;
 
 	if (tracing_disabled || tracing_selftest_running)
@@ -3350,7 +3346,7 @@
 	/* Don't pollute graph traces with trace_vprintk internals */
 	pause_graph_tracing();
 
-	trace_ctx = tracing_gen_ctx();
+	pc = preempt_count();
 	preempt_disable_notrace();
 
 
@@ -3362,10 +3358,11 @@
 
 	len = vscnprintf(tbuffer, TRACE_BUF_SIZE, fmt, args);
 
+	local_save_flags(flags);
 	size = sizeof(*entry) + len + 1;
 	ring_buffer_nest_start(buffer);
 	event = __trace_buffer_lock_reserve(buffer, TRACE_PRINT, size,
-					    trace_ctx);
+					    flags, pc);
 	if (!event)
 		goto out;
 	entry = ring_buffer_event_data(event);
@@ -3374,7 +3371,7 @@
 	memcpy(&entry->buf, tbuffer, len + 1);
 	if (!call_filter_check_discard(call, entry, buffer, event)) {
 		__buffer_unlock_commit(buffer, event);
-		ftrace_trace_stack(&global_trace, buffer, trace_ctx, 6, NULL);
+		ftrace_trace_stack(&global_trace, buffer, flags, 6, pc, NULL);
 	}
 
 out:
@@ -3840,17 +3837,14 @@
 
 static void print_lat_help_header(struct seq_file *m)
 {
-	seq_puts(m, "#                    _--------=> CPU#            \n"
-		    "#                   / _-------=> irqs-off        \n"
-		    "#                  | / _------=> need-resched    \n"
-		    "#                  || / _-----=> need-resched-lazy\n"
-		    "#                  ||| / _----=> hardirq/softirq \n"
-		    "#                  |||| / _---=> preempt-depth   \n"
-		    "#                  ||||| / _--=> preempt-lazy-depth\n"
-		    "#                  |||||| / _-=> migrate-disable \n"
-		    "#                  ||||||| /     delay           \n"
-		    "#  cmd     pid     |||||||| time  |   caller     \n"
-		    "#     \\   /        ||||||||  \\    |    /       \n");
+	seq_puts(m, "#                    _------=> CPU#            \n"
+		    "#                   / _-----=> irqs-off        \n"
+		    "#                  | / _----=> need-resched    \n"
+		    "#                  || / _---=> hardirq/softirq \n"
+		    "#                  ||| / _--=> preempt-depth   \n"
+		    "#                  |||| /     delay            \n"
+		    "#  cmd     pid     ||||| time  |   caller      \n"
+		    "#     \\   /        |||||  \\    |   /         \n");
 }
 
 static void print_event_info(struct array_buffer *buf, struct seq_file *m)
@@ -3884,16 +3878,13 @@
 
 	print_event_info(buf, m);
 
-	seq_printf(m, "#                            %.*s  _-------=> irqs-off\n", prec, space);
-	seq_printf(m, "#                            %.*s / _------=> need-resched\n", prec, space);
-	seq_printf(m, "#                            %.*s| / _-----=> need-resched-lazy\n", prec, space);
-	seq_printf(m, "#                            %.*s|| / _----=> hardirq/softirq\n", prec, space);
-	seq_printf(m, "#                            %.*s||| / _---=> preempt-depth\n", prec, space);
-	seq_printf(m, "#                            %.*s|||| / _--=> preempt-lazy-depth\n", prec, space);
-	seq_printf(m, "#                            %.*s||||| / _-=> migrate-disable\n", prec, space);
-	seq_printf(m, "#                            %.*s|||||| /     delay\n", prec, space);
-	seq_printf(m, "#           TASK-PID  %.*s CPU#  |||||||  TIMESTAMP  FUNCTION\n", prec, "     TGID   ");
-	seq_printf(m, "#              | |    %.*s   |   |||||||      |         |\n", prec, "       |    ");
+	seq_printf(m, "#                            %.*s  _-----=> irqs-off\n", prec, space);
+	seq_printf(m, "#                            %.*s / _----=> need-resched\n", prec, space);
+	seq_printf(m, "#                            %.*s| / _---=> hardirq/softirq\n", prec, space);
+	seq_printf(m, "#                            %.*s|| / _--=> preempt-depth\n", prec, space);
+	seq_printf(m, "#                            %.*s||| /     delay\n", prec, space);
+	seq_printf(m, "#           TASK-PID  %.*s CPU#  ||||   TIMESTAMP  FUNCTION\n", prec, "     TGID   ");
+	seq_printf(m, "#              | |    %.*s   |   ||||      |         |\n", prec, "       |    ");
 }
 
 void
@@ -6698,6 +6689,7 @@
 	enum event_trigger_type tt = ETT_NONE;
 	struct trace_buffer *buffer;
 	struct print_entry *entry;
+	unsigned long irq_flags;
 	ssize_t written;
 	int size;
 	int len;
@@ -6717,6 +6709,7 @@
 
 	BUILD_BUG_ON(TRACE_BUF_SIZE >= PAGE_SIZE);
 
+	local_save_flags(irq_flags);
 	size = sizeof(*entry) + cnt + 2; /* add '\0' and possible '\n' */
 
 	/* If less than "<faulted>", then make sure we can still add that */
@@ -6725,7 +6718,7 @@
 
 	buffer = tr->array_buffer.buffer;
 	event = __trace_buffer_lock_reserve(buffer, TRACE_PRINT, size,
-					    tracing_gen_ctx());
+					    irq_flags, preempt_count());
 	if (unlikely(!event))
 		/* Ring buffer disabled, return as if not open for write */
 		return -EBADF;
@@ -6777,6 +6770,7 @@
 	struct ring_buffer_event *event;
 	struct trace_buffer *buffer;
 	struct raw_data_entry *entry;
+	unsigned long irq_flags;
 	ssize_t written;
 	int size;
 	int len;
@@ -6798,13 +6792,14 @@
 
 	BUILD_BUG_ON(TRACE_BUF_SIZE >= PAGE_SIZE);
 
+	local_save_flags(irq_flags);
 	size = sizeof(*entry) + cnt;
 	if (cnt < FAULT_SIZE_ID)
 		size += FAULT_SIZE_ID - cnt;
 
 	buffer = tr->array_buffer.buffer;
 	event = __trace_buffer_lock_reserve(buffer, TRACE_RAW_DATA, size,
-					    tracing_gen_ctx());
+					    irq_flags, preempt_count());
 	if (!event)
 		/* Ring buffer disabled, return as if not open for write */
 		return -EBADF;
@@ -9391,6 +9386,7 @@
 	tracing_off();
 
 	local_irq_save(flags);
+	printk_nmi_direct_enter();
 
 	/* Simulate the iterator */
 	trace_init_global_iter(&iter);
@@ -9478,6 +9474,7 @@
 		atomic_dec(&per_cpu_ptr(iter.array_buffer->data, cpu)->disabled);
 	}
 	atomic_dec(&dump_running);
+	printk_nmi_direct_exit();
 	local_irq_restore(flags);
 }
 EXPORT_SYMBOL_GPL(ftrace_dump);
diff --git a/kernel/kernel/trace/trace.h b/kernel/kernel/trace/trace.h
index bfb9fe2..8d67f7f 100644
--- a/kernel/kernel/trace/trace.h
+++ b/kernel/kernel/trace/trace.h
@@ -136,6 +136,25 @@
 	unsigned long		ret_ip;
 };
 
+/*
+ * trace_flag_type is an enumeration that holds different
+ * states when a trace occurs. These are:
+ *  IRQS_OFF		- interrupts were disabled
+ *  IRQS_NOSUPPORT	- arch does not support irqs_disabled_flags
+ *  NEED_RESCHED	- reschedule is requested
+ *  HARDIRQ		- inside an interrupt handler
+ *  SOFTIRQ		- inside a softirq handler
+ */
+enum trace_flag_type {
+	TRACE_FLAG_IRQS_OFF		= 0x01,
+	TRACE_FLAG_IRQS_NOSUPPORT	= 0x02,
+	TRACE_FLAG_NEED_RESCHED		= 0x04,
+	TRACE_FLAG_HARDIRQ		= 0x08,
+	TRACE_FLAG_SOFTIRQ		= 0x10,
+	TRACE_FLAG_PREEMPT_RESCHED	= 0x20,
+	TRACE_FLAG_NMI			= 0x40,
+};
+
 #define TRACE_BUF_SIZE		1024
 
 struct trace_array;
@@ -726,7 +745,8 @@
 trace_buffer_lock_reserve(struct trace_buffer *buffer,
 			  int type,
 			  unsigned long len,
-			  unsigned int trace_ctx);
+			  unsigned long flags,
+			  int pc);
 
 struct trace_entry *tracing_get_trace_entry(struct trace_array *tr,
 						struct trace_array_cpu *data);
@@ -751,11 +771,11 @@
 void trace_function(struct trace_array *tr,
 		    unsigned long ip,
 		    unsigned long parent_ip,
-		    unsigned int trace_ctx);
+		    unsigned long flags, int pc);
 void trace_graph_function(struct trace_array *tr,
 		    unsigned long ip,
 		    unsigned long parent_ip,
-		    unsigned int trace_ctx);
+		    unsigned long flags, int pc);
 void trace_latency_header(struct seq_file *m);
 void trace_default_header(struct seq_file *m);
 void print_trace_header(struct seq_file *m, struct trace_iterator *iter);
@@ -823,10 +843,11 @@
 #endif
 
 #ifdef CONFIG_STACKTRACE
-void __trace_stack(struct trace_array *tr, unsigned int trace_ctx, int skip);
+void __trace_stack(struct trace_array *tr, unsigned long flags, int skip,
+		   int pc);
 #else
-static inline void __trace_stack(struct trace_array *tr, unsigned int trace_ctx,
-				 int skip)
+static inline void __trace_stack(struct trace_array *tr, unsigned long flags,
+				 int skip, int pc)
 {
 }
 #endif /* CONFIG_STACKTRACE */
@@ -966,10 +987,10 @@
 extern void graph_trace_close(struct trace_iterator *iter);
 extern int __trace_graph_entry(struct trace_array *tr,
 			       struct ftrace_graph_ent *trace,
-			       unsigned int trace_ctx);
+			       unsigned long flags, int pc);
 extern void __trace_graph_return(struct trace_array *tr,
 				 struct ftrace_graph_ret *trace,
-				 unsigned int trace_ctx);
+				 unsigned long flags, int pc);
 
 #ifdef CONFIG_DYNAMIC_FTRACE
 extern struct ftrace_hash __rcu *ftrace_graph_hash;
@@ -1432,15 +1453,15 @@
 void trace_buffer_unlock_commit_regs(struct trace_array *tr,
 				     struct trace_buffer *buffer,
 				     struct ring_buffer_event *event,
-				     unsigned int trcace_ctx,
+				     unsigned long flags, int pc,
 				     struct pt_regs *regs);
 
 static inline void trace_buffer_unlock_commit(struct trace_array *tr,
 					      struct trace_buffer *buffer,
 					      struct ring_buffer_event *event,
-					      unsigned int trace_ctx)
+					      unsigned long flags, int pc)
 {
-	trace_buffer_unlock_commit_regs(tr, buffer, event, trace_ctx, NULL);
+	trace_buffer_unlock_commit_regs(tr, buffer, event, flags, pc, NULL);
 }
 
 DECLARE_PER_CPU(struct ring_buffer_event *, trace_buffered_event);
@@ -1513,7 +1534,8 @@
  * @buffer: The ring buffer that the event is being written to
  * @event: The event meta data in the ring buffer
  * @entry: The event itself
- * @trace_ctx: The tracing context flags.
+ * @irq_flags: The state of the interrupts at the start of the event
+ * @pc: The state of the preempt count at the start of the event.
  *
  * This is a helper function to handle triggers that require data
  * from the event itself. It also tests the event against filters and
@@ -1523,12 +1545,12 @@
 event_trigger_unlock_commit(struct trace_event_file *file,
 			    struct trace_buffer *buffer,
 			    struct ring_buffer_event *event,
-			    void *entry, unsigned int trace_ctx)
+			    void *entry, unsigned long irq_flags, int pc)
 {
 	enum event_trigger_type tt = ETT_NONE;
 
 	if (!__event_trigger_test_discard(file, buffer, event, entry, &tt))
-		trace_buffer_unlock_commit(file->tr, buffer, event, trace_ctx);
+		trace_buffer_unlock_commit(file->tr, buffer, event, irq_flags, pc);
 
 	if (tt)
 		event_triggers_post_call(file, tt);
@@ -1540,7 +1562,8 @@
  * @buffer: The ring buffer that the event is being written to
  * @event: The event meta data in the ring buffer
  * @entry: The event itself
- * @trace_ctx: The tracing context flags.
+ * @irq_flags: The state of the interrupts at the start of the event
+ * @pc: The state of the preempt count at the start of the event.
  *
  * This is a helper function to handle triggers that require data
  * from the event itself. It also tests the event against filters and
@@ -1553,14 +1576,14 @@
 event_trigger_unlock_commit_regs(struct trace_event_file *file,
 				 struct trace_buffer *buffer,
 				 struct ring_buffer_event *event,
-				 void *entry, unsigned int trace_ctx,
+				 void *entry, unsigned long irq_flags, int pc,
 				 struct pt_regs *regs)
 {
 	enum event_trigger_type tt = ETT_NONE;
 
 	if (!__event_trigger_test_discard(file, buffer, event, entry, &tt))
 		trace_buffer_unlock_commit_regs(file->tr, buffer, event,
-						trace_ctx, regs);
+						irq_flags, pc, regs);
 
 	if (tt)
 		event_triggers_post_call(file, tt);
diff --git a/kernel/kernel/trace/trace_branch.c b/kernel/kernel/trace/trace_branch.c
index e47fdb4..eff0991 100644
--- a/kernel/kernel/trace/trace_branch.c
+++ b/kernel/kernel/trace/trace_branch.c
@@ -37,7 +37,7 @@
 	struct ring_buffer_event *event;
 	struct trace_branch *entry;
 	unsigned long flags;
-	unsigned int trace_ctx;
+	int pc;
 	const char *p;
 
 	if (current->trace_recursion & TRACE_BRANCH_BIT)
@@ -59,10 +59,10 @@
 	if (atomic_read(&data->disabled))
 		goto out;
 
-	trace_ctx = tracing_gen_ctx_flags(flags);
+	pc = preempt_count();
 	buffer = tr->array_buffer.buffer;
 	event = trace_buffer_lock_reserve(buffer, TRACE_BRANCH,
-					  sizeof(*entry), trace_ctx);
+					  sizeof(*entry), flags, pc);
 	if (!event)
 		goto out;
 
diff --git a/kernel/kernel/trace/trace_event_perf.c b/kernel/kernel/trace/trace_event_perf.c
index 0443dd6..643e0b1 100644
--- a/kernel/kernel/trace/trace_event_perf.c
+++ b/kernel/kernel/trace/trace_event_perf.c
@@ -421,8 +421,11 @@
 void perf_trace_buf_update(void *record, u16 type)
 {
 	struct trace_entry *entry = record;
+	int pc = preempt_count();
+	unsigned long flags;
 
-	tracing_generic_entry_update(entry, type, tracing_gen_ctx());
+	local_save_flags(flags);
+	tracing_generic_entry_update(entry, type, flags, pc);
 }
 NOKPROBE_SYMBOL(perf_trace_buf_update);
 
diff --git a/kernel/kernel/trace/trace_events.c b/kernel/kernel/trace/trace_events.c
index ca64598..bac13f2 100644
--- a/kernel/kernel/trace/trace_events.c
+++ b/kernel/kernel/trace/trace_events.c
@@ -184,8 +184,6 @@
 	__common_field(unsigned char, flags);
 	__common_field(unsigned char, preempt_count);
 	__common_field(int, pid);
-	__common_field(unsigned char, migrate_disable);
-	__common_field(unsigned char, preempt_lazy_count);
 
 	return ret;
 }
@@ -261,19 +259,22 @@
 	    trace_event_ignore_this_pid(trace_file))
 		return NULL;
 
+	local_save_flags(fbuffer->flags);
+	fbuffer->pc = preempt_count();
 	/*
 	 * If CONFIG_PREEMPTION is enabled, then the tracepoint itself disables
 	 * preemption (adding one to the preempt_count). Since we are
 	 * interested in the preempt_count at the time the tracepoint was
 	 * hit, we need to subtract one to offset the increment.
 	 */
-	fbuffer->trace_ctx = tracing_gen_ctx_dec();
+	if (IS_ENABLED(CONFIG_PREEMPTION))
+		fbuffer->pc--;
 	fbuffer->trace_file = trace_file;
 
 	fbuffer->event =
 		trace_event_buffer_lock_reserve(&fbuffer->buffer, trace_file,
 						event_call->event.type, len,
-						fbuffer->trace_ctx);
+						fbuffer->flags, fbuffer->pc);
 	if (!fbuffer->event)
 		return NULL;
 
@@ -3698,11 +3699,12 @@
 	struct trace_buffer *buffer;
 	struct ring_buffer_event *event;
 	struct ftrace_entry *entry;
-	unsigned int trace_ctx;
+	unsigned long flags;
 	long disabled;
 	int cpu;
+	int pc;
 
-	trace_ctx = tracing_gen_ctx();
+	pc = preempt_count();
 	preempt_disable_notrace();
 	cpu = raw_smp_processor_id();
 	disabled = atomic_inc_return(&per_cpu(ftrace_test_event_disable, cpu));
@@ -3710,9 +3712,11 @@
 	if (disabled != 1)
 		goto out;
 
+	local_save_flags(flags);
+
 	event = trace_event_buffer_lock_reserve(&buffer, &event_trace_file,
 						TRACE_FN, sizeof(*entry),
-						trace_ctx);
+						flags, pc);
 	if (!event)
 		goto out;
 	entry	= ring_buffer_event_data(event);
@@ -3720,7 +3724,7 @@
 	entry->parent_ip		= parent_ip;
 
 	event_trigger_unlock_commit(&event_trace_file, buffer, event,
-				    entry, trace_ctx);
+				    entry, flags, pc);
  out:
 	atomic_dec(&per_cpu(ftrace_test_event_disable, cpu));
 	preempt_enable_notrace();
diff --git a/kernel/kernel/trace/trace_events_inject.c b/kernel/kernel/trace/trace_events_inject.c
index c188045..22bcf7c 100644
--- a/kernel/kernel/trace/trace_events_inject.c
+++ b/kernel/kernel/trace/trace_events_inject.c
@@ -192,6 +192,7 @@
 static int parse_entry(char *str, struct trace_event_call *call, void **pentry)
 {
 	struct ftrace_event_field *field;
+	unsigned long irq_flags;
 	void *entry = NULL;
 	int entry_size;
 	u64 val = 0;
@@ -202,8 +203,9 @@
 	if (!entry)
 		return -ENOMEM;
 
-	tracing_generic_entry_update(entry, call->event.type,
-				     tracing_gen_ctx());
+	local_save_flags(irq_flags);
+	tracing_generic_entry_update(entry, call->event.type, irq_flags,
+				     preempt_count());
 
 	while ((len = parse_field(str, call, &field, &val)) > 0) {
 		if (is_function_field(field))
diff --git a/kernel/kernel/trace/trace_events_trigger.c b/kernel/kernel/trace/trace_events_trigger.c
index 3c6229f..4bc9096 100644
--- a/kernel/kernel/trace/trace_events_trigger.c
+++ b/kernel/kernel/trace/trace_events_trigger.c
@@ -1220,10 +1220,12 @@
 		   struct ring_buffer_event *event)
 {
 	struct trace_event_file *file = data->private_data;
+	unsigned long flags;
 
-	if (file)
-		__trace_stack(file->tr, tracing_gen_ctx(), STACK_SKIP);
-	else
+	if (file) {
+		local_save_flags(flags);
+		__trace_stack(file->tr, flags, STACK_SKIP, preempt_count());
+	} else
 		trace_dump_stack(STACK_SKIP);
 }
 
diff --git a/kernel/kernel/trace/trace_functions.c b/kernel/kernel/trace/trace_functions.c
index 8606cb7..93e20ed 100644
--- a/kernel/kernel/trace/trace_functions.c
+++ b/kernel/kernel/trace/trace_functions.c
@@ -133,14 +133,15 @@
 {
 	struct trace_array *tr = op->private;
 	struct trace_array_cpu *data;
-	unsigned int trace_ctx;
+	unsigned long flags;
 	int bit;
 	int cpu;
+	int pc;
 
 	if (unlikely(!tr->function_enabled))
 		return;
 
-	trace_ctx = tracing_gen_ctx();
+	pc = preempt_count();
 	preempt_disable_notrace();
 
 	bit = trace_test_and_set_recursion(TRACE_FTRACE_START);
@@ -149,9 +150,10 @@
 
 	cpu = smp_processor_id();
 	data = per_cpu_ptr(tr->array_buffer.data, cpu);
-	if (!atomic_read(&data->disabled))
-		trace_function(tr, ip, parent_ip, trace_ctx);
-
+	if (!atomic_read(&data->disabled)) {
+		local_save_flags(flags);
+		trace_function(tr, ip, parent_ip, flags, pc);
+	}
 	trace_clear_recursion(bit);
 
  out:
@@ -185,7 +187,7 @@
 	unsigned long flags;
 	long disabled;
 	int cpu;
-	unsigned int trace_ctx;
+	int pc;
 
 	if (unlikely(!tr->function_enabled))
 		return;
@@ -200,9 +202,9 @@
 	disabled = atomic_inc_return(&data->disabled);
 
 	if (likely(disabled == 1)) {
-		trace_ctx = tracing_gen_ctx_flags(flags);
-		trace_function(tr, ip, parent_ip, trace_ctx);
-		__trace_stack(tr, trace_ctx, STACK_SKIP);
+		pc = preempt_count();
+		trace_function(tr, ip, parent_ip, flags, pc);
+		__trace_stack(tr, flags, STACK_SKIP, pc);
 	}
 
 	atomic_dec(&data->disabled);
@@ -405,11 +407,13 @@
 
 static __always_inline void trace_stack(struct trace_array *tr)
 {
-	unsigned int trace_ctx;
+	unsigned long flags;
+	int pc;
 
-	trace_ctx = tracing_gen_ctx();
+	local_save_flags(flags);
+	pc = preempt_count();
 
-	__trace_stack(tr, trace_ctx, FTRACE_STACK_SKIP);
+	__trace_stack(tr, flags, FTRACE_STACK_SKIP, pc);
 }
 
 static void
diff --git a/kernel/kernel/trace/trace_functions_graph.c b/kernel/kernel/trace/trace_functions_graph.c
index b086ba8..60d6627 100644
--- a/kernel/kernel/trace/trace_functions_graph.c
+++ b/kernel/kernel/trace/trace_functions_graph.c
@@ -96,7 +96,8 @@
 
 int __trace_graph_entry(struct trace_array *tr,
 				struct ftrace_graph_ent *trace,
-				unsigned int trace_ctx)
+				unsigned long flags,
+				int pc)
 {
 	struct trace_event_call *call = &event_funcgraph_entry;
 	struct ring_buffer_event *event;
@@ -104,7 +105,7 @@
 	struct ftrace_graph_ent_entry *entry;
 
 	event = trace_buffer_lock_reserve(buffer, TRACE_GRAPH_ENT,
-					  sizeof(*entry), trace_ctx);
+					  sizeof(*entry), flags, pc);
 	if (!event)
 		return 0;
 	entry	= ring_buffer_event_data(event);
@@ -128,10 +129,10 @@
 	struct trace_array *tr = graph_array;
 	struct trace_array_cpu *data;
 	unsigned long flags;
-	unsigned int trace_ctx;
 	long disabled;
 	int ret;
 	int cpu;
+	int pc;
 
 	if (trace_recursion_test(TRACE_GRAPH_NOTRACE_BIT))
 		return 0;
@@ -173,8 +174,8 @@
 	data = per_cpu_ptr(tr->array_buffer.data, cpu);
 	disabled = atomic_inc_return(&data->disabled);
 	if (likely(disabled == 1)) {
-		trace_ctx = tracing_gen_ctx_flags(flags);
-		ret = __trace_graph_entry(tr, trace, trace_ctx);
+		pc = preempt_count();
+		ret = __trace_graph_entry(tr, trace, flags, pc);
 	} else {
 		ret = 0;
 	}
@@ -187,7 +188,7 @@
 
 static void
 __trace_graph_function(struct trace_array *tr,
-		unsigned long ip, unsigned int trace_ctx)
+		unsigned long ip, unsigned long flags, int pc)
 {
 	u64 time = trace_clock_local();
 	struct ftrace_graph_ent ent = {
@@ -201,21 +202,22 @@
 		.rettime  = time,
 	};
 
-	__trace_graph_entry(tr, &ent, trace_ctx);
-	__trace_graph_return(tr, &ret, trace_ctx);
+	__trace_graph_entry(tr, &ent, flags, pc);
+	__trace_graph_return(tr, &ret, flags, pc);
 }
 
 void
 trace_graph_function(struct trace_array *tr,
 		unsigned long ip, unsigned long parent_ip,
-		unsigned int trace_ctx)
+		unsigned long flags, int pc)
 {
-	__trace_graph_function(tr, ip, trace_ctx);
+	__trace_graph_function(tr, ip, flags, pc);
 }
 
 void __trace_graph_return(struct trace_array *tr,
 				struct ftrace_graph_ret *trace,
-				unsigned int trace_ctx)
+				unsigned long flags,
+				int pc)
 {
 	struct trace_event_call *call = &event_funcgraph_exit;
 	struct ring_buffer_event *event;
@@ -223,7 +225,7 @@
 	struct ftrace_graph_ret_entry *entry;
 
 	event = trace_buffer_lock_reserve(buffer, TRACE_GRAPH_RET,
-					  sizeof(*entry), trace_ctx);
+					  sizeof(*entry), flags, pc);
 	if (!event)
 		return;
 	entry	= ring_buffer_event_data(event);
@@ -237,9 +239,9 @@
 	struct trace_array *tr = graph_array;
 	struct trace_array_cpu *data;
 	unsigned long flags;
-	unsigned int trace_ctx;
 	long disabled;
 	int cpu;
+	int pc;
 
 	ftrace_graph_addr_finish(trace);
 
@@ -253,8 +255,8 @@
 	data = per_cpu_ptr(tr->array_buffer.data, cpu);
 	disabled = atomic_inc_return(&data->disabled);
 	if (likely(disabled == 1)) {
-		trace_ctx = tracing_gen_ctx_flags(flags);
-		__trace_graph_return(tr, trace, trace_ctx);
+		pc = preempt_count();
+		__trace_graph_return(tr, trace, flags, pc);
 	}
 	atomic_dec(&data->disabled);
 	local_irq_restore(flags);
diff --git a/kernel/kernel/trace/trace_hwlat.c b/kernel/kernel/trace/trace_hwlat.c
index 4c01c5d..d071fc2 100644
--- a/kernel/kernel/trace/trace_hwlat.c
+++ b/kernel/kernel/trace/trace_hwlat.c
@@ -108,9 +108,14 @@
 	struct trace_buffer *buffer = tr->array_buffer.buffer;
 	struct ring_buffer_event *event;
 	struct hwlat_entry *entry;
+	unsigned long flags;
+	int pc;
+
+	pc = preempt_count();
+	local_save_flags(flags);
 
 	event = trace_buffer_lock_reserve(buffer, TRACE_HWLAT, sizeof(*entry),
-					  tracing_gen_ctx());
+					  flags, pc);
 	if (!event)
 		return;
 	entry	= ring_buffer_event_data(event);
diff --git a/kernel/kernel/trace/trace_irqsoff.c b/kernel/kernel/trace/trace_irqsoff.c
index f11add8..ee4571b 100644
--- a/kernel/kernel/trace/trace_irqsoff.c
+++ b/kernel/kernel/trace/trace_irqsoff.c
@@ -143,14 +143,11 @@
 	struct trace_array *tr = irqsoff_trace;
 	struct trace_array_cpu *data;
 	unsigned long flags;
-	unsigned int trace_ctx;
 
 	if (!func_prolog_dec(tr, &data, &flags))
 		return;
 
-	trace_ctx = tracing_gen_ctx_flags(flags);
-
-	trace_function(tr, ip, parent_ip, trace_ctx);
+	trace_function(tr, ip, parent_ip, flags, preempt_count());
 
 	atomic_dec(&data->disabled);
 }
@@ -180,8 +177,8 @@
 	struct trace_array *tr = irqsoff_trace;
 	struct trace_array_cpu *data;
 	unsigned long flags;
-	unsigned int trace_ctx;
 	int ret;
+	int pc;
 
 	if (ftrace_graph_ignore_func(trace))
 		return 0;
@@ -198,8 +195,8 @@
 	if (!func_prolog_dec(tr, &data, &flags))
 		return 0;
 
-	trace_ctx = tracing_gen_ctx_flags(flags);
-	ret = __trace_graph_entry(tr, trace, trace_ctx);
+	pc = preempt_count();
+	ret = __trace_graph_entry(tr, trace, flags, pc);
 	atomic_dec(&data->disabled);
 
 	return ret;
@@ -210,15 +207,15 @@
 	struct trace_array *tr = irqsoff_trace;
 	struct trace_array_cpu *data;
 	unsigned long flags;
-	unsigned int trace_ctx;
+	int pc;
 
 	ftrace_graph_addr_finish(trace);
 
 	if (!func_prolog_dec(tr, &data, &flags))
 		return;
 
-	trace_ctx = tracing_gen_ctx_flags(flags);
-	__trace_graph_return(tr, trace, trace_ctx);
+	pc = preempt_count();
+	__trace_graph_return(tr, trace, flags, pc);
 	atomic_dec(&data->disabled);
 }
 
@@ -270,12 +267,12 @@
 static void
 __trace_function(struct trace_array *tr,
 		 unsigned long ip, unsigned long parent_ip,
-		 unsigned int trace_ctx)
+		 unsigned long flags, int pc)
 {
 	if (is_graph(tr))
-		trace_graph_function(tr, ip, parent_ip, trace_ctx);
+		trace_graph_function(tr, ip, parent_ip, flags, pc);
 	else
-		trace_function(tr, ip, parent_ip, trace_ctx);
+		trace_function(tr, ip, parent_ip, flags, pc);
 }
 
 #else
@@ -325,13 +322,15 @@
 {
 	u64 T0, T1, delta;
 	unsigned long flags;
-	unsigned int trace_ctx;
+	int pc;
 
 	T0 = data->preempt_timestamp;
 	T1 = ftrace_now(cpu);
 	delta = T1-T0;
 
-	trace_ctx = tracing_gen_ctx();
+	local_save_flags(flags);
+
+	pc = preempt_count();
 
 	if (!report_latency(tr, delta))
 		goto out;
@@ -342,9 +341,9 @@
 	if (!report_latency(tr, delta))
 		goto out_unlock;
 
-	__trace_function(tr, CALLER_ADDR0, parent_ip, trace_ctx);
+	__trace_function(tr, CALLER_ADDR0, parent_ip, flags, pc);
 	/* Skip 5 functions to get to the irq/preempt enable function */
-	__trace_stack(tr, trace_ctx, 5);
+	__trace_stack(tr, flags, 5, pc);
 
 	if (data->critical_sequence != max_sequence)
 		goto out_unlock;
@@ -364,15 +363,16 @@
 out:
 	data->critical_sequence = max_sequence;
 	data->preempt_timestamp = ftrace_now(cpu);
-	__trace_function(tr, CALLER_ADDR0, parent_ip, trace_ctx);
+	__trace_function(tr, CALLER_ADDR0, parent_ip, flags, pc);
 }
 
 static nokprobe_inline void
-start_critical_timing(unsigned long ip, unsigned long parent_ip)
+start_critical_timing(unsigned long ip, unsigned long parent_ip, int pc)
 {
 	int cpu;
 	struct trace_array *tr = irqsoff_trace;
 	struct trace_array_cpu *data;
+	unsigned long flags;
 
 	if (!tracer_enabled || !tracing_is_enabled())
 		return;
@@ -393,7 +393,9 @@
 	data->preempt_timestamp = ftrace_now(cpu);
 	data->critical_start = parent_ip ? : ip;
 
-	__trace_function(tr, ip, parent_ip, tracing_gen_ctx());
+	local_save_flags(flags);
+
+	__trace_function(tr, ip, parent_ip, flags, pc);
 
 	per_cpu(tracing_cpu, cpu) = 1;
 
@@ -401,12 +403,12 @@
 }
 
 static nokprobe_inline void
-stop_critical_timing(unsigned long ip, unsigned long parent_ip)
+stop_critical_timing(unsigned long ip, unsigned long parent_ip, int pc)
 {
 	int cpu;
 	struct trace_array *tr = irqsoff_trace;
 	struct trace_array_cpu *data;
-	unsigned int trace_ctx;
+	unsigned long flags;
 
 	cpu = raw_smp_processor_id();
 	/* Always clear the tracing cpu on stopping the trace */
@@ -426,8 +428,8 @@
 
 	atomic_inc(&data->disabled);
 
-	trace_ctx = tracing_gen_ctx();
-	__trace_function(tr, ip, parent_ip, trace_ctx);
+	local_save_flags(flags);
+	__trace_function(tr, ip, parent_ip, flags, pc);
 	check_critical_timing(tr, data, parent_ip ? : ip, cpu);
 	data->critical_start = 0;
 	atomic_dec(&data->disabled);
@@ -436,16 +438,20 @@
 /* start and stop critical timings used to for stoppage (in idle) */
 void start_critical_timings(void)
 {
-	if (preempt_trace(preempt_count()) || irq_trace())
-		start_critical_timing(CALLER_ADDR0, CALLER_ADDR1);
+	int pc = preempt_count();
+
+	if (preempt_trace(pc) || irq_trace())
+		start_critical_timing(CALLER_ADDR0, CALLER_ADDR1, pc);
 }
 EXPORT_SYMBOL_GPL(start_critical_timings);
 NOKPROBE_SYMBOL(start_critical_timings);
 
 void stop_critical_timings(void)
 {
-	if (preempt_trace(preempt_count()) || irq_trace())
-		stop_critical_timing(CALLER_ADDR0, CALLER_ADDR1);
+	int pc = preempt_count();
+
+	if (preempt_trace(pc) || irq_trace())
+		stop_critical_timing(CALLER_ADDR0, CALLER_ADDR1, pc);
 }
 EXPORT_SYMBOL_GPL(stop_critical_timings);
 NOKPROBE_SYMBOL(stop_critical_timings);
@@ -607,15 +613,19 @@
  */
 void tracer_hardirqs_on(unsigned long a0, unsigned long a1)
 {
-	if (!preempt_trace(preempt_count()) && irq_trace())
-		stop_critical_timing(a0, a1);
+	unsigned int pc = preempt_count();
+
+	if (!preempt_trace(pc) && irq_trace())
+		stop_critical_timing(a0, a1, pc);
 }
 NOKPROBE_SYMBOL(tracer_hardirqs_on);
 
 void tracer_hardirqs_off(unsigned long a0, unsigned long a1)
 {
-	if (!preempt_trace(preempt_count()) && irq_trace())
-		start_critical_timing(a0, a1);
+	unsigned int pc = preempt_count();
+
+	if (!preempt_trace(pc) && irq_trace())
+		start_critical_timing(a0, a1, pc);
 }
 NOKPROBE_SYMBOL(tracer_hardirqs_off);
 
@@ -655,14 +665,18 @@
 #ifdef CONFIG_PREEMPT_TRACER
 void tracer_preempt_on(unsigned long a0, unsigned long a1)
 {
-	if (preempt_trace(preempt_count()) && !irq_trace())
-		stop_critical_timing(a0, a1);
+	int pc = preempt_count();
+
+	if (preempt_trace(pc) && !irq_trace())
+		stop_critical_timing(a0, a1, pc);
 }
 
 void tracer_preempt_off(unsigned long a0, unsigned long a1)
 {
-	if (preempt_trace(preempt_count()) && !irq_trace())
-		start_critical_timing(a0, a1);
+	int pc = preempt_count();
+
+	if (preempt_trace(pc) && !irq_trace())
+		start_critical_timing(a0, a1, pc);
 }
 
 static int preemptoff_tracer_init(struct trace_array *tr)
diff --git a/kernel/kernel/trace/trace_kprobe.c b/kernel/kernel/trace/trace_kprobe.c
index 8ac26fd..41dd173 100644
--- a/kernel/kernel/trace/trace_kprobe.c
+++ b/kernel/kernel/trace/trace_kprobe.c
@@ -1393,7 +1393,8 @@
 	if (trace_trigger_soft_disabled(trace_file))
 		return;
 
-	fbuffer.trace_ctx = tracing_gen_ctx();
+	local_save_flags(fbuffer.flags);
+	fbuffer.pc = preempt_count();
 	fbuffer.trace_file = trace_file;
 
 	dsize = __get_data_size(&tk->tp, regs);
@@ -1402,7 +1403,7 @@
 		trace_event_buffer_lock_reserve(&fbuffer.buffer, trace_file,
 					call->event.type,
 					sizeof(*entry) + tk->tp.size + dsize,
-					fbuffer.trace_ctx);
+					fbuffer.flags, fbuffer.pc);
 	if (!fbuffer.event)
 		return;
 
@@ -1440,7 +1441,8 @@
 	if (trace_trigger_soft_disabled(trace_file))
 		return;
 
-	fbuffer.trace_ctx = tracing_gen_ctx();
+	local_save_flags(fbuffer.flags);
+	fbuffer.pc = preempt_count();
 	fbuffer.trace_file = trace_file;
 
 	dsize = __get_data_size(&tk->tp, regs);
@@ -1448,7 +1450,7 @@
 		trace_event_buffer_lock_reserve(&fbuffer.buffer, trace_file,
 					call->event.type,
 					sizeof(*entry) + tk->tp.size + dsize,
-					fbuffer.trace_ctx);
+					fbuffer.flags, fbuffer.pc);
 	if (!fbuffer.event)
 		return;
 
diff --git a/kernel/kernel/trace/trace_mmiotrace.c b/kernel/kernel/trace/trace_mmiotrace.c
index 7221ae0..84582bf 100644
--- a/kernel/kernel/trace/trace_mmiotrace.c
+++ b/kernel/kernel/trace/trace_mmiotrace.c
@@ -300,11 +300,10 @@
 	struct trace_buffer *buffer = tr->array_buffer.buffer;
 	struct ring_buffer_event *event;
 	struct trace_mmiotrace_rw *entry;
-	unsigned int trace_ctx;
+	int pc = preempt_count();
 
-	trace_ctx = tracing_gen_ctx_flags(0);
 	event = trace_buffer_lock_reserve(buffer, TRACE_MMIO_RW,
-					  sizeof(*entry), trace_ctx);
+					  sizeof(*entry), 0, pc);
 	if (!event) {
 		atomic_inc(&dropped_count);
 		return;
@@ -313,7 +312,7 @@
 	entry->rw			= *rw;
 
 	if (!call_filter_check_discard(call, entry, buffer, event))
-		trace_buffer_unlock_commit(tr, buffer, event, trace_ctx);
+		trace_buffer_unlock_commit(tr, buffer, event, 0, pc);
 }
 
 void mmio_trace_rw(struct mmiotrace_rw *rw)
@@ -331,11 +330,10 @@
 	struct trace_buffer *buffer = tr->array_buffer.buffer;
 	struct ring_buffer_event *event;
 	struct trace_mmiotrace_map *entry;
-	unsigned int trace_ctx;
+	int pc = preempt_count();
 
-	trace_ctx = tracing_gen_ctx_flags(0);
 	event = trace_buffer_lock_reserve(buffer, TRACE_MMIO_MAP,
-					  sizeof(*entry), trace_ctx);
+					  sizeof(*entry), 0, pc);
 	if (!event) {
 		atomic_inc(&dropped_count);
 		return;
@@ -344,7 +342,7 @@
 	entry->map			= *map;
 
 	if (!call_filter_check_discard(call, entry, buffer, event))
-		trace_buffer_unlock_commit(tr, buffer, event, trace_ctx);
+		trace_buffer_unlock_commit(tr, buffer, event, 0, pc);
 }
 
 void mmio_trace_mapping(struct mmiotrace_map *map)
diff --git a/kernel/kernel/trace/trace_output.c b/kernel/kernel/trace/trace_output.c
index bc24ae8..000e9dc 100644
--- a/kernel/kernel/trace/trace_output.c
+++ b/kernel/kernel/trace/trace_output.c
@@ -441,7 +441,6 @@
 {
 	char hardsoft_irq;
 	char need_resched;
-	char need_resched_lazy;
 	char irqs_off;
 	int hardirq;
 	int softirq;
@@ -472,9 +471,6 @@
 		break;
 	}
 
-	need_resched_lazy =
-		(entry->flags & TRACE_FLAG_NEED_RESCHED_LAZY) ? 'L' : '.';
-
 	hardsoft_irq =
 		(nmi && hardirq)     ? 'Z' :
 		nmi                  ? 'z' :
@@ -483,22 +479,11 @@
 		softirq              ? 's' :
 		                       '.' ;
 
-	trace_seq_printf(s, "%c%c%c%c",
-			 irqs_off, need_resched, need_resched_lazy,
-			 hardsoft_irq);
+	trace_seq_printf(s, "%c%c%c",
+			 irqs_off, need_resched, hardsoft_irq);
 
 	if (entry->preempt_count)
 		trace_seq_printf(s, "%x", entry->preempt_count);
-	else
-		trace_seq_putc(s, '.');
-
-	if (entry->preempt_lazy_count)
-		trace_seq_printf(s, "%x", entry->preempt_lazy_count);
-	else
-		trace_seq_putc(s, '.');
-
-	if (entry->migrate_disable)
-		trace_seq_printf(s, "%x", entry->migrate_disable);
 	else
 		trace_seq_putc(s, '.');
 
diff --git a/kernel/kernel/trace/trace_sched_wakeup.c b/kernel/kernel/trace/trace_sched_wakeup.c
index f1c6033..97b10bb 100644
--- a/kernel/kernel/trace/trace_sched_wakeup.c
+++ b/kernel/kernel/trace/trace_sched_wakeup.c
@@ -67,7 +67,7 @@
 static int
 func_prolog_preempt_disable(struct trace_array *tr,
 			    struct trace_array_cpu **data,
-			    unsigned int *trace_ctx)
+			    int *pc)
 {
 	long disabled;
 	int cpu;
@@ -75,7 +75,7 @@
 	if (likely(!wakeup_task))
 		return 0;
 
-	*trace_ctx = tracing_gen_ctx();
+	*pc = preempt_count();
 	preempt_disable_notrace();
 
 	cpu = raw_smp_processor_id();
@@ -116,8 +116,8 @@
 {
 	struct trace_array *tr = wakeup_trace;
 	struct trace_array_cpu *data;
-	unsigned int trace_ctx;
-	int ret = 0;
+	unsigned long flags;
+	int pc, ret = 0;
 
 	if (ftrace_graph_ignore_func(trace))
 		return 0;
@@ -131,10 +131,11 @@
 	if (ftrace_graph_notrace_addr(trace->func))
 		return 1;
 
-	if (!func_prolog_preempt_disable(tr, &data, &trace_ctx))
+	if (!func_prolog_preempt_disable(tr, &data, &pc))
 		return 0;
 
-	ret = __trace_graph_entry(tr, trace, trace_ctx);
+	local_save_flags(flags);
+	ret = __trace_graph_entry(tr, trace, flags, pc);
 	atomic_dec(&data->disabled);
 	preempt_enable_notrace();
 
@@ -145,14 +146,16 @@
 {
 	struct trace_array *tr = wakeup_trace;
 	struct trace_array_cpu *data;
-	unsigned int trace_ctx;
+	unsigned long flags;
+	int pc;
 
 	ftrace_graph_addr_finish(trace);
 
-	if (!func_prolog_preempt_disable(tr, &data, &trace_ctx))
+	if (!func_prolog_preempt_disable(tr, &data, &pc))
 		return;
 
-	__trace_graph_return(tr, trace, trace_ctx);
+	local_save_flags(flags);
+	__trace_graph_return(tr, trace, flags, pc);
 	atomic_dec(&data->disabled);
 
 	preempt_enable_notrace();
@@ -214,13 +217,13 @@
 	struct trace_array *tr = wakeup_trace;
 	struct trace_array_cpu *data;
 	unsigned long flags;
-	unsigned int trace_ctx;
+	int pc;
 
-	if (!func_prolog_preempt_disable(tr, &data, &trace_ctx))
+	if (!func_prolog_preempt_disable(tr, &data, &pc))
 		return;
 
 	local_irq_save(flags);
-	trace_function(tr, ip, parent_ip, trace_ctx);
+	trace_function(tr, ip, parent_ip, flags, pc);
 	local_irq_restore(flags);
 
 	atomic_dec(&data->disabled);
@@ -300,12 +303,12 @@
 static void
 __trace_function(struct trace_array *tr,
 		 unsigned long ip, unsigned long parent_ip,
-		 unsigned int trace_ctx)
+		 unsigned long flags, int pc)
 {
 	if (is_graph(tr))
-		trace_graph_function(tr, ip, parent_ip, trace_ctx);
+		trace_graph_function(tr, ip, parent_ip, flags, pc);
 	else
-		trace_function(tr, ip, parent_ip, trace_ctx);
+		trace_function(tr, ip, parent_ip, flags, pc);
 }
 
 static int wakeup_flag_changed(struct trace_array *tr, u32 mask, int set)
@@ -372,7 +375,7 @@
 tracing_sched_switch_trace(struct trace_array *tr,
 			   struct task_struct *prev,
 			   struct task_struct *next,
-			   unsigned int trace_ctx)
+			   unsigned long flags, int pc)
 {
 	struct trace_event_call *call = &event_context_switch;
 	struct trace_buffer *buffer = tr->array_buffer.buffer;
@@ -380,7 +383,7 @@
 	struct ctx_switch_entry *entry;
 
 	event = trace_buffer_lock_reserve(buffer, TRACE_CTX,
-					  sizeof(*entry), trace_ctx);
+					  sizeof(*entry), flags, pc);
 	if (!event)
 		return;
 	entry	= ring_buffer_event_data(event);
@@ -393,14 +396,14 @@
 	entry->next_cpu	= task_cpu(next);
 
 	if (!call_filter_check_discard(call, entry, buffer, event))
-		trace_buffer_unlock_commit(tr, buffer, event, trace_ctx);
+		trace_buffer_unlock_commit(tr, buffer, event, flags, pc);
 }
 
 static void
 tracing_sched_wakeup_trace(struct trace_array *tr,
 			   struct task_struct *wakee,
 			   struct task_struct *curr,
-			   unsigned int trace_ctx)
+			   unsigned long flags, int pc)
 {
 	struct trace_event_call *call = &event_wakeup;
 	struct ring_buffer_event *event;
@@ -408,7 +411,7 @@
 	struct trace_buffer *buffer = tr->array_buffer.buffer;
 
 	event = trace_buffer_lock_reserve(buffer, TRACE_WAKE,
-					  sizeof(*entry), trace_ctx);
+					  sizeof(*entry), flags, pc);
 	if (!event)
 		return;
 	entry	= ring_buffer_event_data(event);
@@ -421,7 +424,7 @@
 	entry->next_cpu			= task_cpu(wakee);
 
 	if (!call_filter_check_discard(call, entry, buffer, event))
-		trace_buffer_unlock_commit(tr, buffer, event, trace_ctx);
+		trace_buffer_unlock_commit(tr, buffer, event, flags, pc);
 }
 
 static void notrace
@@ -433,7 +436,7 @@
 	unsigned long flags;
 	long disabled;
 	int cpu;
-	unsigned int trace_ctx;
+	int pc;
 
 	tracing_record_cmdline(prev);
 
@@ -452,6 +455,8 @@
 	if (next != wakeup_task)
 		return;
 
+	pc = preempt_count();
+
 	/* disable local data, not wakeup_cpu data */
 	cpu = raw_smp_processor_id();
 	disabled = atomic_inc_return(&per_cpu_ptr(wakeup_trace->array_buffer.data, cpu)->disabled);
@@ -459,8 +464,6 @@
 		goto out;
 
 	local_irq_save(flags);
-	trace_ctx = tracing_gen_ctx_flags(flags);
-
 	arch_spin_lock(&wakeup_lock);
 
 	/* We could race with grabbing wakeup_lock */
@@ -470,9 +473,9 @@
 	/* The task we are waiting for is waking up */
 	data = per_cpu_ptr(wakeup_trace->array_buffer.data, wakeup_cpu);
 
-	__trace_function(wakeup_trace, CALLER_ADDR0, CALLER_ADDR1, trace_ctx);
-	tracing_sched_switch_trace(wakeup_trace, prev, next, trace_ctx);
-	__trace_stack(wakeup_trace, trace_ctx, 0);
+	__trace_function(wakeup_trace, CALLER_ADDR0, CALLER_ADDR1, flags, pc);
+	tracing_sched_switch_trace(wakeup_trace, prev, next, flags, pc);
+	__trace_stack(wakeup_trace, flags, 0, pc);
 
 	T0 = data->preempt_timestamp;
 	T1 = ftrace_now(cpu);
@@ -524,8 +527,9 @@
 {
 	struct trace_array_cpu *data;
 	int cpu = smp_processor_id();
+	unsigned long flags;
 	long disabled;
-	unsigned int trace_ctx;
+	int pc;
 
 	if (likely(!tracer_enabled))
 		return;
@@ -546,11 +550,10 @@
 	    (!dl_task(p) && (p->prio >= wakeup_prio || p->prio >= current->prio)))
 		return;
 
+	pc = preempt_count();
 	disabled = atomic_inc_return(&per_cpu_ptr(wakeup_trace->array_buffer.data, cpu)->disabled);
 	if (unlikely(disabled != 1))
 		goto out;
-
-	trace_ctx = tracing_gen_ctx();
 
 	/* interrupts should be off from try_to_wake_up */
 	arch_spin_lock(&wakeup_lock);
@@ -578,17 +581,19 @@
 
 	wakeup_task = get_task_struct(p);
 
+	local_save_flags(flags);
+
 	data = per_cpu_ptr(wakeup_trace->array_buffer.data, wakeup_cpu);
 	data->preempt_timestamp = ftrace_now(cpu);
-	tracing_sched_wakeup_trace(wakeup_trace, p, current, trace_ctx);
-	__trace_stack(wakeup_trace, trace_ctx, 0);
+	tracing_sched_wakeup_trace(wakeup_trace, p, current, flags, pc);
+	__trace_stack(wakeup_trace, flags, 0, pc);
 
 	/*
 	 * We must be careful in using CALLER_ADDR2. But since wake_up
 	 * is not called by an assembly function  (where as schedule is)
 	 * it should be safe to use it here.
 	 */
-	__trace_function(wakeup_trace, CALLER_ADDR1, CALLER_ADDR2, trace_ctx);
+	__trace_function(wakeup_trace, CALLER_ADDR1, CALLER_ADDR2, flags, pc);
 
 out_locked:
 	arch_spin_unlock(&wakeup_lock);
diff --git a/kernel/kernel/trace/trace_syscalls.c b/kernel/kernel/trace/trace_syscalls.c
index 8bfcd3b..d85a2f0 100644
--- a/kernel/kernel/trace/trace_syscalls.c
+++ b/kernel/kernel/trace/trace_syscalls.c
@@ -298,8 +298,9 @@
 	struct syscall_metadata *sys_data;
 	struct ring_buffer_event *event;
 	struct trace_buffer *buffer;
-	unsigned int trace_ctx;
+	unsigned long irq_flags;
 	unsigned long args[6];
+	int pc;
 	int syscall_nr;
 	int size;
 
@@ -321,11 +322,12 @@
 
 	size = sizeof(*entry) + sizeof(unsigned long) * sys_data->nb_args;
 
-	trace_ctx = tracing_gen_ctx();
+	local_save_flags(irq_flags);
+	pc = preempt_count();
 
 	buffer = tr->array_buffer.buffer;
 	event = trace_buffer_lock_reserve(buffer,
-			sys_data->enter_event->event.type, size, trace_ctx);
+			sys_data->enter_event->event.type, size, irq_flags, pc);
 	if (!event)
 		return;
 
@@ -335,7 +337,7 @@
 	memcpy(entry->args, args, sizeof(unsigned long) * sys_data->nb_args);
 
 	event_trigger_unlock_commit(trace_file, buffer, event, entry,
-				    trace_ctx);
+				    irq_flags, pc);
 }
 
 static void ftrace_syscall_exit(void *data, struct pt_regs *regs, long ret)
@@ -346,7 +348,8 @@
 	struct syscall_metadata *sys_data;
 	struct ring_buffer_event *event;
 	struct trace_buffer *buffer;
-	unsigned int trace_ctx;
+	unsigned long irq_flags;
+	int pc;
 	int syscall_nr;
 
 	syscall_nr = trace_get_syscall_nr(current, regs);
@@ -365,12 +368,13 @@
 	if (!sys_data)
 		return;
 
-	trace_ctx = tracing_gen_ctx();
+	local_save_flags(irq_flags);
+	pc = preempt_count();
 
 	buffer = tr->array_buffer.buffer;
 	event = trace_buffer_lock_reserve(buffer,
 			sys_data->exit_event->event.type, sizeof(*entry),
-			trace_ctx);
+			irq_flags, pc);
 	if (!event)
 		return;
 
@@ -379,7 +383,7 @@
 	entry->ret = syscall_get_return_value(current, regs);
 
 	event_trigger_unlock_commit(trace_file, buffer, event, entry,
-				    trace_ctx);
+				    irq_flags, pc);
 }
 
 static int reg_event_syscall_enter(struct trace_event_file *file,
diff --git a/kernel/kernel/trace/trace_uprobe.c b/kernel/kernel/trace/trace_uprobe.c
index 7be4f5d..9900d4e 100644
--- a/kernel/kernel/trace/trace_uprobe.c
+++ b/kernel/kernel/trace/trace_uprobe.c
@@ -965,7 +965,7 @@
 	esize = SIZEOF_TRACE_ENTRY(is_ret_probe(tu));
 	size = esize + tu->tp.size + dsize;
 	event = trace_event_buffer_lock_reserve(&buffer, trace_file,
-						call->event.type, size, 0);
+						call->event.type, size, 0, 0);
 	if (!event)
 		return;
 
@@ -981,7 +981,7 @@
 
 	memcpy(data, ucb->buf, tu->tp.size + dsize);
 
-	event_trigger_unlock_commit(trace_file, buffer, event, entry, 0);
+	event_trigger_unlock_commit(trace_file, buffer, event, entry, 0, 0);
 }
 
 /* uprobe handler */
diff --git a/kernel/kernel/watchdog.c b/kernel/kernel/watchdog.c
index a94b71e..b47f393 100644
--- a/kernel/kernel/watchdog.c
+++ b/kernel/kernel/watchdog.c
@@ -29,6 +29,10 @@
 
 #include <trace/hooks/softlockup.h>
 
+#if IS_ENABLED(CONFIG_ROCKCHIP_MINIDUMP)
+#include <soc/rockchip/rk_minidump.h>
+#endif
+
 static DEFINE_MUTEX(watchdog_mutex);
 
 #if defined(CONFIG_HARDLOCKUP_DETECTOR) || defined(CONFIG_HAVE_NMI_WATCHDOG)
@@ -412,12 +416,13 @@
 		if (per_cpu(hard_watchdog_warn, next_cpu) == true)
 			return;
 
+		atomic_notifier_call_chain(&hardlock_notifier_list, next_cpu, NULL);
+
 		if (hardlockup_panic)
 			panic("Watchdog detected hard LOCKUP on cpu %u", next_cpu);
 		else
 			WARN(1, "Watchdog detected hard LOCKUP on cpu %u", next_cpu);
 
-		atomic_notifier_call_chain(&hardlock_notifier_list, 0, NULL);
 		per_cpu(hard_watchdog_warn, next_cpu) = true;
 	} else {
 		per_cpu(hard_watchdog_warn, next_cpu) = false;
@@ -541,6 +546,9 @@
 
 		trace_android_vh_watchdog_timer_softlockup(duration, regs, !!softlockup_panic);
 		add_taint(TAINT_SOFTLOCKUP, LOCKDEP_STILL_OK);
+#if IS_ENABLED(CONFIG_ROCKCHIP_MINIDUMP)
+		rk_minidump_update_cpu_regs(regs);
+#endif
 		if (softlockup_panic)
 			panic("softlockup: hung tasks");
 	}
diff --git a/kernel/kernel/workqueue.c b/kernel/kernel/workqueue.c
index 499ac12..cb057e3 100644
--- a/kernel/kernel/workqueue.c
+++ b/kernel/kernel/workqueue.c
@@ -4954,10 +4954,6 @@
 		pool->flags |= POOL_DISASSOCIATED;
 
 		raw_spin_unlock_irq(&pool->lock);
-
-		for_each_pool_worker(worker, pool)
-			WARN_ON_ONCE(set_cpus_allowed_ptr(worker->task, cpu_active_mask) < 0);
-
 		mutex_unlock(&wq_pool_attach_mutex);
 
 		/*
diff --git a/kernel/lib/Kconfig.debug b/kernel/lib/Kconfig.debug
index 10bcea7..8608332 100644
--- a/kernel/lib/Kconfig.debug
+++ b/kernel/lib/Kconfig.debug
@@ -1386,7 +1386,7 @@
 
 config DEBUG_LOCKING_API_SELFTESTS
 	bool "Locking API boot-time self-tests"
-	depends on DEBUG_KERNEL && !PREEMPT_RT
+	depends on DEBUG_KERNEL
 	help
 	  Say Y here if you want the kernel to run a short self-test during
 	  bootup. The self-test checks whether common types of locking bugs
diff --git a/kernel/lib/bug.c b/kernel/lib/bug.c
index 8291e05..f6d9aac 100644
--- a/kernel/lib/bug.c
+++ b/kernel/lib/bug.c
@@ -204,7 +204,6 @@
 	else
 		pr_crit("Kernel BUG at %pB [verbose debug info unavailable]\n",
 			(void *)bugaddr);
-	pr_flush(1000, true);
 
 	trace_android_rvh_report_bug(file, line, bugaddr);
 
diff --git a/kernel/lib/cpumask.c b/kernel/lib/cpumask.c
index c3c76b8..fb22fb2 100644
--- a/kernel/lib/cpumask.c
+++ b/kernel/lib/cpumask.c
@@ -261,21 +261,3 @@
 	return next;
 }
 EXPORT_SYMBOL(cpumask_any_and_distribute);
-
-int cpumask_any_distribute(const struct cpumask *srcp)
-{
-	int next, prev;
-
-	/* NOTE: our first selection will skip 0. */
-	prev = __this_cpu_read(distribute_cpu_mask_prev);
-
-	next = cpumask_next(prev, srcp);
-	if (next >= nr_cpu_ids)
-		next = cpumask_first(srcp);
-
-	if (next < nr_cpu_ids)
-		__this_cpu_write(distribute_cpu_mask_prev, next);
-
-	return next;
-}
-EXPORT_SYMBOL(cpumask_any_distribute);
diff --git a/kernel/lib/dump_stack.c b/kernel/lib/dump_stack.c
index 586e3f2..b9acd9c 100644
--- a/kernel/lib/dump_stack.c
+++ b/kernel/lib/dump_stack.c
@@ -12,7 +12,6 @@
 #include <linux/atomic.h>
 #include <linux/kexec.h>
 #include <linux/utsname.h>
-#include <linux/stop_machine.h>
 
 static char dump_stack_arch_desc_str[128];
 
@@ -58,7 +57,6 @@
 		       log_lvl, dump_stack_arch_desc_str);
 
 	print_worker_info(log_lvl, current);
-	print_stop_info(log_lvl, current);
 }
 
 /**
diff --git a/kernel/lib/irq_poll.c b/kernel/lib/irq_poll.c
index 7557bf7..2f17b48 100644
--- a/kernel/lib/irq_poll.c
+++ b/kernel/lib/irq_poll.c
@@ -37,7 +37,6 @@
 	list_add_tail(&iop->list, this_cpu_ptr(&blk_cpu_iopoll));
 	raise_softirq_irqoff(IRQ_POLL_SOFTIRQ);
 	local_irq_restore(flags);
-	preempt_check_resched_rt();
 }
 EXPORT_SYMBOL(irq_poll_sched);
 
@@ -73,7 +72,6 @@
 	local_irq_save(flags);
 	__irq_poll_complete(iop);
 	local_irq_restore(flags);
-	preempt_check_resched_rt();
 }
 EXPORT_SYMBOL(irq_poll_complete);
 
@@ -98,7 +96,6 @@
 		}
 
 		local_irq_enable();
-		preempt_check_resched_rt();
 
 		/* Even though interrupts have been re-enabled, this
 		 * access is safe because interrupts can only add new
@@ -136,7 +133,6 @@
 		__raise_softirq_irqoff(IRQ_POLL_SOFTIRQ);
 
 	local_irq_enable();
-	preempt_check_resched_rt();
 }
 
 /**
@@ -200,7 +196,6 @@
 			 this_cpu_ptr(&blk_cpu_iopoll));
 	__raise_softirq_irqoff(IRQ_POLL_SOFTIRQ);
 	local_irq_enable();
-	preempt_check_resched_rt();
 
 	return 0;
 }
diff --git a/kernel/lib/locking-selftest.c b/kernel/lib/locking-selftest.c
index 98c376b..76c52b0 100644
--- a/kernel/lib/locking-selftest.c
+++ b/kernel/lib/locking-selftest.c
@@ -787,8 +787,6 @@
 #include "locking-selftest-spin-hardirq.h"
 GENERATE_PERMUTATIONS_2_EVENTS(irqsafe1_hard_spin)
 
-#ifndef CONFIG_PREEMPT_RT
-
 #include "locking-selftest-rlock-hardirq.h"
 GENERATE_PERMUTATIONS_2_EVENTS(irqsafe1_hard_rlock)
 
@@ -804,12 +802,9 @@
 #include "locking-selftest-wlock-softirq.h"
 GENERATE_PERMUTATIONS_2_EVENTS(irqsafe1_soft_wlock)
 
-#endif
-
 #undef E1
 #undef E2
 
-#ifndef CONFIG_PREEMPT_RT
 /*
  * Enabling hardirqs with a softirq-safe lock held:
  */
@@ -842,8 +837,6 @@
 #undef E1
 #undef E2
 
-#endif
-
 /*
  * Enabling irqs with an irq-safe lock held:
  */
@@ -867,8 +860,6 @@
 #include "locking-selftest-spin-hardirq.h"
 GENERATE_PERMUTATIONS_2_EVENTS(irqsafe2B_hard_spin)
 
-#ifndef CONFIG_PREEMPT_RT
-
 #include "locking-selftest-rlock-hardirq.h"
 GENERATE_PERMUTATIONS_2_EVENTS(irqsafe2B_hard_rlock)
 
@@ -883,8 +874,6 @@
 
 #include "locking-selftest-wlock-softirq.h"
 GENERATE_PERMUTATIONS_2_EVENTS(irqsafe2B_soft_wlock)
-
-#endif
 
 #undef E1
 #undef E2
@@ -917,8 +906,6 @@
 #include "locking-selftest-spin-hardirq.h"
 GENERATE_PERMUTATIONS_3_EVENTS(irqsafe3_hard_spin)
 
-#ifndef CONFIG_PREEMPT_RT
-
 #include "locking-selftest-rlock-hardirq.h"
 GENERATE_PERMUTATIONS_3_EVENTS(irqsafe3_hard_rlock)
 
@@ -933,8 +920,6 @@
 
 #include "locking-selftest-wlock-softirq.h"
 GENERATE_PERMUTATIONS_3_EVENTS(irqsafe3_soft_wlock)
-
-#endif
 
 #undef E1
 #undef E2
@@ -969,8 +954,6 @@
 #include "locking-selftest-spin-hardirq.h"
 GENERATE_PERMUTATIONS_3_EVENTS(irqsafe4_hard_spin)
 
-#ifndef CONFIG_PREEMPT_RT
-
 #include "locking-selftest-rlock-hardirq.h"
 GENERATE_PERMUTATIONS_3_EVENTS(irqsafe4_hard_rlock)
 
@@ -986,13 +969,9 @@
 #include "locking-selftest-wlock-softirq.h"
 GENERATE_PERMUTATIONS_3_EVENTS(irqsafe4_soft_wlock)
 
-#endif
-
 #undef E1
 #undef E2
 #undef E3
-
-#ifndef CONFIG_PREEMPT_RT
 
 /*
  * read-lock / write-lock irq inversion.
@@ -1183,11 +1162,6 @@
 #undef E1
 #undef E2
 #undef E3
-
-#endif
-
-#ifndef CONFIG_PREEMPT_RT
-
 /*
  * read-lock / write-lock recursion that is actually safe.
  */
@@ -1233,8 +1207,6 @@
 #undef E1
 #undef E2
 #undef E3
-
-#endif
 
 /*
  * read-lock / write-lock recursion that is unsafe.
@@ -2484,7 +2456,6 @@
 
 	printk("  --------------------------------------------------------------------------\n");
 
-#ifndef CONFIG_PREEMPT_RT
 	/*
 	 * irq-context testcases:
 	 */
@@ -2499,28 +2470,6 @@
 	DO_TESTCASE_6x2x2RW("irq read-recursion #2", irq_read_recursion2);
 	DO_TESTCASE_6x2x2RW("irq read-recursion #3", irq_read_recursion3);
 
-#else
-	/* On -rt, we only do hardirq context test for raw spinlock */
-	DO_TESTCASE_1B("hard-irqs-on + irq-safe-A", irqsafe1_hard_spin, 12);
-	DO_TESTCASE_1B("hard-irqs-on + irq-safe-A", irqsafe1_hard_spin, 21);
-
-	DO_TESTCASE_1B("hard-safe-A + irqs-on", irqsafe2B_hard_spin, 12);
-	DO_TESTCASE_1B("hard-safe-A + irqs-on", irqsafe2B_hard_spin, 21);
-
-	DO_TESTCASE_1B("hard-safe-A + unsafe-B #1", irqsafe3_hard_spin, 123);
-	DO_TESTCASE_1B("hard-safe-A + unsafe-B #1", irqsafe3_hard_spin, 132);
-	DO_TESTCASE_1B("hard-safe-A + unsafe-B #1", irqsafe3_hard_spin, 213);
-	DO_TESTCASE_1B("hard-safe-A + unsafe-B #1", irqsafe3_hard_spin, 231);
-	DO_TESTCASE_1B("hard-safe-A + unsafe-B #1", irqsafe3_hard_spin, 312);
-	DO_TESTCASE_1B("hard-safe-A + unsafe-B #1", irqsafe3_hard_spin, 321);
-
-	DO_TESTCASE_1B("hard-safe-A + unsafe-B #2", irqsafe4_hard_spin, 123);
-	DO_TESTCASE_1B("hard-safe-A + unsafe-B #2", irqsafe4_hard_spin, 132);
-	DO_TESTCASE_1B("hard-safe-A + unsafe-B #2", irqsafe4_hard_spin, 213);
-	DO_TESTCASE_1B("hard-safe-A + unsafe-B #2", irqsafe4_hard_spin, 231);
-	DO_TESTCASE_1B("hard-safe-A + unsafe-B #2", irqsafe4_hard_spin, 312);
-	DO_TESTCASE_1B("hard-safe-A + unsafe-B #2", irqsafe4_hard_spin, 321);
-#endif
 	ww_tests();
 
 	force_read_lock_recursive = 0;
diff --git a/kernel/lib/nmi_backtrace.c b/kernel/lib/nmi_backtrace.c
index b09a490..8abe187 100644
--- a/kernel/lib/nmi_backtrace.c
+++ b/kernel/lib/nmi_backtrace.c
@@ -75,6 +75,12 @@
 		touch_softlockup_watchdog();
 	}
 
+	/*
+	 * Force flush any remote buffers that might be stuck in IRQ context
+	 * and therefore could not run their irq_work.
+	 */
+	printk_safe_flush();
+
 	clear_bit_unlock(0, &backtrace_flag);
 	put_cpu();
 }
diff --git a/kernel/lib/scatterlist.c b/kernel/lib/scatterlist.c
index 907f590..a597789 100644
--- a/kernel/lib/scatterlist.c
+++ b/kernel/lib/scatterlist.c
@@ -892,7 +892,7 @@
 			flush_kernel_dcache_page(miter->page);
 
 		if (miter->__flags & SG_MITER_ATOMIC) {
-			WARN_ON_ONCE(!pagefault_disabled());
+			WARN_ON_ONCE(preemptible());
 			kunmap_atomic(miter->addr);
 		} else
 			kunmap(miter->page);
diff --git a/kernel/lib/smp_processor_id.c b/kernel/lib/smp_processor_id.c
index 0c0c42b..2916606 100644
--- a/kernel/lib/smp_processor_id.c
+++ b/kernel/lib/smp_processor_id.c
@@ -26,11 +26,6 @@
 	if (current->nr_cpus_allowed == 1)
 		goto out;
 
-#ifdef CONFIG_SMP
-	if (current->migration_disabled)
-		goto out;
-#endif
-
 	/*
 	 * It is valid to assume CPU-locality during early bootup:
 	 */
diff --git a/kernel/lib/test_lockup.c b/kernel/lib/test_lockup.c
index 07b8b18..bdbe00e 100644
--- a/kernel/lib/test_lockup.c
+++ b/kernel/lib/test_lockup.c
@@ -485,21 +485,6 @@
 		return -EINVAL;
 
 #ifdef CONFIG_DEBUG_SPINLOCK
-#ifdef CONFIG_PREEMPT_RT
-	if (test_magic(lock_spinlock_ptr,
-		       offsetof(spinlock_t, lock.wait_lock.magic),
-		       SPINLOCK_MAGIC) ||
-	    test_magic(lock_rwlock_ptr,
-		       offsetof(rwlock_t, rtmutex.wait_lock.magic),
-		       SPINLOCK_MAGIC) ||
-	    test_magic(lock_mutex_ptr,
-		       offsetof(struct mutex, lock.wait_lock.magic),
-		       SPINLOCK_MAGIC) ||
-	    test_magic(lock_rwsem_ptr,
-		       offsetof(struct rw_semaphore, rtmutex.wait_lock.magic),
-		       SPINLOCK_MAGIC))
-		return -EINVAL;
-#else
 	if (test_magic(lock_spinlock_ptr,
 		       offsetof(spinlock_t, rlock.magic),
 		       SPINLOCK_MAGIC) ||
@@ -513,7 +498,6 @@
 		       offsetof(struct rw_semaphore, wait_lock.magic),
 		       SPINLOCK_MAGIC))
 		return -EINVAL;
-#endif
 #endif
 
 	if ((wait_state != TASK_RUNNING ||
diff --git a/kernel/mm/Kconfig b/kernel/mm/Kconfig
index ec2f7ef..b1799bb 100644
--- a/kernel/mm/Kconfig
+++ b/kernel/mm/Kconfig
@@ -387,7 +387,7 @@
 
 config TRANSPARENT_HUGEPAGE
 	bool "Transparent Hugepage Support"
-	depends on HAVE_ARCH_TRANSPARENT_HUGEPAGE && !PREEMPT_RT
+	depends on HAVE_ARCH_TRANSPARENT_HUGEPAGE
 	select COMPACTION
 	select XARRAY_MULTI
 	help
@@ -913,8 +913,5 @@
         bool
 
 source "mm/damon/Kconfig"
-
-config KMAP_LOCAL
-	bool
 
 endmenu
diff --git a/kernel/mm/highmem.c b/kernel/mm/highmem.c
index 72b9a2d..1352a27 100644
--- a/kernel/mm/highmem.c
+++ b/kernel/mm/highmem.c
@@ -31,6 +31,10 @@
 #include <asm/tlbflush.h>
 #include <linux/vmalloc.h>
 
+#if defined(CONFIG_HIGHMEM) || defined(CONFIG_X86_32)
+DEFINE_PER_CPU(int, __kmap_atomic_idx);
+#endif
+
 /*
  * Virtual_count is not a pure "count".
  *  0 means that it is not mapped, and has not been mapped
@@ -104,7 +108,9 @@
 atomic_long_t _totalhigh_pages __read_mostly;
 EXPORT_SYMBOL(_totalhigh_pages);
 
-unsigned int __nr_free_highpages (void)
+EXPORT_PER_CPU_SYMBOL(__kmap_atomic_idx);
+
+unsigned int nr_free_highpages (void)
 {
 	struct zone *zone;
 	unsigned int pages = 0;
@@ -141,7 +147,7 @@
 		do { spin_unlock(&kmap_lock); (void)(flags); } while (0)
 #endif
 
-struct page *__kmap_to_page(void *vaddr)
+struct page *kmap_to_page(void *vaddr)
 {
 	unsigned long addr = (unsigned long)vaddr;
 
@@ -152,7 +158,7 @@
 
 	return virt_to_page(addr);
 }
-EXPORT_SYMBOL(__kmap_to_page);
+EXPORT_SYMBOL(kmap_to_page);
 
 static void flush_all_zero_pkmaps(void)
 {
@@ -194,7 +200,10 @@
 		flush_tlb_kernel_range(PKMAP_ADDR(0), PKMAP_ADDR(LAST_PKMAP));
 }
 
-void __kmap_flush_unused(void)
+/**
+ * kmap_flush_unused - flush all unused kmap mappings in order to remove stray mappings
+ */
+void kmap_flush_unused(void)
 {
 	lock_kmap();
 	flush_all_zero_pkmaps();
@@ -358,250 +367,9 @@
 	if (need_wakeup)
 		wake_up(pkmap_map_wait);
 }
+
 EXPORT_SYMBOL(kunmap_high);
-#endif /* CONFIG_HIGHMEM */
-
-#ifdef CONFIG_KMAP_LOCAL
-
-#include <asm/kmap_size.h>
-
-/*
- * With DEBUG_HIGHMEM the stack depth is doubled and every second
- * slot is unused which acts as a guard page
- */
-#ifdef CONFIG_DEBUG_HIGHMEM
-# define KM_INCR	2
-#else
-# define KM_INCR	1
-#endif
-
-static inline int kmap_local_idx_push(void)
-{
-	WARN_ON_ONCE(in_irq() && !irqs_disabled());
-	current->kmap_ctrl.idx += KM_INCR;
-	BUG_ON(current->kmap_ctrl.idx >= KM_MAX_IDX);
-	return current->kmap_ctrl.idx - 1;
-}
-
-static inline int kmap_local_idx(void)
-{
-	return current->kmap_ctrl.idx - 1;
-}
-
-static inline void kmap_local_idx_pop(void)
-{
-	current->kmap_ctrl.idx -= KM_INCR;
-	BUG_ON(current->kmap_ctrl.idx < 0);
-}
-
-#ifndef arch_kmap_local_post_map
-# define arch_kmap_local_post_map(vaddr, pteval)	do { } while (0)
-#endif
-
-#ifndef arch_kmap_local_pre_unmap
-# define arch_kmap_local_pre_unmap(vaddr)		do { } while (0)
-#endif
-
-#ifndef arch_kmap_local_post_unmap
-# define arch_kmap_local_post_unmap(vaddr)		do { } while (0)
-#endif
-
-#ifndef arch_kmap_local_map_idx
-#define arch_kmap_local_map_idx(idx, pfn)	kmap_local_calc_idx(idx)
-#endif
-
-#ifndef arch_kmap_local_unmap_idx
-#define arch_kmap_local_unmap_idx(idx, vaddr)	kmap_local_calc_idx(idx)
-#endif
-
-#ifndef arch_kmap_local_high_get
-static inline void *arch_kmap_local_high_get(struct page *page)
-{
-	return NULL;
-}
-#endif
-
-/* Unmap a local mapping which was obtained by kmap_high_get() */
-static inline bool kmap_high_unmap_local(unsigned long vaddr)
-{
-#ifdef ARCH_NEEDS_KMAP_HIGH_GET
-	if (vaddr >= PKMAP_ADDR(0) && vaddr < PKMAP_ADDR(LAST_PKMAP)) {
-		kunmap_high(pte_page(pkmap_page_table[PKMAP_NR(vaddr)]));
-		return true;
-	}
-#endif
-	return false;
-}
-
-static inline int kmap_local_calc_idx(int idx)
-{
-	return idx + KM_MAX_IDX * smp_processor_id();
-}
-
-static pte_t *__kmap_pte;
-
-static pte_t *kmap_get_pte(void)
-{
-	if (!__kmap_pte)
-		__kmap_pte = virt_to_kpte(__fix_to_virt(FIX_KMAP_BEGIN));
-	return __kmap_pte;
-}
-
-void *__kmap_local_pfn_prot(unsigned long pfn, pgprot_t prot)
-{
-	pte_t pteval, *kmap_pte = kmap_get_pte();
-	unsigned long vaddr;
-	int idx;
-
-	/*
-	 * Disable migration so resulting virtual address is stable
-	 * accross preemption.
-	 */
-	migrate_disable();
-	preempt_disable();
-	idx = arch_kmap_local_map_idx(kmap_local_idx_push(), pfn);
-	vaddr = __fix_to_virt(FIX_KMAP_BEGIN + idx);
-	BUG_ON(!pte_none(*(kmap_pte - idx)));
-	pteval = pfn_pte(pfn, prot);
-	set_pte_at(&init_mm, vaddr, kmap_pte - idx, pteval);
-	arch_kmap_local_post_map(vaddr, pteval);
-	current->kmap_ctrl.pteval[kmap_local_idx()] = pteval;
-	preempt_enable();
-
-	return (void *)vaddr;
-}
-EXPORT_SYMBOL_GPL(__kmap_local_pfn_prot);
-
-void *__kmap_local_page_prot(struct page *page, pgprot_t prot)
-{
-	void *kmap;
-
-	if (!PageHighMem(page))
-		return page_address(page);
-
-	/* Try kmap_high_get() if architecture has it enabled */
-	kmap = arch_kmap_local_high_get(page);
-	if (kmap)
-		return kmap;
-
-	return __kmap_local_pfn_prot(page_to_pfn(page), prot);
-}
-EXPORT_SYMBOL(__kmap_local_page_prot);
-
-void kunmap_local_indexed(void *vaddr)
-{
-	unsigned long addr = (unsigned long) vaddr & PAGE_MASK;
-	pte_t *kmap_pte = kmap_get_pte();
-	int idx;
-
-	if (addr < __fix_to_virt(FIX_KMAP_END) ||
-	    addr > __fix_to_virt(FIX_KMAP_BEGIN)) {
-		/*
-		 * Handle mappings which were obtained by kmap_high_get()
-		 * first as the virtual address of such mappings is below
-		 * PAGE_OFFSET. Warn for all other addresses which are in
-		 * the user space part of the virtual address space.
-		 */
-		if (!kmap_high_unmap_local(addr))
-			WARN_ON_ONCE(addr < PAGE_OFFSET);
-		return;
-	}
-
-	preempt_disable();
-	idx = arch_kmap_local_unmap_idx(kmap_local_idx(), addr);
-	WARN_ON_ONCE(addr != __fix_to_virt(FIX_KMAP_BEGIN + idx));
-
-	arch_kmap_local_pre_unmap(addr);
-	pte_clear(&init_mm, addr, kmap_pte - idx);
-	arch_kmap_local_post_unmap(addr);
-	current->kmap_ctrl.pteval[kmap_local_idx()] = __pte(0);
-	kmap_local_idx_pop();
-	preempt_enable();
-	migrate_enable();
-}
-EXPORT_SYMBOL(kunmap_local_indexed);
-
-/*
- * Invoked before switch_to(). This is safe even when during or after
- * clearing the maps an interrupt which needs a kmap_local happens because
- * the task::kmap_ctrl.idx is not modified by the unmapping code so a
- * nested kmap_local will use the next unused index and restore the index
- * on unmap. The already cleared kmaps of the outgoing task are irrelevant
- * because the interrupt context does not know about them. The same applies
- * when scheduling back in for an interrupt which happens before the
- * restore is complete.
- */
-void __kmap_local_sched_out(void)
-{
-	struct task_struct *tsk = current;
-	pte_t *kmap_pte = kmap_get_pte();
-	int i;
-
-	/* Clear kmaps */
-	for (i = 0; i < tsk->kmap_ctrl.idx; i++) {
-		pte_t pteval = tsk->kmap_ctrl.pteval[i];
-		unsigned long addr;
-		int idx;
-
-		/* With debug all even slots are unmapped and act as guard */
-		if (IS_ENABLED(CONFIG_DEBUG_HIGHMEM) && !(i & 0x01)) {
-			WARN_ON_ONCE(!pte_none(pteval));
-			continue;
-		}
-		if (WARN_ON_ONCE(pte_none(pteval)))
-			continue;
-
-		/*
-		 * This is a horrible hack for XTENSA to calculate the
-		 * coloured PTE index. Uses the PFN encoded into the pteval
-		 * and the map index calculation because the actual mapped
-		 * virtual address is not stored in task::kmap_ctrl.
-		 * For any sane architecture this is optimized out.
-		 */
-		idx = arch_kmap_local_map_idx(i, pte_pfn(pteval));
-
-		addr = __fix_to_virt(FIX_KMAP_BEGIN + idx);
-		arch_kmap_local_pre_unmap(addr);
-		pte_clear(&init_mm, addr, kmap_pte - idx);
-		arch_kmap_local_post_unmap(addr);
-	}
-}
-
-void __kmap_local_sched_in(void)
-{
-	struct task_struct *tsk = current;
-	pte_t *kmap_pte = kmap_get_pte();
-	int i;
-
-	/* Restore kmaps */
-	for (i = 0; i < tsk->kmap_ctrl.idx; i++) {
-		pte_t pteval = tsk->kmap_ctrl.pteval[i];
-		unsigned long addr;
-		int idx;
-
-		/* With debug all even slots are unmapped and act as guard */
-		if (IS_ENABLED(CONFIG_DEBUG_HIGHMEM) && !(i & 0x01)) {
-			WARN_ON_ONCE(!pte_none(pteval));
-			continue;
-		}
-		if (WARN_ON_ONCE(pte_none(pteval)))
-			continue;
-
-		/* See comment in __kmap_local_sched_out() */
-		idx = arch_kmap_local_map_idx(i, pte_pfn(pteval));
-		addr = __fix_to_virt(FIX_KMAP_BEGIN + idx);
-		set_pte_at(&init_mm, addr, kmap_pte - idx, pteval);
-		arch_kmap_local_post_map(addr, pteval);
-	}
-}
-
-void kmap_local_fork(struct task_struct *tsk)
-{
-	if (WARN_ON_ONCE(tsk->kmap_ctrl.idx))
-		memset(&tsk->kmap_ctrl, 0, sizeof(tsk->kmap_ctrl));
-}
-
-#endif
+#endif	/* CONFIG_HIGHMEM */
 
 #if defined(HASHED_PAGE_VIRTUAL)
 
diff --git a/kernel/mm/memcontrol.c b/kernel/mm/memcontrol.c
index 7db1e5a..0501a27 100644
--- a/kernel/mm/memcontrol.c
+++ b/kernel/mm/memcontrol.c
@@ -63,7 +63,6 @@
 #include <net/sock.h>
 #include <net/ip.h>
 #include "slab.h"
-#include <linux/local_lock.h>
 
 #include <linux/uaccess.h>
 
@@ -94,13 +93,6 @@
 #ifdef CONFIG_CGROUP_WRITEBACK
 static DECLARE_WAIT_QUEUE_HEAD(memcg_cgwb_frn_waitq);
 #endif
-
-struct event_lock {
-	local_lock_t l;
-};
-static DEFINE_PER_CPU(struct event_lock, event_lock) = {
-	.l      = INIT_LOCAL_LOCK(l),
-};
 
 /* Whether legacy memory+swap accounting is active */
 static bool do_memsw_account(void)
@@ -825,7 +817,6 @@
 	pn = container_of(lruvec, struct mem_cgroup_per_node, lruvec);
 	memcg = pn->memcg;
 
-	preempt_disable_rt();
 	/* Update memcg */
 	__mod_memcg_state(memcg, idx, val);
 
@@ -845,7 +836,6 @@
 		x = 0;
 	}
 	__this_cpu_write(pn->lruvec_stat_cpu->count[idx], x);
-	preempt_enable_rt();
 }
 
 /**
@@ -2243,7 +2233,6 @@
 EXPORT_SYMBOL(unlock_page_memcg);
 
 struct memcg_stock_pcp {
-	local_lock_t lock;
 	struct mem_cgroup *cached; /* this never be root cgroup */
 	unsigned int nr_pages;
 
@@ -2295,7 +2284,7 @@
 	if (nr_pages > MEMCG_CHARGE_BATCH)
 		return ret;
 
-	local_lock_irqsave(&memcg_stock.lock, flags);
+	local_irq_save(flags);
 
 	stock = this_cpu_ptr(&memcg_stock);
 	if (memcg == stock->cached && stock->nr_pages >= nr_pages) {
@@ -2303,7 +2292,7 @@
 		ret = true;
 	}
 
-	local_unlock_irqrestore(&memcg_stock.lock, flags);
+	local_irq_restore(flags);
 
 	return ret;
 }
@@ -2338,14 +2327,14 @@
 	 * The only protection from memory hotplug vs. drain_stock races is
 	 * that we always operate on local CPU stock here with IRQ disabled
 	 */
-	local_lock_irqsave(&memcg_stock.lock, flags);
+	local_irq_save(flags);
 
 	stock = this_cpu_ptr(&memcg_stock);
 	drain_obj_stock(stock);
 	drain_stock(stock);
 	clear_bit(FLUSHING_CACHED_CHARGE, &stock->flags);
 
-	local_unlock_irqrestore(&memcg_stock.lock, flags);
+	local_irq_restore(flags);
 }
 
 /*
@@ -2357,7 +2346,7 @@
 	struct memcg_stock_pcp *stock;
 	unsigned long flags;
 
-	local_lock_irqsave(&memcg_stock.lock, flags);
+	local_irq_save(flags);
 
 	stock = this_cpu_ptr(&memcg_stock);
 	if (stock->cached != memcg) { /* reset if necessary */
@@ -2370,7 +2359,7 @@
 	if (stock->nr_pages > MEMCG_CHARGE_BATCH)
 		drain_stock(stock);
 
-	local_unlock_irqrestore(&memcg_stock.lock, flags);
+	local_irq_restore(flags);
 }
 
 /*
@@ -2390,7 +2379,7 @@
 	 * as well as workers from this path always operate on the local
 	 * per-cpu data. CPU up doesn't touch memcg_stock at all.
 	 */
-	curcpu = get_cpu_light();
+	curcpu = get_cpu();
 	for_each_online_cpu(cpu) {
 		struct memcg_stock_pcp *stock = &per_cpu(memcg_stock, cpu);
 		struct mem_cgroup *memcg;
@@ -2413,7 +2402,7 @@
 				schedule_work_on(cpu, &stock->work);
 		}
 	}
-	put_cpu_light();
+	put_cpu();
 	mutex_unlock(&percpu_charge_mutex);
 }
 
@@ -3178,7 +3167,7 @@
 	unsigned long flags;
 	bool ret = false;
 
-	local_lock_irqsave(&memcg_stock.lock, flags);
+	local_irq_save(flags);
 
 	stock = this_cpu_ptr(&memcg_stock);
 	if (objcg == stock->cached_objcg && stock->nr_bytes >= nr_bytes) {
@@ -3186,7 +3175,7 @@
 		ret = true;
 	}
 
-	local_unlock_irqrestore(&memcg_stock.lock, flags);
+	local_irq_restore(flags);
 
 	return ret;
 }
@@ -3253,7 +3242,7 @@
 	struct memcg_stock_pcp *stock;
 	unsigned long flags;
 
-	local_lock_irqsave(&memcg_stock.lock, flags);
+	local_irq_save(flags);
 
 	stock = this_cpu_ptr(&memcg_stock);
 	if (stock->cached_objcg != objcg) { /* reset if necessary */
@@ -3267,7 +3256,7 @@
 	if (stock->nr_bytes > PAGE_SIZE)
 		drain_obj_stock(stock);
 
-	local_unlock_irqrestore(&memcg_stock.lock, flags);
+	local_irq_restore(flags);
 }
 
 int obj_cgroup_charge(struct obj_cgroup *objcg, gfp_t gfp, size_t size)
@@ -5789,12 +5778,12 @@
 
 	ret = 0;
 
-	local_lock_irq(&event_lock.l);
+	local_irq_disable();
 	mem_cgroup_charge_statistics(to, page, nr_pages);
 	memcg_check_events(to, page);
 	mem_cgroup_charge_statistics(from, page, -nr_pages);
 	memcg_check_events(from, page);
-	local_unlock_irq(&event_lock.l);
+	local_irq_enable();
 out_unlock:
 	unlock_page(page);
 out:
@@ -6862,10 +6851,10 @@
 	css_get(&memcg->css);
 	commit_charge(page, memcg);
 
-	local_lock_irq(&event_lock.l);
+	local_irq_disable();
 	mem_cgroup_charge_statistics(memcg, page, nr_pages);
 	memcg_check_events(memcg, page);
-	local_unlock_irq(&event_lock.l);
+	local_irq_enable();
 
 	/*
 	 * Cgroup1's unified memory+swap counter has been charged with the
@@ -6921,11 +6910,11 @@
 		memcg_oom_recover(ug->memcg);
 	}
 
-	local_lock_irqsave(&event_lock.l, flags);
+	local_irq_save(flags);
 	__count_memcg_events(ug->memcg, PGPGOUT, ug->pgpgout);
 	__this_cpu_add(ug->memcg->vmstats_percpu->nr_page_events, ug->nr_pages);
 	memcg_check_events(ug->memcg, ug->dummy_page);
-	local_unlock_irqrestore(&event_lock.l, flags);
+	local_irq_restore(flags);
 
 	/* drop reference from uncharge_page */
 	css_put(&ug->memcg->css);
@@ -7073,10 +7062,10 @@
 	css_get(&memcg->css);
 	commit_charge(newpage, memcg);
 
-	local_lock_irqsave(&event_lock.l, flags);
+	local_irq_save(flags);
 	mem_cgroup_charge_statistics(memcg, newpage, nr_pages);
 	memcg_check_events(memcg, newpage);
-	local_unlock_irqrestore(&event_lock.l, flags);
+	local_irq_restore(flags);
 }
 
 DEFINE_STATIC_KEY_FALSE(memcg_sockets_enabled_key);
@@ -7196,13 +7185,9 @@
 	cpuhp_setup_state_nocalls(CPUHP_MM_MEMCQ_DEAD, "mm/memctrl:dead", NULL,
 				  memcg_hotplug_cpu_dead);
 
-	for_each_possible_cpu(cpu) {
-		struct memcg_stock_pcp *stock;
-
-		stock = per_cpu_ptr(&memcg_stock, cpu);
-		INIT_WORK(&stock->work, drain_local_stock);
-		local_lock_init(&stock->lock);
-	}
+	for_each_possible_cpu(cpu)
+		INIT_WORK(&per_cpu_ptr(&memcg_stock, cpu)->work,
+			  drain_local_stock);
 
 	for_each_node(node) {
 		struct mem_cgroup_tree_per_node *rtpn;
@@ -7251,7 +7236,6 @@
 	struct mem_cgroup *memcg, *swap_memcg;
 	unsigned int nr_entries;
 	unsigned short oldid;
-	unsigned long flags;
 
 	VM_BUG_ON_PAGE(PageLRU(page), page);
 	VM_BUG_ON_PAGE(page_count(page), page);
@@ -7300,13 +7284,9 @@
 	 * important here to have the interrupts disabled because it is the
 	 * only synchronisation we have for updating the per-CPU variables.
 	 */
-	local_lock_irqsave(&event_lock.l, flags);
-#ifndef CONFIG_PREEMPT_RT
 	VM_BUG_ON(!irqs_disabled());
-#endif
 	mem_cgroup_charge_statistics(memcg, page, -nr_entries);
 	memcg_check_events(memcg, page);
-	local_unlock_irqrestore(&event_lock.l, flags);
 
 	css_put(&memcg->css);
 }
diff --git a/kernel/mm/page_alloc.c b/kernel/mm/page_alloc.c
index 4a15674..3868586 100644
--- a/kernel/mm/page_alloc.c
+++ b/kernel/mm/page_alloc.c
@@ -61,7 +61,6 @@
 #include <linux/hugetlb.h>
 #include <linux/sched/rt.h>
 #include <linux/sched/mm.h>
-#include <linux/local_lock.h>
 #include <linux/page_owner.h>
 #include <linux/page_pinner.h>
 #include <linux/kthread.h>
@@ -385,13 +384,6 @@
 EXPORT_SYMBOL(nr_node_ids);
 EXPORT_SYMBOL(nr_online_nodes);
 #endif
-
-struct pa_lock {
-	local_lock_t l;
-};
-static DEFINE_PER_CPU(struct pa_lock, pa_lock) = {
-	.l	= INIT_LOCAL_LOCK(l),
-};
 
 int page_group_by_mobility_disabled __read_mostly;
 
@@ -1430,7 +1422,7 @@
 }
 
 /*
- * Frees a number of pages which have been collected from the pcp lists.
+ * Frees a number of pages from the PCP lists
  * Assumes all pages on list are in same zone, and of same order.
  * count is the number of pages to free.
  *
@@ -1440,56 +1432,15 @@
  * And clear the zone's pages_scanned counter, to hold off the "all pages are
  * pinned" detection logic.
  */
-static void free_pcppages_bulk(struct zone *zone, struct list_head *head,
-			       bool zone_retry)
-{
-	bool isolated_pageblocks;
-	struct page *page, *tmp;
-	unsigned long flags;
-
-	spin_lock_irqsave(&zone->lock, flags);
-	isolated_pageblocks = has_isolate_pageblock(zone);
-
-	/*
-	 * Use safe version since after __free_one_page(),
-	 * page->lru.next will not point to original list.
-	 */
-	list_for_each_entry_safe(page, tmp, head, lru) {
-		int mt = get_pcppage_migratetype(page);
-
-		if (page_zone(page) != zone) {
-			/*
-			 * free_unref_page_list() sorts pages by zone. If we end
-			 * up with pages from a different NUMA nodes belonging
-			 * to the same ZONE index then we need to redo with the
-			 * correct ZONE pointer. Skip the page for now, redo it
-			 * on the next iteration.
-			 */
-			WARN_ON_ONCE(zone_retry == false);
-			if (zone_retry)
-				continue;
-		}
-
-		/* MIGRATE_ISOLATE page should not go to pcplists */
-		VM_BUG_ON_PAGE(is_migrate_isolate(mt), page);
-		/* Pageblock could have been isolated meanwhile */
-		if (unlikely(isolated_pageblocks))
-			mt = get_pageblock_migratetype(page);
-
-		list_del(&page->lru);
-		__free_one_page(page, page_to_pfn(page), zone, 0, mt, FPI_NONE);
-		trace_mm_page_pcpu_drain(page, 0, mt);
-	}
-	spin_unlock_irqrestore(&zone->lock, flags);
-}
-
-static void isolate_pcp_pages(int count, struct per_cpu_pages *pcp,
-			      struct list_head *dst)
+static void free_pcppages_bulk(struct zone *zone, int count,
+					struct per_cpu_pages *pcp)
 {
 	int migratetype = 0;
 	int batch_free = 0;
 	int prefetch_nr = 0;
-	struct page *page;
+	bool isolated_pageblocks;
+	struct page *page, *tmp;
+	LIST_HEAD(head);
 
 	/*
 	 * Ensure proper count is passed which otherwise would stuck in the
@@ -1526,7 +1477,7 @@
 			if (bulkfree_pcp_prepare(page))
 				continue;
 
-			list_add_tail(&page->lru, dst);
+			list_add_tail(&page->lru, &head);
 
 			/*
 			 * We are going to put the page back to the global
@@ -1541,6 +1492,26 @@
 				prefetch_buddy(page);
 		} while (--count && --batch_free && !list_empty(list));
 	}
+
+	spin_lock(&zone->lock);
+	isolated_pageblocks = has_isolate_pageblock(zone);
+
+	/*
+	 * Use safe version since after __free_one_page(),
+	 * page->lru.next will not point to original list.
+	 */
+	list_for_each_entry_safe(page, tmp, &head, lru) {
+		int mt = get_pcppage_migratetype(page);
+		/* MIGRATE_ISOLATE page should not go to pcplists */
+		VM_BUG_ON_PAGE(is_migrate_isolate(mt), page);
+		/* Pageblock could have been isolated meanwhile */
+		if (unlikely(isolated_pageblocks))
+			mt = get_pageblock_migratetype(page);
+
+		__free_one_page(page, page_to_pfn(page), zone, 0, mt, FPI_NONE);
+		trace_mm_page_pcpu_drain(page, 0, mt);
+	}
+	spin_unlock(&zone->lock);
 }
 
 static void free_one_page(struct zone *zone,
@@ -1648,11 +1619,11 @@
 		return;
 
 	migratetype = get_pfnblock_migratetype(page, pfn);
-	local_lock_irqsave(&pa_lock.l, flags);
+	local_irq_save(flags);
 	__count_vm_events(PGFREE, 1 << order);
 	free_one_page(page_zone(page), page, pfn, order, migratetype,
 		      fpi_flags);
-	local_unlock_irqrestore(&pa_lock.l, flags);
+	local_irq_restore(flags);
 }
 
 void __free_pages_core(struct page *page, unsigned int order)
@@ -3105,18 +3076,13 @@
 {
 	unsigned long flags;
 	int to_drain, batch;
-	LIST_HEAD(dst);
 
-	local_lock_irqsave(&pa_lock.l, flags);
+	local_irq_save(flags);
 	batch = READ_ONCE(pcp->batch);
 	to_drain = min(pcp->count, batch);
 	if (to_drain > 0)
-		isolate_pcp_pages(to_drain, pcp, &dst);
-
-	local_unlock_irqrestore(&pa_lock.l, flags);
-
-	if (to_drain > 0)
-		free_pcppages_bulk(zone, &dst, false);
+		free_pcppages_bulk(zone, to_drain, pcp);
+	local_irq_restore(flags);
 }
 #endif
 
@@ -3132,21 +3098,14 @@
 	unsigned long flags;
 	struct per_cpu_pageset *pset;
 	struct per_cpu_pages *pcp;
-	LIST_HEAD(dst);
-	int count;
 
-	local_lock_irqsave(&pa_lock.l, flags);
+	local_irq_save(flags);
 	pset = per_cpu_ptr(zone->pageset, cpu);
 
 	pcp = &pset->pcp;
-	count = pcp->count;
-	if (count)
-		isolate_pcp_pages(count, pcp, &dst);
-
-	local_unlock_irqrestore(&pa_lock.l, flags);
-
-	if (count)
-		free_pcppages_bulk(zone, &dst, false);
+	if (pcp->count)
+		free_pcppages_bulk(zone, pcp->count, pcp);
+	local_irq_restore(flags);
 }
 
 /*
@@ -3194,9 +3153,9 @@
 	 * cpu which is allright but we also have to make sure to not move to
 	 * a different one.
 	 */
-	migrate_disable();
+	preempt_disable();
 	drain_local_pages(drain->zone);
-	migrate_enable();
+	preempt_enable();
 }
 
 /*
@@ -3345,8 +3304,7 @@
 	return true;
 }
 
-static void free_unref_page_commit(struct page *page, unsigned long pfn,
-				   struct list_head *dst)
+static void free_unref_page_commit(struct page *page, unsigned long pfn)
 {
 	struct zone *zone = page_zone(page);
 	struct per_cpu_pages *pcp;
@@ -3380,8 +3338,7 @@
 	pcp->count++;
 	if (pcp->count >= pcp->high) {
 		unsigned long batch = READ_ONCE(pcp->batch);
-
-		isolate_pcp_pages(batch, pcp, dst);
+		free_pcppages_bulk(zone, batch, pcp);
 	}
 }
 
@@ -3392,17 +3349,13 @@
 {
 	unsigned long flags;
 	unsigned long pfn = page_to_pfn(page);
-	struct zone *zone = page_zone(page);
-	LIST_HEAD(dst);
 
 	if (!free_unref_page_prepare(page, pfn))
 		return;
 
-	local_lock_irqsave(&pa_lock.l, flags);
-	free_unref_page_commit(page, pfn, &dst);
-	local_unlock_irqrestore(&pa_lock.l, flags);
-	if (!list_empty(&dst))
-		free_pcppages_bulk(zone, &dst, false);
+	local_irq_save(flags);
+	free_unref_page_commit(page, pfn);
+	local_irq_restore(flags);
 }
 
 /*
@@ -3413,11 +3366,6 @@
 	struct page *page, *next;
 	unsigned long flags, pfn;
 	int batch_count = 0;
-	struct list_head dsts[__MAX_NR_ZONES];
-	int i;
-
-	for (i = 0; i < __MAX_NR_ZONES; i++)
-		INIT_LIST_HEAD(&dsts[i]);
 
 	/* Prepare pages for freeing */
 	list_for_each_entry_safe(page, next, list, lru) {
@@ -3427,42 +3375,25 @@
 		set_page_private(page, pfn);
 	}
 
-	local_lock_irqsave(&pa_lock.l, flags);
+	local_irq_save(flags);
 	list_for_each_entry_safe(page, next, list, lru) {
 		unsigned long pfn = page_private(page);
-		enum zone_type type;
 
 		set_page_private(page, 0);
 		trace_mm_page_free_batched(page);
-		type = page_zonenum(page);
-		free_unref_page_commit(page, pfn, &dsts[type]);
+		free_unref_page_commit(page, pfn);
 
 		/*
 		 * Guard against excessive IRQ disabled times when we get
 		 * a large list of pages to free.
 		 */
 		if (++batch_count == SWAP_CLUSTER_MAX) {
-			local_unlock_irqrestore(&pa_lock.l, flags);
+			local_irq_restore(flags);
 			batch_count = 0;
-			local_lock_irqsave(&pa_lock.l, flags);
+			local_irq_save(flags);
 		}
 	}
-	local_unlock_irqrestore(&pa_lock.l, flags);
-
-	for (i = 0; i < __MAX_NR_ZONES; ) {
-		struct page *page;
-		struct zone *zone;
-
-		if (list_empty(&dsts[i])) {
-			i++;
-			continue;
-		}
-
-		page = list_first_entry(&dsts[i], struct page, lru);
-		zone = page_zone(page);
-
-		free_pcppages_bulk(zone, &dsts[i], true);
-	}
+	local_irq_restore(flags);
 }
 
 /*
@@ -3629,7 +3560,7 @@
 	struct page *page;
 	unsigned long flags;
 
-	local_lock_irqsave(&pa_lock.l, flags);
+	local_irq_save(flags);
 	pcp = &this_cpu_ptr(zone->pageset)->pcp;
 	page = __rmqueue_pcplist(zone,  migratetype, alloc_flags, pcp,
 				 gfp_flags);
@@ -3637,7 +3568,7 @@
 		__count_zid_vm_events(PGALLOC, page_zonenum(page), 1);
 		zone_statistics(preferred_zone, zone);
 	}
-	local_unlock_irqrestore(&pa_lock.l, flags);
+	local_irq_restore(flags);
 	return page;
 }
 
@@ -3664,8 +3595,7 @@
 	 * allocate greater than order-1 page units with __GFP_NOFAIL.
 	 */
 	WARN_ON_ONCE((gfp_flags & __GFP_NOFAIL) && (order > 1));
-	local_lock_irqsave(&pa_lock.l, flags);
-	spin_lock(&zone->lock);
+	spin_lock_irqsave(&zone->lock, flags);
 
 	do {
 		page = NULL;
@@ -3700,7 +3630,7 @@
 	zone_statistics(preferred_zone, zone);
 	trace_android_vh_rmqueue(preferred_zone, zone, order,
 			gfp_flags, alloc_flags, migratetype);
-	local_unlock_irqrestore(&pa_lock.l, flags);
+	local_irq_restore(flags);
 
 out:
 	/* Separate test+clear to avoid unnecessary atomics */
@@ -3713,7 +3643,7 @@
 	return page;
 
 failed:
-	local_unlock_irqrestore(&pa_lock.l, flags);
+	local_irq_restore(flags);
 	return NULL;
 }
 
@@ -9141,7 +9071,7 @@
 	struct per_cpu_pageset *pset;
 
 	/* avoid races with drain_pages()  */
-	local_lock_irqsave(&pa_lock.l, flags);
+	local_irq_save(flags);
 	if (zone->pageset != &boot_pageset) {
 		for_each_online_cpu(cpu) {
 			pset = per_cpu_ptr(zone->pageset, cpu);
@@ -9150,7 +9080,7 @@
 		free_percpu(zone->pageset);
 		zone->pageset = &boot_pageset;
 	}
-	local_unlock_irqrestore(&pa_lock.l, flags);
+	local_irq_restore(flags);
 }
 
 #ifdef CONFIG_MEMORY_HOTREMOVE
diff --git a/kernel/mm/shmem.c b/kernel/mm/shmem.c
index 682a3e9..768c918 100644
--- a/kernel/mm/shmem.c
+++ b/kernel/mm/shmem.c
@@ -285,10 +285,10 @@
 	ino_t ino;
 
 	if (!(sb->s_flags & SB_KERNMOUNT)) {
-		raw_spin_lock(&sbinfo->stat_lock);
+		spin_lock(&sbinfo->stat_lock);
 		if (sbinfo->max_inodes) {
 			if (!sbinfo->free_inodes) {
-				raw_spin_unlock(&sbinfo->stat_lock);
+				spin_unlock(&sbinfo->stat_lock);
 				return -ENOSPC;
 			}
 			sbinfo->free_inodes--;
@@ -311,7 +311,7 @@
 			}
 			*inop = ino;
 		}
-		raw_spin_unlock(&sbinfo->stat_lock);
+		spin_unlock(&sbinfo->stat_lock);
 	} else if (inop) {
 		/*
 		 * __shmem_file_setup, one of our callers, is lock-free: it
@@ -326,14 +326,13 @@
 		 * to worry about things like glibc compatibility.
 		 */
 		ino_t *next_ino;
-
 		next_ino = per_cpu_ptr(sbinfo->ino_batch, get_cpu());
 		ino = *next_ino;
 		if (unlikely(ino % SHMEM_INO_BATCH == 0)) {
-			raw_spin_lock(&sbinfo->stat_lock);
+			spin_lock(&sbinfo->stat_lock);
 			ino = sbinfo->next_ino;
 			sbinfo->next_ino += SHMEM_INO_BATCH;
-			raw_spin_unlock(&sbinfo->stat_lock);
+			spin_unlock(&sbinfo->stat_lock);
 			if (unlikely(is_zero_ino(ino)))
 				ino++;
 		}
@@ -349,9 +348,9 @@
 {
 	struct shmem_sb_info *sbinfo = SHMEM_SB(sb);
 	if (sbinfo->max_inodes) {
-		raw_spin_lock(&sbinfo->stat_lock);
+		spin_lock(&sbinfo->stat_lock);
 		sbinfo->free_inodes++;
-		raw_spin_unlock(&sbinfo->stat_lock);
+		spin_unlock(&sbinfo->stat_lock);
 	}
 }
 
@@ -1493,10 +1492,10 @@
 {
 	struct mempolicy *mpol = NULL;
 	if (sbinfo->mpol) {
-		raw_spin_lock(&sbinfo->stat_lock);	/* prevent replace/use races */
+		spin_lock(&sbinfo->stat_lock);	/* prevent replace/use races */
 		mpol = sbinfo->mpol;
 		mpol_get(mpol);
-		raw_spin_unlock(&sbinfo->stat_lock);
+		spin_unlock(&sbinfo->stat_lock);
 	}
 	return mpol;
 }
@@ -3563,10 +3562,9 @@
 	struct shmem_options *ctx = fc->fs_private;
 	struct shmem_sb_info *sbinfo = SHMEM_SB(fc->root->d_sb);
 	unsigned long inodes;
-	struct mempolicy *mpol = NULL;
 	const char *err;
 
-	raw_spin_lock(&sbinfo->stat_lock);
+	spin_lock(&sbinfo->stat_lock);
 	inodes = sbinfo->max_inodes - sbinfo->free_inodes;
 	if ((ctx->seen & SHMEM_SEEN_BLOCKS) && ctx->blocks) {
 		if (!sbinfo->max_blocks) {
@@ -3611,15 +3609,14 @@
 	 * Preserve previous mempolicy unless mpol remount option was specified.
 	 */
 	if (ctx->mpol) {
-		mpol = sbinfo->mpol;
+		mpol_put(sbinfo->mpol);
 		sbinfo->mpol = ctx->mpol;	/* transfers initial ref */
 		ctx->mpol = NULL;
 	}
-	raw_spin_unlock(&sbinfo->stat_lock);
-	mpol_put(mpol);
+	spin_unlock(&sbinfo->stat_lock);
 	return 0;
 out:
-	raw_spin_unlock(&sbinfo->stat_lock);
+	spin_unlock(&sbinfo->stat_lock);
 	return invalfc(fc, "%s", err);
 }
 
@@ -3736,7 +3733,7 @@
 	sbinfo->mpol = ctx->mpol;
 	ctx->mpol = NULL;
 
-	raw_spin_lock_init(&sbinfo->stat_lock);
+	spin_lock_init(&sbinfo->stat_lock);
 	if (percpu_counter_init(&sbinfo->used_blocks, 0, GFP_KERNEL))
 		goto failed;
 	spin_lock_init(&sbinfo->shrinklist_lock);
diff --git a/kernel/mm/slab.c b/kernel/mm/slab.c
index 290fafc..aa4ef18 100644
--- a/kernel/mm/slab.c
+++ b/kernel/mm/slab.c
@@ -234,7 +234,7 @@
 	parent->shared = NULL;
 	parent->alien = NULL;
 	parent->colour_next = 0;
-	raw_spin_lock_init(&parent->list_lock);
+	spin_lock_init(&parent->list_lock);
 	parent->free_objects = 0;
 	parent->free_touched = 0;
 }
@@ -559,9 +559,9 @@
 	page_node = page_to_nid(page);
 	n = get_node(cachep, page_node);
 
-	raw_spin_lock(&n->list_lock);
+	spin_lock(&n->list_lock);
 	free_block(cachep, &objp, 1, page_node, &list);
-	raw_spin_unlock(&n->list_lock);
+	spin_unlock(&n->list_lock);
 
 	slabs_destroy(cachep, &list);
 }
@@ -699,7 +699,7 @@
 	struct kmem_cache_node *n = get_node(cachep, node);
 
 	if (ac->avail) {
-		raw_spin_lock(&n->list_lock);
+		spin_lock(&n->list_lock);
 		/*
 		 * Stuff objects into the remote nodes shared array first.
 		 * That way we could avoid the overhead of putting the objects
@@ -710,7 +710,7 @@
 
 		free_block(cachep, ac->entry, ac->avail, node, list);
 		ac->avail = 0;
-		raw_spin_unlock(&n->list_lock);
+		spin_unlock(&n->list_lock);
 	}
 }
 
@@ -783,9 +783,9 @@
 		slabs_destroy(cachep, &list);
 	} else {
 		n = get_node(cachep, page_node);
-		raw_spin_lock(&n->list_lock);
+		spin_lock(&n->list_lock);
 		free_block(cachep, &objp, 1, page_node, &list);
-		raw_spin_unlock(&n->list_lock);
+		spin_unlock(&n->list_lock);
 		slabs_destroy(cachep, &list);
 	}
 	return 1;
@@ -826,10 +826,10 @@
 	 */
 	n = get_node(cachep, node);
 	if (n) {
-		raw_spin_lock_irq(&n->list_lock);
+		spin_lock_irq(&n->list_lock);
 		n->free_limit = (1 + nr_cpus_node(node)) * cachep->batchcount +
 				cachep->num;
-		raw_spin_unlock_irq(&n->list_lock);
+		spin_unlock_irq(&n->list_lock);
 
 		return 0;
 	}
@@ -908,7 +908,7 @@
 		goto fail;
 
 	n = get_node(cachep, node);
-	raw_spin_lock_irq(&n->list_lock);
+	spin_lock_irq(&n->list_lock);
 	if (n->shared && force_change) {
 		free_block(cachep, n->shared->entry,
 				n->shared->avail, node, &list);
@@ -926,7 +926,7 @@
 		new_alien = NULL;
 	}
 
-	raw_spin_unlock_irq(&n->list_lock);
+	spin_unlock_irq(&n->list_lock);
 	slabs_destroy(cachep, &list);
 
 	/*
@@ -965,7 +965,7 @@
 		if (!n)
 			continue;
 
-		raw_spin_lock_irq(&n->list_lock);
+		spin_lock_irq(&n->list_lock);
 
 		/* Free limit for this kmem_cache_node */
 		n->free_limit -= cachep->batchcount;
@@ -976,7 +976,7 @@
 		nc->avail = 0;
 
 		if (!cpumask_empty(mask)) {
-			raw_spin_unlock_irq(&n->list_lock);
+			spin_unlock_irq(&n->list_lock);
 			goto free_slab;
 		}
 
@@ -990,7 +990,7 @@
 		alien = n->alien;
 		n->alien = NULL;
 
-		raw_spin_unlock_irq(&n->list_lock);
+		spin_unlock_irq(&n->list_lock);
 
 		kfree(shared);
 		if (alien) {
@@ -1174,7 +1174,7 @@
 	/*
 	 * Do not assume that spinlocks can be initialized via memcpy:
 	 */
-	raw_spin_lock_init(&ptr->list_lock);
+	spin_lock_init(&ptr->list_lock);
 
 	MAKE_ALL_LISTS(cachep, ptr, nodeid);
 	cachep->node[nodeid] = ptr;
@@ -1345,11 +1345,11 @@
 	for_each_kmem_cache_node(cachep, node, n) {
 		unsigned long total_slabs, free_slabs, free_objs;
 
-		raw_spin_lock_irqsave(&n->list_lock, flags);
+		spin_lock_irqsave(&n->list_lock, flags);
 		total_slabs = n->total_slabs;
 		free_slabs = n->free_slabs;
 		free_objs = n->free_objects;
-		raw_spin_unlock_irqrestore(&n->list_lock, flags);
+		spin_unlock_irqrestore(&n->list_lock, flags);
 
 		pr_warn("  node %d: slabs: %ld/%ld, objs: %ld/%ld\n",
 			node, total_slabs - free_slabs, total_slabs,
@@ -2106,7 +2106,7 @@
 {
 #ifdef CONFIG_SMP
 	check_irq_off();
-	assert_raw_spin_locked(&get_node(cachep, numa_mem_id())->list_lock);
+	assert_spin_locked(&get_node(cachep, numa_mem_id())->list_lock);
 #endif
 }
 
@@ -2114,7 +2114,7 @@
 {
 #ifdef CONFIG_SMP
 	check_irq_off();
-	assert_raw_spin_locked(&get_node(cachep, node)->list_lock);
+	assert_spin_locked(&get_node(cachep, node)->list_lock);
 #endif
 }
 
@@ -2154,9 +2154,9 @@
 	check_irq_off();
 	ac = cpu_cache_get(cachep);
 	n = get_node(cachep, node);
-	raw_spin_lock(&n->list_lock);
+	spin_lock(&n->list_lock);
 	free_block(cachep, ac->entry, ac->avail, node, &list);
-	raw_spin_unlock(&n->list_lock);
+	spin_unlock(&n->list_lock);
 	ac->avail = 0;
 	slabs_destroy(cachep, &list);
 }
@@ -2174,9 +2174,9 @@
 			drain_alien_cache(cachep, n->alien);
 
 	for_each_kmem_cache_node(cachep, node, n) {
-		raw_spin_lock_irq(&n->list_lock);
+		spin_lock_irq(&n->list_lock);
 		drain_array_locked(cachep, n->shared, node, true, &list);
-		raw_spin_unlock_irq(&n->list_lock);
+		spin_unlock_irq(&n->list_lock);
 
 		slabs_destroy(cachep, &list);
 	}
@@ -2198,10 +2198,10 @@
 	nr_freed = 0;
 	while (nr_freed < tofree && !list_empty(&n->slabs_free)) {
 
-		raw_spin_lock_irq(&n->list_lock);
+		spin_lock_irq(&n->list_lock);
 		p = n->slabs_free.prev;
 		if (p == &n->slabs_free) {
-			raw_spin_unlock_irq(&n->list_lock);
+			spin_unlock_irq(&n->list_lock);
 			goto out;
 		}
 
@@ -2214,7 +2214,7 @@
 		 * to the cache.
 		 */
 		n->free_objects -= cache->num;
-		raw_spin_unlock_irq(&n->list_lock);
+		spin_unlock_irq(&n->list_lock);
 		slab_destroy(cache, page);
 		nr_freed++;
 	}
@@ -2650,7 +2650,7 @@
 	INIT_LIST_HEAD(&page->slab_list);
 	n = get_node(cachep, page_to_nid(page));
 
-	raw_spin_lock(&n->list_lock);
+	spin_lock(&n->list_lock);
 	n->total_slabs++;
 	if (!page->active) {
 		list_add_tail(&page->slab_list, &n->slabs_free);
@@ -2660,7 +2660,7 @@
 
 	STATS_INC_GROWN(cachep);
 	n->free_objects += cachep->num - page->active;
-	raw_spin_unlock(&n->list_lock);
+	spin_unlock(&n->list_lock);
 
 	fixup_objfreelist_debug(cachep, &list);
 }
@@ -2826,7 +2826,7 @@
 {
 	struct page *page;
 
-	assert_raw_spin_locked(&n->list_lock);
+	assert_spin_locked(&n->list_lock);
 	page = list_first_entry_or_null(&n->slabs_partial, struct page,
 					slab_list);
 	if (!page) {
@@ -2853,10 +2853,10 @@
 	if (!gfp_pfmemalloc_allowed(flags))
 		return NULL;
 
-	raw_spin_lock(&n->list_lock);
+	spin_lock(&n->list_lock);
 	page = get_first_slab(n, true);
 	if (!page) {
-		raw_spin_unlock(&n->list_lock);
+		spin_unlock(&n->list_lock);
 		return NULL;
 	}
 
@@ -2865,7 +2865,7 @@
 
 	fixup_slab_list(cachep, n, page, &list);
 
-	raw_spin_unlock(&n->list_lock);
+	spin_unlock(&n->list_lock);
 	fixup_objfreelist_debug(cachep, &list);
 
 	return obj;
@@ -2924,7 +2924,7 @@
 	if (!n->free_objects && (!shared || !shared->avail))
 		goto direct_grow;
 
-	raw_spin_lock(&n->list_lock);
+	spin_lock(&n->list_lock);
 	shared = READ_ONCE(n->shared);
 
 	/* See if we can refill from the shared array */
@@ -2948,7 +2948,7 @@
 must_grow:
 	n->free_objects -= ac->avail;
 alloc_done:
-	raw_spin_unlock(&n->list_lock);
+	spin_unlock(&n->list_lock);
 	fixup_objfreelist_debug(cachep, &list);
 
 direct_grow:
@@ -3172,7 +3172,7 @@
 	BUG_ON(!n);
 
 	check_irq_off();
-	raw_spin_lock(&n->list_lock);
+	spin_lock(&n->list_lock);
 	page = get_first_slab(n, false);
 	if (!page)
 		goto must_grow;
@@ -3190,12 +3190,12 @@
 
 	fixup_slab_list(cachep, n, page, &list);
 
-	raw_spin_unlock(&n->list_lock);
+	spin_unlock(&n->list_lock);
 	fixup_objfreelist_debug(cachep, &list);
 	return obj;
 
 must_grow:
-	raw_spin_unlock(&n->list_lock);
+	spin_unlock(&n->list_lock);
 	page = cache_grow_begin(cachep, gfp_exact_node(flags), nodeid);
 	if (page) {
 		/* This slab isn't counted yet so don't update free_objects */
@@ -3381,7 +3381,7 @@
 
 	check_irq_off();
 	n = get_node(cachep, node);
-	raw_spin_lock(&n->list_lock);
+	spin_lock(&n->list_lock);
 	if (n->shared) {
 		struct array_cache *shared_array = n->shared;
 		int max = shared_array->limit - shared_array->avail;
@@ -3410,7 +3410,7 @@
 		STATS_SET_FREEABLE(cachep, i);
 	}
 #endif
-	raw_spin_unlock(&n->list_lock);
+	spin_unlock(&n->list_lock);
 	ac->avail -= batchcount;
 	memmove(ac->entry, &(ac->entry[batchcount]), sizeof(void *)*ac->avail);
 	slabs_destroy(cachep, &list);
@@ -3854,9 +3854,9 @@
 
 		node = cpu_to_mem(cpu);
 		n = get_node(cachep, node);
-		raw_spin_lock_irq(&n->list_lock);
+		spin_lock_irq(&n->list_lock);
 		free_block(cachep, ac->entry, ac->avail, node, &list);
-		raw_spin_unlock_irq(&n->list_lock);
+		spin_unlock_irq(&n->list_lock);
 		slabs_destroy(cachep, &list);
 	}
 	free_percpu(prev);
@@ -3951,9 +3951,9 @@
 		return;
 	}
 
-	raw_spin_lock_irq(&n->list_lock);
+	spin_lock_irq(&n->list_lock);
 	drain_array_locked(cachep, ac, node, false, &list);
-	raw_spin_unlock_irq(&n->list_lock);
+	spin_unlock_irq(&n->list_lock);
 
 	slabs_destroy(cachep, &list);
 }
@@ -4037,7 +4037,7 @@
 
 	for_each_kmem_cache_node(cachep, node, n) {
 		check_irq_on();
-		raw_spin_lock_irq(&n->list_lock);
+		spin_lock_irq(&n->list_lock);
 
 		total_slabs += n->total_slabs;
 		free_slabs += n->free_slabs;
@@ -4046,7 +4046,7 @@
 		if (n->shared)
 			shared_avail += n->shared->avail;
 
-		raw_spin_unlock_irq(&n->list_lock);
+		spin_unlock_irq(&n->list_lock);
 	}
 	num_objs = total_slabs * cachep->num;
 	active_slabs = total_slabs - free_slabs;
diff --git a/kernel/mm/slab.h b/kernel/mm/slab.h
index 9d92331..4738d19 100644
--- a/kernel/mm/slab.h
+++ b/kernel/mm/slab.h
@@ -595,7 +595,7 @@
  * The slab lists for all objects.
  */
 struct kmem_cache_node {
-	raw_spinlock_t list_lock;
+	spinlock_t list_lock;
 
 #ifdef CONFIG_SLAB
 	struct list_head slabs_partial;	/* partial list first, better asm code */
diff --git a/kernel/mm/slub.c b/kernel/mm/slub.c
index 3be07ee..3acf083 100644
--- a/kernel/mm/slub.c
+++ b/kernel/mm/slub.c
@@ -431,7 +431,7 @@
 
 #ifdef CONFIG_SLUB_DEBUG
 static unsigned long object_map[BITS_TO_LONGS(MAX_OBJS_PER_PAGE)];
-static DEFINE_RAW_SPINLOCK(object_map_lock);
+static DEFINE_SPINLOCK(object_map_lock);
 
 static void __fill_map(unsigned long *obj_map, struct kmem_cache *s,
 		       struct page *page)
@@ -456,7 +456,7 @@
 {
 	VM_BUG_ON(!irqs_disabled());
 
-	raw_spin_lock(&object_map_lock);
+	spin_lock(&object_map_lock);
 
 	__fill_map(object_map, s, page);
 
@@ -466,7 +466,7 @@
 static void put_map(unsigned long *map) __releases(&object_map_lock)
 {
 	VM_BUG_ON(map != object_map);
-	raw_spin_unlock(&object_map_lock);
+	spin_unlock(&object_map_lock);
 }
 
 static inline unsigned int size_from_object(struct kmem_cache *s)
@@ -1255,7 +1255,7 @@
 	unsigned long flags;
 	int ret = 0;
 
-	raw_spin_lock_irqsave(&n->list_lock, flags);
+	spin_lock_irqsave(&n->list_lock, flags);
 	slab_lock(page);
 
 	if (s->flags & SLAB_CONSISTENCY_CHECKS) {
@@ -1290,7 +1290,7 @@
 			 bulk_cnt, cnt);
 
 	slab_unlock(page);
-	raw_spin_unlock_irqrestore(&n->list_lock, flags);
+	spin_unlock_irqrestore(&n->list_lock, flags);
 	if (!ret)
 		slab_fix(s, "Object at 0x%p not freed", object);
 	return ret;
@@ -1537,12 +1537,6 @@
 	return false;
 }
 #endif /* CONFIG_SLUB_DEBUG */
-
-struct slub_free_list {
-	raw_spinlock_t		lock;
-	struct list_head	list;
-};
-static DEFINE_PER_CPU(struct slub_free_list, slub_free_list);
 
 /*
  * Hooks for other subsystems that check memory allocations. In a typical
@@ -1804,18 +1798,10 @@
 	void *start, *p, *next;
 	int idx;
 	bool shuffle;
-	bool enableirqs = false;
 
 	flags &= gfp_allowed_mask;
 
 	if (gfpflags_allow_blocking(flags))
-		enableirqs = true;
-
-#ifdef CONFIG_PREEMPT_RT
-	if (system_state > SYSTEM_BOOTING && system_state < SYSTEM_SUSPEND)
-		enableirqs = true;
-#endif
-	if (enableirqs)
 		local_irq_enable();
 
 	flags |= s->allocflags;
@@ -1874,7 +1860,7 @@
 	page->frozen = 1;
 
 out:
-	if (enableirqs)
+	if (gfpflags_allow_blocking(flags))
 		local_irq_disable();
 	if (!page)
 		return NULL;
@@ -1917,16 +1903,6 @@
 	__free_pages(page, order);
 }
 
-static void free_delayed(struct list_head *h)
-{
-	while (!list_empty(h)) {
-		struct page *page = list_first_entry(h, struct page, lru);
-
-		list_del(&page->lru);
-		__free_slab(page->slab_cache, page);
-	}
-}
-
 static void rcu_free_slab(struct rcu_head *h)
 {
 	struct page *page = container_of(h, struct page, rcu_head);
@@ -1938,12 +1914,6 @@
 {
 	if (unlikely(s->flags & SLAB_TYPESAFE_BY_RCU)) {
 		call_rcu(&page->rcu_head, rcu_free_slab);
-	} else if (irqs_disabled()) {
-		struct slub_free_list *f = this_cpu_ptr(&slub_free_list);
-
-		raw_spin_lock(&f->lock);
-		list_add(&page->lru, &f->list);
-		raw_spin_unlock(&f->lock);
 	} else
 		__free_slab(s, page);
 }
@@ -2051,7 +2021,7 @@
 	if (!n || !n->nr_partial)
 		return NULL;
 
-	raw_spin_lock(&n->list_lock);
+	spin_lock(&n->list_lock);
 	list_for_each_entry_safe(page, page2, &n->partial, slab_list) {
 		void *t;
 
@@ -2076,7 +2046,7 @@
 			break;
 
 	}
-	raw_spin_unlock(&n->list_lock);
+	spin_unlock(&n->list_lock);
 	return object;
 }
 
@@ -2330,7 +2300,7 @@
 			 * that acquire_slab() will see a slab page that
 			 * is frozen
 			 */
-			raw_spin_lock(&n->list_lock);
+			spin_lock(&n->list_lock);
 		}
 	} else {
 		m = M_FULL;
@@ -2342,7 +2312,7 @@
 			 * slabs from diagnostic functions will not see
 			 * any frozen slabs.
 			 */
-			raw_spin_lock(&n->list_lock);
+			spin_lock(&n->list_lock);
 		}
 #endif
 	}
@@ -2367,7 +2337,7 @@
 		goto redo;
 
 	if (lock)
-		raw_spin_unlock(&n->list_lock);
+		spin_unlock(&n->list_lock);
 
 	if (m == M_PARTIAL)
 		stat(s, tail);
@@ -2407,10 +2377,10 @@
 		n2 = get_node(s, page_to_nid(page));
 		if (n != n2) {
 			if (n)
-				raw_spin_unlock(&n->list_lock);
+				spin_unlock(&n->list_lock);
 
 			n = n2;
-			raw_spin_lock(&n->list_lock);
+			spin_lock(&n->list_lock);
 		}
 
 		do {
@@ -2439,7 +2409,7 @@
 	}
 
 	if (n)
-		raw_spin_unlock(&n->list_lock);
+		spin_unlock(&n->list_lock);
 
 	while (discard_page) {
 		page = discard_page;
@@ -2476,21 +2446,14 @@
 			pobjects = oldpage->pobjects;
 			pages = oldpage->pages;
 			if (drain && pobjects > slub_cpu_partial(s)) {
-				struct slub_free_list *f;
 				unsigned long flags;
-				LIST_HEAD(tofree);
 				/*
 				 * partial array is full. Move the existing
 				 * set to the per node partial list.
 				 */
 				local_irq_save(flags);
 				unfreeze_partials(s, this_cpu_ptr(s->cpu_slab));
-				f = this_cpu_ptr(&slub_free_list);
-				raw_spin_lock(&f->lock);
-				list_splice_init(&f->list, &tofree);
-				raw_spin_unlock(&f->lock);
 				local_irq_restore(flags);
-				free_delayed(&tofree);
 				oldpage = NULL;
 				pobjects = 0;
 				pages = 0;
@@ -2556,19 +2519,7 @@
 
 static void flush_all(struct kmem_cache *s)
 {
-	LIST_HEAD(tofree);
-	int cpu;
-
 	on_each_cpu_cond(has_cpu_slab, flush_cpu_slab, s, 1);
-	for_each_online_cpu(cpu) {
-		struct slub_free_list *f;
-
-		f = &per_cpu(slub_free_list, cpu);
-		raw_spin_lock_irq(&f->lock);
-		list_splice_init(&f->list, &tofree);
-		raw_spin_unlock_irq(&f->lock);
-		free_delayed(&tofree);
-	}
 }
 
 /*
@@ -2623,10 +2574,10 @@
 	unsigned long x = 0;
 	struct page *page;
 
-	raw_spin_lock_irqsave(&n->list_lock, flags);
+	spin_lock_irqsave(&n->list_lock, flags);
 	list_for_each_entry(page, &n->partial, slab_list)
 		x += get_count(page);
-	raw_spin_unlock_irqrestore(&n->list_lock, flags);
+	spin_unlock_irqrestore(&n->list_lock, flags);
 	return x;
 }
 #endif /* CONFIG_SLUB_DEBUG || CONFIG_SLUB_SYSFS */
@@ -2765,10 +2716,8 @@
  * already disabled (which is the case for bulk allocation).
  */
 static void *___slab_alloc(struct kmem_cache *s, gfp_t gfpflags, int node,
-			  unsigned long addr, struct kmem_cache_cpu *c,
-			  struct list_head *to_free)
+			  unsigned long addr, struct kmem_cache_cpu *c)
 {
-	struct slub_free_list *f;
 	void *freelist;
 	struct page *page;
 
@@ -2837,13 +2786,6 @@
 	VM_BUG_ON(!c->page->frozen);
 	c->freelist = get_freepointer(s, freelist);
 	c->tid = next_tid(c->tid);
-
-out:
-	f = this_cpu_ptr(&slub_free_list);
-	raw_spin_lock(&f->lock);
-	list_splice_init(&f->list, to_free);
-	raw_spin_unlock(&f->lock);
-
 	return freelist;
 
 new_slab:
@@ -2859,7 +2801,7 @@
 
 	if (unlikely(!freelist)) {
 		slab_out_of_memory(s, gfpflags, node);
-		goto out;
+		return NULL;
 	}
 
 	page = c->page;
@@ -2872,7 +2814,7 @@
 		goto new_slab;	/* Slab failed checks. Next slab needed */
 
 	deactivate_slab(s, page, get_freepointer(s, freelist), c);
-	goto out;
+	return freelist;
 }
 
 /*
@@ -2884,7 +2826,6 @@
 {
 	void *p;
 	unsigned long flags;
-	LIST_HEAD(tofree);
 
 	local_irq_save(flags);
 #ifdef CONFIG_PREEMPTION
@@ -2896,9 +2837,8 @@
 	c = this_cpu_ptr(s->cpu_slab);
 #endif
 
-	p = ___slab_alloc(s, gfpflags, node, addr, c, &tofree);
+	p = ___slab_alloc(s, gfpflags, node, addr, c);
 	local_irq_restore(flags);
-	free_delayed(&tofree);
 	return p;
 }
 
@@ -2933,10 +2873,6 @@
 	unsigned long tid;
 	struct obj_cgroup *objcg = NULL;
 	bool init = false;
-
-	if (IS_ENABLED(CONFIG_PREEMPT_RT) && IS_ENABLED(CONFIG_DEBUG_ATOMIC_SLEEP))
-		WARN_ON_ONCE(!preemptible() &&
-			     (system_state > SYSTEM_BOOTING && system_state < SYSTEM_SUSPEND));
 
 	s = slab_pre_alloc_hook(s, &objcg, 1, gfpflags);
 	if (!s)
@@ -3110,7 +3046,7 @@
 
 	do {
 		if (unlikely(n)) {
-			raw_spin_unlock_irqrestore(&n->list_lock, flags);
+			spin_unlock_irqrestore(&n->list_lock, flags);
 			n = NULL;
 		}
 		prior = page->freelist;
@@ -3142,7 +3078,7 @@
 				 * Otherwise the list_lock will synchronize with
 				 * other processors updating the list of slabs.
 				 */
-				raw_spin_lock_irqsave(&n->list_lock, flags);
+				spin_lock_irqsave(&n->list_lock, flags);
 
 			}
 		}
@@ -3184,7 +3120,7 @@
 		add_partial(n, page, DEACTIVATE_TO_TAIL);
 		stat(s, FREE_ADD_PARTIAL);
 	}
-	raw_spin_unlock_irqrestore(&n->list_lock, flags);
+	spin_unlock_irqrestore(&n->list_lock, flags);
 	return;
 
 slab_empty:
@@ -3199,7 +3135,7 @@
 		remove_full(s, n, page);
 	}
 
-	raw_spin_unlock_irqrestore(&n->list_lock, flags);
+	spin_unlock_irqrestore(&n->list_lock, flags);
 	stat(s, FREE_SLAB);
 	discard_slab(s, page);
 }
@@ -3416,13 +3352,8 @@
 			  void **p)
 {
 	struct kmem_cache_cpu *c;
-	LIST_HEAD(to_free);
 	int i;
 	struct obj_cgroup *objcg = NULL;
-
-	if (IS_ENABLED(CONFIG_PREEMPT_RT) && IS_ENABLED(CONFIG_DEBUG_ATOMIC_SLEEP))
-		WARN_ON_ONCE(!preemptible() &&
-			     (system_state > SYSTEM_BOOTING && system_state < SYSTEM_SUSPEND));
 
 	/* memcg and kmem_cache debug support */
 	s = slab_pre_alloc_hook(s, &objcg, size, flags);
@@ -3460,7 +3391,7 @@
 			 * of re-populating per CPU c->freelist
 			 */
 			p[i] = ___slab_alloc(s, flags, NUMA_NO_NODE,
-					    _RET_IP_, c, &to_free);
+					    _RET_IP_, c);
 			if (unlikely(!p[i]))
 				goto error;
 
@@ -3475,7 +3406,6 @@
 	}
 	c->tid = next_tid(c->tid);
 	local_irq_enable();
-	free_delayed(&to_free);
 
 	/*
 	 * memcg and kmem_cache debug support and memory initialization.
@@ -3486,7 +3416,6 @@
 	return i;
 error:
 	local_irq_enable();
-	free_delayed(&to_free);
 	slab_post_alloc_hook(s, objcg, flags, i, p, false);
 	__kmem_cache_free_bulk(s, i, p);
 	return 0;
@@ -3622,7 +3551,7 @@
 init_kmem_cache_node(struct kmem_cache_node *n)
 {
 	n->nr_partial = 0;
-	raw_spin_lock_init(&n->list_lock);
+	spin_lock_init(&n->list_lock);
 	INIT_LIST_HEAD(&n->partial);
 #ifdef CONFIG_SLUB_DEBUG
 	atomic_long_set(&n->nr_slabs, 0);
@@ -4016,7 +3945,7 @@
 	struct page *page, *h;
 
 	BUG_ON(irqs_disabled());
-	raw_spin_lock_irq(&n->list_lock);
+	spin_lock_irq(&n->list_lock);
 	list_for_each_entry_safe(page, h, &n->partial, slab_list) {
 		if (!page->inuse) {
 			remove_partial(n, page);
@@ -4026,7 +3955,7 @@
 			  "Objects remaining in %s on __kmem_cache_shutdown()");
 		}
 	}
-	raw_spin_unlock_irq(&n->list_lock);
+	spin_unlock_irq(&n->list_lock);
 
 	list_for_each_entry_safe(page, h, &discard, slab_list)
 		discard_slab(s, page);
@@ -4301,7 +4230,7 @@
 		for (i = 0; i < SHRINK_PROMOTE_MAX; i++)
 			INIT_LIST_HEAD(promote + i);
 
-		raw_spin_lock_irqsave(&n->list_lock, flags);
+		spin_lock_irqsave(&n->list_lock, flags);
 
 		/*
 		 * Build lists of slabs to discard or promote.
@@ -4332,7 +4261,7 @@
 		for (i = SHRINK_PROMOTE_MAX - 1; i >= 0; i--)
 			list_splice(promote + i, &n->partial);
 
-		raw_spin_unlock_irqrestore(&n->list_lock, flags);
+		spin_unlock_irqrestore(&n->list_lock, flags);
 
 		/* Release empty slabs */
 		list_for_each_entry_safe(page, t, &discard, slab_list)
@@ -4507,12 +4436,6 @@
 {
 	static __initdata struct kmem_cache boot_kmem_cache,
 		boot_kmem_cache_node;
-	int cpu;
-
-	for_each_possible_cpu(cpu) {
-		raw_spin_lock_init(&per_cpu(slub_free_list, cpu).lock);
-		INIT_LIST_HEAD(&per_cpu(slub_free_list, cpu).list);
-	}
 
 	if (debug_guardpage_minorder())
 		slub_max_order = 0;
@@ -4705,7 +4628,7 @@
 	struct page *page;
 	unsigned long flags;
 
-	raw_spin_lock_irqsave(&n->list_lock, flags);
+	spin_lock_irqsave(&n->list_lock, flags);
 
 	list_for_each_entry(page, &n->partial, slab_list) {
 		validate_slab(s, page);
@@ -4727,7 +4650,7 @@
 		       s->name, count, atomic_long_read(&n->nr_slabs));
 
 out:
-	raw_spin_unlock_irqrestore(&n->list_lock, flags);
+	spin_unlock_irqrestore(&n->list_lock, flags);
 	return count;
 }
 
@@ -4782,9 +4705,6 @@
 {
 	struct location *l;
 	int order;
-
-	if (IS_ENABLED(CONFIG_PREEMPT_RT) && flags == GFP_ATOMIC)
-		return 0;
 
 	order = get_order(sizeof(struct location) * max);
 
@@ -5920,12 +5840,12 @@
 		if (!atomic_long_read(&n->nr_slabs))
 			continue;
 
-		raw_spin_lock_irqsave(&n->list_lock, flags);
+		spin_lock_irqsave(&n->list_lock, flags);
 		list_for_each_entry(page, &n->partial, slab_list)
 			process_slab(t, s, page, alloc, obj_map);
 		list_for_each_entry(page, &n->full, slab_list)
 			process_slab(t, s, page, alloc, obj_map);
-		raw_spin_unlock_irqrestore(&n->list_lock, flags);
+		spin_unlock_irqrestore(&n->list_lock, flags);
 	}
 
 	bitmap_free(obj_map);
diff --git a/kernel/mm/vmalloc.c b/kernel/mm/vmalloc.c
index 3f6567f..3b56c30 100644
--- a/kernel/mm/vmalloc.c
+++ b/kernel/mm/vmalloc.c
@@ -1549,7 +1549,7 @@
 	struct vmap_block *vb;
 	struct vmap_area *va;
 	unsigned long vb_idx;
-	int node, err, cpu;
+	int node, err;
 	void *vaddr;
 
 	node = numa_node_id();
@@ -1586,12 +1586,11 @@
 		return ERR_PTR(err);
 	}
 
-	cpu = get_cpu_light();
-	vbq = this_cpu_ptr(&vmap_block_queue);
+	vbq = &get_cpu_var(vmap_block_queue);
 	spin_lock(&vbq->lock);
 	list_add_tail_rcu(&vb->free_list, &vbq->free);
 	spin_unlock(&vbq->lock);
-	put_cpu_light();
+	put_cpu_var(vmap_block_queue);
 
 	return vaddr;
 }
@@ -1656,7 +1655,6 @@
 	struct vmap_block *vb;
 	void *vaddr = NULL;
 	unsigned int order;
-	int cpu;
 
 	BUG_ON(offset_in_page(size));
 	BUG_ON(size > PAGE_SIZE*VMAP_MAX_ALLOC);
@@ -1671,8 +1669,7 @@
 	order = get_order(size);
 
 	rcu_read_lock();
-	cpu = get_cpu_light();
-	vbq = this_cpu_ptr(&vmap_block_queue);
+	vbq = &get_cpu_var(vmap_block_queue);
 	list_for_each_entry_rcu(vb, &vbq->free, free_list) {
 		unsigned long pages_off;
 
@@ -1695,7 +1692,7 @@
 		break;
 	}
 
-	put_cpu_light();
+	put_cpu_var(vmap_block_queue);
 	rcu_read_unlock();
 
 	/* Allocate new block if nothing was found */
diff --git a/kernel/mm/vmstat.c b/kernel/mm/vmstat.c
index 12e7c5e..604a993 100644
--- a/kernel/mm/vmstat.c
+++ b/kernel/mm/vmstat.c
@@ -321,7 +321,6 @@
 	long x;
 	long t;
 
-	preempt_disable_rt();
 	x = delta + __this_cpu_read(*p);
 
 	t = __this_cpu_read(pcp->stat_threshold);
@@ -331,7 +330,6 @@
 		x = 0;
 	}
 	__this_cpu_write(*p, x);
-	preempt_enable_rt();
 }
 EXPORT_SYMBOL(__mod_zone_page_state);
 
@@ -348,7 +346,6 @@
 		delta >>= PAGE_SHIFT;
 	}
 
-	preempt_disable_rt();
 	x = delta + __this_cpu_read(*p);
 
 	t = __this_cpu_read(pcp->stat_threshold);
@@ -358,7 +355,6 @@
 		x = 0;
 	}
 	__this_cpu_write(*p, x);
-	preempt_enable_rt();
 }
 EXPORT_SYMBOL(__mod_node_page_state);
 
@@ -391,7 +387,6 @@
 	s8 __percpu *p = pcp->vm_stat_diff + item;
 	s8 v, t;
 
-	preempt_disable_rt();
 	v = __this_cpu_inc_return(*p);
 	t = __this_cpu_read(pcp->stat_threshold);
 	if (unlikely(v > t)) {
@@ -400,7 +395,6 @@
 		zone_page_state_add(v + overstep, zone, item);
 		__this_cpu_write(*p, -overstep);
 	}
-	preempt_enable_rt();
 }
 
 void __inc_node_state(struct pglist_data *pgdat, enum node_stat_item item)
@@ -411,7 +405,6 @@
 
 	VM_WARN_ON_ONCE(vmstat_item_in_bytes(item));
 
-	preempt_disable_rt();
 	v = __this_cpu_inc_return(*p);
 	t = __this_cpu_read(pcp->stat_threshold);
 	if (unlikely(v > t)) {
@@ -420,7 +413,6 @@
 		node_page_state_add(v + overstep, pgdat, item);
 		__this_cpu_write(*p, -overstep);
 	}
-	preempt_enable_rt();
 }
 
 void __inc_zone_page_state(struct page *page, enum zone_stat_item item)
@@ -441,7 +433,6 @@
 	s8 __percpu *p = pcp->vm_stat_diff + item;
 	s8 v, t;
 
-	preempt_disable_rt();
 	v = __this_cpu_dec_return(*p);
 	t = __this_cpu_read(pcp->stat_threshold);
 	if (unlikely(v < - t)) {
@@ -450,7 +441,6 @@
 		zone_page_state_add(v - overstep, zone, item);
 		__this_cpu_write(*p, overstep);
 	}
-	preempt_enable_rt();
 }
 
 void __dec_node_state(struct pglist_data *pgdat, enum node_stat_item item)
@@ -461,7 +451,6 @@
 
 	VM_WARN_ON_ONCE(vmstat_item_in_bytes(item));
 
-	preempt_disable_rt();
 	v = __this_cpu_dec_return(*p);
 	t = __this_cpu_read(pcp->stat_threshold);
 	if (unlikely(v < - t)) {
@@ -470,7 +459,6 @@
 		node_page_state_add(v - overstep, pgdat, item);
 		__this_cpu_write(*p, overstep);
 	}
-	preempt_enable_rt();
 }
 
 void __dec_zone_page_state(struct page *page, enum zone_stat_item item)
diff --git a/kernel/mm/workingset.c b/kernel/mm/workingset.c
index c3d098c..975a4d2 100644
--- a/kernel/mm/workingset.c
+++ b/kernel/mm/workingset.c
@@ -432,8 +432,6 @@
 
 void workingset_update_node(struct xa_node *node)
 {
-	struct address_space *mapping;
-
 	/*
 	 * Track non-empty nodes that contain only shadow entries;
 	 * unlink those that contain pages or are being freed.
@@ -442,8 +440,7 @@
 	 * already where they should be. The list_empty() test is safe
 	 * as node->private_list is protected by the i_pages lock.
 	 */
-	mapping = container_of(node->array, struct address_space, i_pages);
-	lockdep_assert_held(&mapping->i_pages.xa_lock);
+	VM_WARN_ON_ONCE(!irqs_disabled());  /* For __inc_lruvec_page_state */
 
 	if (node->count && node->count == node->nr_values) {
 		if (list_empty(&node->private_list)) {
diff --git a/kernel/mm/z3fold.c b/kernel/mm/z3fold.c
index f3d875f..912ac9a 100644
--- a/kernel/mm/z3fold.c
+++ b/kernel/mm/z3fold.c
@@ -623,16 +623,14 @@
 {
 	if (zhdr->first_chunks == 0 || zhdr->last_chunks == 0 ||
 			zhdr->middle_chunks == 0) {
-		struct list_head *unbuddied;
-		int freechunks = num_free_chunks(zhdr);
+		struct list_head *unbuddied = get_cpu_ptr(pool->unbuddied);
 
-		migrate_disable();
-		unbuddied = this_cpu_ptr(pool->unbuddied);
+		int freechunks = num_free_chunks(zhdr);
 		spin_lock(&pool->lock);
 		list_add(&zhdr->buddy, &unbuddied[freechunks]);
 		spin_unlock(&pool->lock);
 		zhdr->cpu = smp_processor_id();
-		migrate_enable();
+		put_cpu_ptr(pool->unbuddied);
 	}
 }
 
@@ -882,9 +880,8 @@
 	int chunks = size_to_chunks(size), i;
 
 lookup:
-	migrate_disable();
 	/* First, try to find an unbuddied z3fold page. */
-	unbuddied = this_cpu_ptr(pool->unbuddied);
+	unbuddied = get_cpu_ptr(pool->unbuddied);
 	for_each_unbuddied_list(i, chunks) {
 		struct list_head *l = &unbuddied[i];
 
@@ -902,7 +899,7 @@
 		    !z3fold_page_trylock(zhdr)) {
 			spin_unlock(&pool->lock);
 			zhdr = NULL;
-			migrate_enable();
+			put_cpu_ptr(pool->unbuddied);
 			if (can_sleep)
 				cond_resched();
 			goto lookup;
@@ -916,7 +913,7 @@
 		    test_bit(PAGE_CLAIMED, &page->private)) {
 			z3fold_page_unlock(zhdr);
 			zhdr = NULL;
-			migrate_enable();
+			put_cpu_ptr(pool->unbuddied);
 			if (can_sleep)
 				cond_resched();
 			goto lookup;
@@ -931,7 +928,7 @@
 		kref_get(&zhdr->refcount);
 		break;
 	}
-	migrate_enable();
+	put_cpu_ptr(pool->unbuddied);
 
 	if (!zhdr) {
 		int cpu;
diff --git a/kernel/mm/zsmalloc.c b/kernel/mm/zsmalloc.c
index fab4cdf..1b309c6 100644
--- a/kernel/mm/zsmalloc.c
+++ b/kernel/mm/zsmalloc.c
@@ -57,7 +57,6 @@
 #include <linux/wait.h>
 #include <linux/pagemap.h>
 #include <linux/fs.h>
-#include <linux/local_lock.h>
 
 #define ZSPAGE_MAGIC	0x58
 
@@ -77,20 +76,6 @@
 #define ZS_MAX_PAGES_PER_ZSPAGE (_AC(1, UL) << ZS_MAX_ZSPAGE_ORDER)
 
 #define ZS_HANDLE_SIZE (sizeof(unsigned long))
-
-#ifdef CONFIG_PREEMPT_RT
-
-struct zsmalloc_handle {
-	unsigned long addr;
-	spinlock_t lock;
-};
-
-#define ZS_HANDLE_ALLOC_SIZE (sizeof(struct zsmalloc_handle))
-
-#else
-
-#define ZS_HANDLE_ALLOC_SIZE (sizeof(unsigned long))
-#endif
 
 /*
  * Object location (<PFN>, <obj_idx>) is encoded as
@@ -308,7 +293,6 @@
 };
 
 struct mapping_area {
-	local_lock_t lock;
 	char *vm_buf; /* copy buffer for objects that span pages */
 	char *vm_addr; /* address of kmap_atomic()'ed pages */
 	enum zs_mapmode vm_mm; /* mapping mode */
@@ -338,7 +322,7 @@
 
 static int create_cache(struct zs_pool *pool)
 {
-	pool->handle_cachep = kmem_cache_create("zs_handle", ZS_HANDLE_ALLOC_SIZE,
+	pool->handle_cachep = kmem_cache_create("zs_handle", ZS_HANDLE_SIZE,
 					0, 0, NULL);
 	if (!pool->handle_cachep)
 		return 1;
@@ -362,26 +346,9 @@
 
 static unsigned long cache_alloc_handle(struct zs_pool *pool, gfp_t gfp)
 {
-	void *p;
-
-	p = kmem_cache_alloc(pool->handle_cachep,
-			     gfp & ~(__GFP_HIGHMEM|__GFP_MOVABLE));
-#ifdef CONFIG_PREEMPT_RT
-	if (p) {
-		struct zsmalloc_handle *zh = p;
-
-		spin_lock_init(&zh->lock);
-	}
-#endif
-	return (unsigned long)p;
+	return (unsigned long)kmem_cache_alloc(pool->handle_cachep,
+			gfp & ~(__GFP_HIGHMEM|__GFP_MOVABLE|__GFP_CMA));
 }
-
-#ifdef CONFIG_PREEMPT_RT
-static struct zsmalloc_handle *zs_get_pure_handle(unsigned long handle)
-{
-	return (void *)(handle &~((1 << OBJ_TAG_BITS) - 1));
-}
-#endif
 
 static void cache_free_handle(struct zs_pool *pool, unsigned long handle)
 {
@@ -401,18 +368,12 @@
 
 static void record_obj(unsigned long handle, unsigned long obj)
 {
-#ifdef CONFIG_PREEMPT_RT
-	struct zsmalloc_handle *zh = zs_get_pure_handle(handle);
-
-	WRITE_ONCE(zh->addr, obj);
-#else
 	/*
 	 * lsb of @obj represents handle lock while other bits
 	 * represent object value the handle is pointing so
 	 * updating shouldn't do store tearing.
 	 */
 	WRITE_ONCE(*(unsigned long *)handle, obj);
-#endif
 }
 
 /* zpool driver */
@@ -494,10 +455,7 @@
 #endif /* CONFIG_ZPOOL */
 
 /* per-cpu VM mapping areas for zspage accesses that cross page boundaries */
-static DEFINE_PER_CPU(struct mapping_area, zs_map_area) = {
-	/* XXX remove this and use a spin_lock_t in pin_tag() */
-	.lock	= INIT_LOCAL_LOCK(lock),
-};
+static DEFINE_PER_CPU(struct mapping_area, zs_map_area);
 
 static bool is_zspage_isolated(struct zspage *zspage)
 {
@@ -907,13 +865,7 @@
 
 static unsigned long handle_to_obj(unsigned long handle)
 {
-#ifdef CONFIG_PREEMPT_RT
-	struct zsmalloc_handle *zh = zs_get_pure_handle(handle);
-
-	return zh->addr;
-#else
 	return *(unsigned long *)handle;
-#endif
 }
 
 static unsigned long obj_to_head(struct page *page, void *obj)
@@ -927,46 +879,22 @@
 
 static inline int testpin_tag(unsigned long handle)
 {
-#ifdef CONFIG_PREEMPT_RT
-	struct zsmalloc_handle *zh = zs_get_pure_handle(handle);
-
-	return spin_is_locked(&zh->lock);
-#else
 	return bit_spin_is_locked(HANDLE_PIN_BIT, (unsigned long *)handle);
-#endif
 }
 
 static inline int trypin_tag(unsigned long handle)
 {
-#ifdef CONFIG_PREEMPT_RT
-	struct zsmalloc_handle *zh = zs_get_pure_handle(handle);
-
-	return spin_trylock(&zh->lock);
-#else
 	return bit_spin_trylock(HANDLE_PIN_BIT, (unsigned long *)handle);
-#endif
 }
 
 static void pin_tag(unsigned long handle) __acquires(bitlock)
 {
-#ifdef CONFIG_PREEMPT_RT
-	struct zsmalloc_handle *zh = zs_get_pure_handle(handle);
-
-	return spin_lock(&zh->lock);
-#else
 	bit_spin_lock(HANDLE_PIN_BIT, (unsigned long *)handle);
-#endif
 }
 
 static void unpin_tag(unsigned long handle) __releases(bitlock)
 {
-#ifdef CONFIG_PREEMPT_RT
-	struct zsmalloc_handle *zh = zs_get_pure_handle(handle);
-
-	return spin_unlock(&zh->lock);
-#else
 	bit_spin_unlock(HANDLE_PIN_BIT, (unsigned long *)handle);
-#endif
 }
 
 static void reset_page(struct page *page)
@@ -1350,8 +1278,7 @@
 	class = pool->size_class[class_idx];
 	off = (class->size * obj_idx) & ~PAGE_MASK;
 
-	local_lock(&zs_map_area.lock);
-	area = this_cpu_ptr(&zs_map_area);
+	area = &get_cpu_var(zs_map_area);
 	area->vm_mm = mm;
 	if (off + class->size <= PAGE_SIZE) {
 		/* this object is contained entirely within a page */
@@ -1405,7 +1332,7 @@
 
 		__zs_unmap_object(area, pages, off, class->size);
 	}
-	local_unlock(&zs_map_area.lock);
+	put_cpu_var(zs_map_area);
 
 	migrate_read_unlock(zspage);
 	unpin_tag(handle);
diff --git a/kernel/mm/zswap.c b/kernel/mm/zswap.c
index b24f761..fbb7829 100644
--- a/kernel/mm/zswap.c
+++ b/kernel/mm/zswap.c
@@ -18,7 +18,6 @@
 #include <linux/highmem.h>
 #include <linux/slab.h>
 #include <linux/spinlock.h>
-#include <linux/local_lock.h>
 #include <linux/types.h>
 #include <linux/atomic.h>
 #include <linux/frontswap.h>
@@ -388,37 +387,27 @@
 /*********************************
 * per-cpu code
 **********************************/
-struct zswap_comp {
-	/* Used for per-CPU dstmem and tfm */
-	local_lock_t lock;
-	u8 *dstmem;
-};
-
-static DEFINE_PER_CPU(struct zswap_comp, zswap_comp) = {
-	.lock = INIT_LOCAL_LOCK(lock),
-};
+static DEFINE_PER_CPU(u8 *, zswap_dstmem);
 
 static int zswap_dstmem_prepare(unsigned int cpu)
 {
-	struct zswap_comp *zcomp;
 	u8 *dst;
 
 	dst = kmalloc_node(PAGE_SIZE * 2, GFP_KERNEL, cpu_to_node(cpu));
 	if (!dst)
 		return -ENOMEM;
 
-	zcomp = per_cpu_ptr(&zswap_comp, cpu);
-	zcomp->dstmem = dst;
+	per_cpu(zswap_dstmem, cpu) = dst;
 	return 0;
 }
 
 static int zswap_dstmem_dead(unsigned int cpu)
 {
-	struct zswap_comp *zcomp;
+	u8 *dst;
 
-	zcomp = per_cpu_ptr(&zswap_comp, cpu);
-	kfree(zcomp->dstmem);
-	zcomp->dstmem = NULL;
+	dst = per_cpu(zswap_dstmem, cpu);
+	kfree(dst);
+	per_cpu(zswap_dstmem, cpu) = NULL;
 
 	return 0;
 }
@@ -930,11 +919,10 @@
 		dlen = PAGE_SIZE;
 		src = (u8 *)zhdr + sizeof(struct zswap_header);
 		dst = kmap_atomic(page);
-		local_lock(&zswap_comp.lock);
-		tfm = *this_cpu_ptr(entry->pool->tfm);
+		tfm = *get_cpu_ptr(entry->pool->tfm);
 		ret = crypto_comp_decompress(tfm, src, entry->length,
 					     dst, &dlen);
-		local_unlock(&zswap_comp.lock);
+		put_cpu_ptr(entry->pool->tfm);
 		kunmap_atomic(dst);
 		BUG_ON(ret);
 		BUG_ON(dlen != PAGE_SIZE);
@@ -1086,12 +1074,12 @@
 	}
 
 	/* compress */
-	local_lock(&zswap_comp.lock);
-	dst = *this_cpu_ptr(&zswap_comp.dstmem);
-	tfm = *this_cpu_ptr(entry->pool->tfm);
+	dst = get_cpu_var(zswap_dstmem);
+	tfm = *get_cpu_ptr(entry->pool->tfm);
 	src = kmap_atomic(page);
 	ret = crypto_comp_compress(tfm, src, PAGE_SIZE, dst, &dlen);
 	kunmap_atomic(src);
+	put_cpu_ptr(entry->pool->tfm);
 	if (ret) {
 		ret = -EINVAL;
 		goto put_dstmem;
@@ -1115,7 +1103,7 @@
 	memcpy(buf, &zhdr, hlen);
 	memcpy(buf + hlen, dst, dlen);
 	zpool_unmap_handle(entry->pool->zpool, handle);
-	local_unlock(&zswap_comp.lock);
+	put_cpu_var(zswap_dstmem);
 
 	/* populate entry */
 	entry->offset = offset;
@@ -1143,7 +1131,7 @@
 	return 0;
 
 put_dstmem:
-	local_unlock(&zswap_comp.lock);
+	put_cpu_var(zswap_dstmem);
 	zswap_pool_put(entry->pool);
 freepage:
 	zswap_entry_cache_free(entry);
@@ -1188,10 +1176,9 @@
 	if (zpool_evictable(entry->pool->zpool))
 		src += sizeof(struct zswap_header);
 	dst = kmap_atomic(page);
-	local_lock(&zswap_comp.lock);
-	tfm = *this_cpu_ptr(entry->pool->tfm);
+	tfm = *get_cpu_ptr(entry->pool->tfm);
 	ret = crypto_comp_decompress(tfm, src, entry->length, dst, &dlen);
-	local_unlock(&zswap_comp.lock);
+	put_cpu_ptr(entry->pool->tfm);
 	kunmap_atomic(dst);
 	zpool_unmap_handle(entry->pool->zpool, entry->handle);
 	BUG_ON(ret);
diff --git a/kernel/net/Kconfig b/kernel/net/Kconfig
index 05b0f04..d656716 100644
--- a/kernel/net/Kconfig
+++ b/kernel/net/Kconfig
@@ -282,7 +282,7 @@
 
 config NET_RX_BUSY_POLL
 	bool
-	default y if !PREEMPT_RT
+	default y
 
 config BQL
 	bool
diff --git a/kernel/net/core/dev.c b/kernel/net/core/dev.c
index d3bc4e8..bc5dcf5 100644
--- a/kernel/net/core/dev.c
+++ b/kernel/net/core/dev.c
@@ -222,14 +222,14 @@
 static inline void rps_lock(struct softnet_data *sd)
 {
 #ifdef CONFIG_RPS
-	raw_spin_lock(&sd->input_pkt_queue.raw_lock);
+	spin_lock(&sd->input_pkt_queue.lock);
 #endif
 }
 
 static inline void rps_unlock(struct softnet_data *sd)
 {
 #ifdef CONFIG_RPS
-	raw_spin_unlock(&sd->input_pkt_queue.raw_lock);
+	spin_unlock(&sd->input_pkt_queue.lock);
 #endif
 }
 
@@ -3055,7 +3055,6 @@
 	sd->output_queue_tailp = &q->next_sched;
 	raise_softirq_irqoff(NET_TX_SOFTIRQ);
 	local_irq_restore(flags);
-	preempt_check_resched_rt();
 }
 
 void __netif_schedule(struct Qdisc *q)
@@ -3118,7 +3117,6 @@
 	__this_cpu_write(softnet_data.completion_queue, skb);
 	raise_softirq_irqoff(NET_TX_SOFTIRQ);
 	local_irq_restore(flags);
-	preempt_check_resched_rt();
 }
 EXPORT_SYMBOL(__dev_kfree_skb_irq);
 
@@ -3797,11 +3795,7 @@
 	 * This permits qdisc->running owner to get the lock more
 	 * often and dequeue packets faster.
 	 */
-#ifdef CONFIG_PREEMPT_RT
-	contended = true;
-#else
 	contended = qdisc_is_running(q);
-#endif
 	if (unlikely(contended))
 		spin_lock(&q->busylock);
 
@@ -4601,7 +4595,6 @@
 	rps_unlock(sd);
 
 	local_irq_restore(flags);
-	preempt_check_resched_rt();
 
 	atomic_long_inc(&skb->dev->rx_dropped);
 	kfree_skb(skb);
@@ -4817,7 +4810,7 @@
 		struct rps_dev_flow voidflow, *rflow = &voidflow;
 		int cpu;
 
-		migrate_disable();
+		preempt_disable();
 		rcu_read_lock();
 
 		cpu = get_rps_cpu(skb->dev, skb, &rflow);
@@ -4827,14 +4820,14 @@
 		ret = enqueue_to_backlog(skb, cpu, &rflow->last_qtail);
 
 		rcu_read_unlock();
-		migrate_enable();
+		preempt_enable();
 	} else
 #endif
 	{
 		unsigned int qtail;
 
-		ret = enqueue_to_backlog(skb, get_cpu_light(), &qtail);
-		put_cpu_light();
+		ret = enqueue_to_backlog(skb, get_cpu(), &qtail);
+		put_cpu();
 	}
 	return ret;
 }
@@ -4873,9 +4866,11 @@
 
 	trace_netif_rx_ni_entry(skb);
 
-	local_bh_disable();
+	preempt_disable();
 	err = netif_rx_internal(skb);
-	local_bh_enable();
+	if (local_softirq_pending())
+		do_softirq();
+	preempt_enable();
 	trace_netif_rx_ni_exit(err);
 
 	return err;
@@ -6351,14 +6346,12 @@
 		sd->rps_ipi_list = NULL;
 
 		local_irq_enable();
-		preempt_check_resched_rt();
 
 		/* Send pending IPI's to kick RPS processing on remote cpus. */
 		net_rps_send_ipi(remsd);
 	} else
 #endif
 		local_irq_enable();
-	preempt_check_resched_rt();
 }
 
 static bool sd_has_rps_ipi_waiting(struct softnet_data *sd)
@@ -6436,7 +6429,6 @@
 	local_irq_save(flags);
 	____napi_schedule(this_cpu_ptr(&softnet_data), n);
 	local_irq_restore(flags);
-	preempt_check_resched_rt();
 }
 EXPORT_SYMBOL(__napi_schedule);
 
@@ -10978,7 +10970,6 @@
 
 	raise_softirq_irqoff(NET_TX_SOFTIRQ);
 	local_irq_enable();
-	preempt_check_resched_rt();
 
 #ifdef CONFIG_RPS
 	remsd = oldsd->rps_ipi_list;
@@ -10992,7 +10983,7 @@
 		netif_rx_ni(skb);
 		input_queue_head_incr(oldsd);
 	}
-	while ((skb = __skb_dequeue(&oldsd->input_pkt_queue))) {
+	while ((skb = skb_dequeue(&oldsd->input_pkt_queue))) {
 		netif_rx_ni(skb);
 		input_queue_head_incr(oldsd);
 	}
@@ -11308,7 +11299,7 @@
 
 		INIT_WORK(flush, flush_backlog);
 
-		skb_queue_head_init_raw(&sd->input_pkt_queue);
+		skb_queue_head_init(&sd->input_pkt_queue);
 		skb_queue_head_init(&sd->process_queue);
 #ifdef CONFIG_XFRM_OFFLOAD
 		skb_queue_head_init(&sd->xfrm_backlog);
diff --git a/kernel/net/core/gen_estimator.c b/kernel/net/core/gen_estimator.c
index e51f485..8e582e2 100644
--- a/kernel/net/core/gen_estimator.c
+++ b/kernel/net/core/gen_estimator.c
@@ -42,7 +42,7 @@
 struct net_rate_estimator {
 	struct gnet_stats_basic_packed	*bstats;
 	spinlock_t		*stats_lock;
-	net_seqlock_t		*running;
+	seqcount_t		*running;
 	struct gnet_stats_basic_cpu __percpu *cpu_bstats;
 	u8			ewma_log;
 	u8			intvl_log; /* period : (250ms << intvl_log) */
@@ -125,7 +125,7 @@
 		      struct gnet_stats_basic_cpu __percpu *cpu_bstats,
 		      struct net_rate_estimator __rcu **rate_est,
 		      spinlock_t *lock,
-		      net_seqlock_t *running,
+		      seqcount_t *running,
 		      struct nlattr *opt)
 {
 	struct gnet_estimator *parm = nla_data(opt);
@@ -226,7 +226,7 @@
 			  struct gnet_stats_basic_cpu __percpu *cpu_bstats,
 			  struct net_rate_estimator __rcu **rate_est,
 			  spinlock_t *lock,
-			  net_seqlock_t *running, struct nlattr *opt)
+			  seqcount_t *running, struct nlattr *opt)
 {
 	return gen_new_estimator(bstats, cpu_bstats, rate_est,
 				 lock, running, opt);
diff --git a/kernel/net/core/gen_stats.c b/kernel/net/core/gen_stats.c
index ef432ce..e491b08 100644
--- a/kernel/net/core/gen_stats.c
+++ b/kernel/net/core/gen_stats.c
@@ -137,7 +137,7 @@
 }
 
 void
-__gnet_stats_copy_basic(net_seqlock_t *running,
+__gnet_stats_copy_basic(const seqcount_t *running,
 			struct gnet_stats_basic_packed *bstats,
 			struct gnet_stats_basic_cpu __percpu *cpu,
 			struct gnet_stats_basic_packed *b)
@@ -150,15 +150,15 @@
 	}
 	do {
 		if (running)
-			seq = net_seq_begin(running);
+			seq = read_seqcount_begin(running);
 		bstats->bytes = b->bytes;
 		bstats->packets = b->packets;
-	} while (running && net_seq_retry(running, seq));
+	} while (running && read_seqcount_retry(running, seq));
 }
 EXPORT_SYMBOL(__gnet_stats_copy_basic);
 
 static int
-___gnet_stats_copy_basic(net_seqlock_t *running,
+___gnet_stats_copy_basic(const seqcount_t *running,
 			 struct gnet_dump *d,
 			 struct gnet_stats_basic_cpu __percpu *cpu,
 			 struct gnet_stats_basic_packed *b,
@@ -204,7 +204,7 @@
  * if the room in the socket buffer was not sufficient.
  */
 int
-gnet_stats_copy_basic(net_seqlock_t *running,
+gnet_stats_copy_basic(const seqcount_t *running,
 		      struct gnet_dump *d,
 		      struct gnet_stats_basic_cpu __percpu *cpu,
 		      struct gnet_stats_basic_packed *b)
@@ -228,7 +228,7 @@
  * if the room in the socket buffer was not sufficient.
  */
 int
-gnet_stats_copy_basic_hw(net_seqlock_t *running,
+gnet_stats_copy_basic_hw(const seqcount_t *running,
 			 struct gnet_dump *d,
 			 struct gnet_stats_basic_cpu __percpu *cpu,
 			 struct gnet_stats_basic_packed *b)
diff --git a/kernel/net/core/sock.c b/kernel/net/core/sock.c
index 4c630e7..0506590 100644
--- a/kernel/net/core/sock.c
+++ b/kernel/net/core/sock.c
@@ -3069,11 +3069,12 @@
 	if (sk->sk_lock.owned)
 		__lock_sock(sk);
 	sk->sk_lock.owned = 1;
-	spin_unlock_bh(&sk->sk_lock.slock);
+	spin_unlock(&sk->sk_lock.slock);
 	/*
 	 * The sk_lock has mutex_lock() semantics here:
 	 */
 	mutex_acquire(&sk->sk_lock.dep_map, subclass, 0, _RET_IP_);
+	local_bh_enable();
 }
 EXPORT_SYMBOL(lock_sock_nested);
 
@@ -3122,11 +3123,12 @@
 
 	__lock_sock(sk);
 	sk->sk_lock.owned = 1;
-	spin_unlock_bh(&sk->sk_lock.slock);
+	spin_unlock(&sk->sk_lock.slock);
 	/*
 	 * The sk_lock has mutex_lock() semantics here:
 	 */
 	mutex_acquire(&sk->sk_lock.dep_map, 0, 0, _RET_IP_);
+	local_bh_enable();
 	return true;
 }
 EXPORT_SYMBOL(lock_sock_fast);
diff --git a/kernel/net/rfkill/rfkill-bt.c b/kernel/net/rfkill/rfkill-bt.c
index 719a23d..73b802c 100644
--- a/kernel/net/rfkill/rfkill-bt.c
+++ b/kernel/net/rfkill/rfkill-bt.c
@@ -175,15 +175,11 @@
 		rfkill->irq_req = 1;
 		LOG("** disable irq\n");
 		disable_irq(irq->irq);
-		ret = enable_irq_wake(irq->irq);
-		if (ret)
-			goto fail3;
+		/*ret = disable_irq_wake(irq->irq);init irq wake is disabled,no need to disable*/
 	}
 
 	return ret;
 
-fail3:
-	free_irq(irq->irq, rfkill);
 fail2:
 	gpio_free(irq->gpio.io);
 fail1:
@@ -292,12 +288,6 @@
 
 	toggle = rfkill->pdata->power_toggle;
 
-	if (toggle) {
-		if (rfkill_get_wifi_power_state(&wifi_power)) {
-			LOG("%s: cannot get wifi power state!\n", __func__);
-			return -1;
-		}
-	}
 
 	DBG("%s: toggle = %s\n", __func__, toggle ? "true" : "false");
 
@@ -323,8 +313,6 @@
 				gpio_direction_output(poweron->io,
 						      poweron->enable);
 				msleep(20);
-				if (gpio_is_valid(wake_host->io))
-					gpio_direction_input(wake_host->io);
 			}
 		}
 
@@ -335,6 +323,11 @@
 				msleep(20);
 				gpio_direction_output(reset->io, reset->enable);
 			}
+		}
+
+		if (gpio_is_valid(wake_host->io)) {
+			LOG("%s: set bt wake_host input!\n", __func__);
+			gpio_direction_input(wake_host->io);
 		}
 
 		if (pinctrl && gpio_is_valid(rts->io)) {
@@ -369,6 +362,10 @@
 			}
 		}
 		if (toggle) {
+			if (rfkill_get_wifi_power_state(&wifi_power)) {
+				LOG("%s: cannot get wifi power state!\n", __func__);
+				return -EPERM;
+			}
 			if (!wifi_power) {
 				LOG("%s: bt will set vbat to low\n", __func__);
 				rfkill_set_wifi_bt_power(0);
@@ -410,6 +407,7 @@
 	if (gpio_is_valid(wake_host_irq->gpio.io) && bt_power_state) {
 		DBG("enable irq for bt wakeup host\n");
 		enable_irq(wake_host_irq->irq);
+		enable_irq_wake(wake_host_irq->irq);
 	}
 
 #ifdef CONFIG_RFKILL_RESET
@@ -439,6 +437,7 @@
 	if (gpio_is_valid(wake_host_irq->gpio.io) && bt_power_state) {
 		LOG("** disable irq\n");
 		disable_irq(wake_host_irq->irq);
+		disable_irq_wake(wake_host_irq->irq);
 	}
 
 	if (rfkill->pdata->pinctrl && gpio_is_valid(rts->io)) {
diff --git a/kernel/net/rfkill/rfkill-wlan.c b/kernel/net/rfkill/rfkill-wlan.c
index 89d9787..338b59c 100644
--- a/kernel/net/rfkill/rfkill-wlan.c
+++ b/kernel/net/rfkill/rfkill-wlan.c
@@ -319,6 +319,11 @@
 			}
 
 			wifi_power_state = 0;
+
+			if (!rfkill_get_bt_power_state(&bt_power, &toggle)) {
+				LOG("%s: toggle = %s\n", __func__, toggle ? "true" : "false");
+			}
+
 			if (toggle) {
 				if (!bt_power) {
 					LOG("%s: wifi will set vbat to low\n", __func__);
diff --git a/kernel/net/sched/sch_api.c b/kernel/net/sched/sch_api.c
index 8e01a18..d8ffe41 100644
--- a/kernel/net/sched/sch_api.c
+++ b/kernel/net/sched/sch_api.c
@@ -1265,7 +1265,7 @@
 		rcu_assign_pointer(sch->stab, stab);
 	}
 	if (tca[TCA_RATE]) {
-		net_seqlock_t *running;
+		seqcount_t *running;
 
 		err = -EOPNOTSUPP;
 		if (sch->flags & TCQ_F_MQROOT) {
diff --git a/kernel/net/sched/sch_generic.c b/kernel/net/sched/sch_generic.c
index 2483cb2..0f37da3 100644
--- a/kernel/net/sched/sch_generic.c
+++ b/kernel/net/sched/sch_generic.c
@@ -578,11 +578,7 @@
 	.ops		=	&noop_qdisc_ops,
 	.q.lock		=	__SPIN_LOCK_UNLOCKED(noop_qdisc.q.lock),
 	.dev_queue	=	&noop_netdev_queue,
-#ifdef CONFIG_PREEMPT_RT
-	.running	=	__SEQLOCK_UNLOCKED(noop_qdisc.running),
-#else
 	.running	=	SEQCNT_ZERO(noop_qdisc.running),
-#endif
 	.busylock	=	__SPIN_LOCK_UNLOCKED(noop_qdisc.busylock),
 	.gso_skb = {
 		.next = (struct sk_buff *)&noop_qdisc.gso_skb,
@@ -893,15 +889,9 @@
 	lockdep_set_class(&sch->seqlock,
 			  dev->qdisc_tx_busylock ?: &qdisc_tx_busylock);
 
-#ifdef CONFIG_PREEMPT_RT
-	seqlock_init(&sch->running);
-	lockdep_set_class(&sch->running.lock,
-			  dev->qdisc_running_key ?: &qdisc_running_key);
-#else
 	seqcount_init(&sch->running);
 	lockdep_set_class(&sch->running,
 			  dev->qdisc_running_key ?: &qdisc_running_key);
-#endif
 
 	sch->ops = ops;
 	sch->flags = ops->static_flags;
diff --git a/kernel/net/sunrpc/svc_xprt.c b/kernel/net/sunrpc/svc_xprt.c
index 16bd127..06e5034 100644
--- a/kernel/net/sunrpc/svc_xprt.c
+++ b/kernel/net/sunrpc/svc_xprt.c
@@ -422,7 +422,7 @@
 	if (test_and_set_bit(XPT_BUSY, &xprt->xpt_flags))
 		return;
 
-	cpu = get_cpu_light();
+	cpu = get_cpu();
 	pool = svc_pool_for_cpu(xprt->xpt_server, cpu);
 
 	atomic_long_inc(&pool->sp_stats.packets);
@@ -446,7 +446,7 @@
 	rqstp = NULL;
 out_unlock:
 	rcu_read_unlock();
-	put_cpu_light();
+	put_cpu();
 	trace_svc_xprt_do_enqueue(xprt, rqstp);
 }
 EXPORT_SYMBOL_GPL(svc_xprt_do_enqueue);
diff --git a/kernel/net/xfrm/xfrm_state.c b/kernel/net/xfrm/xfrm_state.c
index 4ce3f4d..5f4b51e 100644
--- a/kernel/net/xfrm/xfrm_state.c
+++ b/kernel/net/xfrm/xfrm_state.c
@@ -2676,8 +2676,7 @@
 	net->xfrm.state_num = 0;
 	INIT_WORK(&net->xfrm.state_hash_work, xfrm_hash_resize);
 	spin_lock_init(&net->xfrm.xfrm_state_lock);
-	seqcount_spinlock_init(&net->xfrm.xfrm_state_hash_generation,
-			       &net->xfrm.xfrm_state_lock);
+	seqcount_init(&net->xfrm.xfrm_state_hash_generation);
 	return 0;
 
 out_byspi:
diff --git a/kernel/sound/soc/codecs/Kconfig b/kernel/sound/soc/codecs/Kconfig
index 8121978..cb3b581 100644
--- a/kernel/sound/soc/codecs/Kconfig
+++ b/kernel/sound/soc/codecs/Kconfig
@@ -216,6 +216,7 @@
 	imply SND_SOC_TAS5720
 	imply SND_SOC_TAS6424
 	imply SND_SOC_TDA7419
+	imply SND_SOC_TDA7803
 	imply SND_SOC_TFA9879
 	imply SND_SOC_TLV320ADCX140
 	imply SND_SOC_TLV320AIC23_I2C
@@ -1442,6 +1443,11 @@
 	depends on I2C
 	select REGMAP_I2C
 
+config SND_SOC_TDA7803
+	tristate "ST TDA7803 audio processor"
+	depends on I2C
+	select REGMAP_I2C
+
 config SND_SOC_TFA9879
 	tristate "NXP Semiconductors TFA9879 amplifier"
 	depends on I2C
diff --git a/kernel/sound/soc/codecs/Makefile b/kernel/sound/soc/codecs/Makefile
index 20ee390..fd3e686 100644
--- a/kernel/sound/soc/codecs/Makefile
+++ b/kernel/sound/soc/codecs/Makefile
@@ -232,6 +232,7 @@
 snd-soc-tas5720-objs := tas5720.o
 snd-soc-tas6424-objs := tas6424.o
 snd-soc-tda7419-objs := tda7419.o
+snd-soc-tda7803-objs := tda7803.o
 snd-soc-tas2770-objs := tas2770.o
 snd-soc-tfa9879-objs := tfa9879.o
 snd-soc-tlv320aic23-objs := tlv320aic23.o
@@ -560,6 +561,7 @@
 obj-$(CONFIG_SND_SOC_TAS5720)	+= snd-soc-tas5720.o
 obj-$(CONFIG_SND_SOC_TAS6424)	+= snd-soc-tas6424.o
 obj-$(CONFIG_SND_SOC_TDA7419)	+= snd-soc-tda7419.o
+obj-$(CONFIG_SND_SOC_TDA7803)	+= snd-soc-tda7803.o
 obj-$(CONFIG_SND_SOC_TAS2770) += snd-soc-tas2770.o
 obj-$(CONFIG_SND_SOC_TFA9879)	+= snd-soc-tfa9879.o
 obj-$(CONFIG_SND_SOC_TLV320AIC23)	+= snd-soc-tlv320aic23.o
diff --git a/kernel/sound/soc/codecs/rk817_codec.c b/kernel/sound/soc/codecs/rk817_codec.c
index a334f76..ef06558 100644
--- a/kernel/sound/soc/codecs/rk817_codec.c
+++ b/kernel/sound/soc/codecs/rk817_codec.c
@@ -286,10 +286,10 @@
 	{RK817_CODEC_AREF_RTCFG1, 0x40},
 	{RK817_CODEC_DDAC_POPD_DACST, 0x02},
 	/* APLL */
-	{RK817_CODEC_APLL_CFG0, 0x04},
+	/* {RK817_CODEC_APLL_CFG0, 0x04}, */
 	{RK817_CODEC_APLL_CFG1, 0x58},
 	{RK817_CODEC_APLL_CFG2, 0x2d},
-	{RK817_CODEC_APLL_CFG4, 0xa5},
+	/* {RK817_CODEC_APLL_CFG4, 0xa5}, */
 	{RK817_CODEC_APLL_CFG5, 0x00},
 
 	{RK817_CODEC_DI2S_RXCMD_TSD, 0x00},
@@ -324,10 +324,10 @@
 	{RK817_CODEC_AREF_RTCFG1, 0x40},
 	{RK817_CODEC_DADC_SR_ACL0, 0x02},
 	/* {RK817_CODEC_DTOP_DIGEN_CLKE, 0xff}, */
-	{RK817_CODEC_APLL_CFG0, 0x04},
+	/* {RK817_CODEC_APLL_CFG0, 0x04}, */
 	{RK817_CODEC_APLL_CFG1, 0x58},
 	{RK817_CODEC_APLL_CFG2, 0x2d},
-	{RK817_CODEC_APLL_CFG4, 0xa5},
+	/* {RK817_CODEC_APLL_CFG4, 0xa5}, */
 	{RK817_CODEC_APLL_CFG5, 0x00},
 
 	/*{RK817_CODEC_DI2S_RXCMD_TSD, 0x00},*/
@@ -378,12 +378,16 @@
 						playback_power_up_list[i].value);
 		}
 
-		/* Re-configure APLL CFG0/4 if (chip_ver <= 0x4) */
+		/* configure APLL CFG0/4 */
 		if (rk817->chip_ver <= 0x4) {
 			DBG("%s (%d): SMIC TudorAG and previous versions\n",
 			    __func__, __LINE__);
 			snd_soc_component_write(component, RK817_CODEC_APLL_CFG0, 0x0c);
 			snd_soc_component_write(component, RK817_CODEC_APLL_CFG4, 0x95);
+		} else {
+			DBG("%s: SMIC TudorAG version later\n", __func__);
+			snd_soc_component_write(component, RK817_CODEC_APLL_CFG0, 0x04);
+			snd_soc_component_write(component, RK817_CODEC_APLL_CFG4, 0xa5);
 		}
 
 		snd_soc_component_update_bits(component, RK817_CODEC_DTOP_DIGEN_CLKE,
@@ -405,12 +409,16 @@
 						capture_power_up_list[i].value);
 		}
 
-		/* Re-configure APLL CFG0/4 if (chip_ver <= 0x4) */
+		/* configure APLL CFG0/4 */
 		if (rk817->chip_ver <= 0x4) {
 			DBG("%s (%d): SMIC TudorAG and previous versions\n",
 			    __func__, __LINE__);
 			snd_soc_component_write(component, RK817_CODEC_APLL_CFG0, 0x0c);
 			snd_soc_component_write(component, RK817_CODEC_APLL_CFG4, 0x95);
+		} else {
+			DBG("%s: SMIC TudorAG version later\n", __func__);
+			snd_soc_component_write(component, RK817_CODEC_APLL_CFG0, 0x04);
+			snd_soc_component_write(component, RK817_CODEC_APLL_CFG4, 0xa5);
 		}
 
 		snd_soc_component_update_bits(component, RK817_CODEC_DTOP_DIGEN_CLKE,
@@ -1230,9 +1238,9 @@
 	rk817->chip_ver = (chip_ver & 0x0f);
 	dev_info(component->dev, "%s: chip_name:0x%x, chip_ver:0x%x\n", __func__, chip_name, chip_ver);
 
+	/* always enable mclk, and will disable mclk in rk817_remove */
 	clk_prepare_enable(rk817->mclk);
 	rk817_reset(component);
-	clk_disable_unprepare(rk817->mclk);
 	mutex_init(&rk817->clk_lock);
 	rk817->clk_capture = 0;
 	rk817->clk_playback = 0;
@@ -1257,6 +1265,7 @@
 	rk817_codec_power_down(component, RK817_CODEC_ALL);
 	snd_soc_component_exit_regmap(component);
 	mutex_destroy(&rk817->clk_lock);
+	clk_disable_unprepare(rk817->mclk);
 	mdelay(10);
 
 }
diff --git a/kernel/sound/soc/codecs/rt5640.c b/kernel/sound/soc/codecs/rt5640.c
index 0db73e7..8e28262 100644
--- a/kernel/sound/soc/codecs/rt5640.c
+++ b/kernel/sound/soc/codecs/rt5640.c
@@ -1840,9 +1840,6 @@
 	unsigned int reg_val = 0;
 	unsigned int pll_bit = 0;
 
-	if (freq == rt5640->sysclk && clk_id == rt5640->sysclk_src)
-		return 0;
-
 	switch (clk_id) {
 	case RT5640_SCLK_S_MCLK:
 		reg_val |= RT5640_SCLK_SRC_MCLK;
diff --git a/kernel/sound/soc/codecs/rv1106_codec.c b/kernel/sound/soc/codecs/rv1106_codec.c
index f239b33..a835d8c 100644
--- a/kernel/sound/soc/codecs/rv1106_codec.c
+++ b/kernel/sound/soc/codecs/rv1106_codec.c
@@ -97,6 +97,9 @@
 	unsigned int mic_mute_l;
 	unsigned int mic_mute_r;
 
+	/* DAC Control Manually */
+	unsigned int dac_ctrl_manual;
+
 	/* For the high pass filter */
 	unsigned int hpf_cutoff;
 
@@ -182,10 +185,20 @@
 					 struct snd_ctl_elem_value *ucontrol);
 static int rv1106_codec_main_micbias_put(struct snd_kcontrol *kcontrol,
 					 struct snd_ctl_elem_value *ucontrol);
+static int rv1106_codec_dac_ctrl_manual_get(struct snd_kcontrol *kcontrol,
+					    struct snd_ctl_elem_value *ucontrol);
+static int rv1106_codec_dac_ctrl_manual_put(struct snd_kcontrol *kcontrol,
+					    struct snd_ctl_elem_value *ucontrol);
 
 static const char *offon_text[2] = {
 	[0] = "Off",
 	[1] = "On",
+};
+
+static const char *noneoffon_text[3] = {
+	[0] = "None",
+	[1] = "Off",
+	[2] = "On",
 };
 
 static const char *mute_text[2] = {
@@ -252,6 +265,11 @@
 static const struct soc_enum rv1106_mic_mute_enum_array[] = {
 	SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(mute_text), mute_text),
 	SOC_ENUM_SINGLE(0, 1, ARRAY_SIZE(mute_text), mute_text),
+};
+
+/* DAC Control Manually */
+static const struct soc_enum rv1106_dac_pa_ctrl_maunal_enum_array[] = {
+	SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(noneoffon_text), noneoffon_text),
 };
 
 /* ALC AGC Approximate Sample Rate */
@@ -422,6 +440,10 @@
 			   rv1106_codec_hpmix_gain_get,
 			   rv1106_codec_hpmix_gain_put,
 			   rv1106_codec_dac_hpmix_gain_tlv),
+
+	/* DAC Control Manually */
+	SOC_ENUM_EXT("DAC Control Manually", rv1106_dac_pa_ctrl_maunal_enum_array[0],
+		     rv1106_codec_dac_ctrl_manual_get, rv1106_codec_dac_ctrl_manual_put),
 };
 
 static unsigned int using_adc_lr(enum adc_mode_e adc_mode)
@@ -1015,35 +1037,47 @@
 	return 0;
 }
 
+static int rv1106_codec_dac_mute(struct rv1106_codec_priv *rv1106, int mute)
+{
+	if (mute) {
+		/* Mute DAC HPMIX/LINEOUT */
+		regmap_update_bits(rv1106->regmap,
+				   ACODEC_DAC_ANA_CTL1,
+				   ACODEC_DAC_L_LINEOUT_MUTE_MSK,
+				   ACODEC_DAC_L_LINEOUT_MUTE);
+		regmap_update_bits(rv1106->regmap,
+				   ACODEC_DAC_HPMIX_CTL,
+				   ACODEC_DAC_HPMIX_MUTE_MSK,
+				   ACODEC_DAC_HPMIX_MUTE);
+		rv1106_codec_pa_ctrl(rv1106, false);
+	} else {
+		/* Unmute DAC HPMIX/LINEOUT */
+		regmap_update_bits(rv1106->regmap,
+				   ACODEC_DAC_HPMIX_CTL,
+				   ACODEC_DAC_HPMIX_MUTE_MSK,
+				   ACODEC_DAC_HPMIX_WORK);
+		regmap_update_bits(rv1106->regmap,
+				   ACODEC_DAC_ANA_CTL1,
+				   ACODEC_DAC_L_LINEOUT_MUTE_MSK,
+				   ACODEC_DAC_L_LINEOUT_WORK);
+		rv1106_codec_pa_ctrl(rv1106, true);
+	}
+
+	return 0;
+}
+
 static int rv1106_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
 {
 	struct snd_soc_component *component = dai->component;
 	struct rv1106_codec_priv *rv1106 = snd_soc_component_get_drvdata(component);
 
 	if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
-		if (mute) {
-			/* Mute DAC HPMIX/LINEOUT */
-			regmap_update_bits(rv1106->regmap,
-					   ACODEC_DAC_ANA_CTL1,
-					   ACODEC_DAC_L_LINEOUT_MUTE_MSK,
-					   ACODEC_DAC_L_LINEOUT_MUTE);
-			regmap_update_bits(rv1106->regmap,
-					   ACODEC_DAC_HPMIX_CTL,
-					   ACODEC_DAC_HPMIX_MUTE_MSK,
-					   ACODEC_DAC_HPMIX_MUTE);
-			rv1106_codec_pa_ctrl(rv1106, false);
-		} else {
-			/* Unmute DAC HPMIX/LINEOUT */
-			regmap_update_bits(rv1106->regmap,
-					   ACODEC_DAC_HPMIX_CTL,
-					   ACODEC_DAC_HPMIX_MUTE_MSK,
-					   ACODEC_DAC_HPMIX_WORK);
-			regmap_update_bits(rv1106->regmap,
-					   ACODEC_DAC_L_LINEOUT_MUTE_MSK,
-					   ACODEC_DAC_MUTE_MSK,
-					   ACODEC_DAC_L_LINEOUT_WORK);
-			rv1106_codec_pa_ctrl(rv1106, true);
-		}
+		if (rv1106->dac_ctrl_manual == 1)
+			mute = 1;  /* Force DAC control off manually */
+		else if (rv1106->dac_ctrl_manual == 2)
+			mute = 0;  /* Force DAC control on manually */
+
+		rv1106_codec_dac_mute(rv1106, mute);
 	}
 
 	return 0;
@@ -1341,6 +1375,36 @@
 	}
 
 	return snd_soc_put_volsw_range(kcontrol, ucontrol);
+}
+
+static int rv1106_codec_dac_ctrl_manual_get(struct snd_kcontrol *kcontrol,
+					    struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+	struct rv1106_codec_priv *rv1106 = snd_soc_component_get_drvdata(component);
+
+	ucontrol->value.integer.value[0] = rv1106->dac_ctrl_manual;
+
+	return 0;
+}
+
+static int rv1106_codec_dac_ctrl_manual_put(struct snd_kcontrol *kcontrol,
+					    struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+	struct rv1106_codec_priv *rv1106 = snd_soc_component_get_drvdata(component);
+
+	rv1106->dac_ctrl_manual = ucontrol->value.integer.value[0];
+
+	if (rv1106->dac_ctrl_manual == 0)
+		return 0;
+
+	if (rv1106->dac_ctrl_manual == 1)
+		rv1106_codec_dac_mute(rv1106, 1); /* Force DAC control off manually */
+	else if (rv1106->dac_ctrl_manual == 2)
+		rv1106_codec_dac_mute(rv1106, 0); /* Force DAC control on manually */
+
+	return 0;
 }
 
 static int rv1106_codec_adc_enable(struct rv1106_codec_priv *rv1106)
@@ -1774,6 +1838,7 @@
 static int rv1106_codec_dapm_controls_prepare(struct rv1106_codec_priv *rv1106)
 {
 	rv1106->adc_mode = DIFF_ADCL;
+	rv1106->dac_ctrl_manual = 0;
 	rv1106->hpf_cutoff = 0;
 	rv1106->agc_l = 0;
 	rv1106->agc_r = 0;
diff --git a/kernel/sound/soc/codecs/tda7803.c b/kernel/sound/soc/codecs/tda7803.c
new file mode 100644
index 0000000..b754591
--- /dev/null
+++ b/kernel/sound/soc/codecs/tda7803.c
@@ -0,0 +1,171 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+/*
+ * Copyright (c) 2023 Rockchip Electronics Co., Ltd.
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/soc.h>
+
+#include "tda7803.h"
+
+#define TDA7803_SAMPLE_RATE \
+		(SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | \
+		 SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000)
+
+struct tda7803_priv {
+	struct regmap *regmap;
+	u32 input_format;
+};
+
+static int tda7803_startup(struct snd_pcm_substream *substream,
+			   struct snd_soc_dai *dai)
+{
+	struct snd_soc_component *component = dai->component;
+	struct tda7803_priv *tda7803 = snd_soc_component_get_drvdata(component);
+	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
+	struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+	int val = 0;
+
+	snd_soc_component_write(component, TDA7803_REG2, DIGITAL_MUTE_OFF |
+				CH2_4_UMUTE | CH1_3_UMUTE |
+				MUTE_TIME_SETTING_1_45MS);
+	snd_soc_component_write(component, TDA7803_REG7, AMPLIEFIR_SWITCH_ON);
+
+	switch (tda7803->input_format) {
+	case 0:
+		val = INPUT_FORMAT_TDM_8CH_MODEL1;
+		break;
+	case 1:
+		val = INPUT_FORMAT_TDM_8CH_MODEL2;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	snd_soc_dai_set_fmt(codec_dai, val);
+
+	return 0;
+}
+
+static int tda7803_hw_params(struct snd_pcm_substream *substream,
+			     struct snd_pcm_hw_params *params,
+			     struct snd_soc_dai *dai)
+{
+	struct snd_soc_component *component = dai->component;
+	int val = 0;
+
+	switch (params_rate(params)) {
+	case 44100:
+		val = SAMPLE_FREQUENCY_RANGE_44100HZ;
+		break;
+	case 48000:
+		val = SAMPLE_FREQUENCY_RANGE_48000HZ;
+		break;
+	case 96000:
+		val = SAMPLE_FREQUENCY_RANGE_96000HZ;
+		break;
+	case 192000:
+		val = SAMPLE_FREQUENCY_RANGE_192000HZ;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	snd_soc_component_write(component, TDA7803_REG3, val);
+
+	return 0;
+}
+
+static int tda7803_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+	struct snd_soc_component *component = dai->component;
+
+	snd_soc_component_write(component, TDA7803_REG3, fmt);
+
+	return 0;
+}
+
+static const struct snd_soc_dai_ops tda7803_ops = {
+	.startup = tda7803_startup,
+	.hw_params = tda7803_hw_params,
+	.set_fmt = tda7803_set_fmt,
+};
+
+static struct snd_soc_dai_driver tda7803_dai = {
+	.name = "tda7803-hifi",
+	.playback = {
+		.stream_name = "Playback",
+		.channels_min = 2,
+		.channels_max = 8,
+		.rates = TDA7803_SAMPLE_RATE,
+		.formats = SNDRV_PCM_FMTBIT_S32_LE |
+			   SNDRV_PCM_FMTBIT_S24_LE |
+			   SNDRV_PCM_FMTBIT_S16_LE,
+	},
+	.ops = &tda7803_ops,
+};
+
+static const struct snd_soc_component_driver soc_codec_dev_tda7803 = {
+	.name = "tda7803",
+};
+
+static const struct regmap_config tda7803_i2c_regmap = {
+	.reg_bits = 8,
+	.val_bits = 8,
+	.max_register = TDA7803_REGMAX,
+	.cache_type = REGCACHE_RBTREE,
+};
+
+static int tda7803_i2c_probe(struct i2c_client *i2c,
+			     const struct i2c_device_id *id)
+{
+	struct tda7803_priv *tda7803;
+	int val;
+
+	tda7803 = devm_kzalloc(&i2c->dev, sizeof(*tda7803), GFP_KERNEL);
+	if (!tda7803)
+		return -ENOMEM;
+
+	i2c_set_clientdata(i2c, tda7803);
+
+	tda7803->regmap = devm_regmap_init_i2c(i2c, &tda7803_i2c_regmap);
+	if (IS_ERR(tda7803->regmap))
+		return PTR_ERR(tda7803->regmap);
+
+	if (!device_property_read_u32(&i2c->dev, "st,tda7803-format", &val))
+		tda7803->input_format = val;
+
+	return devm_snd_soc_register_component(&i2c->dev,
+					       &soc_codec_dev_tda7803,
+					       &tda7803_dai, 1);
+}
+
+static const struct i2c_device_id tda7803_i2c_id[] = {
+	{ "tda7803", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, tda7803_i2c_id);
+
+static const struct of_device_id tda7803_of_match[] = {
+	{ .compatible = "st,tda7803" },
+	{ },
+};
+
+static struct i2c_driver tda7803_i2c_driver = {
+	.driver = {
+		.name   = "tda7803",
+		.of_match_table = of_match_ptr(tda7803_of_match),
+	},
+	.probe          = tda7803_i2c_probe,
+	.id_table       = tda7803_i2c_id,
+};
+module_i2c_driver(tda7803_i2c_driver);
+
+MODULE_AUTHOR("Jun Zeng <jun.zeng@rock-chips.com>");
+MODULE_DESCRIPTION("TDA7803 audio processor driver");
+MODULE_LICENSE("GPL");
diff --git a/kernel/sound/soc/codecs/tda7803.h b/kernel/sound/soc/codecs/tda7803.h
new file mode 100644
index 0000000..54a7b61
--- /dev/null
+++ b/kernel/sound/soc/codecs/tda7803.h
@@ -0,0 +1,138 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2023 Rockchip Electronics Co., Ltd.
+ *
+ */
+
+#ifndef __TDA7803_H__
+#define __TDA7803_H__
+
+/* tda7803 registers space*/
+#define TDA7803_REG0                          (0x00)
+#define CH1_AMP_SBI_MODE                      (0x00 << 0)
+#define CH1_AMP_ABI_MODE                      (0x01 << 0)
+#define CH2_AMP_SBI_MODE                      (0x00 << 1)
+#define CH2_AMP_ABI_MODE                      (0x01 << 1)
+#define CH3_AMP_SBI_MODE                      (0x00 << 2)
+#define CH3_AMP_ABI_MODE                      (0x01 << 2)
+#define CH4_AMP_SBI_MODE                      (0x00 << 3)
+#define CH4_AMP_ABI_MODE                      (0x01 << 3)
+#define CH1_TRI_MODE_OFF                      (0x00 << 4)
+#define CH1_TRI_MODE_ON                       (0x01 << 4)
+#define CH2_TRI_MODE_OFF                      (0x00 << 5)
+#define CH2_TRI_MODE_ON                       (0x01 << 5)
+#define CH3_TRI_MODE_OFF                      (0x00 << 6)
+#define CH3_TRI_MODE_ON                       (0x01 << 6)
+#define CH4_TRI_MODE_OFF                      (0x00 << 7)
+#define CH4_TRI_MODE_ON                       (0x01 << 7)
+
+#define TDA7803_REG1                          (0x01)
+#define CH2_4_GAIN_GV1                        (0x00 << 0)
+#define CH2_4_GAIN_GV2                        (0x01 << 0)
+#define CH2_4_GAIN_GV3                        (0x02 << 0)
+#define CH2_4_GAIN_GV4                        (0x03 << 0)
+#define CH1_3_GAIN_GV1                        (0x00 << 2)
+#define CH1_3_GAIN_GV2                        (0x01 << 2)
+#define CH1_3_GAIN_GV3                        (0x02 << 2)
+#define CH1_3_GAIN_GV4                        (0x03 << 2)
+#define GAIN_SELECT_NO                        (0x00 << 4)
+#define GAIN_SELECT_6DB                       (0x01 << 4)
+#define GAIN_SELECT_12DB                      (0x02 << 4)
+#define GAIN_SELECT_NOT_USED                  (0x03 << 4)
+#define IMPEDANCE_OPTIMIZER_REAR_2OHM         (0x00 << 6)
+#define IMPEDANCE_OPTIMIZER_REAR_4OHM         (0x01 << 6)
+#define IMPEDANCE_OPTIMIZER_FRONT_2OHM        (0x00 << 7)
+#define IMPEDANCE_OPTIMIZER_FRONT_4OHM        (0x01 << 7)
+
+#define TDA7803_REG2                          (0x02)
+#define LOW_BATTERY_MUTE_THRESHOLD_1          (0x00 << 0)
+#define LOW_BATTERY_MUTE_THRESHOLD_2          (0x01 << 0)
+#define DIGITAL_MUTE_ON                       (0x00 << 2)
+#define DIGITAL_MUTE_OFF                      (0x01 << 2)
+#define CH2_4_MUTE                            (0x00 << 3)
+#define CH2_4_UMUTE                           (0x01 << 3)
+#define CH1_3_MUTE                            (0x00 << 4)
+#define CH1_3_UMUTE                           (0x01 << 4)
+#define MUTE_TIME_SETTING_1_45MS              (0x00 << 5)
+#define MUTE_TIME_SETTING_5_8MS               (0x01 << 5)
+#define MUTE_TIME_SETTING_11_6MS              (0x02 << 5)
+#define MUTE_TIME_SETTING_23_2MS              (0x03 << 5)
+#define MUTE_TIME_SETTING_46_4MS              (0x04 << 5)
+#define MUTE_TIME_SETTING_92_8MS              (0x05 << 5)
+#define MUTE_TIME_SETTING_185_5MS             (0x06 << 5)
+#define MUTE_TIME_SETTING_371_1MS             (0x07 << 5)
+
+#define TDA7803_REG3                          (0x03)
+#define HIGH_PASS_FILTER_DISABLE              (0x00 << 0)
+#define HIGH_PASS_FILTER_ENABLE               (0x01 << 0)
+#define INPUT_OFFSET_DETECTION_DIS            (0x00 << 1)
+#define INPUT_OFFSET_DETECTION_EN             (0x01 << 1)
+#define NOISE_GATING_FUNCTION_EN              (0x00 << 2)
+#define NOISE_GATING_FUNCTION_DIS             (0x01 << 2)
+#define INPUT_FORMAT_I2S_STD                  (0x00 << 3)
+#define INPUT_FORMAT_TDM_4CH                  (0x01 << 3)
+#define INPUT_FORMAT_TDM_8CH_MODEL1           (0x02 << 3)
+#define INPUT_FORMAT_TDM_8CH_MODEL2           (0x03 << 3)
+#define INPUT_FORMAT_TDM_16CH_MODEL1          (0x04 << 3)
+#define INPUT_FORMAT_TDM_16CH_MODEL2          (0x05 << 3)
+#define INPUT_FORMAT_TDM_16CH_MODEL3          (0x06 << 3)
+#define INPUT_FORMAT_TDM_16CH_MODEL4          (0x07 << 3)
+#define SAMPLE_FREQUENCY_RANGE_44100HZ        (0x00 << 6)
+#define SAMPLE_FREQUENCY_RANGE_48000HZ        (0x01 << 6)
+#define SAMPLE_FREQUENCY_RANGE_96000HZ        (0x02 << 6)
+#define SAMPLE_FREQUENCY_RANGE_192000HZ       (0x03 << 6)
+
+#define TDA7803_REG4                          (0x04)
+#define DIAGNOSTIC_MODE_DISABLE               (0x00 << 0)
+#define DIAGNOSTIC_MODE_ENABLE                (0x01 << 0)
+#define CH2_4_SPEAKER_MODE                    (0x00 << 1)
+#define CH2_4_LINE_DRIVER_MODE                (0x01 << 1)
+#define CH1_3_SPEAKER_MODE                    (0x00 << 2)
+#define CH1_3_LINE_DRIVER_MODE                (0x01 << 2)
+#define DIAGNOSTIC_DISABLE                    (0X00 << 3)
+#define DIAGNOSTIC_ENABLE                     (0X01 << 3)
+#define DIAGNOSTIC_CURRENT_THRESHOLD_HIGH     (0X00 << 4)
+#define DIAGNOSTIC_CURRENT_THRESHOLD_LOW      (0X01 << 4)
+#define OFFSET_INFORMATION_YES                (0X00 << 5)
+#define OFFSET_INFORMATION_NO                 (0X01 << 5)
+#define SHORT_FAULT_INFORMATION_YES           (0X00 << 6)
+#define SHORT_FAULT_INFORMATION_NO            (0X01 << 6)
+
+#define TDA7803_REG5                          (0x05)
+#define CAPABILITY_ENHANCER_DISABLE           (0x00 << 1)
+#define CAPABILITY_ENHANCER_ENABLE            (0x0F << 1)
+#define THERMAL_THRESHOLD_DEFAULT             (0x00 << 6)
+#define THERMAL_THRESHOLD_TW_NEGATIVE_10      (0x01 << 6)
+#define THERMAL_THRESHOLD_TW_NEGATIVE_20      (0x02 << 6)
+
+#define TDA7803_REG6                          (0x06)
+#define PARALLEL_MODE_CONFIG_MODE_1           (0x00 << 2)
+#define PARALLEL_MODE_CONFIG_MODE_2           (0x01 << 2)
+#define PARALLEL_MODE_CONFIG_MODE_3           (0x02 << 2)
+#define PARALLEL_MODE_CONFIG_MODE_4           (0x03 << 2)
+#define DIAGNOSITC_PULSE_STRETCH_MODE_1       (0x00 << 5)
+#define DIAGNOSITC_PULSE_STRETCH_MODE_2       (0x01 << 5)
+#define DIAGNOSITC_PULSE_STRETCH_MODE_3       (0x02 << 5)
+#define DIAGNOSITC_PULSE_STRETCH_MODE_4       (0x03 << 5)
+#define DIAGNOSITC_PULSE_STRETCH_MODE_5       (0x04 << 5)
+#define DIAGNOSITC_PULSE_STRETCH_DEFAULT      (0x05 << 5)
+
+#define TDA7803_REG7                          (0x07)
+#define AMPLIEFIR_SWITCH_OFF                  (0x00 << 0)
+#define AMPLIEFIR_SWITCH_ON                   (0x01 << 0)
+#define CLIPP_LEVEL_1_REAR_CHANNELS2_4        (0x00 << 1)
+#define CLIPP_LEVEL_2_REAR_CHANNELS2_4        (0x01 << 1)
+#define CLIPP_LEVEL_3_REAR_CHANNELS2_4        (0x02 << 1)
+#define NOT_CLIPP_FOR_REAR_CHANNELS2_4        (0x03 << 1)
+#define CLIPP_LEVEL_1_REAR_CHANNELS1_3        (0x00 << 3)
+#define CLIPP_LEVEL_2_REAR_CHANNELS1_3        (0x01 << 3)
+#define CLIPP_LEVEL_3_REAR_CHANNELS1_3        (0x02 << 3)
+#define NOT_CLIPP_FOR_REAR_CHANNELS1_3        (0x03 << 3)
+#define TEMPERATURE_WARNING_TW1               (0x00 << 5)
+#define TEMPERATURE_WARNING_TW2               (0x01 << 5)
+#define TEMPERATURE_WARNING_TW3               (0x02 << 5)
+#define TEMPERATURE_WARNING_TW4               (0x03 << 5)
+#define NOT_TEMPERATURE_WARNING               (0x04 << 5)
+
+#define TDA7803_REGMAX                        (0x08)
+#endif /* __TDA7803_H__ */
diff --git a/kernel/sound/soc/rockchip/Kconfig b/kernel/sound/soc/rockchip/Kconfig
index eac5bc3..337ead6 100644
--- a/kernel/sound/soc/rockchip/Kconfig
+++ b/kernel/sound/soc/rockchip/Kconfig
@@ -32,6 +32,13 @@
 	  Rockchip I2S/TDM device. The device supports up to maximum of
 	  8 channels each for play and record.
 
+config SND_SOC_ROCKCHIP_I2S_TDM_MULTI_LANES
+	bool "Rockchip TDM Multi Lanes"
+	depends on SND_SOC_ROCKCHIP_I2S_TDM
+	help
+	  Say Y or M if you want to add support for TDM Multi Lanes
+	  based on I2S_TDM controller.
+
 config SND_SOC_ROCKCHIP_MULTI_DAIS
 	tristate "Rockchip Multi-DAIS Device Driver"
 	depends on CLKDEV_LOOKUP && SND_SOC_ROCKCHIP
@@ -58,6 +65,12 @@
 	  Rockchip SAI Controller. The Controller supports up to maximum of
 	  128 channels each for play and record.
 
+config SND_SOC_ROCKCHIP_SAI_VERBOSE
+	bool "Rockchip SAI Verbose Controls"
+	depends on SND_SOC_ROCKCHIP_SAI
+	help
+	  Say Y if you want to export much more controls and info for SAI.
+
 config SND_SOC_ROCKCHIP_SPDIF
 	tristate "Rockchip SPDIF Device Driver"
 	depends on CLKDEV_LOOKUP && SND_SOC_ROCKCHIP
diff --git a/kernel/sound/soc/rockchip/rockchip_i2s.c b/kernel/sound/soc/rockchip/rockchip_i2s.c
index 1262a91..4e27cbd 100644
--- a/kernel/sound/soc/rockchip/rockchip_i2s.c
+++ b/kernel/sound/soc/rockchip/rockchip_i2s.c
@@ -27,6 +27,13 @@
 #define CLK_PPM_MIN		(-1000)
 #define CLK_PPM_MAX		(1000)
 
+#define DEFAULT_MCLK_FS		256
+#define DEFAULT_FS		48000
+
+#define WAIT_TIME_MS_MAX	10000
+
+#define QUIRK_ALWAYS_ON		BIT(0)
+
 struct rk_i2s_pins {
 	u32 reg_offset;
 	u32 shift;
@@ -44,6 +51,9 @@
 
 	struct regmap *regmap;
 	struct regmap *grf;
+
+	struct snd_pcm_substream *substreams[SNDRV_PCM_STREAM_LAST + 1];
+	unsigned int wait_time[SNDRV_PCM_STREAM_LAST + 1];
 
 	bool has_capture;
 	bool has_playback;
@@ -66,6 +76,17 @@
 	int clk_ppm;
 	bool mclk_calibrate;
 
+	unsigned int quirks;
+};
+
+static struct i2s_of_quirks {
+	char *quirk;
+	int id;
+} of_quirks[] = {
+	{
+		.quirk = "rockchip,always-on",
+		.id = QUIRK_ALWAYS_ON,
+	},
 };
 
 static int i2s_runtime_suspend(struct device *dev)
@@ -157,7 +178,7 @@
 		regmap_update_bits(i2s->regmap, I2S_DMACR,
 				   I2S_DMACR_TDE_ENABLE, I2S_DMACR_TDE_DISABLE);
 
-		if (!i2s->rx_start) {
+		if (!i2s->rx_start && !(i2s->quirks & QUIRK_ALWAYS_ON)) {
 			regmap_update_bits(i2s->regmap, I2S_XFER,
 					   I2S_XFER_TXS_START |
 					   I2S_XFER_RXS_START,
@@ -189,7 +210,7 @@
 		regmap_update_bits(i2s->regmap, I2S_DMACR,
 				   I2S_DMACR_RDE_ENABLE, I2S_DMACR_RDE_DISABLE);
 
-		if (!i2s->tx_start) {
+		if (!i2s->tx_start && !(i2s->quirks & QUIRK_ALWAYS_ON)) {
 			regmap_update_bits(i2s->regmap, I2S_XFER,
 					   I2S_XFER_TXS_START |
 					   I2S_XFER_RXS_START,
@@ -353,6 +374,7 @@
 		val |= I2S_TXCR_VDW(24);
 		break;
 	case SNDRV_PCM_FORMAT_S32_LE:
+	case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE:
 		val |= I2S_TXCR_VDW(32);
 		break;
 	default:
@@ -588,12 +610,41 @@
 		i2s->has_capture  ? &i2s->capture_dma_data  : NULL);
 
 	if (i2s->mclk_calibrate)
-		snd_soc_add_dai_controls(dai, &rockchip_i2s_compensation_control, 1);
+		snd_soc_add_component_controls(dai->component,
+					       &rockchip_i2s_compensation_control,
+					       1);
 
 	return 0;
 }
 
+static int rockchip_i2s_startup(struct snd_pcm_substream *substream,
+				struct snd_soc_dai *dai)
+{
+	struct rk_i2s_dev *i2s = snd_soc_dai_get_drvdata(dai);
+	int stream = substream->stream;
+
+	if (i2s->substreams[stream])
+		return -EBUSY;
+
+	if (i2s->wait_time[stream])
+		substream->wait_time = msecs_to_jiffies(i2s->wait_time[stream]);
+
+	i2s->substreams[stream] = substream;
+
+	return 0;
+}
+
+static void rockchip_i2s_shutdown(struct snd_pcm_substream *substream,
+				  struct snd_soc_dai *dai)
+{
+	struct rk_i2s_dev *i2s = snd_soc_dai_get_drvdata(dai);
+
+	i2s->substreams[substream->stream] = NULL;
+}
+
 static const struct snd_soc_dai_ops rockchip_i2s_dai_ops = {
+	.startup = rockchip_i2s_startup,
+	.shutdown = rockchip_i2s_shutdown,
 	.hw_params = rockchip_i2s_hw_params,
 	.set_bclk_ratio	= rockchip_i2s_set_bclk_ratio,
 	.set_sysclk = rockchip_i2s_set_sysclk,
@@ -606,8 +657,116 @@
 	.ops = &rockchip_i2s_dai_ops,
 };
 
+static int rockchip_i2s_get_bclk_ratio(struct snd_kcontrol *kcontrol,
+				       struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *compnt = snd_soc_kcontrol_component(kcontrol);
+	struct rk_i2s_dev *i2s = snd_soc_component_get_drvdata(compnt);
+
+	ucontrol->value.integer.value[0] = i2s->bclk_ratio;
+
+	return 0;
+}
+
+static int rockchip_i2s_put_bclk_ratio(struct snd_kcontrol *kcontrol,
+				       struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *compnt = snd_soc_kcontrol_component(kcontrol);
+	struct rk_i2s_dev *i2s = snd_soc_component_get_drvdata(compnt);
+	int value = ucontrol->value.integer.value[0];
+
+	if (value == i2s->bclk_ratio)
+		return 0;
+
+	i2s->bclk_ratio = value;
+
+	return 1;
+}
+
+static int rockchip_i2s_wait_time_info(struct snd_kcontrol *kcontrol,
+				       struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = WAIT_TIME_MS_MAX;
+	uinfo->value.integer.step = 1;
+
+	return 0;
+}
+
+static int rockchip_i2s_rd_wait_time_get(struct snd_kcontrol *kcontrol,
+					 struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+	struct rk_i2s_dev *i2s = snd_soc_component_get_drvdata(component);
+
+	ucontrol->value.integer.value[0] = i2s->wait_time[SNDRV_PCM_STREAM_CAPTURE];
+
+	return 0;
+}
+
+static int rockchip_i2s_rd_wait_time_put(struct snd_kcontrol *kcontrol,
+					 struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+	struct rk_i2s_dev *i2s = snd_soc_component_get_drvdata(component);
+
+	if (ucontrol->value.integer.value[0] > WAIT_TIME_MS_MAX)
+		return -EINVAL;
+
+	i2s->wait_time[SNDRV_PCM_STREAM_CAPTURE] = ucontrol->value.integer.value[0];
+
+	return 1;
+}
+
+static int rockchip_i2s_wr_wait_time_get(struct snd_kcontrol *kcontrol,
+					 struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+	struct rk_i2s_dev *i2s = snd_soc_component_get_drvdata(component);
+
+	ucontrol->value.integer.value[0] = i2s->wait_time[SNDRV_PCM_STREAM_PLAYBACK];
+
+	return 0;
+}
+
+static int rockchip_i2s_wr_wait_time_put(struct snd_kcontrol *kcontrol,
+					 struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+	struct rk_i2s_dev *i2s = snd_soc_component_get_drvdata(component);
+
+	if (ucontrol->value.integer.value[0] > WAIT_TIME_MS_MAX)
+		return -EINVAL;
+
+	i2s->wait_time[SNDRV_PCM_STREAM_PLAYBACK] = ucontrol->value.integer.value[0];
+
+	return 1;
+}
+
+#define SAI_PCM_WAIT_TIME(xname, xhandler_get, xhandler_put)	\
+{	.iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = xname,	\
+	.info = rockchip_i2s_wait_time_info,			\
+	.get = xhandler_get, .put = xhandler_put }
+
+static const struct snd_kcontrol_new rockchip_i2s_snd_controls[] = {
+	SOC_SINGLE_EXT("BCLK Ratio", 0, 0, INT_MAX, 0,
+		       rockchip_i2s_get_bclk_ratio,
+		       rockchip_i2s_put_bclk_ratio),
+
+	SAI_PCM_WAIT_TIME("PCM Read Wait Time MS",
+			  rockchip_i2s_rd_wait_time_get,
+			  rockchip_i2s_rd_wait_time_put),
+	SAI_PCM_WAIT_TIME("PCM Write Wait Time MS",
+			  rockchip_i2s_wr_wait_time_get,
+			  rockchip_i2s_wr_wait_time_put),
+};
+
 static const struct snd_soc_component_driver rockchip_i2s_component = {
 	.name = DRV_NAME,
+	.controls = rockchip_i2s_snd_controls,
+	.num_controls = ARRAY_SIZE(rockchip_i2s_snd_controls),
 };
 
 static bool rockchip_i2s_wr_reg(struct device *dev, unsigned int reg)
@@ -772,7 +931,8 @@
 					SNDRV_PCM_FMTBIT_S16_LE |
 					SNDRV_PCM_FMTBIT_S20_3LE |
 					SNDRV_PCM_FMTBIT_S24_LE |
-					SNDRV_PCM_FMTBIT_S32_LE;
+					SNDRV_PCM_FMTBIT_S32_LE |
+					SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE;
 
 		i2s->playback_dma_data.addr = res->start + I2S_TXDR;
 		i2s->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
@@ -793,7 +953,8 @@
 				       SNDRV_PCM_FMTBIT_S16_LE |
 				       SNDRV_PCM_FMTBIT_S20_3LE |
 				       SNDRV_PCM_FMTBIT_S24_LE |
-				       SNDRV_PCM_FMTBIT_S32_LE;
+				       SNDRV_PCM_FMTBIT_S32_LE |
+				       SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE;
 
 		i2s->capture_dma_data.addr = res->start + I2S_RXDR;
 		i2s->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
@@ -823,6 +984,38 @@
 	return 0;
 }
 
+static int rockchip_i2s_keep_clk_always_on(struct rk_i2s_dev *i2s)
+{
+	unsigned int mclk_rate = DEFAULT_FS * DEFAULT_MCLK_FS;
+	unsigned int bclk_rate = i2s->bclk_ratio * DEFAULT_FS;
+	unsigned int div_lrck = i2s->bclk_ratio;
+	unsigned int div_bclk;
+
+	div_bclk = DIV_ROUND_CLOSEST(mclk_rate, bclk_rate);
+
+	/* assign generic freq */
+	clk_set_rate(i2s->mclk, mclk_rate);
+
+	regmap_update_bits(i2s->regmap, I2S_CKR,
+			   I2S_CKR_MDIV_MASK,
+			   I2S_CKR_MDIV(div_bclk));
+	regmap_update_bits(i2s->regmap, I2S_CKR,
+			   I2S_CKR_TSD_MASK |
+			   I2S_CKR_RSD_MASK,
+			   I2S_CKR_TSD(div_lrck) |
+			   I2S_CKR_RSD(div_lrck));
+	regmap_update_bits(i2s->regmap, I2S_XFER,
+			   I2S_XFER_TXS_START | I2S_XFER_RXS_START,
+			   I2S_XFER_TXS_START | I2S_XFER_RXS_START);
+
+	pm_runtime_forbid(i2s->dev);
+
+	dev_info(i2s->dev, "CLK-ALWAYS-ON: mclk: %d, bclk: %d, fsync: %d\n",
+		 mclk_rate, bclk_rate, DEFAULT_FS);
+
+	return 0;
+}
+
 static int rockchip_i2s_probe(struct platform_device *pdev)
 {
 	struct device_node *node = pdev->dev.of_node;
@@ -831,7 +1024,7 @@
 	struct snd_soc_dai_driver *dai;
 	struct resource *res;
 	void __iomem *regs;
-	int ret;
+	int ret, i, val;
 
 	i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL);
 	if (!i2s)
@@ -849,6 +1042,10 @@
 		i2s->pins = of_id->data;
 	}
 
+	for (i = 0; i < ARRAY_SIZE(of_quirks); i++)
+		if (device_property_read_bool(i2s->dev, of_quirks[i].quirk))
+			i2s->quirks |= of_quirks[i].id;
+
 	regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
 	if (IS_ERR(regs))
 		return PTR_ERR(regs);
@@ -862,6 +1059,10 @@
 	}
 
 	i2s->bclk_ratio = 64;
+	if (!device_property_read_u32(&pdev->dev, "rockchip,bclk-fs", &val)) {
+		if ((val >= 32) && (val % 2 == 0))
+			i2s->bclk_ratio = val;
+	}
 
 	dev_set_drvdata(&pdev->dev, i2s);
 
@@ -894,16 +1095,37 @@
 		return ret;
 	}
 
+	ret = rockchip_i2s_init_dai(i2s, res, &dai);
+	if (ret)
+		goto err_clk;
+
+	/*
+	 * CLK_ALWAYS_ON should be placed after all registers write done,
+	 * because this situation will enable XFER bit which will make
+	 * some registers(depend on XFER) write failed.
+	 */
+	if (i2s->quirks & QUIRK_ALWAYS_ON) {
+		ret = rockchip_i2s_keep_clk_always_on(i2s);
+		if (ret)
+			goto err_clk;
+	}
+
+	/*
+	 * MUST: after pm_runtime_enable step, any register R/W
+	 * should be wrapped with pm_runtime_get_sync/put.
+	 *
+	 * Another approach is to enable the regcache true to
+	 * avoid access HW registers.
+	 *
+	 * Alternatively, performing the registers R/W before
+	 * pm_runtime_enable is also a good option.
+	 */
 	pm_runtime_enable(&pdev->dev);
 	if (!pm_runtime_enabled(&pdev->dev)) {
 		ret = i2s_runtime_resume(&pdev->dev);
 		if (ret)
 			goto err_pm_disable;
 	}
-
-	ret = rockchip_i2s_init_dai(i2s, res, &dai);
-	if (ret)
-		goto err_pm_disable;
 
 	ret = devm_snd_soc_register_component(&pdev->dev,
 					      &rockchip_i2s_component,
@@ -932,7 +1154,7 @@
 		i2s_runtime_suspend(&pdev->dev);
 err_pm_disable:
 	pm_runtime_disable(&pdev->dev);
-
+err_clk:
 	clk_disable_unprepare(i2s->hclk);
 
 	return ret;
diff --git a/kernel/sound/soc/rockchip/rockchip_i2s_tdm.c b/kernel/sound/soc/rockchip/rockchip_i2s_tdm.c
index a8da138..b0c4ce0 100644
--- a/kernel/sound/soc/rockchip/rockchip_i2s_tdm.c
+++ b/kernel/sound/soc/rockchip/rockchip_i2s_tdm.c
@@ -19,6 +19,7 @@
 #include <linux/clk.h>
 #include <linux/clk-provider.h>
 #include <linux/clk/rockchip.h>
+#include <linux/pinctrl/consumer.h>
 #include <linux/pm_runtime.h>
 #include <linux/regmap.h>
 #include <linux/reset.h>
@@ -35,6 +36,34 @@
 #define HAVE_SYNC_RESET
 #endif
 
+#ifdef CONFIG_SND_SOC_ROCKCHIP_I2S_TDM_MULTI_LANES
+/*
+ * Example: RK3588
+ *
+ * Use I2S2_2CH as Clk-Gen to serve TDM_MULTI_LANES
+ *
+ * I2S2_2CH ----> BCLK,I2S_LRCK --------> I2S0_8CH_TX (Slave TRCM-TXONLY)
+ *     |
+ *     |--------> BCLK,TDM_SYNC --------> TDM Device (Slave)
+ *
+ * Note:
+ *
+ * I2S2_2CH_MCLK: BCLK
+ * I2S2_2CH_SCLK: I2S_LRCK (GPIO2_B7)
+ * I2S2_2CH_LRCK: TDM_SYNC (GPIO2_C0)
+ *
+ */
+
+#define CLK_MAX_COUNT				1000
+#define NSAMPLES				4
+#define XFER_EN					0x3
+#define XFER_DIS				0x0
+#define CKR_V(m, r, t)				((m - 1) << 16 | (r - 1) << 8 | (t - 1) << 0)
+#define I2S_XCR_IBM_V(v)			((v) & I2S_TXCR_IBM_MASK)
+#define I2S_XCR_IBM_NORMAL			I2S_TXCR_IBM_NORMAL
+#define I2S_XCR_IBM_LSJM			I2S_TXCR_IBM_LSJM
+#endif
+
 #define DEFAULT_MCLK_FS				256
 #define DEFAULT_FS				48000
 #define CH_GRP_MAX				4  /* The max channel 8 / 2 */
@@ -42,6 +71,7 @@
 #define CLK_PPM_MIN				(-1000)
 #define CLK_PPM_MAX				(1000)
 #define MAXBURST_PER_FIFO			8
+#define WAIT_TIME_MS_MAX			10000
 
 #define QUIRK_ALWAYS_ON				BIT(0)
 #define QUIRK_HDMI_PATH				BIT(1)
@@ -86,8 +116,11 @@
 	struct snd_dmaengine_dai_dma_data capture_dma_data;
 	struct snd_dmaengine_dai_dma_data playback_dma_data;
 	struct snd_pcm_substream *substreams[SNDRV_PCM_STREAM_LAST + 1];
+	unsigned int wait_time[SNDRV_PCM_STREAM_LAST + 1];
 	struct reset_control *tx_reset;
 	struct reset_control *rx_reset;
+	struct pinctrl *pinctrl;
+	struct pinctrl_state *clk_state;
 	const struct rk_i2s_soc_data *soc_data;
 #ifdef HAVE_SYNC_RESET
 	void __iomem *cru_base;
@@ -99,6 +132,7 @@
 	bool mclk_calibrate;
 	bool tdm_mode;
 	bool tdm_fsync_half_frame;
+	bool is_dma_active[SNDRV_PCM_STREAM_LAST + 1];
 	unsigned int mclk_rx_freq;
 	unsigned int mclk_tx_freq;
 	unsigned int mclk_root0_freq;
@@ -110,9 +144,19 @@
 	unsigned int i2s_sdis[CH_GRP_MAX];
 	unsigned int i2s_sdos[CH_GRP_MAX];
 	unsigned int quirks;
+	unsigned int lrck_ratio;
 	int clk_ppm;
 	atomic_t refcount;
 	spinlock_t lock; /* xfer lock */
+#ifdef CONFIG_SND_SOC_ROCKCHIP_I2S_TDM_MULTI_LANES
+	struct snd_soc_dai *clk_src_dai;
+	struct gpio_desc *i2s_lrck_gpio;
+	struct gpio_desc *tdm_fsync_gpio;
+	unsigned int tx_lanes;
+	unsigned int rx_lanes;
+	void __iomem *clk_src_base;
+	bool is_tdm_multi_lanes;
+#endif
 };
 
 static struct i2s_of_quirks {
@@ -160,6 +204,20 @@
 	clk_disable_unprepare(i2s_tdm->mclk_tx);
 	clk_disable_unprepare(i2s_tdm->mclk_rx);
 
+	pinctrl_pm_select_idle_state(dev);
+
+	return 0;
+}
+
+static int rockchip_i2s_tdm_pinctrl_select_clk_state(struct device *dev)
+{
+	struct rk_i2s_tdm_dev *i2s_tdm = dev_get_drvdata(dev);
+
+	if (IS_ERR_OR_NULL(i2s_tdm->pinctrl) || !i2s_tdm->clk_state)
+		return 0;
+
+	pinctrl_select_state(i2s_tdm->pinctrl, i2s_tdm->clk_state);
+
 	return 0;
 }
 
@@ -167,6 +225,13 @@
 {
 	struct rk_i2s_tdm_dev *i2s_tdm = dev_get_drvdata(dev);
 	int ret;
+
+	/*
+	 * pinctrl default state is invoked by ASoC framework, so,
+	 * we just handle clk state here if DT assigned.
+	 */
+	if (i2s_tdm->is_master_mode)
+		rockchip_i2s_tdm_pinctrl_select_clk_state(dev);
 
 	ret = clk_prepare_enable(i2s_tdm->mclk_tx);
 	if (ret)
@@ -181,6 +246,13 @@
 	ret = regcache_sync(i2s_tdm->regmap);
 	if (ret)
 		goto err_regmap;
+
+	/*
+	 * should be placed after regcache sync done to back
+	 * to the slave mode and then enable clk state.
+	 */
+	if (!i2s_tdm->is_master_mode)
+		rockchip_i2s_tdm_pinctrl_select_clk_state(dev);
 
 	return 0;
 
@@ -207,6 +279,18 @@
 		return (val & I2S_XFER_TXS_START);
 	else
 		return (val & I2S_XFER_RXS_START);
+}
+
+static inline bool is_dma_active(struct rk_i2s_tdm_dev *i2s_tdm, int stream)
+{
+	unsigned int val;
+
+	regmap_read(i2s_tdm->regmap, I2S_DMACR, &val);
+
+	if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+		return (val & I2S_DMACR_TDE_MASK);
+	else
+		return (val & I2S_DMACR_RDE_MASK);
 }
 
 #ifdef HAVE_SYNC_RESET
@@ -515,9 +599,289 @@
 		rockchip_i2s_tdm_fifo_xrun_detect(i2s_tdm, stream, 1);
 }
 
+#ifdef CONFIG_SND_SOC_ROCKCHIP_I2S_TDM_MULTI_LANES
+static const char * const tx_lanes_text[] = { "Auto", "SDOx1", "SDOx2", "SDOx3", "SDOx4" };
+static const char * const rx_lanes_text[] = { "Auto", "SDIx1", "SDIx2", "SDIx3", "SDIx4" };
+static const struct soc_enum tx_lanes_enum =
+	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tx_lanes_text), tx_lanes_text);
+static const struct soc_enum rx_lanes_enum =
+	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(rx_lanes_text), rx_lanes_text);
+
+static int rockchip_i2s_tdm_tx_lanes_get(struct snd_kcontrol *kcontrol,
+					 struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+	struct rk_i2s_tdm_dev *i2s_tdm = snd_soc_component_get_drvdata(component);
+
+	ucontrol->value.enumerated.item[0] = i2s_tdm->tx_lanes;
+
+	return 0;
+}
+
+static int rockchip_i2s_tdm_tx_lanes_put(struct snd_kcontrol *kcontrol,
+					 struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+	struct rk_i2s_tdm_dev *i2s_tdm = snd_soc_component_get_drvdata(component);
+	int num;
+
+	num = ucontrol->value.enumerated.item[0];
+	if (num >= ARRAY_SIZE(tx_lanes_text))
+		return -EINVAL;
+
+	i2s_tdm->tx_lanes = num;
+
+	return 1;
+}
+
+static int rockchip_i2s_tdm_rx_lanes_get(struct snd_kcontrol *kcontrol,
+					 struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+	struct rk_i2s_tdm_dev *i2s_tdm = snd_soc_component_get_drvdata(component);
+
+	ucontrol->value.enumerated.item[0] = i2s_tdm->rx_lanes;
+
+	return 0;
+}
+
+static int rockchip_i2s_tdm_rx_lanes_put(struct snd_kcontrol *kcontrol,
+					 struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+	struct rk_i2s_tdm_dev *i2s_tdm = snd_soc_component_get_drvdata(component);
+	int num;
+
+	num = ucontrol->value.enumerated.item[0];
+	if (num >= ARRAY_SIZE(rx_lanes_text))
+		return -EINVAL;
+
+	i2s_tdm->rx_lanes = num;
+
+	return 1;
+}
+
+static int rockchip_i2s_tdm_get_lanes(struct rk_i2s_tdm_dev *i2s_tdm, int stream)
+{
+	unsigned int lanes = 1;
+
+	if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		if (i2s_tdm->tx_lanes)
+			lanes = i2s_tdm->tx_lanes;
+	} else {
+		if (i2s_tdm->rx_lanes)
+			lanes = i2s_tdm->rx_lanes;
+	}
+
+	return lanes;
+}
+
+static struct snd_soc_dai *rockchip_i2s_tdm_find_dai(struct device_node *np)
+{
+	struct snd_soc_dai_link_component dai_component = { 0 };
+
+	dai_component.of_node = np;
+
+	return snd_soc_find_dai_with_mutex(&dai_component);
+}
+
+static int rockchip_i2s_tdm_multi_lanes_set_clk(struct snd_pcm_substream *substream,
+						struct snd_pcm_hw_params *params,
+						struct snd_soc_dai *cpu_dai)
+{
+	struct rk_i2s_tdm_dev *i2s_tdm = to_info(cpu_dai);
+	struct snd_soc_dai *dai = i2s_tdm->clk_src_dai;
+	unsigned int div, mclk_rate;
+	unsigned int lanes, ch_per_lane;
+
+	lanes = rockchip_i2s_tdm_get_lanes(i2s_tdm, substream->stream);
+	ch_per_lane = params_channels(params) / lanes;
+	mclk_rate = ch_per_lane * params_rate(params) * 32;
+	div = ch_per_lane / 2;
+
+	/* Do nothing when use external clk src */
+	if (dai && dai->driver->ops) {
+		if (dai->driver->ops->set_sysclk)
+			dai->driver->ops->set_sysclk(dai, substream->stream, mclk_rate, 0);
+
+		writel(XFER_DIS, i2s_tdm->clk_src_base + I2S_XFER);
+		writel(CKR_V(64, div, div), i2s_tdm->clk_src_base + I2S_CKR);
+		writel(XFER_EN, i2s_tdm->clk_src_base + I2S_XFER);
+	}
+
+	i2s_tdm->lrck_ratio = div;
+	i2s_tdm->mclk_tx_freq = mclk_rate;
+	i2s_tdm->mclk_rx_freq = mclk_rate;
+
+	return 0;
+}
+
+static inline int tdm_multi_lanes_clk_assert_h(const struct gpio_desc *desc)
+{
+	int cnt = CLK_MAX_COUNT;
+
+	while (gpiod_get_raw_value(desc) && --cnt)
+		;
+
+	return cnt;
+}
+
+static inline int tdm_multi_lanes_clk_assert_l(const struct gpio_desc *desc)
+{
+	int cnt = CLK_MAX_COUNT;
+
+	while (!gpiod_get_raw_value(desc) && --cnt)
+		;
+
+	return cnt;
+}
+
+static inline bool rockchip_i2s_tdm_clk_valid(struct rk_i2s_tdm_dev *i2s_tdm)
+{
+	int dc_h = CLK_MAX_COUNT, dc_l = CLK_MAX_COUNT;
+
+	/*
+	 * TBD: optimize debounce and get value
+	 *
+	 * debounce at least one cycle found, otherwise, the clk ref maybe
+	 * not on the fly.
+	 */
+
+	/* check HIGH-Level */
+	dc_h = tdm_multi_lanes_clk_assert_h(i2s_tdm->i2s_lrck_gpio);
+	if (!dc_h)
+		return false;
+
+	/* check LOW-Level */
+	dc_l = tdm_multi_lanes_clk_assert_l(i2s_tdm->i2s_lrck_gpio);
+	if (!dc_l)
+		return false;
+
+	/* check HIGH-Level */
+	dc_h = tdm_multi_lanes_clk_assert_h(i2s_tdm->tdm_fsync_gpio);
+	if (!dc_h)
+		return false;
+
+	/* check LOW-Level */
+	dc_l = tdm_multi_lanes_clk_assert_l(i2s_tdm->tdm_fsync_gpio);
+	if (!dc_l)
+		return false;
+
+	return true;
+}
+
+static void __maybe_unused rockchip_i2s_tdm_gpio_clk_meas(struct rk_i2s_tdm_dev *i2s_tdm,
+							  const struct gpio_desc *desc,
+							  const char *name)
+{
+	int h[NSAMPLES], l[NSAMPLES], i;
+
+	dev_dbg(i2s_tdm->dev, "%s:\n", name);
+
+	if (!rockchip_i2s_tdm_clk_valid(i2s_tdm))
+		return;
+
+	for (i = 0; i < NSAMPLES; i++) {
+		h[i] = tdm_multi_lanes_clk_assert_h(desc);
+		l[i] = tdm_multi_lanes_clk_assert_l(desc);
+	}
+
+	for (i = 0; i < NSAMPLES; i++)
+		dev_dbg(i2s_tdm->dev, "H[%d]: %2d, L[%d]: %2d\n",
+			i, CLK_MAX_COUNT - h[i], i, CLK_MAX_COUNT - l[i]);
+}
+
+static int rockchip_i2s_tdm_multi_lanes_start(struct rk_i2s_tdm_dev *i2s_tdm, int stream)
+{
+	unsigned int tdm_h = 0, tdm_l = 0, i2s_h = 0, i2s_l = 0;
+	unsigned int msk, val, reg, fmt;
+	unsigned long flags;
+
+	if (!i2s_tdm->tdm_fsync_gpio || !i2s_tdm->i2s_lrck_gpio)
+		return -ENOSYS;
+
+	if (i2s_tdm->lrck_ratio != 4 && i2s_tdm->lrck_ratio != 8)
+		return -EINVAL;
+
+	if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		msk = I2S_XFER_TXS_MASK;
+		val = I2S_XFER_TXS_START;
+		reg = I2S_TXCR;
+	} else {
+		msk = I2S_XFER_RXS_MASK;
+		val = I2S_XFER_RXS_START;
+		reg = I2S_RXCR;
+	}
+
+	regmap_read(i2s_tdm->regmap, reg, &fmt);
+	fmt = I2S_XCR_IBM_V(fmt);
+
+	local_irq_save(flags);
+
+	if (!rockchip_i2s_tdm_clk_valid(i2s_tdm)) {
+		local_irq_restore(flags);
+		dev_err(i2s_tdm->dev, "Invalid LRCK / FSYNC measured by ref IO\n");
+		return -EINVAL;
+	}
+
+	switch (fmt) {
+	case I2S_XCR_IBM_NORMAL:
+		tdm_h = tdm_multi_lanes_clk_assert_h(i2s_tdm->tdm_fsync_gpio);
+		tdm_l = tdm_multi_lanes_clk_assert_l(i2s_tdm->tdm_fsync_gpio);
+
+		if (i2s_tdm->lrck_ratio == 8) {
+			tdm_multi_lanes_clk_assert_l(i2s_tdm->i2s_lrck_gpio);
+			tdm_multi_lanes_clk_assert_h(i2s_tdm->i2s_lrck_gpio);
+			tdm_multi_lanes_clk_assert_l(i2s_tdm->i2s_lrck_gpio);
+			tdm_multi_lanes_clk_assert_h(i2s_tdm->i2s_lrck_gpio);
+		}
+
+		i2s_l = tdm_multi_lanes_clk_assert_l(i2s_tdm->i2s_lrck_gpio);
+
+		if (stream == SNDRV_PCM_STREAM_CAPTURE)
+			i2s_h = tdm_multi_lanes_clk_assert_h(i2s_tdm->i2s_lrck_gpio);
+		break;
+	case I2S_XCR_IBM_LSJM:
+		tdm_l = tdm_multi_lanes_clk_assert_l(i2s_tdm->tdm_fsync_gpio);
+		tdm_h = tdm_multi_lanes_clk_assert_h(i2s_tdm->tdm_fsync_gpio);
+
+		if (i2s_tdm->lrck_ratio == 8) {
+			tdm_multi_lanes_clk_assert_h(i2s_tdm->i2s_lrck_gpio);
+			tdm_multi_lanes_clk_assert_l(i2s_tdm->i2s_lrck_gpio);
+			tdm_multi_lanes_clk_assert_h(i2s_tdm->i2s_lrck_gpio);
+			tdm_multi_lanes_clk_assert_l(i2s_tdm->i2s_lrck_gpio);
+		}
+
+		tdm_multi_lanes_clk_assert_h(i2s_tdm->i2s_lrck_gpio);
+
+		i2s_l = tdm_multi_lanes_clk_assert_l(i2s_tdm->i2s_lrck_gpio);
+		i2s_h = tdm_multi_lanes_clk_assert_h(i2s_tdm->i2s_lrck_gpio);
+		break;
+	default:
+		local_irq_restore(flags);
+		return -EINVAL;
+	}
+
+	regmap_update_bits(i2s_tdm->regmap, I2S_XFER, msk, val);
+	local_irq_restore(flags);
+
+	dev_dbg(i2s_tdm->dev, "STREAM[%d]: TDM-H: %d, TDM-L: %d, I2S-H: %d, I2S-L: %d\n", stream,
+		CLK_MAX_COUNT - tdm_h, CLK_MAX_COUNT - tdm_l,
+		CLK_MAX_COUNT - i2s_h, CLK_MAX_COUNT - i2s_l);
+
+	return 0;
+}
+#endif
+
 static void rockchip_i2s_tdm_xfer_start(struct rk_i2s_tdm_dev *i2s_tdm,
 					int stream)
 {
+#ifdef CONFIG_SND_SOC_ROCKCHIP_I2S_TDM_MULTI_LANES
+	if (i2s_tdm->is_tdm_multi_lanes) {
+		if (rockchip_i2s_tdm_multi_lanes_start(i2s_tdm, stream) != -ENOSYS)
+			return;
+	}
+#endif
 	if (i2s_tdm->clk_trcm) {
 		rockchip_i2s_tdm_reset_assert(i2s_tdm);
 		regmap_update_bits(i2s_tdm->regmap, I2S_XFER,
@@ -593,6 +957,9 @@
 	int stream = substream->stream;
 	int bstream = SNDRV_PCM_STREAM_LAST - stream;
 
+	/* store the current state, prepare for resume if necessary */
+	i2s_tdm->is_dma_active[bstream] = is_dma_active(i2s_tdm, bstream);
+
 	/* disable dma for both tx and rx */
 	rockchip_i2s_tdm_dma_ctrl(i2s_tdm, stream, 0);
 	rockchip_i2s_tdm_dma_ctrl(i2s_tdm, bstream, 0);
@@ -608,7 +975,8 @@
 	 * just resume bstream, because current stream will be
 	 * startup in the trigger-cmd-START
 	 */
-	rockchip_i2s_tdm_dma_ctrl(i2s_tdm, bstream, 1);
+	if (i2s_tdm->is_dma_active[bstream])
+		rockchip_i2s_tdm_dma_ctrl(i2s_tdm, bstream, 1);
 	rockchip_i2s_tdm_xfer_start(i2s_tdm, bstream);
 }
 
@@ -1235,6 +1603,32 @@
 	unsigned int reg_fmt, fmt;
 	int ret = 0;
 
+#ifdef CONFIG_SND_SOC_ROCKCHIP_I2S_TDM_MULTI_LANES
+	if (i2s_tdm->is_tdm_multi_lanes) {
+		unsigned int lanes = rockchip_i2s_tdm_get_lanes(i2s_tdm,
+								substream->stream);
+
+		switch (lanes) {
+		case 4:
+			ret = I2S_CHN_8;
+			break;
+		case 3:
+			ret = I2S_CHN_6;
+			break;
+		case 2:
+			ret = I2S_CHN_4;
+			break;
+		case 1:
+			ret = I2S_CHN_2;
+			break;
+		default:
+			ret = -EINVAL;
+			break;
+		}
+
+		return ret;
+	}
+#endif
 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
 		reg_fmt = I2S_TXCR;
 	else
@@ -1293,8 +1687,12 @@
 	struct clk *mclk;
 	int ret = 0;
 	unsigned int val = 0;
-	unsigned int mclk_rate, bclk_rate, div_bclk = 4, div_lrck = 64;
+	unsigned int mclk_rate, bclk_rate, lrck_rate, div_bclk = 4, div_lrck = 64;
 
+#ifdef CONFIG_SND_SOC_ROCKCHIP_I2S_TDM_MULTI_LANES
+	if (i2s_tdm->is_tdm_multi_lanes)
+		rockchip_i2s_tdm_multi_lanes_set_clk(substream, params, dai);
+#endif
 	dma_data = snd_soc_dai_get_dma_data(dai, substream);
 	dma_data->maxburst = MAXBURST_PER_FIFO * params_channels(params) / 2;
 
@@ -1307,13 +1705,14 @@
 		goto err;
 
 	mclk_rate = clk_get_rate(mclk);
-	bclk_rate = i2s_tdm->bclk_fs * params_rate(params);
+	lrck_rate = params_rate(params) * i2s_tdm->lrck_ratio;
+	bclk_rate = i2s_tdm->bclk_fs * lrck_rate;
 	if (!bclk_rate) {
 		ret = -EINVAL;
 		goto err;
 	}
 	div_bclk = DIV_ROUND_CLOSEST(mclk_rate, bclk_rate);
-	div_lrck = bclk_rate / params_rate(params);
+	div_lrck = bclk_rate / lrck_rate;
 
 	switch (params_format(params)) {
 	case SNDRV_PCM_FORMAT_S8:
@@ -1542,10 +1941,116 @@
 	return 0;
 }
 
+static const char * const rpaths_text[] = {
+	"From SDI0", "From SDI1", "From SDI2", "From SDI3" };
+
+static const char * const tpaths_text[] = {
+	"From PATH0", "From PATH1", "From PATH2", "From PATH3" };
+
+/* TXCR */
+static SOC_ENUM_SINGLE_DECL(tpath3_enum, I2S_TXCR, 29, tpaths_text);
+static SOC_ENUM_SINGLE_DECL(tpath2_enum, I2S_TXCR, 27, tpaths_text);
+static SOC_ENUM_SINGLE_DECL(tpath1_enum, I2S_TXCR, 25, tpaths_text);
+static SOC_ENUM_SINGLE_DECL(tpath0_enum, I2S_TXCR, 23, tpaths_text);
+
+/* RXCR */
+static SOC_ENUM_SINGLE_DECL(rpath3_enum, I2S_RXCR, 23, rpaths_text);
+static SOC_ENUM_SINGLE_DECL(rpath2_enum, I2S_RXCR, 21, rpaths_text);
+static SOC_ENUM_SINGLE_DECL(rpath1_enum, I2S_RXCR, 19, rpaths_text);
+static SOC_ENUM_SINGLE_DECL(rpath0_enum, I2S_RXCR, 17, rpaths_text);
+
+static int rockchip_i2s_tdm_wait_time_info(struct snd_kcontrol *kcontrol,
+					   struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = WAIT_TIME_MS_MAX;
+	uinfo->value.integer.step = 1;
+
+	return 0;
+}
+
+static int rockchip_i2s_tdm_rd_wait_time_get(struct snd_kcontrol *kcontrol,
+					     struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+	struct rk_i2s_tdm_dev *i2s_tdm = snd_soc_component_get_drvdata(component);
+
+	ucontrol->value.integer.value[0] = i2s_tdm->wait_time[SNDRV_PCM_STREAM_CAPTURE];
+
+	return 0;
+}
+
+static int rockchip_i2s_tdm_rd_wait_time_put(struct snd_kcontrol *kcontrol,
+					     struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+	struct rk_i2s_tdm_dev *i2s_tdm = snd_soc_component_get_drvdata(component);
+
+	if (ucontrol->value.integer.value[0] > WAIT_TIME_MS_MAX)
+		return -EINVAL;
+
+	i2s_tdm->wait_time[SNDRV_PCM_STREAM_CAPTURE] = ucontrol->value.integer.value[0];
+
+	return 1;
+}
+
+static int rockchip_i2s_tdm_wr_wait_time_get(struct snd_kcontrol *kcontrol,
+					     struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+	struct rk_i2s_tdm_dev *i2s_tdm = snd_soc_component_get_drvdata(component);
+
+	ucontrol->value.integer.value[0] = i2s_tdm->wait_time[SNDRV_PCM_STREAM_PLAYBACK];
+
+	return 0;
+}
+
+static int rockchip_i2s_tdm_wr_wait_time_put(struct snd_kcontrol *kcontrol,
+					     struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+	struct rk_i2s_tdm_dev *i2s_tdm = snd_soc_component_get_drvdata(component);
+
+	if (ucontrol->value.integer.value[0] > WAIT_TIME_MS_MAX)
+		return -EINVAL;
+
+	i2s_tdm->wait_time[SNDRV_PCM_STREAM_PLAYBACK] = ucontrol->value.integer.value[0];
+
+	return 1;
+}
+
+#define SAI_PCM_WAIT_TIME(xname, xhandler_get, xhandler_put)	\
+{	.iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = xname,	\
+	.info = rockchip_i2s_tdm_wait_time_info,			\
+	.get = xhandler_get, .put = xhandler_put }
+
 static const struct snd_kcontrol_new rockchip_i2s_tdm_snd_controls[] = {
+	SOC_ENUM("Receive PATH3 Source Select", rpath3_enum),
+	SOC_ENUM("Receive PATH2 Source Select", rpath2_enum),
+	SOC_ENUM("Receive PATH1 Source Select", rpath1_enum),
+	SOC_ENUM("Receive PATH0 Source Select", rpath0_enum),
+	SOC_ENUM("Transmit SDO3 Source Select", tpath3_enum),
+	SOC_ENUM("Transmit SDO2 Source Select", tpath2_enum),
+	SOC_ENUM("Transmit SDO1 Source Select", tpath1_enum),
+	SOC_ENUM("Transmit SDO0 Source Select", tpath0_enum),
+
 	SOC_ENUM_EXT("I2STDM Digital Loopback Mode", loopback_mode,
 		     rockchip_i2s_tdm_loopback_get,
 		     rockchip_i2s_tdm_loopback_put),
+#ifdef CONFIG_SND_SOC_ROCKCHIP_I2S_TDM_MULTI_LANES
+	SOC_ENUM_EXT("Transmit SDOx Select", tx_lanes_enum,
+		     rockchip_i2s_tdm_tx_lanes_get, rockchip_i2s_tdm_tx_lanes_put),
+	SOC_ENUM_EXT("Receive SDIx Select", rx_lanes_enum,
+		     rockchip_i2s_tdm_rx_lanes_get, rockchip_i2s_tdm_rx_lanes_put),
+#endif
+	SAI_PCM_WAIT_TIME("PCM Read Wait Time MS",
+			  rockchip_i2s_tdm_rd_wait_time_get,
+			  rockchip_i2s_tdm_rd_wait_time_put),
+	SAI_PCM_WAIT_TIME("PCM Write Wait Time MS",
+			  rockchip_i2s_tdm_wr_wait_time_get,
+			  rockchip_i2s_tdm_wr_wait_time_put),
 };
 
 static int rockchip_i2s_tdm_dai_probe(struct snd_soc_dai *dai)
@@ -1556,7 +2061,9 @@
 	dai->playback_dma_data = &i2s_tdm->playback_dma_data;
 
 	if (i2s_tdm->mclk_calibrate)
-		snd_soc_add_dai_controls(dai, &rockchip_i2s_tdm_compensation_control, 1);
+		snd_soc_add_component_controls(dai->component,
+					       &rockchip_i2s_tdm_compensation_control,
+					       1);
 
 	return 0;
 }
@@ -1587,11 +2094,15 @@
 				    struct snd_soc_dai *dai)
 {
 	struct rk_i2s_tdm_dev *i2s_tdm = snd_soc_dai_get_drvdata(dai);
+	int stream = substream->stream;
 
-	if (i2s_tdm->substreams[substream->stream])
+	if (i2s_tdm->substreams[stream])
 		return -EBUSY;
 
-	i2s_tdm->substreams[substream->stream] = substream;
+	if (i2s_tdm->wait_time[stream])
+		substream->wait_time = msecs_to_jiffies(i2s_tdm->wait_time[stream]);
+
+	i2s_tdm->substreams[stream] = substream;
 
 	return 0;
 }
@@ -1862,7 +2373,7 @@
 		.playback = {
 			.stream_name = "Playback",
 			.channels_min = 2,
-			.channels_max = 16,
+			.channels_max = 64,
 			.rates = SNDRV_PCM_RATE_8000_192000,
 			.formats = (SNDRV_PCM_FMTBIT_S8 |
 				    SNDRV_PCM_FMTBIT_S16_LE |
@@ -1874,7 +2385,7 @@
 		.capture = {
 			.stream_name = "Capture",
 			.channels_min = 2,
-			.channels_max = 16,
+			.channels_max = 64,
 			.rates = SNDRV_PCM_RATE_8000_192000,
 			.formats = (SNDRV_PCM_FMTBIT_S8 |
 				    SNDRV_PCM_FMTBIT_S16_LE |
@@ -2070,6 +2581,9 @@
 		dev_warn_ratelimited(i2s_tdm->dev, "TX FIFO Underrun\n");
 		regmap_update_bits(i2s_tdm->regmap, I2S_INTCR,
 				   I2S_INTCR_TXUIC, I2S_INTCR_TXUIC);
+		regmap_update_bits(i2s_tdm->regmap, I2S_INTCR,
+				   I2S_INTCR_TXUIE_MASK,
+				   I2S_INTCR_TXUIE(0));
 		substream = i2s_tdm->substreams[SNDRV_PCM_STREAM_PLAYBACK];
 		if (substream)
 			snd_pcm_stop_xrun(substream);
@@ -2079,12 +2593,53 @@
 		dev_warn_ratelimited(i2s_tdm->dev, "RX FIFO Overrun\n");
 		regmap_update_bits(i2s_tdm->regmap, I2S_INTCR,
 				   I2S_INTCR_RXOIC, I2S_INTCR_RXOIC);
+		regmap_update_bits(i2s_tdm->regmap, I2S_INTCR,
+				   I2S_INTCR_RXOIE_MASK,
+				   I2S_INTCR_RXOIE(0));
 		substream = i2s_tdm->substreams[SNDRV_PCM_STREAM_CAPTURE];
 		if (substream)
 			snd_pcm_stop_xrun(substream);
 	}
 
 	return IRQ_HANDLED;
+}
+
+static int rockchip_i2s_tdm_keep_clk_always_on(struct rk_i2s_tdm_dev *i2s_tdm)
+{
+	unsigned int mclk_rate = DEFAULT_FS * DEFAULT_MCLK_FS;
+	unsigned int bclk_rate = i2s_tdm->bclk_fs * DEFAULT_FS;
+	unsigned int div_lrck = i2s_tdm->bclk_fs;
+	unsigned int div_bclk;
+	int ret;
+
+	div_bclk = DIV_ROUND_CLOSEST(mclk_rate, bclk_rate);
+
+	/* assign generic freq */
+	clk_set_rate(i2s_tdm->mclk_rx, mclk_rate);
+	clk_set_rate(i2s_tdm->mclk_tx, mclk_rate);
+
+	ret = rockchip_i2s_tdm_mclk_reparent(i2s_tdm);
+	if (ret)
+		return ret;
+
+	regmap_update_bits(i2s_tdm->regmap, I2S_CLKDIV,
+			   I2S_CLKDIV_RXM_MASK | I2S_CLKDIV_TXM_MASK,
+			   I2S_CLKDIV_RXM(div_bclk) | I2S_CLKDIV_TXM(div_bclk));
+	regmap_update_bits(i2s_tdm->regmap, I2S_CKR,
+			   I2S_CKR_RSD_MASK | I2S_CKR_TSD_MASK,
+			   I2S_CKR_RSD(div_lrck) | I2S_CKR_TSD(div_lrck));
+
+	if (i2s_tdm->clk_trcm)
+		rockchip_i2s_tdm_xfer_trcm_start(i2s_tdm);
+	else
+		rockchip_i2s_tdm_xfer_start(i2s_tdm, SNDRV_PCM_STREAM_PLAYBACK);
+
+	pm_runtime_forbid(i2s_tdm->dev);
+
+	dev_info(i2s_tdm->dev, "CLK-ALWAYS-ON: mclk: %d, bclk: %d, fsync: %d\n",
+		 mclk_rate, bclk_rate, DEFAULT_FS);
+
+	return 0;
 }
 
 static int rockchip_i2s_tdm_probe(struct platform_device *pdev)
@@ -2109,10 +2664,63 @@
 		return -ENOMEM;
 
 	i2s_tdm->dev = &pdev->dev;
+	i2s_tdm->lrck_ratio = 1;
 
 	of_id = of_match_device(rockchip_i2s_tdm_match, &pdev->dev);
 	if (!of_id)
 		return -EINVAL;
+
+#ifdef CONFIG_SND_SOC_ROCKCHIP_I2S_TDM_MULTI_LANES
+	i2s_tdm->is_tdm_multi_lanes =
+		device_property_read_bool(i2s_tdm->dev, "rockchip,tdm-multi-lanes");
+
+	if (i2s_tdm->is_tdm_multi_lanes) {
+		struct device_node *clk_src_node = NULL;
+
+		i2s_tdm->tx_lanes = 1;
+		i2s_tdm->rx_lanes = 1;
+
+		if (!device_property_read_u32(i2s_tdm->dev, "rockchip,tdm-tx-lanes", &val)) {
+			if ((val >= 1) && (val <= 4))
+				i2s_tdm->tx_lanes = val;
+		}
+
+		if (!device_property_read_u32(i2s_tdm->dev, "rockchip,tdm-rx-lanes", &val)) {
+			if ((val >= 1) && (val <= 4))
+				i2s_tdm->rx_lanes = val;
+		}
+
+		i2s_tdm->i2s_lrck_gpio = devm_gpiod_get_optional(&pdev->dev, "i2s-lrck", GPIOD_IN);
+		if (IS_ERR(i2s_tdm->i2s_lrck_gpio)) {
+			ret = PTR_ERR(i2s_tdm->i2s_lrck_gpio);
+			dev_err(&pdev->dev, "Failed to get i2s_lrck_gpio %d\n", ret);
+			return ret;
+		}
+
+		i2s_tdm->tdm_fsync_gpio = devm_gpiod_get_optional(&pdev->dev, "tdm-fsync", GPIOD_IN);
+		if (IS_ERR(i2s_tdm->tdm_fsync_gpio)) {
+			ret = PTR_ERR(i2s_tdm->tdm_fsync_gpio);
+			dev_err(&pdev->dev, "Failed to get tdm_fsync_gpio %d\n", ret);
+			return ret;
+		}
+
+		/* It's optional, required when use soc clk src, such as: i2s2_2ch */
+		clk_src_node = of_parse_phandle(node, "rockchip,clk-src", 0);
+		if (clk_src_node) {
+			i2s_tdm->clk_src_base = of_iomap(clk_src_node, 0);
+			if (!i2s_tdm->clk_src_base)
+				return -ENOENT;
+
+			i2s_tdm->clk_src_dai = rockchip_i2s_tdm_find_dai(clk_src_node);
+			if (!i2s_tdm->clk_src_dai)
+				return -EPROBE_DEFER;
+
+			pm_runtime_forbid(i2s_tdm->clk_src_dai->dev);
+		}
+
+		dev_info(&pdev->dev, "Used as TDM_MULTI_LANES mode\n");
+	}
+#endif
 
 	spin_lock_init(&i2s_tdm->lock);
 	i2s_tdm->soc_data = (const struct rk_i2s_soc_data *)of_id->data;
@@ -2145,6 +2753,15 @@
 		soc_dai->playback.channels_min = 0;
 
 	i2s_tdm->grf = syscon_regmap_lookup_by_phandle(node, "rockchip,grf");
+
+	i2s_tdm->pinctrl = devm_pinctrl_get(&pdev->dev);
+	if (!IS_ERR_OR_NULL(i2s_tdm->pinctrl)) {
+		i2s_tdm->clk_state = pinctrl_lookup_state(i2s_tdm->pinctrl, "clk");
+		if (IS_ERR(i2s_tdm->clk_state)) {
+			i2s_tdm->clk_state = NULL;
+			dev_dbg(i2s_tdm->dev, "Have no clk pinctrl state\n");
+		}
+	}
 
 #ifdef HAVE_SYNC_RESET
 	sync = of_device_is_compatible(node, "rockchip,px30-i2s-tdm") ||
@@ -2264,43 +2881,6 @@
 	atomic_set(&i2s_tdm->refcount, 0);
 	dev_set_drvdata(&pdev->dev, i2s_tdm);
 
-	pm_runtime_enable(&pdev->dev);
-	if (!pm_runtime_enabled(&pdev->dev)) {
-		ret = i2s_tdm_runtime_resume(&pdev->dev);
-		if (ret)
-			goto err_pm_disable;
-	}
-
-	if (i2s_tdm->quirks & QUIRK_ALWAYS_ON) {
-		unsigned int rate = DEFAULT_FS * DEFAULT_MCLK_FS;
-		unsigned int div_bclk = DEFAULT_FS * DEFAULT_MCLK_FS;
-		unsigned int div_lrck = i2s_tdm->bclk_fs;
-
-		div_bclk = DIV_ROUND_CLOSEST(rate, div_lrck * DEFAULT_FS);
-
-		/* assign generic freq */
-		clk_set_rate(i2s_tdm->mclk_rx, rate);
-		clk_set_rate(i2s_tdm->mclk_tx, rate);
-
-		ret = rockchip_i2s_tdm_mclk_reparent(i2s_tdm);
-		if (ret)
-			goto err_pm_disable;
-
-		regmap_update_bits(i2s_tdm->regmap, I2S_CLKDIV,
-				   I2S_CLKDIV_RXM_MASK | I2S_CLKDIV_TXM_MASK,
-				   I2S_CLKDIV_RXM(div_bclk) | I2S_CLKDIV_TXM(div_bclk));
-		regmap_update_bits(i2s_tdm->regmap, I2S_CKR,
-				   I2S_CKR_RSD_MASK | I2S_CKR_TSD_MASK,
-				   I2S_CKR_RSD(div_lrck) | I2S_CKR_TSD(div_lrck));
-
-		if (i2s_tdm->clk_trcm)
-			rockchip_i2s_tdm_xfer_trcm_start(i2s_tdm);
-		else
-			rockchip_i2s_tdm_xfer_start(i2s_tdm, SNDRV_PCM_STREAM_PLAYBACK);
-
-		pm_runtime_forbid(&pdev->dev);
-	}
-
 	regmap_update_bits(i2s_tdm->regmap, I2S_DMACR, I2S_DMACR_TDL_MASK,
 			   I2S_DMACR_TDL(16));
 	regmap_update_bits(i2s_tdm->regmap, I2S_DMACR, I2S_DMACR_RDL_MASK,
@@ -2311,6 +2891,34 @@
 	if (i2s_tdm->soc_data && i2s_tdm->soc_data->init)
 		i2s_tdm->soc_data->init(&pdev->dev, res->start);
 
+	/*
+	 * CLK_ALWAYS_ON should be placed after all registers write done,
+	 * because this situation will enable XFER bit which will make
+	 * some registers(depend on XFER) write failed.
+	 */
+	if (i2s_tdm->quirks & QUIRK_ALWAYS_ON) {
+		ret = rockchip_i2s_tdm_keep_clk_always_on(i2s_tdm);
+		if (ret)
+			return ret;
+	}
+
+	/*
+	 * MUST: after pm_runtime_enable step, any register R/W
+	 * should be wrapped with pm_runtime_get_sync/put.
+	 *
+	 * Another approach is to enable the regcache true to
+	 * avoid access HW registers.
+	 *
+	 * Alternatively, performing the registers R/W before
+	 * pm_runtime_enable is also a good option.
+	 */
+	pm_runtime_enable(&pdev->dev);
+	if (!pm_runtime_enabled(&pdev->dev)) {
+		ret = i2s_tdm_runtime_resume(&pdev->dev);
+		if (ret)
+			goto err_pm_disable;
+	}
+
 	ret = devm_snd_soc_register_component(&pdev->dev,
 					      &rockchip_i2s_tdm_component,
 					      soc_dai, 1);
diff --git a/kernel/sound/soc/rockchip/rockchip_multi_dais.c b/kernel/sound/soc/rockchip/rockchip_multi_dais.c
index e52be21..d4994f6 100644
--- a/kernel/sound/soc/rockchip/rockchip_multi_dais.c
+++ b/kernel/sound/soc/rockchip/rockchip_multi_dais.c
@@ -23,6 +23,8 @@
 #define DAIS_DRV_NAME		"rockchip-mdais"
 #define RK3308_GRF_SOC_CON2	0x308
 
+#define SOUND_NAME_PREFIX	"sound-name-prefix"
+
 static inline struct rk_mdais_dev *to_info(struct snd_soc_dai *dai)
 {
 	return snd_soc_dai_get_drvdata(dai);
@@ -220,13 +222,23 @@
 static int rockchip_mdais_dai_probe(struct snd_soc_dai *dai)
 {
 	struct rk_mdais_dev *mdais = to_info(dai);
+	struct snd_soc_component *comp;
 	struct snd_soc_dai *child;
+	const char *str;
 	int ret, i = 0;
 
 	for (i = 0; i < mdais->num_dais; i++) {
 		child = mdais->dais[i].dai;
+		comp = child->component;
 		if (!child->probed && child->driver->probe) {
-			child->component->card = dai->component->card;
+			if (!comp->name_prefix) {
+				ret = device_property_read_string(child->dev,
+								  SOUND_NAME_PREFIX, &str);
+				if (!ret)
+					comp->name_prefix = str;
+			}
+
+			comp->card = dai->component->card;
 			ret = child->driver->probe(child);
 			if (ret < 0) {
 				dev_err(child->dev,
@@ -234,6 +246,14 @@
 					child->name, ret);
 				return ret;
 			}
+
+			ret = snd_soc_add_component_controls(comp,
+							     comp->driver->controls,
+							     comp->driver->num_controls);
+			if (ret)
+				dev_err(dai->dev, "%s: Failed to add controls, should add '%s' in DT\n",
+					dev_name(child->dev), SOUND_NAME_PREFIX);
+
 			dai->probed = 1;
 		}
 	}
@@ -383,9 +403,9 @@
 		.probe = rockchip_mdais_dai_probe,
 		.playback = {
 			.stream_name = "Playback",
-			.channels_min = 2,
-			.channels_max = 32,
-			.rates = SNDRV_PCM_RATE_8000_192000,
+			.channels_min = 1,
+			.channels_max = 512,
+			.rates = SNDRV_PCM_RATE_8000_384000,
 			.formats = (SNDRV_PCM_FMTBIT_S8 |
 				    SNDRV_PCM_FMTBIT_S16_LE |
 				    SNDRV_PCM_FMTBIT_S20_3LE |
@@ -394,9 +414,9 @@
 		},
 		.capture = {
 			.stream_name = "Capture",
-			.channels_min = 2,
-			.channels_max = 32,
-			.rates = SNDRV_PCM_RATE_8000_192000,
+			.channels_min = 1,
+			.channels_max = 512,
+			.rates = SNDRV_PCM_RATE_8000_384000,
 			.formats = (SNDRV_PCM_FMTBIT_S8 |
 				    SNDRV_PCM_FMTBIT_S16_LE |
 				    SNDRV_PCM_FMTBIT_S20_3LE |
diff --git a/kernel/sound/soc/rockchip/rockchip_multi_dais_pcm.c b/kernel/sound/soc/rockchip/rockchip_multi_dais_pcm.c
index d69395f..88daa5d 100644
--- a/kernel/sound/soc/rockchip/rockchip_multi_dais_pcm.c
+++ b/kernel/sound/soc/rockchip/rockchip_multi_dais_pcm.c
@@ -20,6 +20,10 @@
 #define MAX_FIFO_SIZE	32 /* max fifo size in frames */
 #define SND_DMAENGINE_MPCM_DRV_NAME "snd_dmaengine_mpcm"
 
+static unsigned int prealloc_buffer_size_kbytes = 512;
+module_param(prealloc_buffer_size_kbytes, uint, 0444);
+MODULE_PARM_DESC(prealloc_buffer_size_kbytes, "Preallocate DMA buffer size (KB).");
+
 struct dmaengine_mpcm {
 	struct rk_mdais_dev *mdais;
 	struct dma_chan *tx_chans[MAX_DAIS];
@@ -563,7 +567,7 @@
 	size_t max_buffer_size;
 	unsigned int i;
 
-	prealloc_buffer_size = 512 * 1024;
+	prealloc_buffer_size = prealloc_buffer_size_kbytes * 1024;
 	max_buffer_size = SIZE_MAX;
 
 	for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_CAPTURE; i++) {
@@ -685,6 +689,9 @@
 	if (!pcm)
 		return -ENOMEM;
 
+#ifdef CONFIG_DEBUG_FS
+	pcm->component.debugfs_prefix = "dma";
+#endif
 	pcm->mdais = mdais;
 	for (i = 0; i < num; i++) {
 		child = mdais->dais[i].dev;
diff --git a/kernel/sound/soc/rockchip/rockchip_pdm.c b/kernel/sound/soc/rockchip/rockchip_pdm.c
index ae529a6..43c954d 100644
--- a/kernel/sound/soc/rockchip/rockchip_pdm.c
+++ b/kernel/sound/soc/rockchip/rockchip_pdm.c
@@ -10,6 +10,7 @@
 #include <linux/clk/rockchip.h>
 #include <linux/of.h>
 #include <linux/of_device.h>
+#include <linux/pinctrl/consumer.h>
 #include <linux/pm_runtime.h>
 #include <linux/rational.h>
 #include <linux/regmap.h>
@@ -48,6 +49,8 @@
 	struct regmap *regmap;
 	struct snd_dmaengine_dai_dma_data capture_dma_data;
 	struct reset_control *reset;
+	struct pinctrl *pinctrl;
+	struct pinctrl_state *clk_state;
 	unsigned int start_delay_ms;
 	unsigned int filter_delay_ms;
 	enum rk_pdm_version version;
@@ -566,7 +569,20 @@
 	return 1;
 }
 
+static const char * const rpaths_text[] = {
+	"From SDI0", "From SDI1", "From SDI2", "From SDI3" };
+
+static SOC_ENUM_SINGLE_DECL(rpath3_enum, PDM_CLK_CTRL, 14, rpaths_text);
+static SOC_ENUM_SINGLE_DECL(rpath2_enum, PDM_CLK_CTRL, 12, rpaths_text);
+static SOC_ENUM_SINGLE_DECL(rpath1_enum, PDM_CLK_CTRL, 10, rpaths_text);
+static SOC_ENUM_SINGLE_DECL(rpath0_enum, PDM_CLK_CTRL, 8, rpaths_text);
+
 static const struct snd_kcontrol_new rockchip_pdm_controls[] = {
+	SOC_ENUM("Receive PATH3 Source Select", rpath3_enum),
+	SOC_ENUM("Receive PATH2 Source Select", rpath2_enum),
+	SOC_ENUM("Receive PATH1 Source Select", rpath1_enum),
+	SOC_ENUM("Receive PATH0 Source Select", rpath0_enum),
+
 	{
 		.iface = SNDRV_CTL_ELEM_IFACE_PCM,
 		.name = "PDM Start Delay Ms",
@@ -637,10 +653,12 @@
 	struct rk_pdm_dev *pdm = to_info(dai);
 
 	dai->capture_dma_data = &pdm->capture_dma_data;
-	snd_soc_add_dai_controls(dai, rockchip_pdm_controls,
-				 ARRAY_SIZE(rockchip_pdm_controls));
+
 	if (pdm->clk_calibrate)
-		snd_soc_add_dai_controls(dai, &rockchip_pdm_compensation_control, 1);
+		snd_soc_add_component_controls(dai->component,
+					       &rockchip_pdm_compensation_control,
+					       1);
+
 	return 0;
 }
 
@@ -721,7 +739,34 @@
 
 static const struct snd_soc_component_driver rockchip_pdm_component = {
 	.name = "rockchip-pdm",
+	.controls = rockchip_pdm_controls,
+	.num_controls = ARRAY_SIZE(rockchip_pdm_controls),
 };
+
+static int rockchip_pdm_pinctrl_select_clk_state(struct device *dev)
+{
+	struct rk_pdm_dev *pdm = dev_get_drvdata(dev);
+
+	if (IS_ERR_OR_NULL(pdm->pinctrl) || !pdm->clk_state)
+		return 0;
+
+	/*
+	 * A necessary delay to make sure the correct
+	 * frac div has been applied when resume from
+	 * power down.
+	 */
+	udelay(10);
+
+	/*
+	 * Must disable the clk to avoid clk glitch
+	 * when pinctrl switch from gpio to pdm clk.
+	 */
+	clk_disable_unprepare(pdm->clk);
+	pinctrl_select_state(pdm->pinctrl, pdm->clk_state);
+	clk_prepare_enable(pdm->clk);
+
+	return 0;
+}
 
 static int rockchip_pdm_runtime_suspend(struct device *dev)
 {
@@ -730,6 +775,8 @@
 	regcache_cache_only(pdm->regmap, true);
 	clk_disable_unprepare(pdm->clk);
 	clk_disable_unprepare(pdm->hclk);
+
+	pinctrl_pm_select_idle_state(dev);
 
 	return 0;
 }
@@ -740,26 +787,31 @@
 	int ret;
 
 	ret = clk_prepare_enable(pdm->clk);
-	if (ret) {
-		dev_err(pdm->dev, "clock enable failed %d\n", ret);
-		return ret;
-	}
+	if (ret)
+		goto err_clk;
 
 	ret = clk_prepare_enable(pdm->hclk);
-	if (ret) {
-		dev_err(pdm->dev, "hclock enable failed %d\n", ret);
-		return ret;
-	}
+	if (ret)
+		goto err_hclk;
 
-	rockchip_pdm_rxctrl(pdm, 0);
 	regcache_cache_only(pdm->regmap, false);
 	regcache_mark_dirty(pdm->regmap);
 	ret = regcache_sync(pdm->regmap);
-	if (ret) {
-		clk_disable_unprepare(pdm->clk);
-		clk_disable_unprepare(pdm->hclk);
-	}
+	if (ret)
+		goto err_regmap;
+
+	rockchip_pdm_rxctrl(pdm, 0);
+
+	rockchip_pdm_pinctrl_select_clk_state(dev);
+
 	return 0;
+
+err_regmap:
+	clk_disable_unprepare(pdm->hclk);
+err_hclk:
+	clk_disable_unprepare(pdm->clk);
+err_clk:
+	return ret;
 }
 
 static bool rockchip_pdm_wr_reg(struct device *dev, unsigned int reg)
@@ -933,6 +985,15 @@
 	pdm->dev = &pdev->dev;
 	dev_set_drvdata(&pdev->dev, pdm);
 
+	pdm->pinctrl = devm_pinctrl_get(&pdev->dev);
+	if (!IS_ERR_OR_NULL(pdm->pinctrl)) {
+		pdm->clk_state = pinctrl_lookup_state(pdm->pinctrl, "clk");
+		if (IS_ERR(pdm->clk_state)) {
+			pdm->clk_state = NULL;
+			dev_dbg(pdm->dev, "Have no clk pinctrl state\n");
+		}
+	}
+
 	pdm->start_delay_ms = PDM_START_DELAY_MS_DEFAULT;
 	pdm->filter_delay_ms = PDM_FILTER_DELAY_MS_MIN;
 
@@ -959,6 +1020,23 @@
 	if (ret)
 		return ret;
 
+	rockchip_pdm_set_samplerate(pdm, PDM_DEFAULT_RATE);
+	rockchip_pdm_rxctrl(pdm, 0);
+
+	ret = rockchip_pdm_path_parse(pdm, node);
+	if (ret != 0 && ret != -ENOENT)
+		goto err_clk;
+
+	/*
+	 * MUST: after pm_runtime_enable step, any register R/W
+	 * should be wrapped with pm_runtime_get_sync/put.
+	 *
+	 * Another approach is to enable the regcache true to
+	 * avoid access HW registers.
+	 *
+	 * Alternatively, performing the registers R/W before
+	 * pm_runtime_enable is also a good option.
+	 */
 	pm_runtime_enable(&pdev->dev);
 	if (!pm_runtime_enabled(&pdev->dev)) {
 		ret = rockchip_pdm_runtime_resume(&pdev->dev);
@@ -975,13 +1053,6 @@
 		goto err_suspend;
 	}
 
-	rockchip_pdm_set_samplerate(pdm, PDM_DEFAULT_RATE);
-	rockchip_pdm_rxctrl(pdm, 0);
-
-	ret = rockchip_pdm_path_parse(pdm, node);
-	if (ret != 0 && ret != -ENOENT)
-		goto err_suspend;
-
 	if (of_property_read_bool(node, "rockchip,no-dmaengine")) {
 		dev_info(&pdev->dev, "Used for Multi-DAI\n");
 		return 0;
@@ -993,6 +1064,8 @@
 		goto err_suspend;
 	}
 
+	clk_disable_unprepare(pdm->hclk);
+
 	return 0;
 
 err_suspend:
@@ -1000,7 +1073,7 @@
 		rockchip_pdm_runtime_suspend(&pdev->dev);
 err_pm_disable:
 	pm_runtime_disable(&pdev->dev);
-
+err_clk:
 	clk_disable_unprepare(pdm->hclk);
 
 	return ret;
@@ -1008,14 +1081,9 @@
 
 static int rockchip_pdm_remove(struct platform_device *pdev)
 {
-	struct rk_pdm_dev *pdm = dev_get_drvdata(&pdev->dev);
-
 	pm_runtime_disable(&pdev->dev);
 	if (!pm_runtime_status_suspended(&pdev->dev))
 		rockchip_pdm_runtime_suspend(&pdev->dev);
-
-	clk_disable_unprepare(pdm->clk);
-	clk_disable_unprepare(pdm->hclk);
 
 	return 0;
 }
diff --git a/kernel/sound/soc/rockchip/rockchip_sai.c b/kernel/sound/soc/rockchip/rockchip_sai.c
index 3e50ba0..6609c1f 100644
--- a/kernel/sound/soc/rockchip/rockchip_sai.c
+++ b/kernel/sound/soc/rockchip/rockchip_sai.c
@@ -22,6 +22,7 @@
 
 #define DRV_NAME		"rockchip-sai"
 
+#define CLK_SHIFT_RATE_HZ_MAX	1 /* 1 Hz */
 #define FW_RATIO_MAX		8
 #define FW_RATIO_MIN		1
 #define MAXBURST_PER_FIFO	8
@@ -496,9 +497,10 @@
 		if (sai->is_clk_auto)
 			clk_set_rate(sai->mclk, bclk_rate);
 		mclk_rate = clk_get_rate(sai->mclk);
-		if (mclk_rate < bclk_rate) {
-			dev_err(sai->dev, "Mismatch mclk: %u, expected %u at least\n",
-				mclk_rate, bclk_rate);
+		if (mclk_rate < bclk_rate - CLK_SHIFT_RATE_HZ_MAX ||
+		    mclk_rate > bclk_rate + CLK_SHIFT_RATE_HZ_MAX) {
+			dev_err(sai->dev, "Mismatch mclk: %u, expected %u (+/- %dHz)\n",
+				mclk_rate, bclk_rate, CLK_SHIFT_RATE_HZ_MAX);
 			return -EINVAL;
 		}
 
@@ -506,6 +508,17 @@
 
 		regmap_update_bits(sai->regmap, SAI_CKR, SAI_CKR_MDIV_MASK,
 				   SAI_CKR_MDIV(div_bclk));
+	}
+
+	return 0;
+}
+
+static int rockchip_sai_prepare(struct snd_pcm_substream *substream,
+				struct snd_soc_dai *dai)
+{
+	struct rk_sai_dev *sai = snd_soc_dai_get_drvdata(dai);
+
+	if (sai->is_master_mode) {
 		/*
 		 * Should wait for one BCLK ready after DIV and then ungate
 		 * output clk to achieve the clean clk.
@@ -628,6 +641,7 @@
 	.hw_params = rockchip_sai_hw_params,
 	.set_sysclk = rockchip_sai_set_sysclk,
 	.set_fmt = rockchip_sai_set_fmt,
+	.prepare = rockchip_sai_prepare,
 	.trigger = rockchip_sai_trigger,
 	.set_tdm_slot = rockchip_sai_set_tdm_slot,
 };
@@ -783,8 +797,8 @@
 	if (sai->has_playback) {
 		dai->playback.stream_name = "Playback";
 		dai->playback.channels_min = 1;
-		dai->playback.channels_max = 128;
-		dai->playback.rates = SNDRV_PCM_RATE_8000_192000;
+		dai->playback.channels_max = 512;
+		dai->playback.rates = SNDRV_PCM_RATE_8000_384000;
 		dai->playback.formats = SNDRV_PCM_FMTBIT_S8 |
 					SNDRV_PCM_FMTBIT_S16_LE |
 					SNDRV_PCM_FMTBIT_S24_LE |
@@ -799,8 +813,8 @@
 	if (sai->has_capture) {
 		dai->capture.stream_name = "Capture";
 		dai->capture.channels_min = 1;
-		dai->capture.channels_max = 128;
-		dai->capture.rates = SNDRV_PCM_RATE_8000_192000;
+		dai->capture.channels_max = 512;
+		dai->capture.rates = SNDRV_PCM_RATE_8000_384000;
 		dai->capture.formats = SNDRV_PCM_FMTBIT_S8 |
 				       SNDRV_PCM_FMTBIT_S16_LE |
 				       SNDRV_PCM_FMTBIT_S24_LE |
@@ -855,14 +869,14 @@
 	"From SDO0", "From SDO1", "From SDO2", "From SDO3" };
 
 static const char * const lps_text[] = { "Disable", "Enable" };
-static const char * const sync_out_text[] = { "External", "Internal" };
-static const char * const sync_in_text[] = { "External", "Internal" };
+static const char * const sync_out_text[] = { "From CRU", "From IO" };
+static const char * const sync_in_text[] = { "From IO", "From Sync Port" };
 
 static const char * const rpaths_text[] = {
 	"From SDI0", "From SDI1", "From SDI2", "From SDI3" };
 
 static const char * const tpaths_text[] = {
-	"To SDO0", "To SDO1", "To SDO2", "To SDO3" };
+	"From PATH0", "From PATH1", "From PATH2", "From PATH3" };
 
 /* TXCR */
 static SOC_ENUM_SINGLE_DECL(tsft_enum, SAI_TXCR, 22, edge_shift_text);
@@ -919,8 +933,8 @@
 static SOC_ENUM_SINGLE_DECL(tpath1_enum, SAI_PATH_SEL, 2, tpaths_text);
 static SOC_ENUM_SINGLE_DECL(tpath0_enum, SAI_PATH_SEL, 0, tpaths_text);
 
-static int rockchip_sai_fpw_get(struct snd_kcontrol *kcontrol,
-				struct snd_ctl_elem_value *ucontrol)
+static int __maybe_unused rockchip_sai_fpw_get(struct snd_kcontrol *kcontrol,
+					       struct snd_ctl_elem_value *ucontrol)
 {
 	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
 	struct rk_sai_dev *sai = snd_soc_component_get_drvdata(component);
@@ -930,8 +944,8 @@
 	return 0;
 }
 
-static int rockchip_sai_fpw_put(struct snd_kcontrol *kcontrol,
-				struct snd_ctl_elem_value *ucontrol)
+static int __maybe_unused rockchip_sai_fpw_put(struct snd_kcontrol *kcontrol,
+					       struct snd_ctl_elem_value *ucontrol)
 {
 	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
 	struct rk_sai_dev *sai = snd_soc_component_get_drvdata(component);
@@ -946,8 +960,8 @@
 	return 1;
 }
 
-static int rockchip_sai_fw_ratio_get(struct snd_kcontrol *kcontrol,
-				     struct snd_ctl_elem_value *ucontrol)
+static int __maybe_unused rockchip_sai_fw_ratio_get(struct snd_kcontrol *kcontrol,
+						    struct snd_ctl_elem_value *ucontrol)
 {
 	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
 	struct rk_sai_dev *sai = snd_soc_component_get_drvdata(component);
@@ -957,8 +971,8 @@
 	return 0;
 }
 
-static int rockchip_sai_fw_ratio_put(struct snd_kcontrol *kcontrol,
-				     struct snd_ctl_elem_value *ucontrol)
+static int __maybe_unused rockchip_sai_fw_ratio_put(struct snd_kcontrol *kcontrol,
+						    struct snd_ctl_elem_value *ucontrol)
 {
 	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
 	struct rk_sai_dev *sai = snd_soc_component_get_drvdata(component);
@@ -1026,8 +1040,8 @@
 	return 1;
 }
 
-static int rockchip_sai_mss_get(struct snd_kcontrol *kcontrol,
-				struct snd_ctl_elem_value *ucontrol)
+static int __maybe_unused rockchip_sai_mss_get(struct snd_kcontrol *kcontrol,
+					       struct snd_ctl_elem_value *ucontrol)
 {
 	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
 	struct rk_sai_dev *sai = snd_soc_component_get_drvdata(component);
@@ -1037,8 +1051,8 @@
 	return 0;
 }
 
-static int rockchip_sai_mss_put(struct snd_kcontrol *kcontrol,
-				struct snd_ctl_elem_value *ucontrol)
+static int __maybe_unused rockchip_sai_mss_put(struct snd_kcontrol *kcontrol,
+					       struct snd_ctl_elem_value *ucontrol)
 {
 	struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
 	struct rk_sai_dev *sai = snd_soc_component_get_drvdata(component);
@@ -1174,21 +1188,17 @@
 	.info = rockchip_sai_wait_time_info,			\
 	.get = xhandler_get, .put = xhandler_put }
 
-static DECLARE_TLV_DB_SCALE(fs_shift_tlv, 0, 8192, 0);
+static __maybe_unused DECLARE_TLV_DB_SCALE(fs_shift_tlv, 0, 8192, 0);
 
 static const struct snd_kcontrol_new rockchip_sai_controls[] = {
-
+#ifdef CONFIG_SND_SOC_ROCKCHIP_SAI_VERBOSE
 	SOC_ENUM("Transmit Edge Shift", tsft_enum),
-	SOC_ENUM_EXT("Transmit SDOx Select", tx_lanes_enum,
-		     rockchip_sai_tx_lanes_get, rockchip_sai_tx_lanes_put),
 	SOC_ENUM("Transmit Store Justified Mode", tsjm_enum),
 	SOC_ENUM("Transmit First Bit Mode", tfbm_enum),
 	SOC_ENUM("Transmit Valid Data Justified", tvdj_enum),
 	SOC_ENUM("Transmit Slot Bit Width", tsbw_enum),
 
 	SOC_ENUM("Receive Edge Shift", rsft_enum),
-	SOC_ENUM_EXT("Receive SDIx Select", rx_lanes_enum,
-		     rockchip_sai_rx_lanes_get, rockchip_sai_rx_lanes_put),
 	SOC_ENUM("Receive Store Justified Mode", rsjm_enum),
 	SOC_ENUM("Receive First Bit Mode", rfbm_enum),
 	SOC_ENUM("Receive Valid Data Justified", rvdj_enum),
@@ -1200,15 +1210,24 @@
 	SOC_ENUM_EXT("Frame Width Ratio", fw_ratio_enum,
 		     rockchip_sai_fw_ratio_get, rockchip_sai_fw_ratio_put),
 
+	SOC_ENUM_EXT("Master Slave Mode Select", mss_switch,
+		     rockchip_sai_mss_get, rockchip_sai_mss_put),
+	SOC_ENUM("Sclk Polarity", sp_switch),
+	SOC_ENUM("Frame Sync Polarity", fp_switch),
+
+	SOC_SINGLE_TLV("Transmit Frame Shift Select", SAI_TX_SHIFT,
+		       0, 8192, 0, fs_shift_tlv),
+	SOC_SINGLE_TLV("Receive Frame Shift Select", SAI_RX_SHIFT,
+		       0, 8192, 0, fs_shift_tlv),
+#endif
+	SOC_ENUM_EXT("Transmit SDOx Select", tx_lanes_enum,
+		     rockchip_sai_tx_lanes_get, rockchip_sai_tx_lanes_put),
+	SOC_ENUM_EXT("Receive SDIx Select", rx_lanes_enum,
+		     rockchip_sai_rx_lanes_get, rockchip_sai_rx_lanes_put),
 	SOC_SINGLE_TLV("Receive Mono Slot Select", SAI_MONO_CR,
 		       2, 128, 0, rmss_tlv),
 	SOC_ENUM("Receive Mono Switch", rmono_switch),
 	SOC_ENUM("Transmit Mono Switch", tmono_switch),
-
-	SOC_ENUM_EXT("Master / Slave Mode Select", mss_switch,
-		     rockchip_sai_mss_get, rockchip_sai_mss_put),
-	SOC_ENUM("Sclk Polarity", sp_switch),
-	SOC_ENUM("Frame Sync Polarity", fp_switch),
 
 	SOC_ENUM("SDI3 Loopback Src Select", lp3_enum),
 	SOC_ENUM("SDI2 Loopback Src Select", lp2_enum),
@@ -1224,15 +1243,10 @@
 	SOC_ENUM("Receive PATH2 Source Select", rpath2_enum),
 	SOC_ENUM("Receive PATH1 Source Select", rpath1_enum),
 	SOC_ENUM("Receive PATH0 Source Select", rpath0_enum),
-	SOC_ENUM("Transmit PATH3 Sink Select", tpath3_enum),
-	SOC_ENUM("Transmit PATH2 Sink Select", tpath2_enum),
-	SOC_ENUM("Transmit PATH1 Sink Select", tpath1_enum),
-	SOC_ENUM("Transmit PATH0 Sink Select", tpath0_enum),
-
-	SOC_SINGLE_TLV("Transmit Frame Shift Select", SAI_TX_SHIFT,
-		       0, 8192, 0, fs_shift_tlv),
-	SOC_SINGLE_TLV("Receive Frame Shift Select", SAI_RX_SHIFT,
-		       0, 8192, 0, fs_shift_tlv),
+	SOC_ENUM("Transmit SDO3 Source Select", tpath3_enum),
+	SOC_ENUM("Transmit SDO2 Source Select", tpath2_enum),
+	SOC_ENUM("Transmit SDO1 Source Select", tpath1_enum),
+	SOC_ENUM("Transmit SDO0 Source Select", tpath0_enum),
 
 	SOC_SINGLE_BOOL_EXT("Clk Auto Switch", 0,
 			    rockchip_sai_clk_auto_get,
@@ -1263,6 +1277,9 @@
 		dev_warn_ratelimited(sai->dev, "TX FIFO Underrun\n");
 		regmap_update_bits(sai->regmap, SAI_INTCR,
 				   SAI_INTCR_TXUIC, SAI_INTCR_TXUIC);
+		regmap_update_bits(sai->regmap, SAI_INTCR,
+				   SAI_INTCR_TXUIE_MASK,
+				   SAI_INTCR_TXUIE(0));
 		substream = sai->substreams[SNDRV_PCM_STREAM_PLAYBACK];
 		if (substream)
 			snd_pcm_stop_xrun(substream);
@@ -1272,6 +1289,9 @@
 		dev_warn_ratelimited(sai->dev, "RX FIFO Overrun\n");
 		regmap_update_bits(sai->regmap, SAI_INTCR,
 				   SAI_INTCR_RXOIC, SAI_INTCR_RXOIC);
+		regmap_update_bits(sai->regmap, SAI_INTCR,
+				   SAI_INTCR_RXOIE_MASK,
+				   SAI_INTCR_RXOIE(0));
 		substream = sai->substreams[SNDRV_PCM_STREAM_CAPTURE];
 		if (substream)
 			snd_pcm_stop_xrun(substream);
@@ -1386,16 +1406,26 @@
 	if (ret)
 		return ret;
 
+	ret = rockchip_sai_init_dai(sai, res, &dai);
+	if (ret)
+		return ret;
+
+	/*
+	 * MUST: after pm_runtime_enable step, any register R/W
+	 * should be wrapped with pm_runtime_get_sync/put.
+	 *
+	 * Another approach is to enable the regcache true to
+	 * avoid access HW registers.
+	 *
+	 * Alternatively, performing the registers R/W before
+	 * pm_runtime_enable is also a good option.
+	 */
 	pm_runtime_enable(&pdev->dev);
 	if (!pm_runtime_enabled(&pdev->dev)) {
 		ret = rockchip_sai_runtime_resume(&pdev->dev);
 		if (ret)
 			goto err_runtime_disable;
 	}
-
-	ret = rockchip_sai_init_dai(sai, res, &dai);
-	if (ret)
-		goto err_runtime_suspend;
 
 	ret = devm_snd_soc_register_component(&pdev->dev,
 					      &rockchip_sai_component,
@@ -1432,33 +1462,9 @@
 	return 0;
 }
 
-#ifdef CONFIG_PM_SLEEP
-static int rockchip_sai_suspend(struct device *dev)
-{
-	struct rk_sai_dev *sai = dev_get_drvdata(dev);
-
-	regcache_mark_dirty(sai->regmap);
-
-	return 0;
-}
-
-static int rockchip_sai_resume(struct device *dev)
-{
-	struct rk_sai_dev *sai = dev_get_drvdata(dev);
-	int ret = pm_runtime_resume_and_get(dev);
-
-	if (ret < 0)
-		return ret;
-	ret = regcache_sync(sai->regmap);
-	pm_runtime_put(dev);
-
-	return ret;
-}
-#endif /* CONFIG_PM_SLEEP */
-
 static const struct dev_pm_ops rockchip_sai_pm_ops = {
 	SET_RUNTIME_PM_OPS(rockchip_sai_runtime_suspend, rockchip_sai_runtime_resume, NULL)
-	SET_SYSTEM_SLEEP_PM_OPS(rockchip_sai_suspend, rockchip_sai_resume)
+	SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
 };
 
 static struct platform_driver rockchip_sai_driver = {

--
Gitblit v1.6.2