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