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>;
- };
+>1x {
+ 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 *)®_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(<6911uxc->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(<6911uxc->delayed_work_enable_hotplug,
lt6911uxc_delayed_work_enable_hotplug);
INIT_DELAYED_WORK(<6911uxc->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(<6911uxe->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(<6911uxe->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(<7911uxc->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(<7911uxc->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 *)®_addr_be;
+
/* Write register address */
msgs[0].addr = client->addr;
msgs[0].flags = 0;
- msgs[0].len = 2;
- msgs[0].buf = (u8 *)®_addr_be;
+ msgs[0].len = reg_len;
+ msgs[0].buf = ®_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 *)®_addr_be;
+
+ /* Write register address */
+ msgs[0].addr = client->addr;
+ msgs[0].flags = 0;
+ msgs[0].len = reg_len;
+ msgs[0].buf = ®_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 *)®_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 *)®_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 *)®_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 *)®_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(¶ms_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(¶ms_buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
params_vdev->first_params = false;
wake_up(¶ms_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(¶ms_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(¶ms_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(¶ms_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, ¶ms->others.lsc_cfg, true);
+ isp_lsc_matrix_cfg_sram(params_vdev, ¶ms->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,
- ¶ms_rec->others.lsc_cfg, false);
+ ¶ms_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 = ¶ms_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, ¶ms_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, ¶ms_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, ¶ms_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, ¶ms_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, ¶ms->others.lsc_cfg, true);
- isp_rawhstbig_cfg_sram(params_vdev, ¶ms->meas.rawhist1, 1, true);
- isp_rawhstbig_cfg_sram(params_vdev, ¶ms->meas.rawhist2, 2, true);
- isp_rawhstbig_cfg_sram(params_vdev, ¶ms->meas.rawhist3, 0, true);
- isp_rawawb_cfg_sram(params_vdev, ¶ms->meas.rawawb, true);
+ isp_lsc_matrix_cfg_sram(params_vdev, ¶ms->others.lsc_cfg, true, id);
+ isp_rawhstbig_cfg_sram(params_vdev, ¶ms->meas.rawhist1, 1, true, id);
+ isp_rawhstbig_cfg_sram(params_vdev, ¶ms->meas.rawhist2, 2, true, id);
+ isp_rawhstbig_cfg_sram(params_vdev, ¶ms->meas.rawhist3, 0, true, id);
+ isp_rawawb_cfg_sram(params_vdev, ¶ms->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(¶ms_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 = ¶ms->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 = ¶ms->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(¶ms_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, ¶ms->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, ¶ms->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, ¶ms->meas.rawhist2, 2, true, 0);
isp_rawhstbig_cfg_sram(params_vdev, ¶ms->meas.rawhist3, 0, true, 0);
isp_rawawb_cfg_sram(params_vdev, ¶ms->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, ¶ms->others.lsc_cfg, true, 1);
isp_rawhstbig_cfg_sram(params_vdev, ¶ms->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(¶ms_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, ®);
+ 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 *)®
+
+ /* 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], ®, 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, <7911d_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(<7911d->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(<7911d_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(<7911d_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, ®_v);
+ sclk_vop_sel = (reg_v & 0x20) ? 1 : 0;
+ rk628_i2c_read(rk628, 0xc00b4, ®_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, ®_v);
+ clk_rx_read_sel = (reg_v & 0x10) ? 1 : 0;
+ rk628_i2c_read(rk628, 0xc00b8, ®_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, ®_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, ®_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, ®_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, ®_v);
+ src_hactive = reg_v & 0xffff;
+
+ rk628_i2c_read(rk628, 0x3014c, ®_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, ®_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, ®_v);
+ src_vactive = reg_v & 0xffff;
+ rk628_i2c_read(rk628, 0x30170, ®_v);
+ src_vtotal = reg_v & 0xffff;
+ rk628_i2c_read(rk628, 0x30164, ®_v);
+ src_voffset = (reg_v & 0xffff);
+
+ rk628_i2c_read(rk628, 0x3015c, ®_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, ®_v);
+ src_hactive = reg_v & 0xffff;
+
+ rk628_i2c_read(rk628, 0x12c, ®_v);
+ src_vactive = (reg_v & 0xffff);
+
+ rk628_i2c_read(rk628, 0x134, ®_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 = &_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 = &_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",
+ &_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, &_ctrl)) {
+ pr_err("%s: get amp gic cpu mask error\n", __func__);
+ goto exit;
+ }
+ gic_of_get_gpio_group(np, &_ctrl);
+ amp_gic_get_irqs_config(np, &_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, ¶m);
- 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, ¶m);
+ 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(¤t->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, ¶m);
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