diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile index 92ea252c13a1..ba18a1394a1e 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile @@ -975,6 +975,7 @@ dtb-$(CONFIG_ARCH_ROCKCHIP) += \ rv1103g-battery-ipc-v10.dtb \ rv1103g-battery-ipc-v11.dtb \ rv1103g-evb-v10.dtb \ + rv1103g-evb-v11.dtb \ rv1103g-rmsl311-dloc-sl-v10.dtb \ rv1103g-scaner-v10.dtb \ rv1106g-38x38-ipc-v10.dtb \ @@ -983,6 +984,8 @@ dtb-$(CONFIG_ARCH_ROCKCHIP) += \ rv1106g-evb1-rgb-display-v11.dtb \ rv1106g-evb1-v10.dtb \ rv1106g-evb1-v11.dtb \ + rv1106g-evb1-v11-cvr.dtb \ + rv1106g-evb1-v11-spi-nand-cvr.dtb \ rv1106g-evb1-v11-sii902x-bt11202hdmi.dtb \ rv1106g-evb1-v11-sii902x-rgb2hdmi.dtb \ rv1106g-evb1-v10-dual-cam.dtb \ diff --git a/arch/arm/boot/dts/rk312x.dtsi b/arch/arm/boot/dts/rk312x.dtsi index 8f879eb79fe1..d18e74e6f1bf 100644 --- a/arch/arm/boot/dts/rk312x.dtsi +++ b/arch/arm/boot/dts/rk312x.dtsi @@ -670,12 +670,12 @@ compatible = "rockchip,rk3128-mipi-dsi"; reg = <0x10110000 0x4000>; interrupts = ; - clocks = <&cru PCLK_MIPI>, <&cru HCLK_VIO_H2P>, <&video_phy>; - clock-names = "pclk", "h2p", "hs_clk"; + clocks = <&cru PCLK_MIPI>, <&cru HCLK_VIO_H2P>; + clock-names = "pclk", "hclk"; resets = <&cru SRST_VIO_MIPI_DSI>; reset-names = "apb"; phys = <&video_phy>; - phy-names = "mipi_dphy"; + phy-names = "dphy"; power-domains = <&power RK3128_PD_VIO>; rockchip,grf = <&grf>; #address-cells = <1>; @@ -996,14 +996,15 @@ }; video_phy: video-phy@20038000 { - compatible = "rockchip,rk3128-video-phy"; + compatible = "rockchip,rk3128-dsi-dphy", "rockchip,rk3128-video-phy"; reg = <0x20038000 0x4000>, <0x10110000 0x4000>; + reg-names = "phy", "host"; clocks = <&cru SCLK_MIPI_24M>, <&cru PCLK_MIPIPHY>, <&cru PCLK_MIPI>; - clock-names = "ref", "pclk_phy", "pclk_host"; + clock-names = "ref", "pclk", "pclk_host"; #clock-cells = <0>; resets = <&cru SRST_MIPIPHY_P>; - reset-names = "rst"; + reset-names = "apb"; power-domains = <&power RK3128_PD_VIO>; #phy-cells = <0>; status = "disabled"; diff --git a/arch/arm/boot/dts/rv1103g-evb-v11.dts b/arch/arm/boot/dts/rv1103g-evb-v11.dts new file mode 100644 index 000000000000..3d56f7515b47 --- /dev/null +++ b/arch/arm/boot/dts/rv1103g-evb-v11.dts @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright (c) 2022 Rockchip Electronics Co., Ltd. + */ + +/dts-v1/; + +#include "rv1103.dtsi" +#include "rv1103-evb-v10.dtsi" +#include "rv1103-evb-cam.dtsi" + +/ { + model = "Rockchip RV1103G EVB V11 Board"; + compatible = "rockchip,rv1103g-evb-v11", "rockchip,rv1103"; + + 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 = <&gpio1 RK_PC6 GPIO_ACTIVE_LOW>; + regulator-name = "vcc3v3_sd"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + enable-active-low; + regulator-always-on; + regulator-boot-on; + pinctrl-names = "default"; + pinctrl-0 = <&sdmmc_pwren>; + }; + + vcc3v3_wifi: vcc3v3-wifi { + compatible = "regulator-fixed"; + regulator-name = "vcc3v3_wifi"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + enable-active-low; + regulator-always-on; + regulator-boot-on; + gpio = <&gpio1 RK_PC7 GPIO_ACTIVE_LOW>; + pinctrl-names = "default"; + pinctrl-0 = <&wifi_pwren>; + }; +}; + +&pinctrl { + sdmmc { + /omit-if-no-ref/ + sdmmc_pwren: sdmmc-pwren { + rockchip,pins = <1 RK_PC6 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; + + wifi { + wifi_pwren: wifi-pwren { + rockchip,pins = <1 RK_PC7 RK_FUNC_GPIO &pcfg_pull_none>; + }; + }; +}; + +&saradc { + status = "okay"; + vref-supply = <&vcc_1v8>; +}; + +&usbdrd_dwc3 { + dr_mode = "host"; +}; diff --git a/arch/arm/boot/dts/rv1106-evb.dtsi b/arch/arm/boot/dts/rv1106-evb.dtsi index 114c23eee5c5..f8e002b9df11 100644 --- a/arch/arm/boot/dts/rv1106-evb.dtsi +++ b/arch/arm/boot/dts/rv1106-evb.dtsi @@ -3,9 +3,28 @@ * Copyright (c) 2022 Rockchip Electronics Co., Ltd. */ #include "rv1106-amp.dtsi" +#include / { + adc-keys { + compatible = "adc-keys"; + io-channels = <&saradc 0>; + io-channel-names = "buttons"; + poll-interval = <100>; + keyup-threshold-microvolt = <1800000>; + key_volumeup-key { + label = "key_volumeup"; + linux,code = ; + press-threshold-microvolt = <0>; + }; + + key_volumedown-key { + label = "key_volumedown"; + linux,code = ; + press-threshold-microvolt = <400781>; + }; + }; }; &fiq_debugger { diff --git a/arch/arm/boot/dts/rv1106g-evb1-v11-cvr.dts b/arch/arm/boot/dts/rv1106g-evb1-v11-cvr.dts new file mode 100644 index 000000000000..035d2abd19ca --- /dev/null +++ b/arch/arm/boot/dts/rv1106g-evb1-v11-cvr.dts @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright (c) 2022 Rockchip Electronics Co., Ltd. + */ + +/dts-v1/; + +#include "rv1106g-evb1-v11.dts" +#include "rv1106-evb-ext-rgb-v10.dtsi" + +/ { + model = "Rockchip RV1106G EVB1 V11 Board For CVR"; + compatible = "rockchip,rv1106g-evb1-v11-cvr", "rockchip,rv1106"; +}; + +&saradc { + status = "okay"; + vref-supply = <&vcc_1v8>; +}; diff --git a/arch/arm/boot/dts/rv1106g-evb1-v11-spi-nand-cvr.dts b/arch/arm/boot/dts/rv1106g-evb1-v11-spi-nand-cvr.dts new file mode 100644 index 000000000000..0b33cc698039 --- /dev/null +++ b/arch/arm/boot/dts/rv1106g-evb1-v11-spi-nand-cvr.dts @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright (c) 2022 Rockchip Electronics Co., Ltd. + */ + +/dts-v1/; + +#include "rv1106g-evb1-v10-spi-nand.dts" +#include "rv1106-evb-ext-rgb-v10.dtsi" + +/ { + model = "Rockchip RV1106G EVB1 V11 Board For CVR"; + compatible = "rockchip,rv1106g-evb1-v11-spi-nand-cvr", "rockchip,rv1106"; +}; + +&saradc { + status = "okay"; + vref-supply = <&vcc_1v8>; +}; diff --git a/arch/arm/configs/rockchip_linux_defconfig b/arch/arm/configs/rockchip_linux_defconfig index 8b4b70ca4df7..51dba582497b 100644 --- a/arch/arm/configs/rockchip_linux_defconfig +++ b/arch/arm/configs/rockchip_linux_defconfig @@ -254,8 +254,6 @@ CONFIG_PINCTRL_RK805=y CONFIG_GPIO_SYSFS=y CONFIG_GPIO_GENERIC_PLATFORM=y CONFIG_GPIO_TPS65910=y -CONFIG_POWER_AVS=y -CONFIG_ROCKCHIP_IODOMAIN=y CONFIG_POWER_RESET=y CONFIG_POWER_RESET_GPIO_RESTART=y CONFIG_SYSCON_REBOOT_MODE=y @@ -438,15 +436,22 @@ CONFIG_ROCKCHIP_IOMMU=y CONFIG_CPU_RK312X=y CONFIG_CPU_RK3288=y CONFIG_CPU_RK322X=y +CONFIG_ROCKCHIP_CPUINFO=y +CONFIG_ROCKCHIP_GRF=y +CONFIG_ROCKCHIP_IODOMAIN=y +CONFIG_ROCKCHIP_IPA=y +CONFIG_ROCKCHIP_OPP=y CONFIG_ROCKCHIP_PM_DOMAINS=y CONFIG_ROCKCHIP_PVTM=y CONFIG_ROCKCHIP_SUSPEND_MODE=y +CONFIG_ROCKCHIP_SYSTEM_MONITOR=y CONFIG_ROCKCHIP_VENDOR_STORAGE_UPDATE_LOADER=y CONFIG_PM_DEVFREQ=y CONFIG_DEVFREQ_GOV_PERFORMANCE=y CONFIG_DEVFREQ_GOV_POWERSAVE=y CONFIG_DEVFREQ_GOV_USERSPACE=y CONFIG_ARM_ROCKCHIP_DMC_DEVFREQ=y +CONFIG_EXTCON=y CONFIG_MEMORY=y CONFIG_IIO=y CONFIG_IIO_BUFFER=y @@ -462,8 +467,7 @@ CONFIG_PWM_ROCKCHIP=y CONFIG_PHY_ROCKCHIP_DP=y CONFIG_PHY_ROCKCHIP_INNO_HDMI=y CONFIG_PHY_ROCKCHIP_INNO_USB2=y -CONFIG_PHY_ROCKCHIP_INNO_VIDEO_COMBO_PHY=y -CONFIG_PHY_ROCKCHIP_INNO_VIDEO_PHY=y +CONFIG_PHY_ROCKCHIP_INNO_DSIDPHY=y CONFIG_PHY_ROCKCHIP_USB=y CONFIG_ANDROID=y CONFIG_ROCKCHIP_EFUSE=y diff --git a/arch/arm/configs/rv1106-evb.config b/arch/arm/configs/rv1106-evb.config index 7261bcf6be6a..4d120e187565 100644 --- a/arch/arm/configs/rv1106-evb.config +++ b/arch/arm/configs/rv1106-evb.config @@ -12,6 +12,7 @@ CONFIG_FB=y CONFIG_FILE_LOCKING=y CONFIG_I2C_GPIO=y CONFIG_I2C_MUX=y +CONFIG_INPUT=y CONFIG_IPV6=m CONFIG_JFFS2_FS=y CONFIG_KCMP=y @@ -374,6 +375,7 @@ CONFIG_DRM_SII902X=y # CONFIG_EXT4_FS_SECURITY is not set CONFIG_EXT4_USE_FOR_EXT2=y # CONFIG_EXTCON_ADC_JACK is not set +# CONFIG_EXTCON_FSA9480 is not set # CONFIG_EXTCON_GPIO is not set # CONFIG_EXTCON_MAX3355 is not set # CONFIG_EXTCON_PTN5150 is not set @@ -425,12 +427,15 @@ CONFIG_FS_POSIX_ACL=y CONFIG_GRACE_PERIOD=y CONFIG_HDMI=y # CONFIG_HI8435 is not set +# CONFIG_HID is not set +# CONFIG_HID_PID is not set # CONFIG_HISI_HIKEY_USB is not set CONFIG_I2C_ALGOBIT=y # CONFIG_I2C_ARB_GPIO_CHALLENGE is not set # CONFIG_I2C_DEMUX_PINCTRL is not set # CONFIG_I2C_DIOLAN_U2C 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 @@ -446,6 +451,20 @@ CONFIG_I2C_ALGOBIT=y # CONFIG_INET6_ESP is not set # CONFIG_INET6_IPCOMP is not set # CONFIG_INFINEON_DHD 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_IPV6_MIP6 is not set @@ -475,6 +494,33 @@ CONFIG_JFFS2_FS_WRITEBUFFER=y # 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 is not set +# 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_KEYS_REQUEST_CACHE is not set # CONFIG_KEY_DH_OPERATIONS is not set # CONFIG_KS7010 is not set @@ -603,9 +649,11 @@ CONFIG_PKCS7_MESSAGE_PARSER=y CONFIG_PWRSEQ_SIMPLE=y # CONFIG_R8188EU is not set # CONFIG_R8712U 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 @@ -619,6 +667,7 @@ CONFIG_REGMAP_SPI=y # CONFIG_ROCKCHIP_LVDS is not set # CONFIG_ROCKCHIP_MMC_VENDOR_STORAGE is not set CONFIG_ROCKCHIP_MTD_VENDOR_STORAGE=y +# CONFIG_ROCKCHIP_REMOTECTL is not set CONFIG_ROCKCHIP_RGA_DEBUGGER=y # CONFIG_ROCKCHIP_RGA_DEBUG_FS is not set CONFIG_ROCKCHIP_RGB=y @@ -651,16 +700,22 @@ CONFIG_ROCKCHIP_VOP=y # CONFIG_SDIO_UART is not set # CONFIG_SECONDARY_TRUSTED_KEYRING 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_SGL_ALLOC=y # CONFIG_SIGNED_PE_FILE_VERIFICATION is not set # CONFIG_SND_BCD2000 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 @@ -673,6 +728,7 @@ CONFIG_SGL_ALLOC=y # 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_SND_USB=y @@ -842,6 +898,7 @@ CONFIG_USB_GADGET_VBUS_DRAW=2 # CONFIG_USB_G_SERIAL is not set # CONFIG_USB_G_WEBCAM is not set # CONFIG_USB_HCD_TEST_MODE is not set +# CONFIG_USB_HID is not set # CONFIG_USB_HSIC_USB3503 is not set # CONFIG_USB_HSIC_USB4604 is not set # CONFIG_USB_HUB_USB251XB is not set @@ -851,6 +908,7 @@ CONFIG_USB_GADGET_VBUS_DRAW=2 # CONFIG_USB_ISP116X_HCD is not set # CONFIG_USB_ISP1301 is not set # CONFIG_USB_ISP1760 is not set +# CONFIG_USB_KBD is not set # CONFIG_USB_LCD is not set # CONFIG_USB_LD is not set # CONFIG_USB_LEGOTOWER is not set @@ -863,6 +921,7 @@ CONFIG_USB_LIBCOMPOSITE=y # CONFIG_USB_MDC800 is not set # CONFIG_USB_MIDI_GADGET is not set # CONFIG_USB_MON is not set +# CONFIG_USB_MOUSE is not set # CONFIG_USB_MUSB_HDRC is not set # CONFIG_USB_MV_U3D is not set # CONFIG_USB_MV_UDC is not set diff --git a/arch/arm64/boot/dts/rockchip/rk3588-nvr-demo.dtsi b/arch/arm64/boot/dts/rockchip/rk3588-nvr-demo.dtsi index baf8655f54f4..02c4202b3d52 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588-nvr-demo.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3588-nvr-demo.dtsi @@ -181,6 +181,10 @@ status = "okay"; }; +&dp1_sound { + status = "okay"; +}; + &gmac0 { /* Use rgmii-rxid mode to disable rx delay inside Soc */ phy-mode = "rgmii-rxid"; @@ -678,6 +682,10 @@ status = "okay"; }; +&spdif_tx5 { + status = "okay"; +}; + &pinctrl { dp { dp1_hdmi_ctl: dp-hdmi-ctl { diff --git a/arch/arm64/boot/dts/rockchip/rk3588-nvr-demo1-v21.dtsi b/arch/arm64/boot/dts/rockchip/rk3588-nvr-demo1-v21.dtsi index 4187e5d8f97c..30d3d7a45adc 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588-nvr-demo1-v21.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3588-nvr-demo1-v21.dtsi @@ -26,6 +26,10 @@ status = "okay"; }; +&dp0_sound { + status = "okay"; +}; + &dp1 { pinctrl-0 = <&dp1m2_pins &dp1_hdmi_reset>; pinctrl-names = "default"; @@ -193,6 +197,10 @@ }; }; +&spdif_tx2 { + status = "okay"; +}; + &pinctrl { dp { dp1_hdmi_reset: dp-hdmi-reset { diff --git a/arch/arm64/boot/dts/rockchip/rk3588-nvr.dtsi b/arch/arm64/boot/dts/rockchip/rk3588-nvr.dtsi index 7c92c561a6c0..8786b4b31bdf 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588-nvr.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3588-nvr.dtsi @@ -28,6 +28,26 @@ }; }; + dp0_sound: dp0-sound { + status = "disabled"; + compatible = "rockchip,hdmi"; + rockchip,card-name= "rockchip,dp0"; + rockchip,mclk-fs = <512>; + rockchip,cpu = <&spdif_tx2>; + rockchip,codec = <&dp0 1>; + rockchip,jack-det; + }; + + dp1_sound: dp1-sound { + status = "disabled"; + compatible = "rockchip,hdmi"; + rockchip,card-name= "rockchip,dp1"; + rockchip,mclk-fs = <512>; + rockchip,cpu = <&spdif_tx5>; + rockchip,codec = <&dp1 1>; + rockchip,jack-det; + }; + hdmi0_sound: hdmi0-sound { status = "disabled"; compatible = "simple-audio-card"; diff --git a/drivers/clk/rockchip/clk-rk3128.c b/drivers/clk/rockchip/clk-rk3128.c index c26cfa55980d..2bcbfe0a9795 100644 --- a/drivers/clk/rockchip/clk-rk3128.c +++ b/drivers/clk/rockchip/clk-rk3128.c @@ -534,7 +534,7 @@ static struct rockchip_clk_branch common_clk_branches[] __initdata = { GATE(PCLK_ACODEC, "pclk_acodec", "pclk_cpu", 0, RK2928_CLKGATE_CON(5), 14, GFLAGS), GATE(0, "pclk_ddrupctl", "pclk_cpu", CLK_IGNORE_UNUSED, RK2928_CLKGATE_CON(5), 7, GFLAGS), GATE(0, "pclk_grf", "pclk_cpu", CLK_IGNORE_UNUSED, RK2928_CLKGATE_CON(5), 4, GFLAGS), - GATE(0, "pclk_mipiphy", "pclk_cpu", CLK_IGNORE_UNUSED, RK2928_CLKGATE_CON(5), 0, GFLAGS), + GATE(PCLK_MIPIPHY, "pclk_mipiphy", "pclk_cpu", 0, RK2928_CLKGATE_CON(5), 0, GFLAGS), GATE(0, "pclk_pmu", "pclk_pmu_pre", CLK_IS_CRITICAL, RK2928_CLKGATE_CON(9), 2, GFLAGS), GATE(0, "pclk_pmu_niu", "pclk_pmu_pre", CLK_IS_CRITICAL, RK2928_CLKGATE_CON(9), 3, GFLAGS), diff --git a/drivers/clk/rockchip/clk-rk3568.c b/drivers/clk/rockchip/clk-rk3568.c index 3dcd7bea97a6..66076d68bb39 100644 --- a/drivers/clk/rockchip/clk-rk3568.c +++ b/drivers/clk/rockchip/clk-rk3568.c @@ -958,7 +958,7 @@ static struct rockchip_clk_branch rk3568_clk_branches[] __initdata = { COMPOSITE_NODIV(HCLK_USB, "hclk_usb", gpll150_gpll100_gpll75_xin24m_p, CLK_IS_CRITICAL, RK3568_CLKSEL_CON(32), 2, 2, MFLAGS, RK3568_CLKGATE_CON(16), 1, GFLAGS), - COMPOSITE_NOMUX(PCLK_USB, "pclk_usb", "aclk_usb", 0, + COMPOSITE_NOMUX(PCLK_USB, "pclk_usb", "aclk_usb", CLK_IS_CRITICAL, RK3568_CLKSEL_CON(32), 4, 4, DFLAGS, RK3568_CLKGATE_CON(16), 2, GFLAGS), GATE(HCLK_USB2HOST0, "hclk_usb2host0", "hclk_usb", 0, diff --git a/drivers/clk/rockchip/clk-rk3588.c b/drivers/clk/rockchip/clk-rk3588.c index ee2826a70742..a7b25c1d309b 100644 --- a/drivers/clk/rockchip/clk-rk3588.c +++ b/drivers/clk/rockchip/clk-rk3588.c @@ -1890,7 +1890,7 @@ static struct rockchip_clk_branch rk3588_clk_branches[] __initdata = { COMPOSITE(ACLK_VOP_ROOT, "aclk_vop_root", gpll_cpll_dmyaupll_npll_spll_p, 0, RK3588_CLKSEL_CON(110), 5, 3, MFLAGS, 0, 5, DFLAGS, RK3588_CLKGATE_CON(52), 0, GFLAGS), - FACTOR(0, "aclk_vop_div2_src", "aclk_vop_root", 0, 1, 2), + FACTOR(ACLK_VOP_DIV2_SRC, "aclk_vop_div2_src", "aclk_vop_root", 0, 1, 2), COMPOSITE_NODIV(ACLK_VOP_LOW_ROOT, "aclk_vop_low_root", mux_400m_200m_100m_24m_p, 0, RK3588_CLKSEL_CON(110), 8, 2, MFLAGS, RK3588_CLKGATE_CON(52), 1, GFLAGS), @@ -1906,8 +1906,9 @@ static struct rockchip_clk_branch rk3588_clk_branches[] __initdata = { COMPOSITE_NODIV(HCLK_VO1USB_TOP_ROOT, "hclk_vo1usb_top_root", mux_200m_100m_50m_24m_p, CLK_IS_CRITICAL, RK3588_CLKSEL_CON(170), 6, 2, MFLAGS, RK3588_CLKGATE_CON(74), 2, GFLAGS), - MUX(ACLK_VOP_SUB_SRC, "aclk_vop_sub_src", aclk_vop_sub_src_p, 0, - RK3588_CLKSEL_CON(115), 9, 1, MFLAGS), + COMPOSITE_NODIV(ACLK_VOP, "aclk_vop", aclk_vop_sub_src_p, CLK_SET_RATE_PARENT, + RK3588_CLKSEL_CON(115), 9, 1, MFLAGS, + RK3588_CLKGATE_CON(52), 9, GFLAGS), GATE(PCLK_EDP0, "pclk_edp0", "pclk_vo1_root", 0, RK3588_CLKGATE_CON(62), 0, GFLAGS), GATE(CLK_EDP0_24M, "clk_edp0_24m", "xin24m", 0, @@ -2064,8 +2065,6 @@ static struct rockchip_clk_branch rk3588_clk_branches[] __initdata = { RK3588_CLKGATE_CON(72), 4, GFLAGS), GATE(HCLK_VOP, "hclk_vop", "hclk_vop_root", 0, RK3588_CLKGATE_CON(52), 8, GFLAGS), - GATE(ACLK_VOP, "aclk_vop", "aclk_vop_sub_src", 0, - RK3588_CLKGATE_CON(52), 9, GFLAGS), COMPOSITE(DCLK_VOP0_SRC, "dclk_vop0_src", gpll_cpll_v0pll_aupll_p, 0, RK3588_CLKSEL_CON(111), 7, 2, MFLAGS, 0, 7, DFLAGS, RK3588_CLKGATE_CON(52), 10, GFLAGS), diff --git a/drivers/dma-buf/heaps/Kconfig b/drivers/dma-buf/heaps/Kconfig index ff52efa83f39..75f4e3e349c1 100644 --- a/drivers/dma-buf/heaps/Kconfig +++ b/drivers/dma-buf/heaps/Kconfig @@ -22,3 +22,10 @@ config DMABUF_HEAPS_CMA Choose this option to enable dma-buf CMA heap. This heap is backed by the Contiguous Memory Allocator (CMA). If your system has these regions, you should say Y here. + +config DMABUF_HEAPS_SRAM + tristate "Export on-chip SRAM pools using DMA-Heaps" + depends on DMABUF_HEAPS && SRAM + help + This driver allows the export of on-chip SRAM marked as exportable + to userspace using the DMA-Heaps interface. diff --git a/drivers/dma-buf/heaps/Makefile b/drivers/dma-buf/heaps/Makefile index 4e134a221a06..07ba63081b0f 100644 --- a/drivers/dma-buf/heaps/Makefile +++ b/drivers/dma-buf/heaps/Makefile @@ -3,3 +3,4 @@ obj-$(CONFIG_DMABUF_HEAPS_DEFERRED_FREE) += deferred-free-helper.o obj-$(CONFIG_DMABUF_HEAPS_PAGE_POOL) += page_pool.o obj-$(CONFIG_DMABUF_HEAPS_SYSTEM) += rk_system_heap.o obj-$(CONFIG_DMABUF_HEAPS_CMA) += rk_cma_heap.o +obj-$(CONFIG_DMABUF_HEAPS_SRAM) += sram_heap.o diff --git a/drivers/dma-buf/heaps/sram_heap.c b/drivers/dma-buf/heaps/sram_heap.c new file mode 100644 index 000000000000..d9a9b70a79d4 --- /dev/null +++ b/drivers/dma-buf/heaps/sram_heap.c @@ -0,0 +1,437 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * SRAM DMA-Heap exporter && support alloc page and dmabuf on kernel + * + * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/ + * + * Author: Andrew F. Davis + * + * Copyright (C) 2022 Rockchip Electronics Co., Ltd. + * + * Author: Huang Lee + */ +#define pr_fmt(fmt) "sram_heap: " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define RK3588_SRAM_BASE 0xff001000 + +struct sram_dma_heap { + struct dma_heap *heap; + struct gen_pool *pool; +}; + +struct sram_dma_heap_buffer { + struct gen_pool *pool; + struct list_head attachments; + struct mutex attachments_lock; + unsigned long len; + void *vaddr; + phys_addr_t paddr; +}; + +struct dma_heap_attachment { + struct device *dev; + struct sg_table *table; + struct list_head list; +}; + +static int dma_heap_attach(struct dma_buf *dmabuf, + struct dma_buf_attachment *attachment) +{ + struct sram_dma_heap_buffer *buffer = dmabuf->priv; + struct dma_heap_attachment *a; + struct sg_table *table; + + a = kzalloc(sizeof(*a), GFP_KERNEL); + if (!a) + return -ENOMEM; + + table = kmalloc(sizeof(*table), GFP_KERNEL); + if (!table) + goto table_alloc_failed; + + if (sg_alloc_table(table, 1, GFP_KERNEL)) + goto sg_alloc_failed; + + /* + * The referenced pfn and page are for setting the sram address to the + * sgtable, and cannot be used for other purposes, and cannot be accessed + * directly or indirectly. + * + * And not sure if there is a problem with the 32-bit system. + * + * page cannot support kmap func. + */ + sg_set_page(table->sgl, pfn_to_page(PFN_DOWN(buffer->paddr)), buffer->len, 0); + + a->table = table; + a->dev = attachment->dev; + INIT_LIST_HEAD(&a->list); + + attachment->priv = a; + + mutex_lock(&buffer->attachments_lock); + list_add(&a->list, &buffer->attachments); + mutex_unlock(&buffer->attachments_lock); + + return 0; + +sg_alloc_failed: + kfree(table); +table_alloc_failed: + kfree(a); + return -ENOMEM; +} + +static void dma_heap_detatch(struct dma_buf *dmabuf, + struct dma_buf_attachment *attachment) +{ + struct sram_dma_heap_buffer *buffer = dmabuf->priv; + struct dma_heap_attachment *a = attachment->priv; + + mutex_lock(&buffer->attachments_lock); + list_del(&a->list); + mutex_unlock(&buffer->attachments_lock); + + sg_free_table(a->table); + kfree(a->table); + kfree(a); +} + +static struct sg_table *dma_heap_map_dma_buf(struct dma_buf_attachment *attachment, + enum dma_data_direction direction) +{ + struct dma_heap_attachment *a = attachment->priv; + struct sg_table *table = a->table; + int ret = 0; + + ret = dma_map_sgtable(attachment->dev, table, direction, DMA_ATTR_SKIP_CPU_SYNC); + if (ret) + return ERR_PTR(-ENOMEM); + + return table; +} + +static void dma_heap_unmap_dma_buf(struct dma_buf_attachment *attachment, + struct sg_table *table, + enum dma_data_direction direction) +{ + dma_unmap_sgtable(attachment->dev, table, direction, DMA_ATTR_SKIP_CPU_SYNC); +} + +static void dma_heap_dma_buf_release(struct dma_buf *dmabuf) +{ + struct sram_dma_heap_buffer *buffer = dmabuf->priv; + + gen_pool_free(buffer->pool, (unsigned long)buffer->vaddr, buffer->len); + kfree(buffer); +} + +static int dma_heap_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma) +{ + struct sram_dma_heap_buffer *buffer = dmabuf->priv; + int ret; + + /* SRAM mappings are not cached */ + vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); + + ret = vm_iomap_memory(vma, buffer->paddr, buffer->len); + if (ret) + pr_err("Could not map buffer to userspace\n"); + + return ret; +} + +static void *dma_heap_vmap(struct dma_buf *dmabuf) +{ + struct sram_dma_heap_buffer *buffer = dmabuf->priv; + + return buffer->vaddr; +} + +static const struct dma_buf_ops sram_dma_heap_buf_ops = { + .attach = dma_heap_attach, + .detach = dma_heap_detatch, + .map_dma_buf = dma_heap_map_dma_buf, + .unmap_dma_buf = dma_heap_unmap_dma_buf, + .release = dma_heap_dma_buf_release, + .mmap = dma_heap_mmap, + .vmap = dma_heap_vmap, +}; + +static struct dma_buf *sram_dma_heap_allocate(struct dma_heap *heap, + unsigned long len, + unsigned long fd_flags, + unsigned long heap_flags) +{ + struct sram_dma_heap *sram_dma_heap = dma_heap_get_drvdata(heap); + struct sram_dma_heap_buffer *buffer; + + DEFINE_DMA_BUF_EXPORT_INFO(exp_info); + struct dma_buf *dmabuf; + int ret = -ENOMEM; + + buffer = kzalloc(sizeof(*buffer), GFP_KERNEL); + if (!buffer) + return ERR_PTR(-ENOMEM); + buffer->pool = sram_dma_heap->pool; + INIT_LIST_HEAD(&buffer->attachments); + mutex_init(&buffer->attachments_lock); + buffer->len = len; + + buffer->vaddr = (void *)gen_pool_alloc(buffer->pool, buffer->len); + if (!buffer->vaddr) { + ret = -ENOMEM; + goto free_buffer; + } + + buffer->paddr = gen_pool_virt_to_phys(buffer->pool, (unsigned long)buffer->vaddr); + if (buffer->paddr == -1) { + ret = -ENOMEM; + goto free_pool; + } + + /* create the dmabuf */ + exp_info.ops = &sram_dma_heap_buf_ops; + exp_info.size = buffer->len; + exp_info.flags = fd_flags; + exp_info.priv = buffer; + dmabuf = dma_buf_export(&exp_info); + if (IS_ERR(dmabuf)) { + ret = PTR_ERR(dmabuf); + goto free_pool; + } + + return dmabuf; + +free_pool: + gen_pool_free(buffer->pool, (unsigned long)buffer->vaddr, buffer->len); +free_buffer: + kfree(buffer); + + return ERR_PTR(ret); +} + +static struct dma_heap_ops sram_dma_heap_ops = { + .allocate = sram_dma_heap_allocate, +}; + +static struct sram_dma_heap *sram_dma_heap_global; + +static int sram_dma_heap_export(const char *name, + struct gen_pool *sram_gp) +{ + struct sram_dma_heap *sram_dma_heap; + struct dma_heap_export_info exp_info; + + pr_info("Exporting SRAM pool '%s'\n", name); + + sram_dma_heap = kzalloc(sizeof(*sram_dma_heap), GFP_KERNEL); + if (!sram_dma_heap) + return -ENOMEM; + sram_dma_heap->pool = sram_gp; + + exp_info.name = "sram_dma_heap"; + exp_info.ops = &sram_dma_heap_ops; + exp_info.priv = sram_dma_heap; + + sram_dma_heap_global = sram_dma_heap; + + sram_dma_heap->heap = dma_heap_add(&exp_info); + if (IS_ERR(sram_dma_heap->heap)) { + int ret = PTR_ERR(sram_dma_heap->heap); + + kfree(sram_dma_heap); + return ret; + } + + return 0; +} + +struct dma_buf *sram_heap_alloc_dma_buf(size_t size) +{ + struct sram_dma_heap *sram_dma_heap = sram_dma_heap_global; + struct sram_dma_heap_buffer *buffer; + + DEFINE_DMA_BUF_EXPORT_INFO(exp_info); + struct dma_buf *dmabuf; + int ret = -ENOMEM; + + buffer = kzalloc(sizeof(*buffer), GFP_KERNEL); + if (!buffer) + return ERR_PTR(-ENOMEM); + + buffer->pool = sram_dma_heap->pool; + INIT_LIST_HEAD(&buffer->attachments); + mutex_init(&buffer->attachments_lock); + buffer->len = size; + + buffer->vaddr = (void *)gen_pool_alloc(buffer->pool, buffer->len); + if (!buffer->vaddr) { + ret = -ENOMEM; + goto free_buffer; + } + + buffer->paddr = gen_pool_virt_to_phys(buffer->pool, (unsigned long)buffer->vaddr); + if (buffer->paddr == -1) { + ret = -ENOMEM; + goto free_pool; + } + + /* create the dmabuf */ + exp_info.ops = &sram_dma_heap_buf_ops; + exp_info.size = buffer->len; + exp_info.priv = buffer; + dmabuf = dma_buf_export(&exp_info); + if (IS_ERR(dmabuf)) { + ret = PTR_ERR(dmabuf); + goto free_pool; + } + + return dmabuf; + +free_pool: + gen_pool_free(buffer->pool, (unsigned long)buffer->vaddr, buffer->len); +free_buffer: + kfree(buffer); + + return ERR_PTR(ret); +} +EXPORT_SYMBOL_GPL(sram_heap_alloc_dma_buf); + +struct page *sram_heap_alloc_pages(size_t size) +{ + struct sram_dma_heap *sram_dma_heap = sram_dma_heap_global; + + void *vaddr; + phys_addr_t paddr; + struct page *p; + + int ret = -ENOMEM; + + vaddr = (void *)gen_pool_alloc(sram_dma_heap->pool, size); + if (!vaddr) { + ret = -ENOMEM; + pr_err("no memory"); + goto failed; + } + + paddr = gen_pool_virt_to_phys(sram_dma_heap->pool, (unsigned long)vaddr); + if (paddr == -1) { + ret = -ENOMEM; + pr_err("gen_pool_virt_to_phys failed"); + goto free_pool; + } + + p = pfn_to_page(PFN_DOWN(paddr)); + + return p; + +free_pool: + gen_pool_free(sram_dma_heap->pool, (unsigned long)vaddr, size); +failed: + + return ERR_PTR(ret); +} +EXPORT_SYMBOL_GPL(sram_heap_alloc_pages); + +static u64 gen_pool_phys_to_virt(struct gen_pool *pool, phys_addr_t paddr) +{ + struct gen_pool_chunk *chunk; + u64 vaddr = 0; + + rcu_read_lock(); + list_for_each_entry_rcu(chunk, &pool->chunks, next_chunk) { + /* TODO: only suit for simple chunk now */ + vaddr = chunk->start_addr + (paddr - chunk->phys_addr); + } + rcu_read_unlock(); + + return vaddr; +} + +void sram_heap_free_pages(struct page *p) +{ + struct sram_dma_heap *sram_dma_heap = sram_dma_heap_global; + void *vaddr; + + vaddr = (void *)gen_pool_phys_to_virt(sram_dma_heap->pool, page_to_phys(p)); + + gen_pool_free(sram_dma_heap->pool, (unsigned long)vaddr, PAGE_SIZE); +} +EXPORT_SYMBOL_GPL(sram_heap_free_pages); + +void sram_heap_free_dma_buf(struct dma_buf *dmabuf) +{ + struct sram_dma_heap_buffer *buffer = dmabuf->priv; + + gen_pool_free(buffer->pool, (unsigned long)buffer->vaddr, buffer->len); + kfree(buffer); +} +EXPORT_SYMBOL_GPL(sram_heap_free_dma_buf); + +void *sram_heap_get_vaddr(struct dma_buf *dmabuf) +{ + struct sram_dma_heap_buffer *buffer = dmabuf->priv; + + return buffer->vaddr; +} +EXPORT_SYMBOL_GPL(sram_heap_get_vaddr); + +phys_addr_t sram_heap_get_paddr(struct dma_buf *dmabuf) +{ + struct sram_dma_heap_buffer *buffer = dmabuf->priv; + + return buffer->paddr; +} +EXPORT_SYMBOL_GPL(sram_heap_get_paddr); + +static int rk_add_default_sram_heap(void) +{ + struct device_node *np = NULL; + struct gen_pool *sram_gp = NULL; + int ret = 0; + + np = of_find_compatible_node(NULL, NULL, "rockchip,sram-heap"); + if (!np) { + pr_info("failed to get device node of sram-heap\n"); + return -ENODEV; + } + + if (!of_device_is_available(np)) { + of_node_put(np); + return ret; + } + + sram_gp = of_gen_pool_get(np, "rockchip,sram", 0); + /* release node */ + of_node_put(np); + if (sram_gp == NULL) { + pr_err("sram gen pool is NULL"); + return -ENOMEM; + } + + ret = sram_dma_heap_export("sram-heap", sram_gp); + + return ret; +} +module_init(rk_add_default_sram_heap); +MODULE_DESCRIPTION("Rockchip DMA-BUF SRAM Heap"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c index ead215ff6183..c8706e2e27d3 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c +++ b/drivers/gpu/drm/bridge/analogix/analogix_dp_core.c @@ -1785,8 +1785,13 @@ static void analogix_dp_bridge_mode_set(struct drm_bridge *bridge, /* Input video interlaces & hsync pol & vsync pol */ video->interlaced = !!(mode->flags & DRM_MODE_FLAG_INTERLACE); - video->v_sync_polarity = !!(mode->flags & DRM_MODE_FLAG_NVSYNC); - video->h_sync_polarity = !!(mode->flags & DRM_MODE_FLAG_NHSYNC); + if (dp->plat_data->dev_type == RK3588_EDP) { + video->v_sync_polarity = true; + video->h_sync_polarity = true; + } else { + video->v_sync_polarity = !!(mode->flags & DRM_MODE_FLAG_NVSYNC); + video->h_sync_polarity = !!(mode->flags & DRM_MODE_FLAG_NHSYNC); + } /* Input video dynamic_range & colorimetry */ vic = drm_match_cea_mode(mode); diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c index 927be632b9a4..fc4445771c05 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c @@ -255,6 +255,8 @@ struct dw_hdmi_qp { bool allm_enable; bool support_hdmi; int force_output; + int vp_id; + int old_vp_id; struct mutex mutex; /* for state below and previous_mode */ struct drm_connector *curr_conn;/* current connector (only valid when !disabled) */ @@ -2262,13 +2264,23 @@ static int dw_hdmi_connector_atomic_check(struct drm_connector *connector, struct drm_connector_state *new_state = drm_atomic_get_new_connector_state(state, connector); struct drm_crtc *crtc = new_state->crtc; - struct drm_crtc_state *crtc_state; + struct drm_crtc *old_crtc = old_state->crtc; + struct drm_crtc_state *crtc_state, *old_crtc_state; struct dw_hdmi_qp *hdmi = container_of(connector, struct dw_hdmi_qp, connector); struct drm_display_mode *mode = NULL; void *data = hdmi->plat_data->phy_data; struct hdmi_vmode_qp *vmode = &hdmi->hdmi_data.video_mode; + if (old_crtc) { + old_crtc_state = drm_atomic_get_crtc_state(state, old_crtc); + if (IS_ERR(old_crtc_state)) + return PTR_ERR(old_crtc_state); + + if (hdmi->plat_data->get_vp_id) + hdmi->old_vp_id = hdmi->plat_data->get_vp_id(old_crtc_state); + } + if (!crtc) return 0; @@ -2276,6 +2288,9 @@ static int dw_hdmi_connector_atomic_check(struct drm_connector *connector, if (IS_ERR(crtc_state)) return PTR_ERR(crtc_state); + if (hdmi->plat_data->get_vp_id) + hdmi->vp_id = hdmi->plat_data->get_vp_id(crtc_state); + mode = &crtc_state->mode; /* * If HDMI is enabled in uboot, it's need to record @@ -2319,7 +2334,7 @@ static int dw_hdmi_connector_atomic_check(struct drm_connector *connector, if (hdmi->initialized && !hdmi->dclk_en) { mutex_lock(&hdmi->audio_mutex); if (hdmi->plat_data->dclk_set) - hdmi->plat_data->dclk_set(data, true); + hdmi->plat_data->dclk_set(data, true, hdmi->vp_id); hdmi->dclk_en = true; mutex_unlock(&hdmi->audio_mutex); hdmi->curr_conn = connector; @@ -2500,7 +2515,7 @@ static void dw_hdmi_qp_bridge_atomic_disable(struct drm_bridge *bridge, if (hdmi->dclk_en) { mutex_lock(&hdmi->audio_mutex); if (hdmi->plat_data->dclk_set) - hdmi->plat_data->dclk_set(data, false); + hdmi->plat_data->dclk_set(data, false, hdmi->old_vp_id); hdmi->dclk_en = false; mutex_unlock(&hdmi->audio_mutex); }; @@ -2542,7 +2557,7 @@ static void dw_hdmi_qp_bridge_atomic_enable(struct drm_bridge *bridge, if (!hdmi->dclk_en) { mutex_lock(&hdmi->audio_mutex); if (hdmi->plat_data->dclk_set) - hdmi->plat_data->dclk_set(data, true); + hdmi->plat_data->dclk_set(data, true, hdmi->vp_id); hdmi->dclk_en = true; mutex_unlock(&hdmi->audio_mutex); } diff --git a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c index fa00e24c128c..12c54e9a0593 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c @@ -26,6 +26,8 @@ #include #include #include +#include +#include #include #define HWVER_131 0x31333100 /* IP version 1.31 */ @@ -239,8 +241,11 @@ struct debugfs_entries { struct dw_mipi_dsi { struct drm_bridge bridge; + struct drm_connector connector; + struct drm_encoder *encoder; struct mipi_dsi_host dsi_host; - struct drm_bridge *panel_bridge; + struct drm_panel *panel; + struct drm_bridge *next_bridge; struct device *dev; void __iomem *base; @@ -250,6 +255,7 @@ struct dw_mipi_dsi { u32 channel; u32 lanes; u32 format; + struct drm_display_mode mode; unsigned long mode_flags; #ifdef CONFIG_DEBUG_FS @@ -299,6 +305,11 @@ static inline struct dw_mipi_dsi *bridge_to_dsi(struct drm_bridge *bridge) return container_of(bridge, struct dw_mipi_dsi, bridge); } +static inline struct dw_mipi_dsi *con_to_dsi(struct drm_connector *con) +{ + return container_of(con, struct dw_mipi_dsi, connector); +} + static inline void dsi_write(struct dw_mipi_dsi *dsi, u32 reg, u32 val) { writel(val, dsi->base + reg); @@ -314,8 +325,6 @@ static int dw_mipi_dsi_host_attach(struct mipi_dsi_host *host, { struct dw_mipi_dsi *dsi = host_to_dsi(host); const struct dw_mipi_dsi_plat_data *pdata = dsi->plat_data; - struct drm_bridge *bridge; - struct drm_panel *panel; int max_data_lanes = dsi->plat_data->max_data_lanes; int ret; @@ -324,20 +333,13 @@ static int dw_mipi_dsi_host_attach(struct mipi_dsi_host *host, dsi->format = device->format; dsi->mode_flags = device->mode_flags; - ret = drm_of_find_panel_or_bridge(host->dev->of_node, 1, 0, - &panel, &bridge); - if (ret) + ret = drm_of_find_panel_or_bridge(host->dev->of_node, 1, -1, + &dsi->panel, &dsi->next_bridge); + if (ret) { + DRM_DEV_ERROR(dsi->dev, "Failed to find panel or bridge: %d\n", ret); return ret; - - if (panel) { - bridge = drm_panel_bridge_add_typed(panel, - DRM_MODE_CONNECTOR_DSI); - if (IS_ERR(bridge)) - return PTR_ERR(bridge); } - dsi->panel_bridge = bridge; - drm_bridge_add(&dsi->bridge); if (pdata->host_ops && pdata->host_ops->attach) { @@ -602,6 +604,9 @@ static void dw_mipi_dsi_disable(struct dw_mipi_dsi *dsi) dsi_write(dsi, DSI_PWR_UP, RESET); dsi_write(dsi, DSI_PHY_RSTZ, PHY_RSTZ); pm_runtime_put(dsi->dev); + + if (dsi->slave) + dw_mipi_dsi_disable(dsi->slave); } static void dw_mipi_dsi_init(struct dw_mipi_dsi *dsi) @@ -855,31 +860,29 @@ static void dw_mipi_dsi_clear_err(struct dw_mipi_dsi *dsi) dsi_write(dsi, DSI_INT_MSK1, 0); } +static void dw_mipi_dsi_post_disable(struct dw_mipi_dsi *dsi) +{ + dw_mipi_dsi_set_mode(dsi, 0); + if (dsi->slave) + dw_mipi_dsi_set_mode(dsi->slave, 0); +} + static void dw_mipi_dsi_bridge_post_disable(struct drm_bridge *bridge) { struct dw_mipi_dsi *dsi = bridge_to_dsi(bridge); - /* - * Switch to command mode before panel-bridge post_disable & - * panel unprepare. - * Note: panel-bridge disable & panel disable has been called - * before by the drm framework. - */ - dw_mipi_dsi_set_mode(dsi, 0); - if (dsi->slave) - dw_mipi_dsi_set_mode(dsi->slave, 0); + if (dsi->panel) + drm_panel_disable(dsi->panel); - /* - * TODO Only way found to call panel-bridge post_disable & - * panel unprepare before the dsi "final" disable... - * This needs to be fixed in the drm_bridge framework and the API - * needs to be updated to manage our own call chains... - */ - if (dsi->panel_bridge->funcs->post_disable) - dsi->panel_bridge->funcs->post_disable(dsi->panel_bridge); + dw_mipi_dsi_post_disable(dsi); +} - if (dsi->slave) - dw_mipi_dsi_disable(dsi->slave); +static void dw_mipi_dsi_bridge_disable(struct drm_bridge *bridge) +{ + struct dw_mipi_dsi *dsi = bridge_to_dsi(bridge); + + if (dsi->panel) + drm_panel_unprepare(dsi->panel); dw_mipi_dsi_disable(dsi); } @@ -898,11 +901,23 @@ static unsigned int dw_mipi_dsi_get_lanes(struct dw_mipi_dsi *dsi) return dsi->lanes; } -static void dw_mipi_dsi_mode_set(struct dw_mipi_dsi *dsi, - const struct drm_display_mode *adjusted_mode) +static void dw_mipi_dsi_bridge_mode_set(struct drm_bridge *bridge, + const struct drm_display_mode *mode, + const struct drm_display_mode *adjusted_mode) +{ + struct dw_mipi_dsi *dsi = bridge_to_dsi(bridge); + + drm_mode_copy(&dsi->mode, adjusted_mode); + + if (dsi->slave) + drm_mode_copy(&dsi->slave->mode, adjusted_mode); +} + +static void dw_mipi_dsi_pre_enable(struct dw_mipi_dsi *dsi) { const struct dw_mipi_dsi_phy_ops *phy_ops = dsi->plat_data->phy_ops; void *priv_data = dsi->plat_data->priv_data; + const struct drm_display_mode *adjusted_mode = &dsi->mode; int ret; u32 lanes = dw_mipi_dsi_get_lanes(dsi); @@ -946,27 +961,23 @@ static void dw_mipi_dsi_mode_set(struct dw_mipi_dsi *dsi, /* Switch to cmd mode for panel-bridge pre_enable & panel prepare */ dw_mipi_dsi_set_mode(dsi, 0); -} -static void dw_mipi_dsi_bridge_mode_set(struct drm_bridge *bridge, - const struct drm_display_mode *mode, - const struct drm_display_mode *adjusted_mode) -{ - struct dw_mipi_dsi *dsi = bridge_to_dsi(bridge); - - dw_mipi_dsi_mode_set(dsi, adjusted_mode); if (dsi->slave) - dw_mipi_dsi_mode_set(dsi->slave, adjusted_mode); - - DRM_DEV_INFO(dsi->dev, "final DSI-Link bandwidth: %u x %d Mbps\n", - dsi->lane_mbps, dsi->slave ? dsi->lanes * 2 : dsi->lanes); + dw_mipi_dsi_pre_enable(dsi->slave); } -static void dw_mipi_dsi_bridge_enable(struct drm_bridge *bridge) +static void dw_mipi_dsi_bridge_pre_enable(struct drm_bridge *bridge) { struct dw_mipi_dsi *dsi = bridge_to_dsi(bridge); - /* Switch to video/cmd mode for panel-bridge enable & panel enable */ + dw_mipi_dsi_pre_enable(dsi); + + if (dsi->panel) + drm_panel_prepare(dsi->panel); +} + +static void dw_mipi_dsi_enable(struct dw_mipi_dsi *dsi) +{ if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) { dw_mipi_dsi_set_mode(dsi, MIPI_DSI_MODE_VIDEO); if (dsi->slave) @@ -976,6 +987,22 @@ static void dw_mipi_dsi_bridge_enable(struct drm_bridge *bridge) if (dsi->slave) dw_mipi_dsi_set_mode(dsi->slave, 0); } + + if (dsi->slave) + dw_mipi_dsi_enable(dsi->slave); +} + +static void dw_mipi_dsi_bridge_enable(struct drm_bridge *bridge) +{ + struct dw_mipi_dsi *dsi = bridge_to_dsi(bridge); + + dw_mipi_dsi_enable(dsi); + + if (dsi->panel) + drm_panel_enable(dsi->panel); + + DRM_DEV_INFO(dsi->dev, "final DSI-Link bandwidth: %u x %d Mbps\n", + dsi->lane_mbps, dsi->slave ? dsi->lanes * 2 : dsi->lanes); } static enum drm_mode_status @@ -1006,15 +1033,20 @@ static int dw_mipi_dsi_bridge_attach(struct drm_bridge *bridge, /* Set the encoder type as caller does not know it */ bridge->encoder->encoder_type = DRM_MODE_ENCODER_DSI; - /* Attach the panel-bridge to the dsi bridge */ - return drm_bridge_attach(bridge->encoder, dsi->panel_bridge, bridge, - flags); + /* Attach the next-bridge to the dsi bridge */ + if (dsi->next_bridge) + return drm_bridge_attach(bridge->encoder, dsi->next_bridge, + bridge, flags); + + return 0; } static const struct drm_bridge_funcs dw_mipi_dsi_bridge_funcs = { .mode_set = dw_mipi_dsi_bridge_mode_set, + .pre_enable = dw_mipi_dsi_bridge_pre_enable, .enable = dw_mipi_dsi_bridge_enable, .post_disable = dw_mipi_dsi_bridge_post_disable, + .disable = dw_mipi_dsi_bridge_disable, .mode_valid = dw_mipi_dsi_bridge_mode_valid, .attach = dw_mipi_dsi_bridge_attach, }; @@ -1209,6 +1241,81 @@ void dw_mipi_dsi_remove(struct dw_mipi_dsi *dsi) } EXPORT_SYMBOL_GPL(dw_mipi_dsi_remove); +static int dw_mipi_dsi_connector_get_modes(struct drm_connector *connector) +{ + struct dw_mipi_dsi *dsi = con_to_dsi(connector); + + if (dsi->next_bridge && (dsi->next_bridge->ops & DRM_BRIDGE_OP_MODES)) + return drm_bridge_get_modes(dsi->next_bridge, connector); + + if (dsi->panel) + return drm_panel_get_modes(dsi->panel, connector); + + return -EINVAL; +} + +static struct drm_connector_helper_funcs dw_mipi_dsi_connector_helper_funcs = { + .get_modes = dw_mipi_dsi_connector_get_modes, +}; + +static enum drm_connector_status +dw_mipi_dsi_connector_detect(struct drm_connector *connector, bool force) +{ + struct dw_mipi_dsi *dsi = con_to_dsi(connector); + + if (dsi->next_bridge && (dsi->next_bridge->ops & DRM_BRIDGE_OP_DETECT)) + return drm_bridge_detect(dsi->next_bridge); + + return connector_status_connected; +} + +static void dw_mipi_dsi_drm_connector_destroy(struct drm_connector *connector) +{ + drm_connector_unregister(connector); + drm_connector_cleanup(connector); +} + +static const struct drm_connector_funcs dw_mipi_dsi_atomic_connector_funcs = { + .fill_modes = drm_helper_probe_single_connector_modes, + .detect = dw_mipi_dsi_connector_detect, + .destroy = dw_mipi_dsi_drm_connector_destroy, + .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 int dw_mipi_dsi_connector_init(struct dw_mipi_dsi *dsi) +{ + struct drm_encoder *encoder = dsi->encoder; + struct drm_connector *connector = &dsi->connector; + struct drm_device *drm_dev = dsi->bridge.dev; + struct device *dev = dsi->dev; + int ret; + + ret = drm_connector_init(drm_dev, connector, + &dw_mipi_dsi_atomic_connector_funcs, + DRM_MODE_CONNECTOR_DSI); + if (ret) { + DRM_DEV_ERROR(dev, "Failed to initialize connector\n"); + return ret; + } + + drm_connector_helper_add(connector, + &dw_mipi_dsi_connector_helper_funcs); + ret = drm_connector_attach_encoder(connector, encoder); + if (ret < 0) { + DRM_DEV_ERROR(dev, "Failed to attach encoder: %d\n", ret); + goto connector_cleanup; + } + + return 0; + +connector_cleanup: + connector->funcs->destroy(connector); + + return ret; +} + /* * Bind/unbind API, used from platforms based on the component framework. */ @@ -1216,6 +1323,8 @@ int dw_mipi_dsi_bind(struct dw_mipi_dsi *dsi, struct drm_encoder *encoder) { int ret; + dsi->encoder = encoder; + ret = drm_bridge_attach(encoder, &dsi->bridge, NULL, 0); if (ret) { DRM_ERROR("Failed to initialize bridge with drm\n"); @@ -1233,7 +1342,33 @@ EXPORT_SYMBOL_GPL(dw_mipi_dsi_unbind); struct drm_connector *dw_mipi_dsi_get_connector(struct dw_mipi_dsi *dsi) { - return drm_panel_bridge_connector(dsi->panel_bridge); + struct drm_connector *connector = NULL; + enum drm_bridge_attach_flags flags = 0; + int ret; + + if (dsi->next_bridge) { + enum drm_bridge_attach_flags flags; + struct list_head *connector_list = + &dsi->next_bridge->dev->mode_config.connector_list; + + flags = dsi->next_bridge->ops & DRM_BRIDGE_OP_MODES ? + DRM_BRIDGE_ATTACH_NO_CONNECTOR : 0; + if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) + list_for_each_entry(connector, connector_list, head) + if (drm_connector_has_possible_encoder(connector, + dsi->encoder)) + break; + } + + if (dsi->panel || (dsi->next_bridge && (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR))) { + ret = dw_mipi_dsi_connector_init(dsi); + if (ret) + return ERR_PTR(ret); + + connector = &dsi->connector; + } + + return connector; } EXPORT_SYMBOL_GPL(dw_mipi_dsi_get_connector); diff --git a/drivers/gpu/drm/drm_gem_framebuffer_helper.c b/drivers/gpu/drm/drm_gem_framebuffer_helper.c index 109d11fb4cd4..aeb808a0ba54 100644 --- a/drivers/gpu/drm/drm_gem_framebuffer_helper.c +++ b/drivers/gpu/drm/drm_gem_framebuffer_helper.c @@ -155,8 +155,10 @@ int drm_gem_fb_init_with_funcs(struct drm_device *dev, int ret, i; info = drm_get_format_info(dev, mode_cmd); - if (!info) + if (!info) { + drm_dbg_kms(dev, "Failed to get FB format info\n"); return -EINVAL; + } for (i = 0; i < info->num_planes; i++) { unsigned int width = mode_cmd->width / (i ? info->hsub : 1); @@ -175,6 +177,9 @@ int drm_gem_fb_init_with_funcs(struct drm_device *dev, + mode_cmd->offsets[i]; if (objs[i]->size < min_size) { + drm_dbg_kms(dev, + "GEM object size (%zu) smaller than minimum size (%u) for plane %d\n", + objs[i]->size, min_size, i); drm_gem_object_put(objs[i]); ret = -EINVAL; goto err_gem_object_put; diff --git a/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c b/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c index 6922f56d1fad..015f983ad39e 100644 --- a/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c +++ b/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c @@ -149,6 +149,11 @@ #define PX30_DSI_TURNDISABLE BIT(5) #define PX30_DSI_LCDC_SEL BIT(0) +#define RK3128_GRF_LVDS_CON0 0x0150 +#define RK3128_DSI_FORCETXSTOPMODE (0xf << 10) +#define RK3128_DSI_FORCERXMODE (0x1 << 9) +#define RK3128_DSI_TURNDISABLE (0x1 << 8) + #define RK3288_GRF_SOC_CON6 0x025c #define RK3288_DSI0_LCDC_SEL BIT(6) #define RK3288_DSI1_LCDC_SEL BIT(9) @@ -213,6 +218,7 @@ enum { enum soc_type { PX30, + RK3128, RK3288, RK3399, RK3568, @@ -1283,6 +1289,21 @@ static const struct rockchip_dw_dsi_chip_data px30_chip_data[] = { { /* sentinel */ } }; +static const struct rockchip_dw_dsi_chip_data rk3128_chip_data[] = { + { + .reg = 0x10110000, + .lanecfg1_grf_reg = RK3128_GRF_LVDS_CON0, + .lanecfg1 = HIWORD_UPDATE(0, RK3128_DSI_TURNDISABLE | + RK3128_DSI_FORCETXSTOPMODE | + RK3128_DSI_FORCERXMODE), + .flags = DW_MIPI_NEEDS_HCLK, + .max_data_lanes = 4, + .max_bit_rate_per_lane = 1000000000UL, + .soc_type = RK3128, + }, + { /* sentinel */ } +}; + static const struct rockchip_dw_dsi_chip_data rk3288_chip_data[] = { { .reg = 0xff960000, @@ -1407,6 +1428,9 @@ static const struct of_device_id dw_mipi_dsi_rockchip_dt_ids[] = { { .compatible = "rockchip,px30-mipi-dsi", .data = &px30_chip_data, + }, { + .compatible = "rockchip,rk3128-mipi-dsi", + .data = &rk3128_chip_data, }, { .compatible = "rockchip,rk3288-mipi-dsi", .data = &rk3288_chip_data, diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c index 69801ca30474..ed06757c8280 100644 --- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c +++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c @@ -184,7 +184,6 @@ struct rockchip_hdmi { unsigned long enc_out_encoding; int color_changed; int hpd_irq; - int vp_id; struct drm_property *color_depth_property; struct drm_property *hdmi_output_property; @@ -2006,7 +2005,6 @@ dw_hdmi_rockchip_encoder_atomic_check(struct drm_encoder *encoder, secondary: drm_mode_copy(&mode, &crtc_state->mode); - hdmi->vp_id = s->vp_id; if (hdmi->plat_data->split_mode) drm_mode_convert_to_origin_mode(&mode); @@ -2216,14 +2214,23 @@ struct dw_hdmi_link_config *dw_hdmi_rockchip_get_link_cfg(void *data) return &hdmi->link_cfg; } -static int dw_hdmi_dclk_set(void *data, bool enable) +static int dw_hdmi_rockchip_get_vp_id(struct drm_crtc_state *crtc_state) +{ + struct rockchip_crtc_state *s; + + s = to_rockchip_crtc_state(crtc_state); + + return s->vp_id; +} + +static int dw_hdmi_dclk_set(void *data, bool enable, int vp_id) { struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; char clk_name[16]; struct clk *dclk; int ret; - snprintf(clk_name, sizeof(clk_name), "dclk_vp%d", hdmi->vp_id); + snprintf(clk_name, sizeof(clk_name), "dclk_vp%d", vp_id); dclk = devm_clk_get(hdmi->dev, clk_name); if (IS_ERR(dclk)) { @@ -2235,7 +2242,7 @@ static int dw_hdmi_dclk_set(void *data, bool enable) ret = clk_prepare_enable(dclk); if (ret < 0) DRM_DEV_ERROR(hdmi->dev, "failed to enable dclk for video port%d - %d\n", - hdmi->vp_id, ret); + vp_id, ret); } else { clk_disable_unprepare(dclk); } @@ -3147,6 +3154,7 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master, plat_data->convert_to_origin_mode = drm_mode_convert_to_origin_mode; plat_data->dclk_set = dw_hdmi_dclk_set; plat_data->link_clk_set = dw_hdmi_link_clk_set; + plat_data->get_vp_id = dw_hdmi_rockchip_get_vp_id; plat_data->property_ops = &dw_hdmi_rockchip_property_ops; diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_logo.c b/drivers/gpu/drm/rockchip/rockchip_drm_logo.c index d0fcbb5c47a4..e50453ebae8d 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_logo.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_logo.c @@ -836,6 +836,7 @@ static int update_state(struct drm_device *drm_dev, const struct drm_encoder_helper_funcs *encoder_helper_funcs; const struct drm_connector_helper_funcs *connector_helper_funcs; struct drm_encoder *encoder; + struct drm_bridge *bridge; connector_helper_funcs = connector->helper_private; if (!connector_helper_funcs) @@ -860,6 +861,9 @@ static int update_state(struct drm_device *drm_dev, conn_state); else if (encoder_helper_funcs->mode_set) encoder_helper_funcs->mode_set(encoder, mode, mode); + + bridge = drm_bridge_chain_get_first_bridge(encoder); + drm_bridge_chain_mode_set(bridge, mode, mode); } primary_state = drm_atomic_get_plane_state(state, crtc->primary); diff --git a/drivers/gpu/drm/rockchip/rockchip_vop2_clk.c b/drivers/gpu/drm/rockchip/rockchip_vop2_clk.c index d381d767d726..3833923bb6a5 100644 --- a/drivers/gpu/drm/rockchip/rockchip_vop2_clk.c +++ b/drivers/gpu/drm/rockchip/rockchip_vop2_clk.c @@ -9,8 +9,6 @@ static int cru_debug; -#define PLL_RATE_MIN 30000000 - #define cru_dbg(format, ...) do { \ if (cru_debug) \ pr_info("%s: " format, __func__, ## __VA_ARGS__); \ @@ -157,6 +155,7 @@ static long clk_virtual_round_rate(struct clk_hw *hw, unsigned long rate, vop2_clk->rate = rate; + cru_dbg("%s rate: %ld\n", clk_hw_get_name(hw), rate); return rate; } @@ -245,10 +244,6 @@ static long vop2_clk_div_round_rate(struct clk_hw *hw, unsigned long rate, if ((*prate % rate)) *prate = rate; - - /* SOC PLL can't output a too low pll freq */ - if (*prate < PLL_RATE_MIN) - *prate = rate << vop2_clk->div.width; } cru_dbg("%s rate: %ld(prate: %ld)\n", clk_hw_get_name(hw), rate, *prate); diff --git a/drivers/media/i2c/hi556.c b/drivers/media/i2c/hi556.c index c66cd1446c0f..81efc125b89a 100644 --- a/drivers/media/i2c/hi556.c +++ b/drivers/media/i2c/hi556.c @@ -1,113 +1,190 @@ // SPDX-License-Identifier: GPL-2.0 -// Copyright (c) 2019 Intel Corporation. +/* + * hi556 driver + * + * Copyright (C) 2022 Rockchip Electronics Co., Ltd. + * + * V0.0X01.0X00 init version + */ -#include -#include +#include +#include #include +#include #include #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include +#include #include +#include +#include +#include +#include + +/* verify default register values */ +//#define CHECK_REG_VALUE + +#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x00) + +#ifndef V4L2_CID_DIGITAL_GAIN +#define V4L2_CID_DIGITAL_GAIN V4L2_CID_GAIN +#endif + +/* pixel rate = link frequency * 2 * lanes / BITS_PER_SAMPLE */ +#define MIPI_FREQ 440000000U +#define HI556_PIXEL_RATE (440000000LL * 2LL * 2LL / 10) +#define HI556_XVCLK_FREQ 24000000 + +#define CHIP_ID 0x0556 +#define HI556_REG_CHIP_ID 0x0f16 + +#define HI556_REG_CTRL_MODE 0x0A00 +#define HI556_MODE_SW_STANDBY 0x00 +#define HI556_MODE_STREAMING 0x01 + +#define HI556_REG_EXPOSURE_H 0x0073 +#define HI556_REG_EXPOSURE_M 0x0074 +#define HI556_REG_EXPOSURE_L 0x0075 + +#define HI556_FETCH_HIGH_BYTE_EXP(VAL) (((VAL) >> 16) & 0xF) /* 4 Bits */ +#define HI556_FETCH_MIDDLE_BYTE_EXP(VAL) (((VAL) >> 8) & 0xFF) /* 8 Bits */ +#define HI556_FETCH_LOW_BYTE_EXP(VAL) ((VAL) & 0xFF) /* 8 Bits */ + +#define HI556_EXPOSURE_MIN 4 +#define HI556_EXPOSURE_STEP 1 +#define HI556_VTS_MAX 0x7fff + +#define HI556_REG_GAIN 0x0077 +#define HI556_GAIN_MASK 0xff + +#define ANALOG_GAIN_MIN 0x00 +#define ANALOG_GAIN_MAX 0xF0 +#define ANALOG_GAIN_STEP 1 +#define ANALOG_GAIN_DEFAULT 0x10 + +#define HI556_REG_GROUP 0x0046 + +#define HI556_REG_TEST_PATTERN 0x0A05 +#define HI556_TEST_PATTERN_ENABLE 0x01 +#define HI556_TEST_PATTERN_DISABLE 0x0 +#define HI556_REG_TEST_PATTERN_SELECT 0x0201 + +#define HI556_REG_VTS 0x0006 +#define HI556_FLIP_MIRROR_REG 0x000e +#define HI556_FETCH_MIRROR(VAL, ENABLE) (ENABLE ? VAL | 0x01 : VAL & 0xfe) +#define HI556_FETCH_FLIP(VAL, ENABLE) (ENABLE ? VAL | 0x02 : VAL & 0xfd) +#define REG_NULL 0xFFFF +#define DELAY_MS 0xEEEE /* Array delay token */ #define HI556_REG_VALUE_08BIT 1 #define HI556_REG_VALUE_16BIT 2 #define HI556_REG_VALUE_24BIT 3 -#define HI556_LINK_FREQ_437MHZ 437000000ULL -#define HI556_MCLK 19200000 -#define HI556_DATA_LANES 2 -#define HI556_RGB_DEPTH 10 +#define HI556_LANES 2 +#define HI556_BITS_PER_SAMPLE 10 -#define HI556_REG_CHIP_ID 0x0f16 -#define HI556_CHIP_ID 0x0556 +#define OF_CAMERA_PINCTRL_STATE_DEFAULT "rockchip,camera_default" +#define OF_CAMERA_PINCTRL_STATE_SLEEP "rockchip,camera_sleep" -#define HI556_REG_MODE_SELECT 0x0a00 -#define HI556_MODE_STANDBY 0x0000 -#define HI556_MODE_STREAMING 0x0100 +#define HI556_NAME "hi556" +#define HI556_MEDIA_BUS_FMT MEDIA_BUS_FMT_SGBRG10_1X10 -/* vertical-timings from sensor */ -#define HI556_REG_FLL 0x0006 -#define HI556_FLL_30FPS 0x0814 -#define HI556_FLL_30FPS_MIN 0x0814 -#define HI556_FLL_MAX 0x7fff - -/* horizontal-timings from sensor */ -#define HI556_REG_LLP 0x0008 - -/* Exposure controls from sensor */ -#define HI556_REG_EXPOSURE 0x0074 -#define HI556_EXPOSURE_MIN 6 -#define HI556_EXPOSURE_MAX_MARGIN 2 -#define HI556_EXPOSURE_STEP 1 - -/* Analog gain controls from sensor */ -#define HI556_REG_ANALOG_GAIN 0x0077 -#define HI556_ANAL_GAIN_MIN 0 -#define HI556_ANAL_GAIN_MAX 240 -#define HI556_ANAL_GAIN_STEP 1 - -/* Digital gain controls from sensor */ -#define HI556_REG_MWB_GR_GAIN 0x0078 -#define HI556_REG_MWB_GB_GAIN 0x007a -#define HI556_REG_MWB_R_GAIN 0x007c -#define HI556_REG_MWB_B_GAIN 0x007e -#define HI556_DGTL_GAIN_MIN 0 -#define HI556_DGTL_GAIN_MAX 2048 -#define HI556_DGTL_GAIN_STEP 1 -#define HI556_DGTL_GAIN_DEFAULT 256 - -/* Test Pattern Control */ -#define HI556_REG_ISP 0X0a05 -#define HI556_REG_ISP_TPG_EN 0x01 -#define HI556_REG_TEST_PATTERN 0x0201 - -enum { - HI556_LINK_FREQ_437MHZ_INDEX, +struct hi556_otp_info { + int flag; // bit[7]: info, bit[6]:wb + int module_id; + int lens_id; + int year; + int month; + int day; + int rg_ratio; + int bg_ratio; }; -struct hi556_reg { - u16 address; +static const char * const hi556_supply_names[] = { + "avdd", /* Analog power */ + "dovdd", /* Digital I/O power */ + "dvdd", /* Digital core power */ +}; + +#define HI556_NUM_SUPPLIES ARRAY_SIZE(hi556_supply_names) + +struct regval { + u16 addr; u16 val; }; -struct hi556_reg_list { - u32 num_of_regs; - const struct hi556_reg *regs; -}; - -struct hi556_link_freq_config { - const struct hi556_reg_list reg_list; -}; - struct hi556_mode { - /* Frame width in pixels */ u32 width; - - /* Frame height in pixels */ u32 height; - - /* Horizontal timining size */ - u32 llp; - - /* Default vertical timining size */ - u32 fll_def; - - /* Min vertical timining size */ - u32 fll_min; - - /* Link frequency needed for this resolution */ - u32 link_freq_index; - - /* Sensor register settings for this resolution */ - const struct hi556_reg_list reg_list; + struct v4l2_fract max_fps; + u32 hts_def; + u32 vts_def; + u32 exp_def; + const struct regval *reg_list; + u32 hdr_mode; }; -#define to_hi556(_sd) container_of(_sd, struct hi556, sd) +struct hi556 { + struct i2c_client *client; + struct clk *xvclk; + struct gpio_desc *power_gpio; + struct gpio_desc *reset_gpio; + struct gpio_desc *pwdn_gpio; + struct regulator_bulk_data supplies[HI556_NUM_SUPPLIES]; -//SENSOR_INITIALIZATION -static const struct hi556_reg mipi_data_rate_874mbps[] = { + 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 hi556_mode *cur_mode; + unsigned int lane_num; + unsigned int cfg_num; + unsigned int pixel_rate; + u32 module_index; + struct hi556_otp_info *otp; + const char *module_facing; + const char *module_name; + const char *len_name; + struct rkmodule_awb_cfg awb_cfg; +}; + +#define to_hi556(sd) container_of(sd, struct hi556, subdev) + +/* + * Xclk 24Mhz + * Pclk 176Mhz + * linelength 2816(0xb00) + * framelength 1988(0x7c0) + * grabwindow_width 2592 + * grabwindow_height 1944 + * max_framerate 30fps + * MIPI speed(Mbps) : 840Mbps x 2Lane + */ +static const struct regval hi556_global_regs[] = { + {0x0a00, 0x0000}, {0x0e00, 0x0102}, {0x0e02, 0x0102}, {0x0e0c, 0x0100}, @@ -257,7 +334,6 @@ static const struct hi556_reg mipi_data_rate_874mbps[] = { {0x3022, 0x140f}, {0x3024, 0x0040}, {0x3026, 0x000f}, - {0x0b00, 0x0000}, {0x0b02, 0x0045}, {0x0b04, 0xb405}, @@ -270,12 +346,12 @@ static const struct hi556_reg mipi_data_rate_874mbps[] = { {0x0b12, 0x004c}, {0x0b14, 0x4068}, {0x0b16, 0x0000}, - {0x0f30, 0x5b15}, + {0x0f30, 0x6e25}, {0x0f32, 0x7067}, {0x0954, 0x0009}, - {0x0956, 0x0000}, - {0x0958, 0xbb80}, - {0x095a, 0x5140}, + {0x0956, 0x1100}, + {0x0958, 0xcc80}, + {0x095a, 0x0000}, {0x0c00, 0x1110}, {0x0c02, 0x0011}, {0x0c04, 0x0000}, @@ -298,27 +374,26 @@ static const struct hi556_reg mipi_data_rate_874mbps[] = { {0x0008, 0x0b00}, {0x005a, 0x0202}, {0x0012, 0x000e}, - {0x0018, 0x0a33}, + {0x0018, 0x0a31}, {0x0022, 0x0008}, {0x0028, 0x0017}, {0x0024, 0x0028}, {0x002a, 0x002d}, {0x0026, 0x0030}, - {0x002c, 0x07c9}, + {0x002c, 0x07c7}, {0x002e, 0x1111}, {0x0030, 0x1111}, {0x0032, 0x1111}, - {0x0006, 0x07bc}, + {0x0006, 0x0823}, {0x0a22, 0x0000}, {0x0a12, 0x0a20}, {0x0a14, 0x0798}, {0x003e, 0x0000}, - {0x0074, 0x080e}, - {0x0070, 0x0407}, + {0x0074, 0x0821}, + {0x0070, 0x0411}, {0x0002, 0x0000}, {0x0a02, 0x0100}, {0x0a24, 0x0100}, - {0x0046, 0x0000}, {0x0076, 0x0000}, {0x0060, 0x0000}, {0x0062, 0x0530}, @@ -327,47 +402,54 @@ static const struct hi556_reg mipi_data_rate_874mbps[] = { {0x0068, 0x0500}, {0x0122, 0x0300}, {0x015a, 0xff08}, - {0x0804, 0x0300}, - {0x0806, 0x0100}, + {0x0804, 0x0200}, {0x005c, 0x0102}, {0x0a1a, 0x0800}, + {0x003c, 0x0101}, //fix framerate + {REG_NULL, 0x00}, }; -static const struct hi556_reg mode_2592x1944_regs[] = { +/* + * Xclk 24Mhz + * Pclk 210Mhz + * linelength 2816 + * framelength 2083 + * grabwindow_width 2592 + * grabwindow_height 1944 + * max_framerate 30fps + * MIPI speed(Mbps): 880Mbps x 2lane + */ +static const struct regval hi556_2592x1944_regs_2lane[] = { {0x0a00, 0x0000}, {0x0b0a, 0x8252}, - {0x0f30, 0x5b15}, + {0x0f30, 0x6e25}, {0x0f32, 0x7067}, {0x004a, 0x0100}, {0x004c, 0x0000}, - {0x004e, 0x0100}, + {0x004e, 0x0000}, {0x000c, 0x0022}, {0x0008, 0x0b00}, {0x005a, 0x0202}, {0x0012, 0x000e}, - {0x0018, 0x0a33}, + {0x0018, 0x0a31}, {0x0022, 0x0008}, {0x0028, 0x0017}, {0x0024, 0x0028}, {0x002a, 0x002d}, {0x0026, 0x0030}, - {0x002c, 0x07c9}, + {0x002c, 0x07c7}, {0x002e, 0x1111}, {0x0030, 0x1111}, {0x0032, 0x1111}, - {0x0006, 0x0814}, + {0x0006, 0x0823}, {0x0a22, 0x0000}, {0x0a12, 0x0a20}, {0x0a14, 0x0798}, {0x003e, 0x0000}, - {0x0074, 0x0812}, - {0x0070, 0x0409}, - {0x0804, 0x0300}, - {0x0806, 0x0100}, + {0x0804, 0x0200}, {0x0a04, 0x014a}, {0x090c, 0x0fdc}, {0x090e, 0x002d}, - {0x0902, 0x4319}, {0x0914, 0xc10a}, {0x0916, 0x071f}, @@ -375,301 +457,835 @@ static const struct hi556_reg mode_2592x1944_regs[] = { {0x091a, 0x0c0d}, {0x091c, 0x0f09}, {0x091e, 0x0a00}, - {0x0958, 0xbb80}, + //{0x0a00, 0x0100}, + {REG_NULL, 0x00}, }; -static const struct hi556_reg mode_1296x972_regs[] = { - {0x0a00, 0x0000}, - {0x0b0a, 0x8259}, - {0x0f30, 0x5b15}, - {0x0f32, 0x7167}, - {0x004a, 0x0100}, - {0x004c, 0x0000}, - {0x004e, 0x0100}, - {0x000c, 0x0122}, - {0x0008, 0x0b00}, - {0x005a, 0x0404}, - {0x0012, 0x000c}, - {0x0018, 0x0a33}, - {0x0022, 0x0008}, - {0x0028, 0x0017}, - {0x0024, 0x0022}, - {0x002a, 0x002b}, - {0x0026, 0x0030}, - {0x002c, 0x07c9}, - {0x002e, 0x3311}, - {0x0030, 0x3311}, - {0x0032, 0x3311}, - {0x0006, 0x0814}, - {0x0a22, 0x0000}, - {0x0a12, 0x0510}, - {0x0a14, 0x03cc}, - {0x003e, 0x0000}, - {0x0074, 0x0812}, - {0x0070, 0x0409}, - {0x0804, 0x0308}, - {0x0806, 0x0100}, - {0x0a04, 0x016a}, - {0x090e, 0x0010}, - {0x090c, 0x09c0}, +static const struct hi556_mode supported_modes_2lane[] = { + { + .width = 2592, + .height = 1944, + .max_fps = { + .numerator = 10000, + .denominator = 300000, + }, + .exp_def = 0x0810, + .hts_def = 0x0B00, + .vts_def = 0x0823, + .reg_list = hi556_2592x1944_regs_2lane, + .hdr_mode = NO_HDR, + } +}; - {0x0902, 0x4319}, - {0x0914, 0xc106}, - {0x0916, 0x040e}, - {0x0918, 0x0304}, - {0x091a, 0x0708}, - {0x091c, 0x0e06}, - {0x091e, 0x0300}, - {0x0958, 0xbb80}, +static const struct hi556_mode *supported_modes; + +static const s64 link_freq_menu_items[] = { + MIPI_FREQ }; static const char * const hi556_test_pattern_menu[] = { "Disabled", - "Solid Colour", - "100% Colour Bars", - "Fade To Grey Colour Bars", + "Solid color bar", + "100% color bars", + "Fade to gray color bars", "PN9", - "Gradient Horizontal", - "Gradient Vertical", - "Check Board", - "Slant Pattern", + "Horizental/Vertical gradient", + "Check board", + "Slant", + "Resolution", }; -static const s64 link_freq_menu_items[] = { - HI556_LINK_FREQ_437MHZ, -}; - -static const struct hi556_link_freq_config link_freq_configs[] = { - [HI556_LINK_FREQ_437MHZ_INDEX] = { - .reg_list = { - .num_of_regs = ARRAY_SIZE(mipi_data_rate_874mbps), - .regs = mipi_data_rate_874mbps, - } - } -}; - -static const struct hi556_mode supported_modes[] = { - { - .width = 2592, - .height = 1944, - .fll_def = HI556_FLL_30FPS, - .fll_min = HI556_FLL_30FPS_MIN, - .llp = 0x0b00, - .reg_list = { - .num_of_regs = ARRAY_SIZE(mode_2592x1944_regs), - .regs = mode_2592x1944_regs, - }, - .link_freq_index = HI556_LINK_FREQ_437MHZ_INDEX, - }, - { - .width = 1296, - .height = 972, - .fll_def = HI556_FLL_30FPS, - .fll_min = HI556_FLL_30FPS_MIN, - .llp = 0x0b00, - .reg_list = { - .num_of_regs = ARRAY_SIZE(mode_1296x972_regs), - .regs = mode_1296x972_regs, - }, - .link_freq_index = HI556_LINK_FREQ_437MHZ_INDEX, - } -}; - -struct hi556 { - struct v4l2_subdev sd; - struct media_pad pad; - struct v4l2_ctrl_handler ctrl_handler; - - /* V4L2 Controls */ - struct v4l2_ctrl *link_freq; - struct v4l2_ctrl *pixel_rate; - struct v4l2_ctrl *vblank; - struct v4l2_ctrl *hblank; - struct v4l2_ctrl *exposure; - - /* Current mode */ - const struct hi556_mode *cur_mode; - - /* To serialize asynchronus callbacks */ - struct mutex mutex; - - /* Streaming on/off */ - bool streaming; -}; - -static u64 to_pixel_rate(u32 f_index) +/* Write registers up to 4 at a time */ +static int hi556_write_reg(struct i2c_client *client, u16 reg, + u32 len, u32 val) { - u64 pixel_rate = link_freq_menu_items[f_index] * 2 * HI556_DATA_LANES; + u32 buf_i, val_i; + u8 buf[6]; + u8 *val_p; + __be32 val_be; - do_div(pixel_rate, HI556_RGB_DEPTH); - - return pixel_rate; -} - -static int hi556_read_reg(struct hi556 *hi556, u16 reg, u16 len, u32 *val) -{ - struct i2c_client *client = v4l2_get_subdevdata(&hi556->sd); - struct i2c_msg msgs[2]; - u8 addr_buf[2]; - u8 data_buf[4] = {0}; - int ret; + dev_dbg(&client->dev, "%s(%d) enter!\n", __func__, __LINE__); + dev_dbg(&client->dev, "write reg(0x%x val:0x%x)!\n", reg, val); if (len > 4) return -EINVAL; - put_unaligned_be16(reg, addr_buf); + 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) { + dev_err(&client->dev, + "write reg(0x%x val:0x%x)failed !\n", reg, val); + return -EIO; + } + return 0; +} + +static int hi556_write_array(struct i2c_client *client, + const struct regval *regs) +{ + int i, delay_ms, ret = 0; + + for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++) { + if (regs[i].addr == DELAY_MS) { + delay_ms = regs[i].val; + dev_info(&client->dev, "delay(%d) ms !\n", delay_ms); + usleep_range(1000 * delay_ms, 1000 * delay_ms + 100); + continue; + } + ret = hi556_write_reg(client, regs[i].addr, + HI556_REG_VALUE_16BIT, regs[i].val); + if (ret) + dev_err(&client->dev, "%s failed !\n", __func__); + } + + return ret; +} + +/* Read registers up to 4 at a time */ +static int hi556_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 = sizeof(addr_buf); - msgs[0].buf = addr_buf; + 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_buf[4 - 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 = get_unaligned_be32(data_buf); + *val = be32_to_cpu(data_be); return 0; } -static int hi556_write_reg(struct hi556 *hi556, u16 reg, u16 len, u32 val) +/* Check Register value */ +#ifdef CHECK_REG_VALUE +static int hi556_reg_verify(struct i2c_client *client, + const struct regval *regs) { - struct i2c_client *client = v4l2_get_subdevdata(&hi556->sd); - u8 buf[6]; + u32 i; + int ret = 0; + u32 value; - if (len > 4) - return -EINVAL; + for (i = 0; ret == 0 && regs[i].addr != REG_NULL; i++) { + ret = hi556_read_reg(client, regs[i].addr, + HI556_REG_VALUE_16BIT, &value); + if (value != regs[i].val) { + dev_info(&client->dev, "%s: 0x%04x is 0x%x instead of 0x%x\n", + __func__, regs[i].addr, value, regs[i].val); + } + } + return ret; +} +#endif - put_unaligned_be16(reg, buf); - put_unaligned_be32(val << 8 * (4 - len), buf + 2); - if (i2c_master_send(client, buf, len + 2) != len + 2) - return -EIO; - - return 0; +static int hi556_get_reso_dist(const struct hi556_mode *mode, + struct v4l2_mbus_framefmt *framefmt) +{ + return abs(mode->width - framefmt->width) + + abs(mode->height - framefmt->height); } -static int hi556_write_reg_list(struct hi556 *hi556, - const struct hi556_reg_list *r_list) +static const struct hi556_mode * +hi556_find_best_fit(struct hi556 *hi556, + struct v4l2_subdev_format *fmt) { - struct i2c_client *client = v4l2_get_subdevdata(&hi556->sd); + struct v4l2_mbus_framefmt *framefmt = &fmt->format; + int dist; + int cur_best_fit = 0; + int cur_best_fit_dist = -1; unsigned int i; - int ret; - for (i = 0; i < r_list->num_of_regs; i++) { - ret = hi556_write_reg(hi556, r_list->regs[i].address, - HI556_REG_VALUE_16BIT, - r_list->regs[i].val); - if (ret) { - dev_err_ratelimited(&client->dev, - "failed to write reg 0x%4.4x. error = %d", - r_list->regs[i].address, ret); - return ret; + for (i = 0; i < hi556->cfg_num; i++) { + dist = hi556_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 hi556_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) +{ + struct hi556 *hi556 = to_hi556(sd); + const struct hi556_mode *mode; + s64 h_blank, vblank_def; + + mutex_lock(&hi556->mutex); + + mode = hi556_find_best_fit(hi556, fmt); + fmt->format.code = HI556_MEDIA_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(&hi556->mutex); + return -ENOTTY; +#endif + } else { + hi556->cur_mode = mode; + h_blank = mode->hts_def - mode->width; + __v4l2_ctrl_modify_range(hi556->hblank, h_blank, + h_blank, 1, h_blank); + vblank_def = mode->vts_def - mode->height; + __v4l2_ctrl_modify_range(hi556->vblank, vblank_def, + HI556_VTS_MAX - mode->height, + 1, vblank_def); + } + + mutex_unlock(&hi556->mutex); + return 0; } -static int hi556_update_digital_gain(struct hi556 *hi556, u32 d_gain) +static int hi556_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *fmt) { - int ret; + struct hi556 *hi556 = to_hi556(sd); + const struct hi556_mode *mode = hi556->cur_mode; - ret = hi556_write_reg(hi556, HI556_REG_MWB_GR_GAIN, - HI556_REG_VALUE_16BIT, d_gain); - if (ret) - return ret; + mutex_lock(&hi556->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(&hi556->mutex); + return -ENOTTY; +#endif + } else { + fmt->format.width = mode->width; + fmt->format.height = mode->height; + fmt->format.code = HI556_MEDIA_BUS_FMT; + fmt->format.field = V4L2_FIELD_NONE; + } + mutex_unlock(&hi556->mutex); - ret = hi556_write_reg(hi556, HI556_REG_MWB_GB_GAIN, - HI556_REG_VALUE_16BIT, d_gain); - if (ret) - return ret; - - ret = hi556_write_reg(hi556, HI556_REG_MWB_R_GAIN, - HI556_REG_VALUE_16BIT, d_gain); - if (ret) - return ret; - - return hi556_write_reg(hi556, HI556_REG_MWB_B_GAIN, - HI556_REG_VALUE_16BIT, d_gain); + return 0; } -static int hi556_test_pattern(struct hi556 *hi556, u32 pattern) +static int hi556_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index != 0) + return -EINVAL; + code->code = HI556_MEDIA_BUS_FMT; + + return 0; +} + +static int hi556_enum_frame_sizes(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct hi556 *hi556 = to_hi556(sd); + + if (fse->index >= hi556->cfg_num) + return -EINVAL; + + if (fse->code != HI556_MEDIA_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 hi556_enable_test_pattern(struct hi556 *hi556, u32 pattern) { - int ret; - u32 val; if (pattern) { - ret = hi556_read_reg(hi556, HI556_REG_ISP, - HI556_REG_VALUE_08BIT, &val); - if (ret) - return ret; + hi556_write_reg(hi556->client, HI556_REG_TEST_PATTERN, + HI556_REG_VALUE_08BIT, HI556_TEST_PATTERN_ENABLE); + hi556_write_reg(hi556->client, HI556_REG_TEST_PATTERN_SELECT, + HI556_REG_VALUE_08BIT, 0x01 << (pattern - 1)); + } else { + hi556_write_reg(hi556->client, HI556_REG_TEST_PATTERN, + HI556_REG_VALUE_08BIT, HI556_TEST_PATTERN_DISABLE); + } + return 0; +} - ret = hi556_write_reg(hi556, HI556_REG_ISP, - HI556_REG_VALUE_08BIT, - val | HI556_REG_ISP_TPG_EN); - if (ret) - return ret; +static int hi556_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + struct hi556 *hi556 = to_hi556(sd); + const struct hi556_mode *mode = hi556->cur_mode; + + fi->interval = mode->max_fps; + + return 0; +} + +static int hi556_g_mbus_config(struct v4l2_subdev *sd, + unsigned int pad_id, + struct v4l2_mbus_config *config) +{ + u32 val = 1 << (HI556_LANES - 1) | + V4L2_MBUS_CSI2_CHANNEL_0 | + V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; + + config->type = V4L2_MBUS_CSI2_DPHY; + config->flags = val; + + return 0; +} + +static void hi556_get_module_inf(struct hi556 *hi556, + struct rkmodule_inf *inf) +{ + memset(inf, 0, sizeof(*inf)); + strscpy(inf->base.sensor, HI556_NAME, sizeof(inf->base.sensor)); + strscpy(inf->base.module, hi556->module_name, + sizeof(inf->base.module)); + strscpy(inf->base.lens, hi556->len_name, sizeof(inf->base.lens)); + +} + +static void hi556_set_awb_cfg(struct hi556 *hi556, + struct rkmodule_awb_cfg *cfg) +{ + mutex_lock(&hi556->mutex); + memcpy(&hi556->awb_cfg, cfg, sizeof(*cfg)); + mutex_unlock(&hi556->mutex); +} + +static long hi556_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct hi556 *hi556 = to_hi556(sd); + long ret = 0; + u32 stream = 0; + + switch (cmd) { + case RKMODULE_GET_MODULE_INFO: + hi556_get_module_inf(hi556, (struct rkmodule_inf *)arg); + break; + case RKMODULE_AWB_CFG: + hi556_set_awb_cfg(hi556, (struct rkmodule_awb_cfg *)arg); + break; + case RKMODULE_SET_QUICK_STREAM: + + stream = *((u32 *)arg); + + if (stream) + ret = hi556_write_reg(hi556->client, HI556_REG_CTRL_MODE, + HI556_REG_VALUE_08BIT, HI556_MODE_STREAMING); + else + ret = hi556_write_reg(hi556->client, HI556_REG_CTRL_MODE, + HI556_REG_VALUE_08BIT, HI556_MODE_SW_STANDBY); + break; + default: + ret = -ENOIOCTLCMD; + break; } - return hi556_write_reg(hi556, HI556_REG_TEST_PATTERN, - HI556_REG_VALUE_08BIT, pattern); + return ret; +} + +#ifdef CONFIG_COMPAT +static long hi556_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 *awb_cfg; + 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 = hi556_ioctl(sd, cmd, inf); + if (!ret) { + ret = copy_to_user(up, inf, sizeof(*inf)); + if (ret) + ret = -EFAULT; + } + kfree(inf); + break; + case RKMODULE_AWB_CFG: + awb_cfg = kzalloc(sizeof(*awb_cfg), GFP_KERNEL); + if (!awb_cfg) { + ret = -ENOMEM; + return ret; + } + + if (copy_from_user(awb_cfg, up, sizeof(*awb_cfg))) { + kfree(awb_cfg); + return -EFAULT; + } + ret = hi556_ioctl(sd, cmd, awb_cfg); + kfree(awb_cfg); + break; + case RKMODULE_SET_QUICK_STREAM: + if (copy_from_user(&stream, up, sizeof(u32))) + return -EFAULT; + ret = hi556_ioctl(sd, cmd, &stream); + break; + default: + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} +#endif + +static int __hi556_start_stream(struct hi556 *hi556) +{ + int ret; + + ret = hi556_write_array(hi556->client, hi556->cur_mode->reg_list); + if (ret) + return ret; + +#ifdef CHECK_REG_VALUE + usleep_range(10000, 20000); + /* verify default values to make sure everything has */ + /* been written correctly as expected */ + dev_info(&hi556->client->dev, "%s:Check register value!\n", + __func__); + ret = hi556_reg_verify(hi556->client, hi556_global_regs); + if (ret) + return ret; + + ret = hi556_reg_verify(hi556->client, hi556->cur_mode->reg_list); + if (ret) + return ret; +#endif + + /* In case these controls are set before streaming */ + mutex_unlock(&hi556->mutex); + ret = v4l2_ctrl_handler_setup(&hi556->ctrl_handler); + mutex_lock(&hi556->mutex); + if (ret) + return ret; + + if (ret) + dev_info(&hi556->client->dev, "APPly otp failed!\n"); + + ret = hi556_write_reg(hi556->client, HI556_REG_CTRL_MODE, + HI556_REG_VALUE_08BIT, HI556_MODE_STREAMING); + return ret; +} + +static int __hi556_stop_stream(struct hi556 *hi556) +{ + return hi556_write_reg(hi556->client, HI556_REG_CTRL_MODE, + HI556_REG_VALUE_08BIT, HI556_MODE_SW_STANDBY); +} + +static int hi556_s_stream(struct v4l2_subdev *sd, int on) +{ + struct hi556 *hi556 = to_hi556(sd); + struct i2c_client *client = hi556->client; + int ret = 0; + + dev_info(&client->dev, "%s: on: %d, %dx%d@%d\n", __func__, on, + hi556->cur_mode->width, + hi556->cur_mode->height, + DIV_ROUND_CLOSEST(hi556->cur_mode->max_fps.denominator, + hi556->cur_mode->max_fps.numerator)); + + mutex_lock(&hi556->mutex); + on = !!on; + if (on == hi556->streaming) + goto unlock_and_return; + + if (on) { + dev_info(&client->dev, "stream on!!!\n"); + ret = pm_runtime_get_sync(&client->dev); + if (ret < 0) { + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + ret = __hi556_start_stream(hi556); + if (ret) { + v4l2_err(sd, "start stream failed while write regs\n"); + pm_runtime_put(&client->dev); + goto unlock_and_return; + } + } else { + dev_info(&client->dev, "stream off!!!\n"); + __hi556_stop_stream(hi556); + pm_runtime_put(&client->dev); + } + + hi556->streaming = on; + +unlock_and_return: + mutex_unlock(&hi556->mutex); + + return ret; +} + +static int hi556_s_power(struct v4l2_subdev *sd, int on) +{ + struct hi556 *hi556 = to_hi556(sd); + struct i2c_client *client = hi556->client; + int ret = 0; + + dev_info(&client->dev, "%s(%d) on(%d)\n", __func__, __LINE__, on); + mutex_lock(&hi556->mutex); + + /* If the power state is not modified - no work to do. */ + if (hi556->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; + } + + ret = hi556_write_array(hi556->client, hi556_global_regs); + if (ret) { + v4l2_err(sd, "could not set init registers\n"); + pm_runtime_put_noidle(&client->dev); + goto unlock_and_return; + } + + hi556->power_on = true; + } else { + pm_runtime_put(&client->dev); + hi556->power_on = false; + } + +unlock_and_return: + mutex_unlock(&hi556->mutex); + + return ret; +} + +/* Calculate the delay in us by clock rate and clock cycles */ +static inline u32 hi556_cal_delay(u32 cycles) +{ + return DIV_ROUND_UP(cycles, HI556_XVCLK_FREQ / 1000 / 1000); +} + +static int __hi556_power_on(struct hi556 *hi556) +{ + int ret; + u32 delay_us; + struct device *dev = &hi556->client->dev; + + if (!IS_ERR(hi556->power_gpio)) + gpiod_set_value_cansleep(hi556->power_gpio, 1); + + usleep_range(1000, 2000); + + if (!IS_ERR_OR_NULL(hi556->pins_default)) { + ret = pinctrl_select_state(hi556->pinctrl, + hi556->pins_default); + if (ret < 0) + dev_err(dev, "could not set pins\n"); + } + ret = clk_set_rate(hi556->xvclk, HI556_XVCLK_FREQ); + if (ret < 0) + dev_warn(dev, "Failed to set xvclk rate (24MHz)\n"); + if (clk_get_rate(hi556->xvclk) != HI556_XVCLK_FREQ) + dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n"); + + ret = clk_prepare_enable(hi556->xvclk); + if (ret < 0) { + dev_err(dev, "Failed to enable xvclk\n"); + return ret; + } + + ret = regulator_bulk_enable(HI556_NUM_SUPPLIES, hi556->supplies); + if (ret < 0) { + dev_err(dev, "Failed to enable regulators\n"); + goto disable_clk; + } + + if (!IS_ERR(hi556->reset_gpio)) + gpiod_set_value_cansleep(hi556->reset_gpio, 1); + + if (!IS_ERR(hi556->pwdn_gpio)) + gpiod_set_value_cansleep(hi556->pwdn_gpio, 1); + + /* 8192 cycles prior to first SCCB transaction */ + delay_us = hi556_cal_delay(8192); + usleep_range(delay_us, delay_us * 2); + usleep_range(10000, 20000); + return 0; + +disable_clk: + clk_disable_unprepare(hi556->xvclk); + + return ret; +} + +static void __hi556_power_off(struct hi556 *hi556) +{ + int ret; + struct device *dev = &hi556->client->dev; + + if (!IS_ERR(hi556->pwdn_gpio)) + gpiod_set_value_cansleep(hi556->pwdn_gpio, 0); + clk_disable_unprepare(hi556->xvclk); + if (!IS_ERR(hi556->reset_gpio)) + gpiod_set_value_cansleep(hi556->reset_gpio, 0); + if (!IS_ERR_OR_NULL(hi556->pins_sleep)) { + ret = pinctrl_select_state(hi556->pinctrl, + hi556->pins_sleep); + if (ret < 0) + dev_dbg(dev, "could not set pins\n"); + } + if (!IS_ERR(hi556->power_gpio)) + gpiod_set_value_cansleep(hi556->power_gpio, 0); + + regulator_bulk_disable(HI556_NUM_SUPPLIES, hi556->supplies); +} + +static int hi556_runtime_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct hi556 *hi556 = to_hi556(sd); + + return __hi556_power_on(hi556); +} + +static int hi556_runtime_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct hi556 *hi556 = to_hi556(sd); + + __hi556_power_off(hi556); + + return 0; +} + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static int hi556_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct hi556 *hi556 = to_hi556(sd); + struct v4l2_mbus_framefmt *try_fmt = + v4l2_subdev_get_try_format(sd, fh->pad, 0); + const struct hi556_mode *def_mode = &supported_modes[0]; + + mutex_lock(&hi556->mutex); + /* Initialize try_fmt */ + try_fmt->width = def_mode->width; + try_fmt->height = def_mode->height; + try_fmt->code = HI556_MEDIA_BUS_FMT; + try_fmt->field = V4L2_FIELD_NONE; + + mutex_unlock(&hi556->mutex); + /* No crop or compose */ + + return 0; +} +#endif + +static int hi556_enum_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_interval_enum *fie) +{ + struct hi556 *hi556 = to_hi556(sd); + + if (fie->index >= hi556->cfg_num) + return -EINVAL; + + if (fie->code != HI556_MEDIA_BUS_FMT) + return -EINVAL; + + 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 hi556_pm_ops = { + SET_RUNTIME_PM_OPS(hi556_runtime_suspend, + hi556_runtime_resume, NULL) +}; + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API +static const struct v4l2_subdev_internal_ops hi556_internal_ops = { + .open = hi556_open, +}; +#endif + +static const struct v4l2_subdev_core_ops hi556_core_ops = { + .s_power = hi556_s_power, + .ioctl = hi556_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = hi556_compat_ioctl32, +#endif +}; + +static const struct v4l2_subdev_video_ops hi556_video_ops = { + .s_stream = hi556_s_stream, + .g_frame_interval = hi556_g_frame_interval, +}; + +static const struct v4l2_subdev_pad_ops hi556_pad_ops = { + .enum_mbus_code = hi556_enum_mbus_code, + .enum_frame_size = hi556_enum_frame_sizes, + .enum_frame_interval = hi556_enum_frame_interval, + .get_fmt = hi556_get_fmt, + .set_fmt = hi556_set_fmt, + .get_mbus_config = hi556_g_mbus_config, +}; + +static const struct v4l2_subdev_ops hi556_subdev_ops = { + .core = &hi556_core_ops, + .video = &hi556_video_ops, + .pad = &hi556_pad_ops, +}; + +static int hi556_set_exposure_reg(struct hi556 *hi556, u32 exposure) +{ + int ret = 0; + u32 cal_shutter = 0; + + cal_shutter = exposure >> 1; + cal_shutter = cal_shutter << 1; + + ret = hi556_write_reg(hi556->client, HI556_REG_GROUP, + HI556_REG_VALUE_08BIT, 0x01); + ret |= hi556_write_reg(hi556->client, + HI556_REG_EXPOSURE_H, + HI556_REG_VALUE_08BIT, + HI556_FETCH_HIGH_BYTE_EXP(cal_shutter)); + ret |= hi556_write_reg(hi556->client, + HI556_REG_EXPOSURE_M, + HI556_REG_VALUE_08BIT, + HI556_FETCH_MIDDLE_BYTE_EXP(cal_shutter)); + ret |= hi556_write_reg(hi556->client, + HI556_REG_EXPOSURE_L, + HI556_REG_VALUE_08BIT, + HI556_FETCH_LOW_BYTE_EXP(cal_shutter)); + ret |= hi556_write_reg(hi556->client, HI556_REG_GROUP, + HI556_REG_VALUE_08BIT, 0x00); + + return ret; +} + +static int hi556_set_gain_reg(struct hi556 *hi556, u32 a_gain) +{ + int ret = 0; + + ret = hi556_write_reg(hi556->client, HI556_REG_GROUP, + HI556_REG_VALUE_08BIT, 0x01); + ret |= hi556_write_reg(hi556->client, HI556_REG_GAIN, + HI556_REG_VALUE_08BIT, a_gain); + ret |= hi556_write_reg(hi556->client, HI556_REG_GROUP, + HI556_REG_VALUE_08BIT, 0x00); + + return ret; } static int hi556_set_ctrl(struct v4l2_ctrl *ctrl) { struct hi556 *hi556 = container_of(ctrl->handler, struct hi556, ctrl_handler); - struct i2c_client *client = v4l2_get_subdevdata(&hi556->sd); - s64 exposure_max; + struct i2c_client *client = hi556->client; + s64 max; + u32 val = 0; int ret = 0; /* Propagate change of current control to all related controls */ - if (ctrl->id == V4L2_CID_VBLANK) { + switch (ctrl->id) { + case V4L2_CID_VBLANK: /* Update max exposure while meeting expected vblanking */ - exposure_max = hi556->cur_mode->height + ctrl->val - - HI556_EXPOSURE_MAX_MARGIN; + max = hi556->cur_mode->height + ctrl->val - 4; __v4l2_ctrl_modify_range(hi556->exposure, - hi556->exposure->minimum, - exposure_max, hi556->exposure->step, - exposure_max); + hi556->exposure->minimum, max, + hi556->exposure->step, + hi556->exposure->default_value); + break; } - /* V4L2 controls values will be applied only when power is already up */ if (!pm_runtime_get_if_in_use(&client->dev)) return 0; switch (ctrl->id) { - case V4L2_CID_ANALOGUE_GAIN: - ret = hi556_write_reg(hi556, HI556_REG_ANALOG_GAIN, - HI556_REG_VALUE_16BIT, ctrl->val); - break; - - case V4L2_CID_DIGITAL_GAIN: - ret = hi556_update_digital_gain(hi556, ctrl->val); - break; - case V4L2_CID_EXPOSURE: - ret = hi556_write_reg(hi556, HI556_REG_EXPOSURE, - HI556_REG_VALUE_16BIT, ctrl->val); + dev_dbg(&client->dev, "set exposure value 0x%x\n", ctrl->val); + /* 4 least significant bits of expsoure are fractional part */ + ret = hi556_set_exposure_reg(hi556, ctrl->val); + break; + case V4L2_CID_ANALOGUE_GAIN: + dev_dbg(&client->dev, "set analog gain value 0x%x\n", ctrl->val); + ret = hi556_set_gain_reg(hi556, ctrl->val); break; - case V4L2_CID_VBLANK: - /* Update FLL that meets expected vertical blanking */ - ret = hi556_write_reg(hi556, HI556_REG_FLL, - HI556_REG_VALUE_16BIT, - hi556->cur_mode->height + ctrl->val); + dev_dbg(&client->dev, "set vb value 0x%x\n", ctrl->val); + ret = hi556_write_reg(hi556->client, HI556_REG_VTS, + HI556_REG_VALUE_16BIT, + ctrl->val + hi556->cur_mode->height); break; - case V4L2_CID_TEST_PATTERN: - ret = hi556_test_pattern(hi556, ctrl->val); + ret = hi556_enable_test_pattern(hi556, ctrl->val); + break; + case V4L2_CID_HFLIP: + ret = hi556_read_reg(hi556->client, HI556_FLIP_MIRROR_REG, + HI556_REG_VALUE_08BIT, &val); + ret |= hi556_write_reg(hi556->client, HI556_FLIP_MIRROR_REG, + HI556_REG_VALUE_08BIT, + HI556_FETCH_MIRROR(val, ctrl->val)); + break; + case V4L2_CID_VFLIP: + ret = hi556_read_reg(hi556->client, HI556_FLIP_MIRROR_REG, + HI556_REG_VALUE_08BIT, &val); + ret |= hi556_write_reg(hi556->client, HI556_FLIP_MIRROR_REG, + HI556_REG_VALUE_08BIT, + HI556_FETCH_FLIP(val, ctrl->val)); break; - default: - ret = -EINVAL; + dev_warn(&client->dev, "%s Unhandled id:0x%x, val:0x%x\n", + __func__, ctrl->id, ctrl->val); break; } @@ -682,411 +1298,294 @@ static const struct v4l2_ctrl_ops hi556_ctrl_ops = { .s_ctrl = hi556_set_ctrl, }; -static int hi556_init_controls(struct hi556 *hi556) +static int hi556_initialize_controls(struct hi556 *hi556) { - struct v4l2_ctrl_handler *ctrl_hdlr; - s64 exposure_max, h_blank; + const struct hi556_mode *mode; + struct v4l2_ctrl_handler *handler; + struct v4l2_ctrl *ctrl; + s64 exposure_max, vblank_def; + u32 h_blank; int ret; - ctrl_hdlr = &hi556->ctrl_handler; - ret = v4l2_ctrl_handler_init(ctrl_hdlr, 8); + handler = &hi556->ctrl_handler; + mode = hi556->cur_mode; + ret = v4l2_ctrl_handler_init(handler, 9); if (ret) return ret; + handler->lock = &hi556->mutex; - ctrl_hdlr->lock = &hi556->mutex; - hi556->link_freq = v4l2_ctrl_new_int_menu(ctrl_hdlr, &hi556_ctrl_ops, - V4L2_CID_LINK_FREQ, - ARRAY_SIZE(link_freq_menu_items) - 1, - 0, link_freq_menu_items); - if (hi556->link_freq) - hi556->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + 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; - hi556->pixel_rate = v4l2_ctrl_new_std - (ctrl_hdlr, &hi556_ctrl_ops, - V4L2_CID_PIXEL_RATE, 0, - to_pixel_rate(HI556_LINK_FREQ_437MHZ_INDEX), - 1, - to_pixel_rate(HI556_LINK_FREQ_437MHZ_INDEX)); - hi556->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &hi556_ctrl_ops, - V4L2_CID_VBLANK, - hi556->cur_mode->fll_min - - hi556->cur_mode->height, - HI556_FLL_MAX - - hi556->cur_mode->height, 1, - hi556->cur_mode->fll_def - - hi556->cur_mode->height); + v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE, + 0, hi556->pixel_rate, 1, hi556->pixel_rate); - h_blank = hi556->cur_mode->llp - hi556->cur_mode->width; - - hi556->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &hi556_ctrl_ops, - V4L2_CID_HBLANK, h_blank, h_blank, 1, - h_blank); + h_blank = mode->hts_def - mode->width; + hi556->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK, + h_blank, h_blank, 1, h_blank); if (hi556->hblank) hi556->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; - v4l2_ctrl_new_std(ctrl_hdlr, &hi556_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, - HI556_ANAL_GAIN_MIN, HI556_ANAL_GAIN_MAX, - HI556_ANAL_GAIN_STEP, HI556_ANAL_GAIN_MIN); - v4l2_ctrl_new_std(ctrl_hdlr, &hi556_ctrl_ops, V4L2_CID_DIGITAL_GAIN, - HI556_DGTL_GAIN_MIN, HI556_DGTL_GAIN_MAX, - HI556_DGTL_GAIN_STEP, HI556_DGTL_GAIN_DEFAULT); - exposure_max = hi556->cur_mode->fll_def - HI556_EXPOSURE_MAX_MARGIN; - hi556->exposure = v4l2_ctrl_new_std(ctrl_hdlr, &hi556_ctrl_ops, - V4L2_CID_EXPOSURE, - HI556_EXPOSURE_MIN, exposure_max, - HI556_EXPOSURE_STEP, - exposure_max); - v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &hi556_ctrl_ops, - V4L2_CID_TEST_PATTERN, - ARRAY_SIZE(hi556_test_pattern_menu) - 1, - 0, 0, hi556_test_pattern_menu); - if (ctrl_hdlr->error) - return ctrl_hdlr->error; + vblank_def = mode->vts_def - mode->height; + hi556->vblank = v4l2_ctrl_new_std(handler, &hi556_ctrl_ops, + V4L2_CID_VBLANK, vblank_def, + HI556_VTS_MAX - mode->height, + 1, vblank_def); - hi556->sd.ctrl_handler = ctrl_hdlr; + exposure_max = mode->vts_def - 4; + hi556->exposure = v4l2_ctrl_new_std(handler, &hi556_ctrl_ops, + V4L2_CID_EXPOSURE, HI556_EXPOSURE_MIN, + exposure_max, HI556_EXPOSURE_STEP, + mode->exp_def); + + hi556->anal_gain = v4l2_ctrl_new_std(handler, &hi556_ctrl_ops, + V4L2_CID_ANALOGUE_GAIN, ANALOG_GAIN_MIN, + ANALOG_GAIN_MAX, ANALOG_GAIN_STEP, + ANALOG_GAIN_DEFAULT); + + hi556->test_pattern = v4l2_ctrl_new_std_menu_items(handler, + &hi556_ctrl_ops, V4L2_CID_TEST_PATTERN, + ARRAY_SIZE(hi556_test_pattern_menu) - 1, + 0, 0, hi556_test_pattern_menu); + + v4l2_ctrl_new_std(handler, &hi556_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(handler, &hi556_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + + if (handler->error) { + ret = handler->error; + dev_err(&hi556->client->dev, + "Failed to init controls(%d)\n", ret); + goto err_free_handler; + } + + hi556->subdev.ctrl_handler = handler; return 0; -} -static void hi556_assign_pad_format(const struct hi556_mode *mode, - struct v4l2_mbus_framefmt *fmt) -{ - fmt->width = mode->width; - fmt->height = mode->height; - fmt->code = MEDIA_BUS_FMT_SGRBG10_1X10; - fmt->field = V4L2_FIELD_NONE; -} - -static int hi556_start_streaming(struct hi556 *hi556) -{ - struct i2c_client *client = v4l2_get_subdevdata(&hi556->sd); - const struct hi556_reg_list *reg_list; - int link_freq_index, ret; - - link_freq_index = hi556->cur_mode->link_freq_index; - reg_list = &link_freq_configs[link_freq_index].reg_list; - ret = hi556_write_reg_list(hi556, reg_list); - if (ret) { - dev_err(&client->dev, "failed to set plls"); - return ret; - } - - reg_list = &hi556->cur_mode->reg_list; - ret = hi556_write_reg_list(hi556, reg_list); - if (ret) { - dev_err(&client->dev, "failed to set mode"); - return ret; - } - - ret = __v4l2_ctrl_handler_setup(hi556->sd.ctrl_handler); - if (ret) - return ret; - - ret = hi556_write_reg(hi556, HI556_REG_MODE_SELECT, - HI556_REG_VALUE_16BIT, HI556_MODE_STREAMING); - - if (ret) { - dev_err(&client->dev, "failed to set stream"); - return ret; - } - - return 0; -} - -static void hi556_stop_streaming(struct hi556 *hi556) -{ - struct i2c_client *client = v4l2_get_subdevdata(&hi556->sd); - - if (hi556_write_reg(hi556, HI556_REG_MODE_SELECT, - HI556_REG_VALUE_16BIT, HI556_MODE_STANDBY)) - dev_err(&client->dev, "failed to set stream"); -} - -static int hi556_set_stream(struct v4l2_subdev *sd, int enable) -{ - struct hi556 *hi556 = to_hi556(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - int ret = 0; - - if (hi556->streaming == enable) - return 0; - - mutex_lock(&hi556->mutex); - if (enable) { - ret = pm_runtime_get_sync(&client->dev); - if (ret < 0) { - pm_runtime_put_noidle(&client->dev); - mutex_unlock(&hi556->mutex); - return ret; - } - - ret = hi556_start_streaming(hi556); - if (ret) { - enable = 0; - hi556_stop_streaming(hi556); - pm_runtime_put(&client->dev); - } - } else { - hi556_stop_streaming(hi556); - pm_runtime_put(&client->dev); - } - - hi556->streaming = enable; - mutex_unlock(&hi556->mutex); +err_free_handler: + v4l2_ctrl_handler_free(handler); return ret; } -static int __maybe_unused hi556_suspend(struct device *dev) +static int hi556_check_sensor_id(struct hi556 *hi556, + struct i2c_client *client) { - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct hi556 *hi556 = to_hi556(sd); - - mutex_lock(&hi556->mutex); - if (hi556->streaming) - hi556_stop_streaming(hi556); - - mutex_unlock(&hi556->mutex); - - return 0; -} - -static int __maybe_unused hi556_resume(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct v4l2_subdev *sd = i2c_get_clientdata(client); - struct hi556 *hi556 = to_hi556(sd); + struct device *dev = &hi556->client->dev; + u32 id = 0; int ret; - mutex_lock(&hi556->mutex); - if (hi556->streaming) { - ret = hi556_start_streaming(hi556); - if (ret) - goto error; + ret = hi556_read_reg(client, HI556_REG_CHIP_ID, + HI556_REG_VALUE_16BIT, &id); + if (id != CHIP_ID) { + dev_err(dev, "Unexpected sensor id(%06x), ret(%d)\n", id, ret); + return -ENODEV; } - mutex_unlock(&hi556->mutex); + dev_info(dev, "Detected Hi%04x sensor\n", CHIP_ID); return 0; - -error: - hi556_stop_streaming(hi556); - hi556->streaming = 0; - mutex_unlock(&hi556->mutex); - return ret; } -static int hi556_set_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *fmt) +static int hi556_configure_regulators(struct hi556 *hi556) { - struct hi556 *hi556 = to_hi556(sd); - const struct hi556_mode *mode; - s32 vblank_def, h_blank; + unsigned int i; - mode = v4l2_find_nearest_size(supported_modes, - ARRAY_SIZE(supported_modes), width, - height, fmt->format.width, - fmt->format.height); + for (i = 0; i < HI556_NUM_SUPPLIES; i++) + hi556->supplies[i].supply = hi556_supply_names[i]; - mutex_lock(&hi556->mutex); - hi556_assign_pad_format(mode, &fmt->format); - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { - *v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = fmt->format; + return devm_regulator_bulk_get(&hi556->client->dev, + HI556_NUM_SUPPLIES, + hi556->supplies); +} + +static int hi556_parse_of(struct hi556 *hi556) +{ + struct device *dev = &hi556->client->dev; + struct device_node *endpoint; + struct fwnode_handle *fwnode; + int rval; + + endpoint = of_graph_get_next_endpoint(dev->of_node, NULL); + if (!endpoint) { + dev_err(dev, "Failed to get endpoint\n"); + return -EINVAL; + } + fwnode = of_fwnode_handle(endpoint); + rval = fwnode_property_read_u32_array(fwnode, "data-lanes", NULL, 0); + if (rval <= 0) { + dev_warn(dev, " Get mipi lane num failed!\n"); + return -1; + } + + hi556->lane_num = rval; + if (hi556->lane_num == 2) { + hi556->cur_mode = &supported_modes_2lane[0]; + supported_modes = supported_modes_2lane; + hi556->cfg_num = ARRAY_SIZE(supported_modes_2lane); + + /* pixel rate = link frequency * 2 * lanes / BITS_PER_SAMPLE */ + hi556->pixel_rate = MIPI_FREQ * 2U * hi556->lane_num / 8U; + dev_info(dev, "lane_num(%d) pixel_rate(%u)\n", + hi556->lane_num, hi556->pixel_rate); } else { - hi556->cur_mode = mode; - __v4l2_ctrl_s_ctrl(hi556->link_freq, mode->link_freq_index); - __v4l2_ctrl_s_ctrl_int64(hi556->pixel_rate, - to_pixel_rate(mode->link_freq_index)); - - /* Update limits and set FPS to default */ - vblank_def = mode->fll_def - mode->height; - __v4l2_ctrl_modify_range(hi556->vblank, - mode->fll_min - mode->height, - HI556_FLL_MAX - mode->height, 1, - vblank_def); - __v4l2_ctrl_s_ctrl(hi556->vblank, vblank_def); - - h_blank = hi556->cur_mode->llp - hi556->cur_mode->width; - - __v4l2_ctrl_modify_range(hi556->hblank, h_blank, h_blank, 1, - h_blank); + dev_err(dev, "unsupported lane_num(%d)\n", hi556->lane_num); + return -1; } - mutex_unlock(&hi556->mutex); - return 0; } -static int hi556_get_format(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *fmt) +static int hi556_probe(struct i2c_client *client, + const struct i2c_device_id *id) { - struct hi556 *hi556 = to_hi556(sd); + struct device *dev = &client->dev; + struct device_node *node = dev->of_node; + struct hi556 *hi556; + struct v4l2_subdev *sd; + char facing[2] = "b"; + int ret; - mutex_lock(&hi556->mutex); - if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) - fmt->format = *v4l2_subdev_get_try_format(&hi556->sd, cfg, - fmt->pad); + dev_info(dev, "driver version: %02x.%02x.%02x", + DRIVER_VERSION >> 16, + (DRIVER_VERSION & 0xff00) >> 8, + DRIVER_VERSION & 0x00ff); + + hi556 = devm_kzalloc(dev, sizeof(*hi556), GFP_KERNEL); + if (!hi556) + return -ENOMEM; + + ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX, + &hi556->module_index); + if (ret) { + dev_warn(dev, "could not get module index!\n"); + hi556->module_index = 0; + } + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING, + &hi556->module_facing); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME, + &hi556->module_name); + ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME, + &hi556->len_name); + if (ret) { + dev_err(dev, "could not get module information!\n"); + return -EINVAL; + } + + hi556->client = client; + + hi556->xvclk = devm_clk_get(dev, "xvclk"); + if (IS_ERR(hi556->xvclk)) { + dev_err(dev, "Failed to get xvclk\n"); + return -EINVAL; + } + + hi556->power_gpio = devm_gpiod_get(dev, "power", GPIOD_OUT_LOW); + if (IS_ERR(hi556->power_gpio)) + dev_warn(dev, "Failed to get power-gpios, maybe no use\n"); + + hi556->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(hi556->reset_gpio)) + dev_warn(dev, "Failed to get reset-gpios, maybe no use\n"); + + hi556->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_OUT_LOW); + if (IS_ERR(hi556->pwdn_gpio)) + dev_warn(dev, "Failed to get pwdn-gpios\n"); + + ret = hi556_configure_regulators(hi556); + if (ret) { + dev_err(dev, "Failed to get power regulators\n"); + return ret; + } + ret = hi556_parse_of(hi556); + if (ret != 0) + return -EINVAL; + + hi556->pinctrl = devm_pinctrl_get(dev); + if (!IS_ERR(hi556->pinctrl)) { + hi556->pins_default = + pinctrl_lookup_state(hi556->pinctrl, + OF_CAMERA_PINCTRL_STATE_DEFAULT); + if (IS_ERR(hi556->pins_default)) + dev_err(dev, "could not get default pinstate\n"); + + hi556->pins_sleep = + pinctrl_lookup_state(hi556->pinctrl, + OF_CAMERA_PINCTRL_STATE_SLEEP); + if (IS_ERR(hi556->pins_sleep)) + dev_err(dev, "could not get sleep pinstate\n"); + } + + mutex_init(&hi556->mutex); + + sd = &hi556->subdev; + v4l2_i2c_subdev_init(sd, client, &hi556_subdev_ops); + ret = hi556_initialize_controls(hi556); + if (ret) + goto err_destroy_mutex; + + ret = __hi556_power_on(hi556); + if (ret) + goto err_free_handler; + + ret = hi556_check_sensor_id(hi556, client); + if (ret < 0) { + dev_info(&client->dev, "%s(%d) Check id failed\n" + "check following information:\n" + "Power/PowerDown/Reset/Mclk/I2cBus !!\n", + __func__, __LINE__); + goto err_power_off; + } + +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API + sd->internal_ops = &hi556_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | + V4L2_SUBDEV_FL_HAS_EVENTS; +#endif +#if defined(CONFIG_MEDIA_CONTROLLER) + hi556->pad.flags = MEDIA_PAD_FL_SOURCE; + sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; + ret = media_entity_pads_init(&sd->entity, 1, &hi556->pad); + if (ret < 0) + goto err_power_off; +#endif + + memset(facing, 0, sizeof(facing)); + if (strcmp(hi556->module_facing, "back") == 0) + facing[0] = 'b'; else - hi556_assign_pad_format(hi556->cur_mode, &fmt->format); + facing[0] = 'f'; - mutex_unlock(&hi556->mutex); + snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s", + hi556->module_index, facing, + HI556_NAME, dev_name(sd->dev)); - return 0; -} - -static int hi556_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_mbus_code_enum *code) -{ - if (code->index > 0) - return -EINVAL; - - code->code = MEDIA_BUS_FMT_SGRBG10_1X10; - - return 0; -} - -static int hi556_enum_frame_size(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 != MEDIA_BUS_FMT_SGRBG10_1X10) - return -EINVAL; - - fse->min_width = supported_modes[fse->index].width; - fse->max_width = fse->min_width; - fse->min_height = supported_modes[fse->index].height; - fse->max_height = fse->min_height; - - return 0; -} - -static int hi556_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) -{ - struct hi556 *hi556 = to_hi556(sd); - - mutex_lock(&hi556->mutex); - hi556_assign_pad_format(&supported_modes[0], - v4l2_subdev_get_try_format(sd, fh->pad, 0)); - mutex_unlock(&hi556->mutex); - - return 0; -} - -static const struct v4l2_subdev_video_ops hi556_video_ops = { - .s_stream = hi556_set_stream, -}; - -static const struct v4l2_subdev_pad_ops hi556_pad_ops = { - .set_fmt = hi556_set_format, - .get_fmt = hi556_get_format, - .enum_mbus_code = hi556_enum_mbus_code, - .enum_frame_size = hi556_enum_frame_size, -}; - -static const struct v4l2_subdev_ops hi556_subdev_ops = { - .video = &hi556_video_ops, - .pad = &hi556_pad_ops, -}; - -static const struct media_entity_operations hi556_subdev_entity_ops = { - .link_validate = v4l2_subdev_link_validate, -}; - -static const struct v4l2_subdev_internal_ops hi556_internal_ops = { - .open = hi556_open, -}; - -static int hi556_identify_module(struct hi556 *hi556) -{ - struct i2c_client *client = v4l2_get_subdevdata(&hi556->sd); - int ret; - u32 val; - - ret = hi556_read_reg(hi556, HI556_REG_CHIP_ID, - HI556_REG_VALUE_16BIT, &val); - if (ret) - return ret; - - if (val != HI556_CHIP_ID) { - dev_err(&client->dev, "chip id mismatch: %x!=%x", - HI556_CHIP_ID, val); - return -ENXIO; - } - - return 0; -} - -static int hi556_check_hwcfg(struct device *dev) -{ - struct fwnode_handle *ep; - struct fwnode_handle *fwnode = dev_fwnode(dev); - struct v4l2_fwnode_endpoint bus_cfg = { - .bus_type = V4L2_MBUS_CSI2_DPHY - }; - u32 mclk; - int ret = 0; - unsigned int i, j; - - if (!fwnode) - return -ENXIO; - - ret = fwnode_property_read_u32(fwnode, "clock-frequency", &mclk); + ret = v4l2_async_register_subdev_sensor_common(sd); if (ret) { - dev_err(dev, "can't get clock frequency"); - return ret; + dev_err(dev, "v4l2 async register subdev failed\n"); + goto err_clean_entity; } - if (mclk != HI556_MCLK) { - dev_err(dev, "external clock %d is not supported", mclk); - return -EINVAL; - } + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + pm_runtime_idle(dev); - ep = fwnode_graph_get_next_endpoint(fwnode, NULL); - if (!ep) - return -ENXIO; + return 0; - ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg); - fwnode_handle_put(ep); - if (ret) - return ret; - - if (bus_cfg.bus.mipi_csi2.num_data_lanes != 2) { - dev_err(dev, "number of CSI2 data lanes %d is not supported", - bus_cfg.bus.mipi_csi2.num_data_lanes); - ret = -EINVAL; - goto check_hwcfg_error; - } - - if (!bus_cfg.nr_of_link_frequencies) { - dev_err(dev, "no link frequencies defined"); - ret = -EINVAL; - goto check_hwcfg_error; - } - - for (i = 0; i < ARRAY_SIZE(link_freq_menu_items); i++) { - for (j = 0; j < bus_cfg.nr_of_link_frequencies; j++) { - if (link_freq_menu_items[i] == - bus_cfg.link_frequencies[j]) - break; - } - - if (j == bus_cfg.nr_of_link_frequencies) { - dev_err(dev, "no link frequency %lld supported", - link_freq_menu_items[i]); - ret = -EINVAL; - goto check_hwcfg_error; - } - } - -check_hwcfg_error: - v4l2_fwnode_endpoint_free(&bus_cfg); +err_clean_entity: +#if defined(CONFIG_MEDIA_CONTROLLER) + media_entity_cleanup(&sd->entity); +#endif +err_power_off: + __hi556_power_off(hi556); +err_free_handler: + v4l2_ctrl_handler_free(&hi556->ctrl_handler); +err_destroy_mutex: + mutex_destroy(&hi556->mutex); return ret; } @@ -1097,104 +1596,57 @@ static int hi556_remove(struct i2c_client *client) struct hi556 *hi556 = to_hi556(sd); v4l2_async_unregister_subdev(sd); +#if defined(CONFIG_MEDIA_CONTROLLER) media_entity_cleanup(&sd->entity); - v4l2_ctrl_handler_free(sd->ctrl_handler); - pm_runtime_disable(&client->dev); - mutex_destroy(&hi556->mutex); - - return 0; -} - -static int hi556_probe(struct i2c_client *client) -{ - struct hi556 *hi556; - int ret; - - ret = hi556_check_hwcfg(&client->dev); - if (ret) { - dev_err(&client->dev, "failed to check HW configuration: %d", - ret); - return ret; - } - - hi556 = devm_kzalloc(&client->dev, sizeof(*hi556), GFP_KERNEL); - if (!hi556) - return -ENOMEM; - - v4l2_i2c_subdev_init(&hi556->sd, client, &hi556_subdev_ops); - ret = hi556_identify_module(hi556); - if (ret) { - dev_err(&client->dev, "failed to find sensor: %d", ret); - return ret; - } - - mutex_init(&hi556->mutex); - hi556->cur_mode = &supported_modes[0]; - ret = hi556_init_controls(hi556); - if (ret) { - dev_err(&client->dev, "failed to init controls: %d", ret); - goto probe_error_v4l2_ctrl_handler_free; - } - - hi556->sd.internal_ops = &hi556_internal_ops; - hi556->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - hi556->sd.entity.ops = &hi556_subdev_entity_ops; - hi556->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR; - hi556->pad.flags = MEDIA_PAD_FL_SOURCE; - ret = media_entity_pads_init(&hi556->sd.entity, 1, &hi556->pad); - if (ret) { - dev_err(&client->dev, "failed to init entity pads: %d", ret); - goto probe_error_v4l2_ctrl_handler_free; - } - - ret = v4l2_async_register_subdev_sensor_common(&hi556->sd); - if (ret < 0) { - dev_err(&client->dev, "failed to register V4L2 subdev: %d", - ret); - goto probe_error_media_entity_cleanup; - } - - pm_runtime_set_active(&client->dev); - pm_runtime_enable(&client->dev); - pm_runtime_idle(&client->dev); - - return 0; - -probe_error_media_entity_cleanup: - media_entity_cleanup(&hi556->sd.entity); - -probe_error_v4l2_ctrl_handler_free: - v4l2_ctrl_handler_free(hi556->sd.ctrl_handler); - mutex_destroy(&hi556->mutex); - - return ret; -} - -static const struct dev_pm_ops hi556_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(hi556_suspend, hi556_resume) -}; - -#ifdef CONFIG_ACPI -static const struct acpi_device_id hi556_acpi_ids[] = { - {"INT3537"}, - {} -}; - -MODULE_DEVICE_TABLE(acpi, hi556_acpi_ids); #endif + v4l2_ctrl_handler_free(&hi556->ctrl_handler); + mutex_destroy(&hi556->mutex); + + pm_runtime_disable(&client->dev); + if (!pm_runtime_status_suspended(&client->dev)) + __hi556_power_off(hi556); + pm_runtime_set_suspended(&client->dev); + + return 0; +} + +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id hi556_of_match[] = { + { .compatible = "hynix,hi556" }, + {}, +}; +MODULE_DEVICE_TABLE(of, hi556_of_match); +#endif + +static const struct i2c_device_id hi556_match_id[] = { + { "hynix,hi556", 0 }, + { }, +}; static struct i2c_driver hi556_i2c_driver = { .driver = { - .name = "hi556", + .name = HI556_NAME, .pm = &hi556_pm_ops, - .acpi_match_table = ACPI_PTR(hi556_acpi_ids), + .of_match_table = of_match_ptr(hi556_of_match), }, - .probe_new = hi556_probe, - .remove = hi556_remove, + .probe = &hi556_probe, + .remove = &hi556_remove, + .id_table = hi556_match_id, }; -module_i2c_driver(hi556_i2c_driver); +static int __init sensor_mod_init(void) +{ + return i2c_add_driver(&hi556_i2c_driver); +} -MODULE_AUTHOR("Shawn Tu "); -MODULE_DESCRIPTION("Hynix HI556 sensor driver"); +static void __exit sensor_mod_exit(void) +{ + i2c_del_driver(&hi556_i2c_driver); +} + +device_initcall_sync(sensor_mod_init); +module_exit(sensor_mod_exit); + +MODULE_DESCRIPTION("Hynix hi556 sensor driver"); MODULE_LICENSE("GPL v2"); + diff --git a/drivers/media/platform/rockchip/hdmirx/rk_hdmirx.c b/drivers/media/platform/rockchip/hdmirx/rk_hdmirx.c index 18b06d4dcb03..1c0705b2ae5d 100644 --- a/drivers/media/platform/rockchip/hdmirx/rk_hdmirx.c +++ b/drivers/media/platform/rockchip/hdmirx/rk_hdmirx.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -195,6 +196,7 @@ struct rk_hdmirx_dev { struct dentry *debugfs_dir; struct freq_qos_request min_sta_freq_req; struct hdmirx_audiostate audio_state; + struct extcon_dev *extcon; struct hdmirx_cec *cec; struct mutex stream_lock; struct mutex work_lock; @@ -245,6 +247,11 @@ struct rk_hdmirx_dev { spinlock_t rst_lock; }; +static const unsigned int hdmirx_extcon_cable[] = { + EXTCON_JACK_VIDEO_IN, + EXTCON_NONE, +}; + static bool tx_5v_power_present(struct rk_hdmirx_dev *hdmirx_dev); static void hdmirx_set_fmt(struct hdmirx_stream *stream, struct v4l2_pix_format_mplane *pixm, bool try); @@ -2672,12 +2679,14 @@ static void hdmirx_plugin(struct rk_hdmirx_dev *hdmirx_dev) } hdmirx_dma_config(hdmirx_dev); hdmirx_interrupts_setup(hdmirx_dev, true); + extcon_set_state_sync(hdmirx_dev->extcon, EXTCON_JACK_VIDEO_IN, true); hdmirx_audio_handle_plugged_change(hdmirx_dev, 1); } static void hdmirx_plugout(struct rk_hdmirx_dev *hdmirx_dev) { hdmirx_audio_handle_plugged_change(hdmirx_dev, 0); + extcon_set_state_sync(hdmirx_dev->extcon, EXTCON_JACK_VIDEO_IN, false); hdmirx_update_bits(hdmirx_dev, SCDC_CONFIG, POWERPROVIDED, 0); hdmirx_interrupts_setup(hdmirx_dev, false); hdmirx_hpd_ctrl(hdmirx_dev, false); @@ -4102,6 +4111,19 @@ static int hdmirx_probe(struct platform_device *pdev) if (ret) goto err_unreg_video_dev; + hdmirx_dev->extcon = devm_extcon_dev_allocate(dev, hdmirx_extcon_cable); + if (IS_ERR(hdmirx_dev->extcon)) { + ret = PTR_ERR(hdmirx_dev->extcon); + dev_err(&pdev->dev, "allocate extcon failed\n"); + goto err_unreg_video_dev; + } + + ret = devm_extcon_dev_register(dev, hdmirx_dev->extcon); + if (ret) { + dev_err(&pdev->dev, "failed to register extcon: %d\n", ret); + goto err_unreg_video_dev; + } + irq = gpiod_to_irq(hdmirx_dev->hdmirx_det_gpio); if (irq < 0) { dev_err(dev, "failed to get hdmirx-det gpio irq\n"); diff --git a/drivers/media/platform/rockchip/isp/capture.c b/drivers/media/platform/rockchip/isp/capture.c index 7dec179e9401..7bb7a8f6cd67 100644 --- a/drivers/media/platform/rockchip/isp/capture.c +++ b/drivers/media/platform/rockchip/isp/capture.c @@ -1552,6 +1552,9 @@ static void rkisp_stream_fast(struct work_struct *work) if (ispdev->isp_ver != ISP_V32) return; + rkisp_chk_tb_over(ispdev); + if (ispdev->tb_head.complete != RKISP_TB_OK) + return; ret = v4l2_pipeline_pm_get(&stream->vnode.vdev.entity); if (ret < 0) { dev_err(ispdev->dev, "%s PM get fail:%d\n", __func__, ret); @@ -1559,11 +1562,8 @@ static void rkisp_stream_fast(struct work_struct *work) return; } - rkisp_chk_tb_over(ispdev); - if (ispdev->tb_head.complete != RKISP_TB_OK) { - v4l2_pipeline_pm_put(&stream->vnode.vdev.entity); - return; - } + if (ispdev->hw_dev->dev_num > 1) + ispdev->hw_dev->is_single = false; ispdev->is_pre_on = true; ispdev->is_rdbk_auto = true; ispdev->pipe.open(&ispdev->pipe, &stream->vnode.vdev.entity, true); diff --git a/drivers/media/platform/rockchip/isp/capture_v30.c b/drivers/media/platform/rockchip/isp/capture_v30.c index f6b06fe2217c..f11c99a3e4dc 100644 --- a/drivers/media/platform/rockchip/isp/capture_v30.c +++ b/drivers/media/platform/rockchip/isp/capture_v30.c @@ -16,6 +16,7 @@ #define CIF_ISP_REQ_BUFS_MIN 0 static int mi_frame_end(struct rkisp_stream *stream); +static int mi_frame_start(struct rkisp_stream *stream, u32 mis); static const struct capture_fmt mp_fmts[] = { /* yuv422 */ @@ -856,6 +857,7 @@ static struct streams_ops rkisp_mp_streams_ops = { .is_stream_stopped = mp_is_stream_stopped, .update_mi = update_mi, .frame_end = mi_frame_end, + .frame_start = mi_frame_start, }; static struct streams_ops rkisp_sp_streams_ops = { @@ -866,6 +868,7 @@ static struct streams_ops rkisp_sp_streams_ops = { .is_stream_stopped = sp_is_stream_stopped, .update_mi = update_mi, .frame_end = mi_frame_end, + .frame_start = mi_frame_start, }; static struct streams_ops rkisp_fbc_streams_ops = { @@ -875,6 +878,7 @@ static struct streams_ops rkisp_fbc_streams_ops = { .is_stream_stopped = fbc_is_stream_stopped, .update_mi = update_mi, .frame_end = mi_frame_end, + .frame_start = mi_frame_start, }; static struct streams_ops rkisp_bp_streams_ops = { @@ -884,8 +888,63 @@ static struct streams_ops rkisp_bp_streams_ops = { .is_stream_stopped = bp_is_stream_stopped, .update_mi = update_mi, .frame_end = mi_frame_end, + .frame_start = mi_frame_start, }; +static void stream_self_update(struct rkisp_stream *stream) +{ + 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); + return; + } + + switch (stream->id) { + case RKISP_STREAM_MP: + val = ISP3X_MPSELF_UPD; + break; + case RKISP_STREAM_SP: + val = ISP3X_SPSELF_UPD; + break; + case RKISP_STREAM_BP: + val = ISP3X_BPSELF_UPD; + break; + default: + return; + } + + rkisp_unite_set_bits(dev, ISP3X_MI_WR_XTD_FORMAT_CTRL, mask, val, false, is_unite); +} + +static int mi_frame_start(struct rkisp_stream *stream, u32 mis) +{ + struct rkisp_device *dev = stream->ispdev; + unsigned long lock_flags = 0; + + /* readback start to update stream buf if null */ + spin_lock_irqsave(&stream->vbq_lock, lock_flags); + if (stream->streaming && !mis && !stream->curr_buf) { + if (!stream->next_buf && !list_empty(&stream->buf_queue)) { + stream->next_buf = list_first_entry(&stream->buf_queue, + struct rkisp_buffer, queue); + list_del(&stream->next_buf->queue); + stream->ops->update_mi(stream); + } + if (dev->hw_dev->is_single && stream->next_buf) { + stream->curr_buf = stream->next_buf; + stream->next_buf = NULL; + stream_self_update(stream); + } + } + spin_unlock_irqrestore(&stream->vbq_lock, lock_flags); + + return 0; +} + /* * This function is called when a frame end come. The next frame * is processing and we should set up buffer for next-next frame, diff --git a/drivers/media/platform/rockchip/isp/capture_v32.c b/drivers/media/platform/rockchip/isp/capture_v32.c index 2b33bbb951e9..bcf6a301d14e 100644 --- a/drivers/media/platform/rockchip/isp/capture_v32.c +++ b/drivers/media/platform/rockchip/isp/capture_v32.c @@ -1645,6 +1645,11 @@ rkisp_start_streaming(struct vb2_queue *queue, unsigned int count) int ret = -EINVAL; mutex_lock(&dev->hw_dev->dev_lock); + if (dev->is_pre_on && + !dev->hw_dev->is_single && + !atomic_read(&dev->hw_dev->refcnt) && + !atomic_read(&dev->cap_dev.refcnt)) + rkisp_hw_enum_isp_size(dev->hw_dev); v4l2_dbg(1, rkisp_debug, v4l2_dev, "%s %s id:%d\n", __func__, node->vdev.name, stream->id); diff --git a/drivers/media/platform/rockchip/isp/csi.c b/drivers/media/platform/rockchip/isp/csi.c index 9b3d5ca001c7..12ec032fd2c3 100644 --- a/drivers/media/platform/rockchip/isp/csi.c +++ b/drivers/media/platform/rockchip/isp/csi.c @@ -592,7 +592,7 @@ int rkisp_csi_config_patch(struct rkisp_device *dev) if (dev->hdr.op_mode == HDR_NORMAL || dev->hdr.op_mode == HDR_COMPR) dev->hdr.op_mode = HDR_RDBK_FRAME1; - if (dev->isp_inp == INP_CIF && dev->hw_dev->is_single && dev->isp_ver > ISP_V21) + if (dev->isp_inp == INP_CIF && dev->isp_ver > ISP_V21) mode.rdbk_mode = dev->is_rdbk_auto ? RKISP_VICAP_RDBK_AUTO : RKISP_VICAP_ONLINE; else mode.rdbk_mode = RKISP_VICAP_RDBK_AIQ; diff --git a/drivers/media/platform/rockchip/isp/dmarx.c b/drivers/media/platform/rockchip/isp/dmarx.c index fade9c243d7e..ef8a788de1cf 100644 --- a/drivers/media/platform/rockchip/isp/dmarx.c +++ b/drivers/media/platform/rockchip/isp/dmarx.c @@ -387,9 +387,7 @@ static void update_rawrd(struct rkisp_stream *stream) rkisp_next_write(dev, stream->config->mi.y_base_ad_init, val, false); } stream->frame_end = false; - if (stream->id == RKISP_STREAM_RAWRD2 && - (stream->out_isp_fmt.fmt_type == FMT_YUV || - dev->dmarx_dev.trigger == T_AUTO)) { + if (stream->id == RKISP_STREAM_RAWRD2 && stream->out_isp_fmt.fmt_type == FMT_YUV) { struct vb2_v4l2_buffer *vbuf = &stream->curr_buf->vb; struct isp2x_csi_trigger trigger = { .frame_timestamp = vbuf->vb2_buf.timestamp, diff --git a/drivers/media/platform/rockchip/isp/hw.c b/drivers/media/platform/rockchip/isp/hw.c index 65934f24a6e8..ee55ec87f7cd 100644 --- a/drivers/media/platform/rockchip/isp/hw.c +++ b/drivers/media/platform/rockchip/isp/hw.c @@ -990,28 +990,18 @@ static int __maybe_unused rkisp_runtime_suspend(struct device *dev) return pinctrl_pm_select_sleep_state(dev); } -static int __maybe_unused rkisp_runtime_resume(struct device *dev) +void rkisp_hw_enum_isp_size(struct rkisp_hw_dev *hw_dev) { - 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 ret, i; + u32 w, h, i; - ret = pinctrl_pm_select_default_state(dev); - if (ret < 0) - return ret; - - enable_sys_clk(hw_dev); memset(hw_dev->isp_size, 0, sizeof(hw_dev->isp_size)); if (!hw_dev->max_in.is_fix) { hw_dev->max_in.w = 0; hw_dev->max_in.h = 0; } + hw_dev->dev_link_num = 0; for (i = 0; i < hw_dev->dev_num; i++) { - void *buf; - u32 w, h; - isp = hw_dev->isp[i]; if (!isp || (isp && !isp->is_hw_link)) continue; @@ -1030,6 +1020,33 @@ static int __maybe_unused rkisp_runtime_resume(struct device *dev) if (hw_dev->max_in.h < h) hw_dev->max_in.h = h; } + } + for (i = 0; i < hw_dev->dev_num; i++) { + isp = hw_dev->isp[i]; + if (!isp || (isp && !isp->is_hw_link)) + continue; + rkisp_params_check_bigmode(&isp->params_vdev); + } +} + +static int __maybe_unused rkisp_runtime_resume(struct device *dev) +{ + 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 ret, i; + void *buf; + + ret = pinctrl_pm_select_default_state(dev); + if (ret < 0) + return ret; + + enable_sys_clk(hw_dev); + for (i = 0; i < hw_dev->dev_num; i++) { + isp = hw_dev->isp[i]; + if (!isp) + continue; buf = isp->sw_base_addr; memset(buf, 0, RKISP_ISP_SW_MAX_SIZE * mult); memcpy_fromio(buf, base, RKISP_ISP_SW_REG_SIZE); @@ -1040,12 +1057,7 @@ static int __maybe_unused rkisp_runtime_resume(struct device *dev) } default_sw_reg_flag(hw_dev->isp[i]); } - for (i = 0; i < hw_dev->dev_num; i++) { - isp = hw_dev->isp[i]; - if (!isp || (isp && !isp->is_hw_link)) - continue; - rkisp_params_check_bigmode(&isp->params_vdev); - } + rkisp_hw_enum_isp_size(hw_dev); hw_dev->monitor.is_en = rkisp_monitor; return 0; } diff --git a/drivers/media/platform/rockchip/isp/hw.h b/drivers/media/platform/rockchip/isp/hw.h index 28ee8c441a29..478d8b37542d 100644 --- a/drivers/media/platform/rockchip/isp/hw.h +++ b/drivers/media/platform/rockchip/isp/hw.h @@ -104,4 +104,5 @@ struct rkisp_hw_dev { int rkisp_register_irq(struct rkisp_hw_dev *dev); void rkisp_soft_reset(struct rkisp_hw_dev *dev, bool is_secure); +void rkisp_hw_enum_isp_size(struct rkisp_hw_dev *hw_dev); #endif diff --git a/drivers/media/platform/rockchip/isp/isp_params_v32.c b/drivers/media/platform/rockchip/isp/isp_params_v32.c index cebb4eefca65..c4450944733c 100644 --- a/drivers/media/platform/rockchip/isp/isp_params_v32.c +++ b/drivers/media/platform/rockchip/isp/isp_params_v32.c @@ -3371,6 +3371,31 @@ isp_bay3d_enable(struct rkisp_isp_params_vdev *params_vdev, bool en) 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->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->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); + 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); + 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); + value = priv_val->bay3d_cur_wrap_line << 16 | 28; + isp3_param_write(params_vdev, value, ISP3X_BAY3D_MI_ST); + /* mibuf_size for fifo_cur_full, set to max: (3072 - 2) / 2, 2 align */ value = 0x5fe << 16; isp3_param_set_bits(params_vdev, ISP3X_BAY3D_IN_IRQ_LINECNT, value); @@ -4085,7 +4110,6 @@ rkisp_alloc_internal_buf(struct rkisp_isp_params_vdev *params_vdev, u32 w = ALIGN(isp_sdev->in_crop.width, 16); u32 h = ALIGN(isp_sdev->in_crop.height, 16); u32 val, wrap_line, wsize, div; - dma_addr_t dma_addr; bool is_alloc; priv_val->is_lo8x8 = (!new_params->others.bay3d_cfg.lo4x8_en && @@ -4120,10 +4144,6 @@ rkisp_alloc_internal_buf(struct rkisp_isp_params_vdev *params_vdev, goto err_3dnr; } } - isp3_param_write(params_vdev, val, ISP3X_MI_BAY3D_IIR_WR_SIZE); - val = priv_val->buf_3dnr_iir.dma_addr; - isp3_param_write(params_vdev, val, ISP3X_MI_BAY3D_IIR_WR_BASE); - isp3_param_write(params_vdev, val, ISP3X_MI_BAY3D_IIR_RD_BASE); div = priv_val->is_lo8x8 ? 64 : 16; val = w * h / div; @@ -4145,10 +4165,6 @@ rkisp_alloc_internal_buf(struct rkisp_isp_params_vdev *params_vdev, goto err_3dnr; } } - isp3_param_write(params_vdev, val, ISP3X_MI_BAY3D_DS_WR_SIZE); - val = priv_val->buf_3dnr_ds.dma_addr; - isp3_param_write(params_vdev, val, ISP3X_MI_BAY3D_DS_WR_BASE); - isp3_param_write(params_vdev, val, ISP3X_MI_BAY3D_DS_RD_BASE); wrap_line = priv_val->is_lo8x8 ? 76 : 36; wsize = is_bwopt_dis ? w : w * 2; @@ -4177,22 +4193,13 @@ rkisp_alloc_internal_buf(struct rkisp_isp_params_vdev *params_vdev, dev_err(dev->dev, "alloc bay3d cur buf fail:%d\n", ret); goto err_3dnr; } - dma_addr = priv_val->buf_3dnr_cur.dma_addr; priv_val->is_sram = false; } else if (val <= dev->hw_dev->sram.size) { - dma_addr = dev->hw_dev->sram.dma_addr; priv_val->is_sram = true; - } else { - dma_addr = priv_val->buf_3dnr_cur.dma_addr; } - isp3_param_write(params_vdev, val, ISP3X_MI_BAY3D_CUR_WR_SIZE); - isp3_param_write(params_vdev, val, ISP32_MI_BAY3D_CUR_RD_SIZE); - isp3_param_write(params_vdev, wsize, ISP3X_MI_BAY3D_CUR_WR_LENGTH); - isp3_param_write(params_vdev, wsize, ISP3X_MI_BAY3D_CUR_RD_LENGTH); - isp3_param_write(params_vdev, dma_addr, ISP3X_MI_BAY3D_CUR_WR_BASE); - isp3_param_write(params_vdev, dma_addr, ISP3X_MI_BAY3D_CUR_RD_BASE); - val = wrap_line << 16 | 28; - isp3_param_write(params_vdev, val, ISP3X_BAY3D_MI_ST); + priv_val->bay3d_cur_size = val; + priv_val->bay3d_cur_wsize = wsize; + priv_val->bay3d_cur_wrap_line = wrap_line; } return 0; err_3dnr: @@ -4421,14 +4428,7 @@ rkisp_params_first_cfg_v32(struct rkisp_isp_params_vdev *params_vdev) static void rkisp_save_first_param_v32(struct rkisp_isp_params_vdev *params_vdev, void *param) { - struct rkisp_isp_params_val_v32 *priv_val = - (struct rkisp_isp_params_val_v32 *)params_vdev->priv_val; - memcpy(params_vdev->isp32_params, param, params_vdev->vdev_fmt.fmt.meta.buffersize); - - if (!params_vdev->first_params) - return; - tasklet_enable(&priv_val->lsc_tasklet); rkisp_alloc_internal_buf(params_vdev, params_vdev->isp32_params); } @@ -4718,7 +4718,6 @@ rkisp_params_stream_stop_v32(struct rkisp_isp_params_vdev *params_vdev) int i; priv_val = (struct rkisp_isp_params_val_v32 *)params_vdev->priv_val; - tasklet_disable(&priv_val->lsc_tasklet); rkisp_free_buffer(ispdev, &priv_val->buf_3dnr_iir); rkisp_free_buffer(ispdev, &priv_val->buf_3dnr_cur); rkisp_free_buffer(ispdev, &priv_val->buf_3dnr_ds); @@ -4944,7 +4943,6 @@ int rkisp_init_params_vdev_v32(struct rkisp_isp_params_vdev *params_vdev) tasklet_init(&priv_val->lsc_tasklet, isp_lsc_cfg_sram_task, (unsigned long)params_vdev); - tasklet_disable(&priv_val->lsc_tasklet); priv_val->buf_info_owner = 0; priv_val->buf_info_cnt = 0; priv_val->buf_info_idx = -1; diff --git a/drivers/media/platform/rockchip/isp/isp_params_v32.h b/drivers/media/platform/rockchip/isp/isp_params_v32.h index 7a3c4d1a15db..540bb6b212f9 100644 --- a/drivers/media/platform/rockchip/isp/isp_params_v32.h +++ b/drivers/media/platform/rockchip/isp/isp_params_v32.h @@ -185,6 +185,9 @@ struct rkisp_isp_params_val_v32 { u32 buf_info_cnt; int buf_info_idx; + u32 bay3d_cur_size; + u32 bay3d_cur_wsize; + u32 bay3d_cur_wrap_line; struct rkisp_dummy_buffer buf_3dnr_iir; struct rkisp_dummy_buffer buf_3dnr_cur; struct rkisp_dummy_buffer buf_3dnr_ds; diff --git a/drivers/media/platform/rockchip/isp/rkisp.c b/drivers/media/platform/rockchip/isp/rkisp.c index 1b63317584eb..5859c1d85439 100644 --- a/drivers/media/platform/rockchip/isp/rkisp.c +++ b/drivers/media/platform/rockchip/isp/rkisp.c @@ -614,7 +614,7 @@ void rkisp_trigger_read_back(struct rkisp_device *dev, u8 dma2frm, u32 mode, boo rkisp_stream_frame_start(dev, 0); if (!hw->is_single && !is_try) { rkisp_update_regs(dev, CTRL_VI_ISP_PATH, SUPER_IMP_COLOR_CR); - rkisp_update_regs(dev, DUAL_CROP_M_H_OFFS, DUAL_CROP_S_V_SIZE); + rkisp_update_regs(dev, DUAL_CROP_M_H_OFFS, ISP3X_DUAL_CROP_FBC_V_SIZE); rkisp_update_regs(dev, ISP_ACQ_PROP, DUAL_CROP_CTRL); rkisp_update_regs(dev, SELF_RESIZE_SCALE_HY, MI_WR_CTRL); rkisp_update_regs(dev, ISP32_BP_RESIZE_SCALE_HY, SELF_RESIZE_CTRL); @@ -722,6 +722,40 @@ run_next: rkisp_unite_write(dev, CSI2RX_CTRL0, val, true, hw->is_unite); } +static void rkisp_fast_switch_rx_buf(struct rkisp_device *dev, bool is_current) +{ + struct rkisp_stream *stream; + struct rkisp_buffer *buf; + u32 i, val; + + for (i = RKISP_STREAM_RAWRD0; i < RKISP_MAX_DMARX_STREAM; i++) { + stream = &dev->dmarx_dev.stream[i]; + if (!stream->ops) + continue; + buf = NULL; + if (is_current) + buf = stream->curr_buf; + else if (!list_empty(&stream->buf_queue)) + buf = list_first_entry(&stream->buf_queue, + struct rkisp_buffer, queue); + if (!buf) + continue; + val = buf->buff_addr[RKISP_PLANE_Y]; + /* f1 -> f0 -> f1 for normal + * L:f1 L:f1 -> L:f0 S:f0 -> L:f1 S:f1 for hdr2 + */ + if (dev->rd_mode == HDR_RDBK_FRAME2 && !is_current && + rkisp_read_reg_cache(dev, ISP3X_HDRMGE_GAIN0) == 0xfff0040) { + if (i == RKISP_STREAM_RAWRD2) + continue; + else + rkisp_write(dev, ISP3X_MI_RAWS_RD_BASE, val, false); + } + rkisp_write(dev, stream->config->mi.y_base_ad_init, val, false); + } +} + + static void rkisp_rdbk_trigger_handle(struct rkisp_device *dev, u32 cmd) { struct rkisp_hw_dev *hw = dev->hw_dev; @@ -766,10 +800,14 @@ static void rkisp_rdbk_trigger_handle(struct rkisp_device *dev, u32 cmd) } } + /* wait 2 frame to start isp for fast */ + if (dev->is_pre_on && max == 1 && !atomic_read(&dev->isp_sdev.frm_sync_seq)) + goto end; + if (max) { - v4l2_dbg(2, rkisp_debug, &dev->v4l2_dev, - "trigger fifo len:%d\n", max); isp = hw->isp[id]; + v4l2_dbg(2, rkisp_debug, &isp->v4l2_dev, + "trigger fifo len:%d\n", max); rkisp_rdbk_trigger_event(isp, T_CMD_DEQUEUE, &t); isp->dmarx_dev.pre_frame = isp->dmarx_dev.cur_frame; if (t.frame_id > isp->dmarx_dev.pre_frame.id && @@ -790,9 +828,10 @@ static void rkisp_rdbk_trigger_handle(struct rkisp_device *dev, u32 cmd) isp->sw_rd_cnt = 1; times = 0; } - if (dev->is_pre_on && t.frame_id == 0) { - dev->is_first_double = true; - dev->skip_frame = 1; + if (isp->is_pre_on && t.frame_id == 0) { + isp->is_first_double = true; + isp->skip_frame = 1; + rkisp_fast_switch_rx_buf(isp, false); } } end: @@ -865,10 +904,12 @@ void rkisp_check_idle(struct rkisp_device *dev, u32 irq) if (!completion_done(&dev->hw_dev->monitor.cmpl)) complete(&dev->hw_dev->monitor.cmpl); } - if (dev->irq_ends != dev->irq_ends_mask || !IS_HDR_RDBK(dev->rd_mode)) + if ((dev->irq_ends & dev->irq_ends_mask) != dev->irq_ends_mask || + !IS_HDR_RDBK(dev->rd_mode)) return; if (dev->is_first_double) { + rkisp_fast_switch_rx_buf(dev, true); dev->skip_frame = 0; dev->irq_ends = 0; return; @@ -2776,9 +2817,16 @@ static void rkisp_rx_qbuf_online(struct rkisp_stream *stream, static void rkisp_rx_qbuf_rdbk(struct rkisp_stream *stream, struct rkisp_rx_buf_pool *pool) { + struct rkisp_device *dev = stream->ispdev; unsigned long lock_flags = 0; struct rkisp_buffer *ispbuf = &pool->buf; - + struct isp2x_csi_trigger trigger = { + .frame_timestamp = ispbuf->vb.vb2_buf.timestamp, + .sof_timestamp = ispbuf->vb.vb2_buf.timestamp, + .frame_id = ispbuf->vb.sequence, + .mode = 0, + .times = 0, + }; spin_lock_irqsave(&stream->vbq_lock, lock_flags); if (list_empty(&stream->buf_queue) && !stream->curr_buf) { stream->curr_buf = ispbuf; @@ -2787,6 +2835,8 @@ static void rkisp_rx_qbuf_rdbk(struct rkisp_stream *stream, list_add_tail(&ispbuf->queue, &stream->buf_queue); } spin_unlock_irqrestore(&stream->vbq_lock, lock_flags); + if (stream->id == RKISP_STREAM_RAWRD2) + rkisp_rdbk_trigger_event(dev, T_CMD_QUEUE, &trigger); } static int rkisp_rx_qbuf(struct rkisp_device *dev, @@ -3639,12 +3689,14 @@ void rkisp_chk_tb_over(struct rkisp_device *isp_dev) head->enable = 0; dma_sync_single_for_device(isp_dev->dev, isp_dev->resmem_addr, sizeof(struct rkisp_thunderboot_resmem_head), - DMA_FROM_DEVICE); + DMA_TO_DEVICE); } - shm_head_poll_timeout(isp_dev, !!head->complete, 5000, 100 * USEC_PER_MSEC); + shm_head_poll_timeout(isp_dev, !!head->complete, 5000, 200 * 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; switch (isp_dev->hw_dev->isp_ver) { @@ -3657,13 +3709,14 @@ void rkisp_chk_tb_over(struct rkisp_device *isp_dev) if (size && size < isp_dev->resmem_size) { dma_sync_single_for_cpu(isp_dev->dev, isp_dev->resmem_addr, size, DMA_FROM_DEVICE); - isp_dev->params_vdev.is_first_cfg = true; + params_vdev->is_first_cfg = true; if (isp_dev->hw_dev->isp_ver == ISP_V32) { struct rkisp32_thunderboot_resmem_head *tmp = resmem_va; - memcpy(isp_dev->params_vdev.isp32_params, &tmp->cfg, - sizeof(struct isp32_isp_params_cfg)); + param = &tmp->cfg; } + 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", diff --git a/drivers/media/platform/rockchip/isp/rkisp_tb_helper.c b/drivers/media/platform/rockchip/isp/rkisp_tb_helper.c index ae2fcf06b6e0..4ae04a4dda10 100644 --- a/drivers/media/platform/rockchip/isp/rkisp_tb_helper.c +++ b/drivers/media/platform/rockchip/isp/rkisp_tb_helper.c @@ -17,12 +17,14 @@ #include #include #include +#include #include "rkisp_tb_helper.h" static struct platform_device *rkisp_tb_pdev; static struct clk_bulk_data *rkisp_tb_clk; static int rkisp_tb_clk_num; +static struct rk_tb_client tb_cl; struct shm_data { int npages; @@ -187,6 +189,11 @@ static int __maybe_unused rkisp_tb_clocks_loader_unprotect(void) return 0; } +static void rkisp_tb_cb(void *data) +{ + rkisp_tb_clocks_loader_unprotect(); +} + static int __maybe_unused rkisp_tb_runtime_suspend(struct device *dev) { return 0; @@ -220,6 +227,12 @@ static int rkisp_tb_plat_probe(struct platform_device *pdev) rkisp_tb_clk_num = 0; } rkisp_tb_clocks_loader_protect(); + + if (IS_ENABLED(CONFIG_ROCKCHIP_THUNDER_BOOT_SERVICE)) { + tb_cl.cb = rkisp_tb_cb; + return rk_tb_client_register_cb(&tb_cl); + } + return 0; } @@ -264,6 +277,9 @@ long rkisp_tb_shm_ioctl(struct rkisp_thunderboot_shmem *shmem) void rkisp_tb_unprotect_clk(void) { + if (IS_ENABLED(CONFIG_ROCKCHIP_THUNDER_BOOT_SERVICE)) + return; + rkisp_tb_clocks_loader_unprotect(); } EXPORT_SYMBOL(rkisp_tb_unprotect_clk); diff --git a/drivers/media/platform/rockchip/ispp/fec.c b/drivers/media/platform/rockchip/ispp/fec.c index a742dc18b8c7..f9e4016dcc37 100644 --- a/drivers/media/platform/rockchip/ispp/fec.c +++ b/drivers/media/platform/rockchip/ispp/fec.c @@ -281,7 +281,7 @@ static int fec_running(struct rkispp_fec_dev *fec, v4l2_dbg(3, rkispp_debug, &fec->v4l2_dev, "0x%x:0x%x 0x%x:0x%x 0x%x:0x%x 0x%x:0x%x 0x%x:0x%x\n" "0x%x:0x%x 0x%x:0x%x 0x%x:0x%x 0x%x:0x%x 0x%x:0x%x\n" - "0x%x:0x%x 0x%x:0x%x 0x%x:0x%x 0x%x:0x%x 0x%x:0x%x\n", + "0x%x:0x%x 0x%x:0x%x 0x%x:0x%x 0x%x:0x%x 0x%x:0x%x 0x%x:0x%x\n", RKISPP_CTRL_SYS_STATUS, readl(base + RKISPP_CTRL_SYS_STATUS), RKISPP_FEC_CTRL, readl(base + RKISPP_FEC_CTRL), RKISPP_FEC_RD_VIR_STRIDE, readl(base + RKISPP_FEC_RD_VIR_STRIDE), @@ -415,6 +415,9 @@ static const struct v4l2_file_operations fec_fops = { .poll = v4l2_m2m_fop_poll, .unlocked_ioctl = video_ioctl2, .mmap = v4l2_m2m_fop_mmap, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = video_ioctl2, +#endif }; static const struct video_device fec_videodev = { diff --git a/drivers/mfd/rk808.c b/drivers/mfd/rk808.c index fd755d98af84..01a0c50a6def 100644 --- a/drivers/mfd/rk808.c +++ b/drivers/mfd/rk808.c @@ -1302,7 +1302,7 @@ static int rk808_probe(struct i2c_client *client, } ret = regmap_add_irq_chip(rk808->regmap, client->irq, - IRQF_ONESHOT, -1, + IRQF_ONESHOT | IRQF_SHARED, -1, rk808->regmap_irq_chip, &rk808->irq_data); if (ret) { dev_err(&client->dev, "Failed to add irq_chip %d\n", ret); diff --git a/drivers/mmc/host/sdhci-of-dwcmshc.c b/drivers/mmc/host/sdhci-of-dwcmshc.c index 157d1c4d3ccd..3cb631766b80 100644 --- a/drivers/mmc/host/sdhci-of-dwcmshc.c +++ b/drivers/mmc/host/sdhci-of-dwcmshc.c @@ -45,6 +45,7 @@ #define DWCMSHC_EMMC_DLL_TIMEOUT BIT(9) #define DWCMSHC_EMMC_DLL_START_POINT 16 #define DWCMSHC_EMMC_DLL_INC 8 +#define DWCMSHC_EMMC_DLL_BYPASS BIT(24) #define DWCMSHC_EMMC_DLL_DLYENA BIT(27) #define DLL_TXCLK_TAPNUM_DEFAULT 0x10 @@ -233,8 +234,11 @@ static void dwcmshc_rk_set_clock(struct sdhci_host *host, unsigned int clock) sdhci_writel(host, extra, DWCMSHC_HOST_CTRL3); if (clock <= 52000000) { - /* Disable DLL and reset both of sample and drive clock */ - sdhci_writel(host, 0, DWCMSHC_EMMC_DLL_CTRL); + /* + * Disable DLL and reset both of sample and drive clock. + * The bypass bit and start bit need to set if DLL is not locked. + */ + sdhci_writel(host, DWCMSHC_EMMC_DLL_BYPASS | DWCMSHC_EMMC_DLL_START, DWCMSHC_EMMC_DLL_CTRL); sdhci_writel(host, 0, DWCMSHC_EMMC_DLL_RXCLK); sdhci_writel(host, 0, DWCMSHC_EMMC_DLL_TXCLK); sdhci_writel(host, 0, DECMSHC_EMMC_DLL_CMDOUT); @@ -264,7 +268,8 @@ static void dwcmshc_rk_set_clock(struct sdhci_host *host, unsigned int clock) extra |= DLL_RXCLK_NO_INVERTER; sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_RXCLK); - /* Init DLL settings */ + /* Init DLL settings, clean start bit before resetting */ + sdhci_writel(host, 0, DWCMSHC_EMMC_DLL_CTRL); extra = 0x5 << DWCMSHC_EMMC_DLL_START_POINT | 0x2 << DWCMSHC_EMMC_DLL_INC | DWCMSHC_EMMC_DLL_START; diff --git a/drivers/pci/controller/dwc/pcie-dw-rockchip.c b/drivers/pci/controller/dwc/pcie-dw-rockchip.c index ff517ce76623..e084f37c4edc 100644 --- a/drivers/pci/controller/dwc/pcie-dw-rockchip.c +++ b/drivers/pci/controller/dwc/pcie-dw-rockchip.c @@ -1279,7 +1279,7 @@ static int rk_pcie_phy_init(struct rk_pcie *rk_pcie) int ret; struct device *dev = rk_pcie->pci->dev; - rk_pcie->phy = devm_phy_get(dev, "pcie-phy"); + rk_pcie->phy = devm_phy_optional_get(dev, "pcie-phy"); if (IS_ERR(rk_pcie->phy)) { if (PTR_ERR(rk_pcie->phy) != -EPROBE_DEFER) dev_info(dev, "missing phy\n"); diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile index f71bd97125ca..4d1d8cbaf1c4 100644 --- a/drivers/power/supply/Makefile +++ b/drivers/power/supply/Makefile @@ -104,7 +104,6 @@ obj-$(CONFIG_CHARGER_BD70528) += bd70528-charger.o obj-$(CONFIG_CHARGER_BD99954) += bd99954-charger.o obj-$(CONFIG_CHARGER_WILCO) += wilco-charger.o obj-$(CONFIG_RN5T618_POWER) += rn5t618_power.o -obj-$(CONFIG_BATTERY_CW2015) += cw2015_battery.o obj-$(CONFIG_BATTERY_RK816) += rk816_battery.o obj-$(CONFIG_BATTERY_RK817) += rk817_battery.o obj-$(CONFIG_CHARGER_RK817) += rk817_charger.o diff --git a/drivers/rk_nand/rk_nand_blk.c b/drivers/rk_nand/rk_nand_blk.c index a4be57920186..5cb6033a40b8 100644 --- a/drivers/rk_nand/rk_nand_blk.c +++ b/drivers/rk_nand/rk_nand_blk.c @@ -188,19 +188,17 @@ static blk_status_t do_blktrans_all_request(struct nand_blk_dev *dev, struct request *req) { unsigned long block, nsect; - char *buf = NULL; + char *buf = NULL, *page_buf; struct req_iterator rq_iter; struct bio_vec bvec; int ret = BLK_STS_IOERR; unsigned long totle_nsect; - unsigned long rq_len = 0; block = blk_rq_pos(req); nsect = blk_rq_cur_bytes(req) >> 9; totle_nsect = (req->__data_len) >> 9; - if (blk_rq_pos(req) + blk_rq_cur_sectors(req) > - get_capacity(req->rq_disk)) + if (blk_rq_pos(req) + blk_rq_cur_sectors(req) > get_capacity(req->rq_disk)) return BLK_STS_IOERR; switch (req_op(req)) { @@ -211,20 +209,16 @@ static blk_status_t do_blktrans_all_request(struct nand_blk_dev *dev, case REQ_OP_READ: buf = mtd_read_temp_buffer; req_check_buffer_align(req, &buf); - ret = nand_dev_transfer(dev, - block, - totle_nsect, - buf, - REQ_OP_READ); + ret = nand_dev_transfer(dev, block, totle_nsect, buf, REQ_OP_READ); if (buf == mtd_read_temp_buffer) { char *p = buf; rq_for_each_segment(bvec, req, rq_iter) { - memcpy(page_address(bvec.bv_page) + - bvec.bv_offset, - p, - bvec.bv_len); + page_buf = kmap_atomic(bvec.bv_page); + + memcpy(page_buf + bvec.bv_offset, p, bvec.bv_len); p += bvec.bv_len; + kunmap_atomic(page_buf); } } @@ -233,34 +227,21 @@ static blk_status_t do_blktrans_all_request(struct nand_blk_dev *dev, else return BLK_STS_OK; case REQ_OP_WRITE: - rq_for_each_segment(bvec, req, rq_iter) { - if ((page_address(bvec.bv_page) + bvec.bv_offset) == (buf + rq_len)) { - rq_len += bvec.bv_len; - } else { - if (rq_len) { - ret = nand_dev_transfer(dev, - block, - rq_len >> 9, - buf, - REQ_OP_WRITE); - if (ret) - return BLK_STS_IOERR; - else - return BLK_STS_OK; - } - block += rq_len >> 9; - buf = (page_address(bvec.bv_page) + bvec.bv_offset); - rq_len = bvec.bv_len; + buf = mtd_read_temp_buffer; + req_check_buffer_align(req, &buf); + + if (buf == mtd_read_temp_buffer) { + char *p = buf; + + rq_for_each_segment(bvec, req, rq_iter) { + page_buf = kmap_atomic(bvec.bv_page); + memcpy(p, page_buf + bvec.bv_offset, bvec.bv_len); + p += bvec.bv_len; + kunmap_atomic(page_buf); } } - if (rq_len) { - ret = nand_dev_transfer(dev, - block, - rq_len >> 9, - buf, - REQ_OP_WRITE); - } + ret = nand_dev_transfer(dev, block, totle_nsect, buf, REQ_OP_WRITE); if (ret) return BLK_STS_IOERR; diff --git a/drivers/rkflash/sfc_nand.c b/drivers/rkflash/sfc_nand.c index 0bc1c6b43fd7..ddb7861880f2 100644 --- a/drivers/rkflash/sfc_nand.c +++ b/drivers/rkflash/sfc_nand.c @@ -881,7 +881,9 @@ u32 sfc_nand_read_page(u8 cs, u32 addr, u32 *p_data, u32 *p_spare) u32 sec_per_page = p_nand_info->sec_per_page; u32 data_size = sec_per_page * SFC_NAND_SECTOR_SIZE; struct nand_mega_area *meta = &p_nand_info->meta; + int retries = 0; +retry: ret = sfc_nand_read_page_raw(cs, addr, gp_page_buf); memcpy(p_data, gp_page_buf, data_size); p_spare[0] = gp_page_buf[(data_size + meta->off0) / 4]; @@ -903,6 +905,10 @@ u32 sfc_nand_read_page(u8 cs, u32 addr, u32 *p_data, u32 *p_spare) if (p_spare) rkflash_print_hex("spare:", p_spare, 4, 2); + if (ret == SFC_NAND_ECC_ERROR && retries < 1) { + retries++; + goto retry; + } } return ret; diff --git a/drivers/rknpu/rknpu_debugger.c b/drivers/rknpu/rknpu_debugger.c index 4d5bebd7e51f..146aa7dfc860 100644 --- a/drivers/rknpu/rknpu_debugger.c +++ b/drivers/rknpu/rknpu_debugger.c @@ -14,8 +14,10 @@ #include #ifndef FPGA_PLATFORM +#ifdef CONFIG_PM_DEVFREQ #include <../drivers/devfreq/governor.h> #endif +#endif #include "rknpu_drv.h" #include "rknpu_mm.h" @@ -191,6 +193,7 @@ static int rknpu_freq_show(struct seq_file *m, void *data) return 0; } +#ifdef CONFIG_PM_DEVFREQ static ssize_t rknpu_freq_set(struct file *file, const char __user *ubuf, size_t len, loff_t *offp) { @@ -233,6 +236,13 @@ static ssize_t rknpu_freq_set(struct file *file, const char __user *ubuf, return len; } +#else +static ssize_t rknpu_freq_set(struct file *file, const char __user *ubuf, + size_t len, loff_t *offp) +{ + return -EFAULT; +} +#endif static int rknpu_volt_show(struct seq_file *m, void *data) { diff --git a/drivers/rknpu/rknpu_drv.c b/drivers/rknpu/rknpu_drv.c index a153ec3791f2..30dfc38f76c9 100644 --- a/drivers/rknpu/rknpu_drv.c +++ b/drivers/rknpu/rknpu_drv.c @@ -41,8 +41,10 @@ #include #include #include +#ifdef CONFIG_PM_DEVFREQ #include <../drivers/devfreq/governor.h> #endif +#endif #include "rknpu_ioctl.h" #include "rknpu_reset.h" @@ -1062,6 +1064,7 @@ static struct devfreq_dev_profile npu_devfreq_profile = { .get_cur_freq = npu_devfreq_get_cur_freq, }; +#ifdef CONFIG_PM_DEVFREQ static int devfreq_rknpu_ondemand_func(struct devfreq *df, unsigned long *freq) { struct rknpu_device *rknpu_dev = df->data; @@ -1085,6 +1088,7 @@ static struct devfreq_governor devfreq_rknpu_ondemand = { .get_target_freq = devfreq_rknpu_ondemand_func, .event_handler = devfreq_rknpu_ondemand_handler, }; +#endif static unsigned long npu_get_static_power(struct devfreq *devfreq, unsigned long voltage) @@ -1191,11 +1195,13 @@ static int rknpu_devfreq_init(struct rknpu_device *rknpu_dev) dev_pm_opp_put(opp); dp->initial_freq = rknpu_dev->current_freq; +#ifdef CONFIG_PM_DEVFREQ ret = devfreq_add_governor(&devfreq_rknpu_ondemand); if (ret) { LOG_DEV_ERROR(dev, "failed to add rknpu_ondemand governor\n"); goto err_remove_table; } +#endif rknpu_dev->devfreq = devm_devfreq_add_device(dev, dp, "rknpu_ondemand", (void *)rknpu_dev); @@ -1247,7 +1253,9 @@ out: return 0; err_remove_governor: +#ifdef CONFIG_PM_DEVFREQ devfreq_remove_governor(&devfreq_rknpu_ondemand); +#endif err_remove_table: dev_pm_opp_of_remove_table(dev); @@ -1325,11 +1333,13 @@ static int rknpu_devfreq_init(struct rknpu_device *rknpu_dev) } dp->initial_freq = rknpu_dev->current_freq; +#ifdef CONFIG_PM_DEVFREQ ret = devfreq_add_governor(&devfreq_rknpu_ondemand); if (ret) { LOG_DEV_ERROR(dev, "failed to add rknpu_ondemand governor\n"); goto err_remove_table; } +#endif rknpu_dev->devfreq = devm_devfreq_add_device(dev, dp, "rknpu_ondemand", (void *)rknpu_dev); @@ -1380,7 +1390,9 @@ out: return 0; err_remove_governor: +#ifdef CONFIG_PM_DEVFREQ devfreq_remove_governor(&devfreq_rknpu_ondemand); +#endif err_remove_table: dev_pm_opp_of_remove_table(dev); @@ -1396,7 +1408,9 @@ static int rknpu_devfreq_remove(struct rknpu_device *rknpu_dev) devfreq_unregister_opp_notifier(rknpu_dev->dev, rknpu_dev->devfreq); dev_pm_opp_of_remove_table(rknpu_dev->dev); +#ifdef CONFIG_PM_DEVFREQ devfreq_remove_governor(&devfreq_rknpu_ondemand); +#endif } return 0; diff --git a/drivers/soc/rockchip/pm_domains.c b/drivers/soc/rockchip/pm_domains.c index 474f05629457..9ba38641b31f 100644 --- a/drivers/soc/rockchip/pm_domains.c +++ b/drivers/soc/rockchip/pm_domains.c @@ -52,6 +52,7 @@ struct rockchip_domain_info { int mem_status_mask; int repair_status_mask; bool keepon_startup; + bool always_on; u32 pwr_offset; u32 mem_offset; u32 req_offset; @@ -117,6 +118,35 @@ module_param_named(always_on, pm_domain_always_on, bool, 0644); MODULE_PARM_DESC(always_on, "Always keep pm domains power on except for system suspend."); +#ifdef MODULE +static bool keepon_startup = true; +static void rockchip_pd_keepon_do_release(void); + +static int pd_param_set_keepon_startup(const char *val, + const struct kernel_param *kp) +{ + int ret; + + ret = param_set_bool(val, kp); + if (ret) + return ret; + + if (!keepon_startup) + rockchip_pd_keepon_do_release(); + + return 0; +} + +static const struct kernel_param_ops pd_keepon_startup_ops = { + .set = pd_param_set_keepon_startup, + .get = param_get_bool, +}; + +module_param_cb(keepon_startup, &pd_keepon_startup_ops, &keepon_startup, 0644); +MODULE_PARM_DESC(keepon_startup, + "Keep pm domains power on during system startup."); +#endif + static void rockchip_pmu_lock(struct rockchip_pm_domain *pd) { mutex_lock(&pd->pmu->mutex); @@ -847,6 +877,26 @@ static void rockchip_pd_qos_init(struct rockchip_pm_domain *pd) } } +static int rockchip_pd_add_alwasy_on_flag(struct rockchip_pm_domain *pd) +{ + int error; + + if (pd->genpd.flags & GENPD_FLAG_ALWAYS_ON) + return 0; + pd->genpd.flags |= GENPD_FLAG_ALWAYS_ON; + if (!rockchip_pmu_domain_is_on(pd)) { + error = rockchip_pd_power(pd, true); + if (error) { + dev_err(pd->pmu->dev, + "failed to power on domain '%s': %d\n", + pd->genpd.name, error); + return error; + } + } + + return 0; +} + static int rockchip_pm_add_one_domain(struct rockchip_pmu *pmu, struct device_node *node) { @@ -1033,20 +1083,11 @@ static int rockchip_pm_add_one_domain(struct rockchip_pmu *pmu, pd->genpd.detach_dev = rockchip_pd_detach_dev; if (pd_info->active_wakeup) pd->genpd.flags |= GENPD_FLAG_ACTIVE_WAKEUP; -#ifndef MODULE - if (pd_info->keepon_startup) { - pd->genpd.flags |= GENPD_FLAG_ALWAYS_ON; - if (!rockchip_pmu_domain_is_on(pd)) { - error = rockchip_pd_power(pd, true); - if (error) { - dev_err(pmu->dev, - "failed to power on domain '%s': %d\n", - node->name, error); - goto err_unprepare_clocks; - } - } + if (pd_info->always_on || pd_info->keepon_startup) { + error = rockchip_pd_add_alwasy_on_flag(pd); + if (error) + goto err_unprepare_clocks; } -#endif rockchip_pd_qos_init(pd); pm_genpd_init(&pd->genpd, NULL, !rockchip_pmu_domain_is_on(pd)); @@ -1181,46 +1222,36 @@ err_out: return error; } -#ifndef MODULE -static void rockchip_pd_keepon_do_release(struct generic_pm_domain *genpd, - struct rockchip_pm_domain *pd) -{ - struct pm_domain_data *pm_data; - int enable_count; - - pd->genpd.flags &= (~GENPD_FLAG_ALWAYS_ON); - list_for_each_entry(pm_data, &genpd->dev_list, list_node) { - if (!atomic_read(&pm_data->dev->power.usage_count)) { - enable_count = 0; - if (!pm_runtime_enabled(pm_data->dev)) { - pm_runtime_enable(pm_data->dev); - enable_count = 1; - } - pm_runtime_get_sync(pm_data->dev); - pm_runtime_put_sync(pm_data->dev); - if (enable_count) - pm_runtime_disable(pm_data->dev); - } - } -} - -static int __init rockchip_pd_keepon_release(void) +static void rockchip_pd_keepon_do_release(void) { struct generic_pm_domain *genpd; struct rockchip_pm_domain *pd; int i; if (!g_pmu) - return 0; + return; for (i = 0; i < g_pmu->genpd_data.num_domains; i++) { genpd = g_pmu->genpd_data.domains[i]; if (genpd) { pd = to_rockchip_pd(genpd); - if (pd->info->keepon_startup) - rockchip_pd_keepon_do_release(genpd, pd); + if (pd->info->always_on) + continue; + if (!pd->info->keepon_startup) + continue; + if (!(genpd->flags & GENPD_FLAG_ALWAYS_ON)) + continue; + genpd->flags &= (~GENPD_FLAG_ALWAYS_ON); + queue_work(pm_wq, &genpd->power_off_work); } } +} + +#ifndef MODULE +static int __init rockchip_pd_keepon_release(void) +{ + rockchip_pd_keepon_do_release(); + return 0; } late_initcall_sync(rockchip_pd_keepon_release); diff --git a/drivers/video/rockchip/mpp/mpp_common.c b/drivers/video/rockchip/mpp/mpp_common.c index 3565926e64c1..d167f837c508 100644 --- a/drivers/video/rockchip/mpp/mpp_common.c +++ b/drivers/video/rockchip/mpp/mpp_common.c @@ -819,6 +819,7 @@ static int mpp_task_run(struct mpp_dev *mpp, */ mpp_reset_down_read(mpp->reset_group); + mpp_iommu_dev_activate(mpp->iommu_info, mpp); if (mpp->dev_ops->run) mpp->dev_ops->run(mpp, task); @@ -2032,39 +2033,21 @@ int mpp_task_dump_reg(struct mpp_dev *mpp, int mpp_task_dump_hw_reg(struct mpp_dev *mpp) { - if (mpp_debug_unlikely(DEBUG_DUMP_ERR_REG)) { - u32 i; - u32 s = mpp->var->hw_info->reg_start; - u32 e = mpp->var->hw_info->reg_end; + u32 i; + u32 s = mpp->var->hw_info->reg_start; + u32 e = mpp->var->hw_info->reg_end; - mpp_err("--- dump hardware register ---\n"); - for (i = s; i <= e; i++) { - u32 reg = i * sizeof(u32); + mpp_err("--- dump hardware register ---\n"); + for (i = s; i <= e; i++) { + u32 reg = i * sizeof(u32); - mpp_err("reg[%03d]: %04x: 0x%08x\n", + mpp_err("reg[%03d]: %04x: 0x%08x\n", i, reg, readl_relaxed(mpp->reg_base + reg)); - } } return 0; } -static int mpp_iommu_handle(struct iommu_domain *iommu, - struct device *iommu_dev, - unsigned long iova, - int status, void *arg) -{ - struct mpp_dev *mpp = (struct mpp_dev *)arg; - - dev_err(mpp->dev, "fault addr 0x%08lx status %x\n", iova, status); - mpp_task_dump_hw_reg(mpp); - - if (mpp->iommu_info->hdl) - mpp->iommu_info->hdl(iommu, iommu_dev, iova, status, arg); - - return 0; -} - void mpp_reg_show(struct mpp_dev *mpp, u32 offset) { if (!mpp) @@ -2167,10 +2150,6 @@ int mpp_dev_probe(struct mpp_dev *mpp, if (ret) goto failed; } - /* set iommu fault handler */ - if (mpp->iommu_info) - iommu_set_fault_handler(mpp->iommu_info->domain, - mpp_iommu_handle, mpp); /* read hardware id */ if (hw_info->reg_id >= 0) { @@ -2270,6 +2249,7 @@ irqreturn_t mpp_dev_irq(int irq, void *param) /* normal condition, set state and wake up isr thread */ set_bit(TASK_STATE_IRQ, &task->state); } + mpp_iommu_dev_deactivate(mpp->iommu_info, mpp); } else { mpp_debug(DEBUG_IRQ_CHECK, "error, task is null\n"); } diff --git a/drivers/video/rockchip/mpp/mpp_common.h b/drivers/video/rockchip/mpp/mpp_common.h index cf62cc396dcf..9d6289baf739 100644 --- a/drivers/video/rockchip/mpp/mpp_common.h +++ b/drivers/video/rockchip/mpp/mpp_common.h @@ -191,6 +191,7 @@ struct mpp_task; struct mpp_session; struct mpp_dma_session; struct mpp_taskqueue; +struct iommu_domain; /* data common struct for parse out */ struct mpp_request { @@ -342,6 +343,8 @@ struct mpp_dev { void __iomem *reg_base; struct mpp_grf_info *grf_info; struct mpp_iommu_info *iommu_info; + int (*fault_handler)(struct iommu_domain *iommu, struct device *iommu_dev, + unsigned long iova, int status, void *arg); atomic_t reset_request; atomic_t session_index; diff --git a/drivers/video/rockchip/mpp/mpp_iommu.c b/drivers/video/rockchip/mpp/mpp_iommu.c index eb91b5d82b9b..bf3fb43b2883 100644 --- a/drivers/video/rockchip/mpp/mpp_iommu.c +++ b/drivers/video/rockchip/mpp/mpp_iommu.c @@ -25,6 +25,7 @@ #include "mpp_debug.h" #include "mpp_iommu.h" +#include "mpp_common.h" static struct mpp_dma_buffer * mpp_dma_find_buffer_fd(struct mpp_dma_session *dma, int fd) @@ -388,6 +389,29 @@ int mpp_iommu_attach(struct mpp_iommu_info *info) return iommu_attach_group(info->domain, info->group); } +static int mpp_iommu_handle(struct iommu_domain *iommu, + struct device *iommu_dev, + unsigned long iova, + int status, void *arg) +{ + struct mpp_dev *mpp = (struct mpp_dev *)arg; + + dev_err(iommu_dev, "fault addr 0x%08lx status %x arg %p\n", + iova, status, arg); + + if (!mpp) { + dev_err(iommu_dev, "pagefault without device to handle\n"); + return 0; + } + + if (mpp->dev_ops && mpp->dev_ops->dump_dev) + mpp->dev_ops->dump_dev(mpp); + else + mpp_task_dump_hw_reg(mpp); + + return 0; +} + struct mpp_iommu_info * mpp_iommu_probe(struct device *dev) { @@ -446,10 +470,12 @@ mpp_iommu_probe(struct device *dev) } init_rwsem(&info->rw_sem); + spin_lock_init(&info->dev_lock); info->dev = dev; info->pdev = pdev; info->group = group; info->domain = domain; + info->dev_active = NULL; info->irq = platform_get_irq(pdev, 0); info->got_irq = (info->irq < 0) ? false : true; @@ -507,3 +533,47 @@ int mpp_iommu_flush_tlb(struct mpp_iommu_info *info) return 0; } + +int mpp_iommu_dev_activate(struct mpp_iommu_info *info, struct mpp_dev *dev) +{ + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&info->dev_lock, flags); + + if (info->dev_active || !dev) { + dev_err(info->dev, "can not activate %s -> %s\n", + info->dev_active ? dev_name(info->dev_active->dev) : NULL, + dev ? dev_name(dev->dev) : NULL); + ret = -EINVAL; + } else { + info->dev_active = dev; + /* switch domain pagefault handler and arg depending on device */ + iommu_set_fault_handler(info->domain, dev->fault_handler ? + dev->fault_handler : mpp_iommu_handle, dev); + + dev_dbg(info->dev, "activate -> %p %s\n", dev, dev_name(dev->dev)); + } + + spin_unlock_irqrestore(&info->dev_lock, flags); + + return ret; +} + +int mpp_iommu_dev_deactivate(struct mpp_iommu_info *info, struct mpp_dev *dev) +{ + unsigned long flags; + + spin_lock_irqsave(&info->dev_lock, flags); + + if (info->dev_active != dev) + dev_err(info->dev, "can not deactivate %s when %s activated\n", + dev_name(dev->dev), + info->dev_active ? dev_name(info->dev_active->dev) : NULL); + + dev_dbg(info->dev, "deactivate %p\n", info->dev_active); + info->dev_active = NULL; + spin_unlock_irqrestore(&info->dev_lock, flags); + + return 0; +} diff --git a/drivers/video/rockchip/mpp/mpp_iommu.h b/drivers/video/rockchip/mpp/mpp_iommu.h index e9b57da39eb9..4419741e5fb0 100644 --- a/drivers/video/rockchip/mpp/mpp_iommu.h +++ b/drivers/video/rockchip/mpp/mpp_iommu.h @@ -64,6 +64,8 @@ struct mpp_rk_iommu { u32 is_paged; }; +struct mpp_dev; + struct mpp_iommu_info { struct rw_semaphore rw_sem; @@ -73,6 +75,10 @@ struct mpp_iommu_info { struct iommu_group *group; struct mpp_rk_iommu *iommu; iommu_fault_handler_t hdl; + + spinlock_t dev_lock; + struct mpp_dev *dev_active; + u32 av1d_iommu; int irq; int got_irq; @@ -110,6 +116,9 @@ int mpp_iommu_flush_tlb(struct mpp_iommu_info *info); int mpp_av1_iommu_disable(struct device *dev); int mpp_av1_iommu_enable(struct device *dev); +int mpp_iommu_dev_activate(struct mpp_iommu_info *info, struct mpp_dev *dev); +int mpp_iommu_dev_deactivate(struct mpp_iommu_info *info, struct mpp_dev *dev); + static inline int mpp_iommu_down_read(struct mpp_iommu_info *info) { if (info) diff --git a/drivers/video/rockchip/mpp/mpp_rkvdec2.c b/drivers/video/rockchip/mpp/mpp_rkvdec2.c index ada9df5961ad..0ce4324796c6 100644 --- a/drivers/video/rockchip/mpp/mpp_rkvdec2.c +++ b/drivers/video/rockchip/mpp/mpp_rkvdec2.c @@ -420,8 +420,10 @@ static int rkvdec2_isr(struct mpp_dev *mpp) RKVDEC_TIMEOUT_STA | RKVDEC_ERROR_STA; if (err_mask & task->irq_status) { atomic_inc(&mpp->reset_request); - mpp_debug(DEBUG_DUMP_ERR_REG, "irq_status: %08x\n", task->irq_status); - mpp_task_dump_hw_reg(mpp); + if (mpp_debug_unlikely(DEBUG_DUMP_ERR_REG)) { + mpp_debug(DEBUG_DUMP_ERR_REG, "irq_status: %08x\n", task->irq_status); + mpp_task_dump_hw_reg(mpp); + } } mpp_task_finish(mpp_task->session, mpp_task); @@ -1442,7 +1444,7 @@ static int rkvdec2_core_probe(struct platform_device *pdev) mpp->dev_ops->task_worker = rkvdec2_soft_ccu_worker; kthread_init_work(&mpp->work, rkvdec2_soft_ccu_worker); - mpp->iommu_info->hdl = rkvdec2_ccu_iommu_fault_handle; + mpp->fault_handler = rkvdec2_ccu_iommu_fault_handle; /* get irq request */ ret = devm_request_threaded_irq(dev, mpp->irq, rkvdec2_soft_ccu_irq, NULL, IRQF_SHARED, dev_name(dev), mpp); diff --git a/drivers/video/rockchip/mpp/mpp_rkvdec2_link.c b/drivers/video/rockchip/mpp/mpp_rkvdec2_link.c index b62bc01589f5..03dba0e20376 100644 --- a/drivers/video/rockchip/mpp/mpp_rkvdec2_link.c +++ b/drivers/video/rockchip/mpp/mpp_rkvdec2_link.c @@ -1066,6 +1066,7 @@ static int rkvdec2_link_power_on(struct mpp_dev *mpp) mpp_clk_set_rate(&dec->cabac_clk_info, CLK_MODE_ADVANCED); mpp_clk_set_rate(&dec->hevc_cabac_clk_info, CLK_MODE_ADVANCED); mpp_devfreq_set_core_rate(mpp, CLK_MODE_ADVANCED); + mpp_iommu_dev_activate(mpp->iommu_info, mpp); } return 0; } @@ -1092,6 +1093,7 @@ static void rkvdec2_link_power_off(struct mpp_dev *mpp) mpp_clk_set_rate(&dec->cabac_clk_info, CLK_MODE_NORMAL); mpp_clk_set_rate(&dec->hevc_cabac_clk_info, CLK_MODE_NORMAL); mpp_devfreq_set_core_rate(mpp, CLK_MODE_NORMAL); + mpp_iommu_dev_deactivate(mpp->iommu_info, mpp); } } @@ -1614,6 +1616,8 @@ static int rkvdec2_ccu_power_on(struct mpp_taskqueue *queue, pm_stay_awake(mpp->dev); if (mpp->hw_ops->clk_on) mpp->hw_ops->clk_on(mpp); + + mpp_iommu_dev_activate(mpp->iommu_info, mpp); } mpp_debug(DEBUG_CCU, "power on\n"); } @@ -1642,6 +1646,7 @@ static int rkvdec2_ccu_power_off(struct mpp_taskqueue *queue, pm_relax(mpp->dev); pm_runtime_mark_last_busy(mpp->dev); pm_runtime_put_autosuspend(mpp->dev); + mpp_iommu_dev_deactivate(mpp->iommu_info, mpp); } mpp_debug(DEBUG_CCU, "power off\n"); } diff --git a/drivers/video/rockchip/mpp/mpp_rkvenc.c b/drivers/video/rockchip/mpp/mpp_rkvenc.c index 36a888193246..e6ab2c639df4 100644 --- a/drivers/video/rockchip/mpp/mpp_rkvenc.c +++ b/drivers/video/rockchip/mpp/mpp_rkvenc.c @@ -530,9 +530,11 @@ static int rkvenc_isr(struct mpp_dev *mpp) if (task->irq_status & RKVENC_INT_ERROR_BITS) { atomic_inc(&mpp->reset_request); - /* dump register */ - mpp_debug(DEBUG_DUMP_ERR_REG, "irq_status: %08x\n", task->irq_status); - mpp_task_dump_hw_reg(mpp); + if (mpp_debug_unlikely(DEBUG_DUMP_ERR_REG)) { + /* dump error register */ + mpp_debug(DEBUG_DUMP_ERR_REG, "irq_status: %08x\n", task->irq_status); + mpp_task_dump_hw_reg(mpp); + } } /* unmap reserve buffer */ diff --git a/drivers/video/rockchip/mpp/mpp_rkvenc2.c b/drivers/video/rockchip/mpp/mpp_rkvenc2.c index 8b2c2d4516a7..b40ae0ab20a4 100644 --- a/drivers/video/rockchip/mpp/mpp_rkvenc2.c +++ b/drivers/video/rockchip/mpp/mpp_rkvenc2.c @@ -1200,9 +1200,10 @@ static int rkvenc_isr(struct mpp_dev *mpp) if (task->irq_status & enc->hw_info->err_mask) { atomic_inc(&mpp->reset_request); - /* dump register */ - mpp_task_dump_hw_reg(mpp); + /* dump register */ + if (mpp_debug_unlikely(DEBUG_DUMP_ERR_REG)) + mpp_task_dump_hw_reg(mpp); } mpp_task_finish(mpp_task->session, mpp_task); diff --git a/drivers/video/rockchip/rga3/include/rga_job.h b/drivers/video/rockchip/rga3/include/rga_job.h index 214472e316a3..cbbfbd6b54c0 100644 --- a/drivers/video/rockchip/rga3/include/rga_job.h +++ b/drivers/video/rockchip/rga3/include/rga_job.h @@ -21,8 +21,6 @@ enum job_flags { RGA_JOB_UNSUPPORT_RGA_MMU = 1 << 4, }; -struct rga_scheduler_t *rga_job_get_scheduler(struct rga_job *job); - void rga_job_session_destroy(struct rga_session *session); void rga_job_scheduler_dump_info(struct rga_scheduler_t *scheduler); @@ -52,10 +50,4 @@ int rga_request_release_signal(struct rga_scheduler_t *scheduler, struct rga_job int rga_request_manager_init(struct rga_pending_request_manager **request_manager_session); int rga_request_manager_remove(struct rga_pending_request_manager **request_manager_session); -struct rga_job * -rga_scheduler_get_pending_job_list(struct rga_scheduler_t *scheduler); - -struct rga_job * -rga_scheduler_get_running_job(struct rga_scheduler_t *scheduler); - #endif /* __LINUX_RKRGA_JOB_H_ */ diff --git a/drivers/video/rockchip/rga3/rga_drv.c b/drivers/video/rockchip/rga3/rga_drv.c index 576b460f91f3..1be32727a2f5 100644 --- a/drivers/video/rockchip/rga3/rga_drv.c +++ b/drivers/video/rockchip/rga3/rga_drv.c @@ -270,8 +270,8 @@ int rga_kernel_commit(struct rga_req *cmd) struct rga_pending_request_manager *request_manager = rga_drvdata->pend_request_manager; session = rga_session_init(); - if (!session) - return -ENOMEM; + if (IS_ERR(session)) + return PTR_ERR(session); request_id = rga_request_alloc(0, session); if (request_id < 0) { @@ -561,22 +561,38 @@ static int rga_session_manager_remove(struct rga_session_manager **session_manag static struct rga_session *rga_session_init(void) { + int new_id; + struct rga_session_manager *session_manager = NULL; - struct rga_session *session = kzalloc(sizeof(*session), GFP_KERNEL); + struct rga_session *session = NULL; session_manager = rga_drvdata->session_manager; if (session_manager == NULL) { pr_err("rga_session_manager is null!\n"); - kfree(session); - return NULL; + return ERR_PTR(-EFAULT); + } + + session = kzalloc(sizeof(*session), GFP_KERNEL); + if (!session) { + pr_err("rga_session alloc failed\n"); + return ERR_PTR(-ENOMEM); } mutex_lock(&session_manager->lock); idr_preload(GFP_KERNEL); - session->id = idr_alloc(&session_manager->ctx_id_idr, session, 1, 0, GFP_ATOMIC); - session_manager->session_cnt++; + new_id = idr_alloc_cyclic(&session_manager->ctx_id_idr, session, 1, 0, GFP_NOWAIT); idr_preload_end(); + if (new_id < 0) { + mutex_unlock(&session_manager->lock); + + pr_err("rga_session alloc id failed!\n"); + kfree(session); + return ERR_PTR(new_id); + } + + session->id = new_id; + session_manager->session_cnt++; mutex_unlock(&session_manager->lock); @@ -1140,8 +1156,8 @@ static int rga_open(struct inode *inode, struct file *file) struct rga_session *session = NULL; session = rga_session_init(); - if (!session) - return -ENOMEM; + if (IS_ERR(session)) + return PTR_ERR(session); file->private_data = (void *)session; diff --git a/drivers/video/rockchip/rga3/rga_job.c b/drivers/video/rockchip/rga3/rga_job.c index 47bb908c8fb7..567df28396a8 100644 --- a/drivers/video/rockchip/rga3/rga_job.c +++ b/drivers/video/rockchip/rga3/rga_job.c @@ -14,42 +14,6 @@ #include "rga_iommu.h" #include "rga_debugger.h" -struct rga_job * -rga_scheduler_get_pending_job_list(struct rga_scheduler_t *scheduler) -{ - unsigned long flags; - struct rga_job *job; - - spin_lock_irqsave(&scheduler->irq_lock, flags); - - job = list_first_entry_or_null(&scheduler->todo_list, - struct rga_job, head); - - spin_unlock_irqrestore(&scheduler->irq_lock, flags); - - return job; -} - -struct rga_job * -rga_scheduler_get_running_job(struct rga_scheduler_t *scheduler) -{ - unsigned long flags; - struct rga_job *job; - - spin_lock_irqsave(&scheduler->irq_lock, flags); - - job = scheduler->running_job; - - spin_unlock_irqrestore(&scheduler->irq_lock, flags); - - return job; -} - -struct rga_scheduler_t *rga_job_get_scheduler(struct rga_job *job) -{ - return job->scheduler; -} - static void rga_job_free(struct rga_job *job) { free_page((unsigned long)job); @@ -411,7 +375,7 @@ static struct rga_scheduler_t *rga_job_schedule(struct rga_job *job) job->scheduler = rga_drvdata->scheduler[0]; } - scheduler = rga_job_get_scheduler(job); + scheduler = job->scheduler; if (scheduler == NULL) { pr_err("failed to get scheduler, %s(%d)\n", __func__, __LINE__); job->ret = -EFAULT; @@ -832,8 +796,6 @@ int rga_request_release_signal(struct rga_scheduler_t *scheduler, struct rga_job rga_request_get(request); mutex_unlock(&request_manager->lock); - rga_job_cleanup(job); - spin_lock_irqsave(&request->lock, flags); if (job->ret < 0) { @@ -848,6 +810,8 @@ int rga_request_release_signal(struct rga_scheduler_t *scheduler, struct rga_job spin_unlock_irqrestore(&request->lock, flags); + rga_job_cleanup(job); + if ((failed_count + finished_count) >= request->task_count) { spin_lock_irqsave(&request->lock, flags); @@ -1188,6 +1152,7 @@ static int rga_request_free_cb(int id, void *ptr, void *data) int rga_request_alloc(uint32_t flags, struct rga_session *session) { + int new_id; struct rga_pending_request_manager *request_manager; struct rga_request *request; @@ -1218,17 +1183,17 @@ int rga_request_alloc(uint32_t flags, struct rga_session *session) mutex_lock(&request_manager->lock); idr_preload(GFP_KERNEL); - request->id = idr_alloc(&request_manager->request_idr, request, 1, 0, GFP_KERNEL); + new_id = idr_alloc_cyclic(&request_manager->request_idr, request, 1, 0, GFP_NOWAIT); idr_preload_end(); - - if (request->id <= 0) { - pr_err("alloc request_id failed!\n"); + if (new_id < 0) { + pr_err("request alloc id failed!\n"); mutex_unlock(&request_manager->lock); kfree(request); - return -EFAULT; + return new_id; } + request->id = new_id; request_manager->request_count++; mutex_unlock(&request_manager->lock); diff --git a/drivers/video/rockchip/rga3/rga_mm.c b/drivers/video/rockchip/rga3/rga_mm.c index 7d59472d0888..6183aa11db47 100644 --- a/drivers/video/rockchip/rga3/rga_mm.c +++ b/drivers/video/rockchip/rga3/rga_mm.c @@ -1869,7 +1869,7 @@ void rga_mm_unmap_job_info(struct rga_job *job) uint32_t rga_mm_import_buffer(struct rga_external_buffer *external_buffer, struct rga_session *session) { - int ret = 0; + int ret = 0, new_id; struct rga_mm *mm; struct rga_internal_buffer *internal_buffer; @@ -1911,9 +1911,14 @@ uint32_t rga_mm_import_buffer(struct rga_external_buffer *external_buffer, * allocation under our spinlock. */ idr_preload(GFP_KERNEL); - internal_buffer->handle = idr_alloc(&mm->memory_idr, internal_buffer, 1, 0, GFP_KERNEL); + new_id = idr_alloc_cyclic(&mm->memory_idr, internal_buffer, 1, 0, GFP_NOWAIT); idr_preload_end(); + if (new_id < 0) { + pr_err("internal_buffer alloc id failed!\n"); + goto FREE_INTERNAL_BUFFER; + } + internal_buffer->handle = new_id; mm->buffer_count++; if (DEBUGGER_EN(MM)) { diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h index 3e2c6ecb6575..a58b7dc9844a 100644 --- a/include/drm/bridge/dw_hdmi.h +++ b/include/drm/bridge/dw_hdmi.h @@ -7,6 +7,7 @@ #define __DW_HDMI__ #include +#include #include #include @@ -248,8 +249,9 @@ struct dw_hdmi_plat_data { u64 (*get_grf_color_fmt)(void *data); void (*convert_to_split_mode)(struct drm_display_mode *mode); void (*convert_to_origin_mode)(struct drm_display_mode *mode); - int (*dclk_set)(void *data, bool enable); + int (*dclk_set)(void *data, bool enable, int vp_id); int (*link_clk_set)(void *data, bool enable); + int (*get_vp_id)(struct drm_crtc_state *crtc_state); /* Vendor Property support */ const struct dw_hdmi_property_ops *property_ops; diff --git a/include/dt-bindings/clock/rk3128-cru.h b/include/dt-bindings/clock/rk3128-cru.h index b7cd27e446b6..9fe8283549b7 100644 --- a/include/dt-bindings/clock/rk3128-cru.h +++ b/include/dt-bindings/clock/rk3128-cru.h @@ -116,6 +116,7 @@ #define PCLK_GMAC 367 #define PCLK_PMU_PRE 368 #define PCLK_SIM_CARD 369 +#define PCLK_MIPIPHY 370 /* hclk gates */ #define HCLK_SFC 439 diff --git a/include/dt-bindings/clock/rk3588-cru.h b/include/dt-bindings/clock/rk3588-cru.h index 09a6a54ae084..33b0f08c8370 100644 --- a/include/dt-bindings/clock/rk3588-cru.h +++ b/include/dt-bindings/clock/rk3588-cru.h @@ -631,7 +631,7 @@ #define CLK_DSIHOST1 635 #define CLK_VOP_PMU 636 #define ACLK_VOP_DOBY 637 -#define ACLK_VOP_SUB_SRC 638 +#define ACLK_VOP_DIV2_SRC 638 #define CLK_USBDP_PHY0_IMMORTAL 639 #define CLK_USBDP_PHY1_IMMORTAL 640 #define CLK_PMU0 641 diff --git a/include/linux/sram_heap.h b/include/linux/sram_heap.h new file mode 100644 index 000000000000..341f501b9153 --- /dev/null +++ b/include/linux/sram_heap.h @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * SRAM DMA-Heap exporter && support alloc page and dmabuf on kernel + * + * Copyright (C) Rockchip Electronics Co., Ltd. + * + * Author: Huang Lee + */ +#ifndef _LINUX_SRAM_HEAP_H +#define _LINUX_SRAM_HEAP_H + +#include +#include +#include + +#if IS_REACHABLE(CONFIG_DMABUF_HEAPS_SRAM) +struct dma_buf *sram_heap_alloc_dma_buf(size_t size); +struct page *sram_heap_alloc_pages(size_t size); +void sram_heap_free_pages(struct page *p); +void sram_heap_free_dma_buf(struct dma_buf *dmabuf); +void *sram_heap_get_vaddr(struct dma_buf *dmabuf); +phys_addr_t sram_heap_get_paddr(struct dma_buf *dmabuf); + +#else +static inline struct dma_buf *sram_heap_alloc_dma_buf(size_t size) +{ + return NULL; +} + +static inline struct page *sram_heap_alloc_pages(size_t size) +{ + return NULL; +} + +static inline void sram_heap_free_pages(struct page *p) {} +static inline void sram_heap_free_dma_buf(struct dma_buf *dmabuf) {} + +static inline void *sram_heap_get_vaddr(struct dma_buf *dmabuf) +{ + return NULL; +} + +static inline phys_addr_t sram_heap_get_paddr(struct dma_buf *dmabuf) +{ + return 0; +} +#endif + +#endif diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 0a6b502dd75a..13afa7980831 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -161,6 +161,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_RK3228 imply SND_SOC_RK3308 imply SND_SOC_RK3328 + imply SND_SOC_RK730 imply SND_SOC_RK817 imply SND_SOC_RT274 imply SND_SOC_RT286 @@ -1096,6 +1097,10 @@ config SND_SOC_RK3328 tristate "Rockchip RK3328 audio CODEC" select REGMAP_MMIO +config SND_SOC_RK730 + tristate "Rockchip RK730 CODEC" + select REGMAP_I2C + config SND_SOC_RK817 tristate "Rockchip RK817 CODEC" depends on MFD_RK808 diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 66305fddd99d..d7e238d7091d 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -168,6 +168,7 @@ snd-soc-rk312x-objs := rk312x_codec.o snd-soc-rk3228-objs := rk3228_codec.o snd-soc-rk3308-objs := rk3308_codec.o snd-soc-rk3328-objs := rk3328_codec.o +snd-soc-rk730-objs := rk730.o snd-soc-rk817-objs := rk817_codec.o snd-soc-rk-codec-digital-objs := rk_codec_digital.o snd-soc-rl6231-objs := rl6231.o @@ -492,6 +493,7 @@ obj-$(CONFIG_SND_SOC_RK312X) += snd-soc-rk312x.o obj-$(CONFIG_SND_SOC_RK3228) += snd-soc-rk3228.o obj-$(CONFIG_SND_SOC_RK3308) += snd-soc-rk3308.o obj-$(CONFIG_SND_SOC_RK3328) += snd-soc-rk3328.o +obj-$(CONFIG_SND_SOC_RK730) += snd-soc-rk730.o obj-$(CONFIG_SND_SOC_RK817) += snd-soc-rk817.o obj-$(CONFIG_SND_SOC_RK_CODEC_DIGITAL) += snd-soc-rk-codec-digital.o obj-$(CONFIG_SND_SOC_RL6231) += snd-soc-rl6231.o diff --git a/sound/soc/codecs/rk730.c b/sound/soc/codecs/rk730.c new file mode 100644 index 000000000000..db25d092d2b3 --- /dev/null +++ b/sound/soc/codecs/rk730.c @@ -0,0 +1,971 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * rk730.c -- RK730 ALSA SoC Audio driver + * + * Copyright (C) 2022 Rockchip Electronics Co.,Ltd + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rk730.h" + +enum rk730_mix_mode { + RK730_MIX_MODE_1_PATH, + RK730_MIX_MODE_2_PATHS, + RK730_MIX_MODE_3_PATHS, +}; + +enum rk730_chop_freq { + RK730_CHOP_FREQ_NONE, + RK730_CHOP_FREQ_200KHZ, + RK730_CHOP_FREQ_400KHZ, + RK730_CHOP_FREQ_800KHZ, +}; + +struct rk730_priv { + struct regmap *regmap; + struct clk *mclk; + atomic_t mix_mode; +}; + +/* ADC Digital Volume */ +static const DECLARE_TLV_DB_SCALE(adc_dig_tlv, -95625, 375, 0); +/* DAC Digital Volume */ +static const DECLARE_TLV_DB_SCALE(dac_dig_tlv, -95625, 375, 0); +/* D2S Volume */ +static const DECLARE_TLV_DB_SCALE(d2s_tlv, -1800, 300, 0); +/* ADC Volume */ +static const DECLARE_TLV_DB_SCALE(adc_tlv, -1200, 300, 0); +/* MUX Volume */ +static const DECLARE_TLV_DB_SCALE(mux_tlv, -600, 600, 0); +/* MIX Buf Volume */ +static const DECLARE_TLV_DB_SCALE(mix_buf_tlv, -1800, 300, 0); +/* HP Volume */ +static const DECLARE_TLV_DB_SCALE(hp_tlv, 0, 300, 0); +/* LINEOUT Volume */ +static const DECLARE_TLV_DB_SCALE(lineout_tlv, 0, 300, 0); +/* MIC Boost Volume */ +static const DECLARE_TLV_DB_RANGE(micboost_tlv, + 0, 2, TLV_DB_SCALE_ITEM(0, 600, 0), + 3, 4, TLV_DB_SCALE_ITEM(2400, 1200, 0), + 5, 7, TLV_DB_SCALE_ITEM(4200, 600, 0), + 8, 8, TLV_DB_SCALE_ITEM(-300, 0, 0), + 16, 16, TLV_DB_SCALE_ITEM(-600, 0, 0), + 24, 24, TLV_DB_SCALE_ITEM(-900, 0, 0) +); + +static const char * const mux_out_l_text[] = { "DIFF", "MIC1N", "MIC2N" }; +static const char * const mux_out_r_text[] = { "DIFF", "MIC2P", "MIC1P" }; +static const char * const mux_input_l_text[] = { "DIFF", "VINP1", "VINN1" }; +static const char * const mux_input_r_text[] = { "DIFF", "VINP2", "VINN2" }; + +static SOC_ENUM_SINGLE_DECL(mux_out_l_enum, RK730_MUXER_0, 2, mux_out_l_text); +static SOC_ENUM_SINGLE_DECL(mux_out_r_enum, RK730_MUXER_0, 6, mux_out_r_text); +static SOC_ENUM_SINGLE_DECL(mux_input_l_enum, RK730_ADC_PGA_BLOCK_0, + 4, mux_input_l_text); +static SOC_ENUM_SINGLE_DECL(mux_input_r_enum, RK730_ADC_PGA_BLOCK_1, + 4, mux_input_r_text); + +static const struct snd_kcontrol_new mux_out_l = + SOC_DAPM_ENUM("Left Out Mux", mux_out_l_enum); +static const struct snd_kcontrol_new mux_out_r = + SOC_DAPM_ENUM("Right Out Mux", mux_out_r_enum); +static const struct snd_kcontrol_new mux_input_l = + SOC_DAPM_ENUM("Left Input Mux", mux_input_l_enum); +static const struct snd_kcontrol_new mux_input_r = + SOC_DAPM_ENUM("Right Input Mux", mux_input_r_enum); + +static const struct snd_kcontrol_new mix_ctls[] = { + SOC_DAPM_SINGLE("Left Out Mux Switch", RK730_MUXER_0, 0, 1, 1), + SOC_DAPM_SINGLE("Right Out Mux Switch", RK730_MUXER_0, 4, 1, 1), +}; + +static const char * const adc_hpf_cutoff_text[] = { + "3.79Hz", "60Hz", "243Hz", "493Hz", +}; + +static const char * const dac_hpf_cutoff_text[] = { + "80Hz", "100Hz", "120Hz", "140Hz", +}; + +static SOC_ENUM_SINGLE_DECL(adc_hpf_cutoff_enum, RK730_DADC_HPF, + 4, adc_hpf_cutoff_text); +static SOC_ENUM_SINGLE_DECL(dac_hpf_cutoff_enum, RK730_DDAC_MUTE_MIXCTL, + 5, dac_hpf_cutoff_text); + +static const char * const chop_freq_text[] = { + "Disabled", "200kHz", "400kHz", "800kHz", +}; + +static SOC_ENUM_SINGLE_DECL(dac_ref_buf_chop_freq_enum, RK730_HK_TOP_1, + 6, chop_freq_text); +static SOC_ENUM_SINGLE_DECL(mic_chop_freq_enum, RK730_MIC_BOOST_3, + 6, chop_freq_text); +static SOC_ENUM_SINGLE_DECL(adc_pga_chop_freq_enum, RK730_ADC_PGA_BLOCK_1, + 6, chop_freq_text); +static SOC_ENUM_SINGLE_DECL(mux_out_chop_freq_enum, RK730_MUXER_1, + 0, chop_freq_text); +static SOC_ENUM_SINGLE_DECL(mix_chop_freq_enum, RK730_MIXER_2, + 6, chop_freq_text); +static SOC_ENUM_SINGLE_DECL(hp_lo_chop_freq_enum, RK730_HP_1, + 5, chop_freq_text); + +static const char * const micbias_volt_text[] = { + "2.0v", "2.2v", "2.5v", "2.8v", +}; + +static const char * const charge_pump_volt_text[] = { + "2.1v", "2.3v", "2.5v", "2.7v", +}; + +static SOC_ENUM_SINGLE_DECL(micbias_volt_enum, RK730_MIC_BIAS, + 2, micbias_volt_text); +static SOC_ENUM_SINGLE_DECL(charge_pump_volt_enum, RK730_CHARGE_PUMP, + 1, charge_pump_volt_text); + +static int rk730_adc_vol_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int val = snd_soc_component_read(component, mc->reg); + unsigned int sign = snd_soc_component_read(component, RK730_DADC_SR_ACL); + unsigned int mask = (1 << fls(mc->max)) - 1; + unsigned int shift = mc->shift; + int mid = mc->max / 2; + int uv; + + uv = (val >> shift) & mask; + sign &= RK730_DADC_SR_ACL_VOLL_POL_MASK; + if (sign) + uv = mid + uv; + else + uv = mid - uv; + + ucontrol->value.integer.value[0] = uv; + ucontrol->value.integer.value[1] = uv; + + return 0; +} + +static int rk730_adc_vol_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int reg = mc->reg; + unsigned int rreg = mc->rreg; + unsigned int shift = mc->shift; + unsigned int mask = (1 << fls(mc->max)) - 1; + unsigned int val, val_mask, sign, sign_mask; + int uv = ucontrol->value.integer.value[0]; + int min = mc->min; + int mid = mc->max / 2; + + sign_mask = RK730_DADC_SR_ACL_VOLL_POL_MASK | RK730_DADC_SR_ACL_VOLR_POL_MASK; + + if (uv > mid) { + sign = RK730_DADC_SR_ACL_VOLL_POS | RK730_DADC_SR_ACL_VOLR_POS; + uv = uv - mid; + } else { + sign = RK730_DADC_SR_ACL_VOLL_NEG | RK730_DADC_SR_ACL_VOLR_NEG; + uv = mid - uv; + } + + val = ((uv + min) & mask); + val_mask = mask << shift; + val = val << shift; + + snd_soc_component_update_bits(component, reg, val_mask, val); + snd_soc_component_update_bits(component, rreg, val_mask, val); + snd_soc_component_update_bits(component, RK730_DADC_SR_ACL, sign_mask, sign); + + return 1; +} + +static int rk730_dac_vol_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int val = snd_soc_component_read(component, mc->reg); + unsigned int sign = snd_soc_component_read(component, RK730_DDAC_SR_LMT); + unsigned int mask = (1 << fls(mc->max)) - 1; + unsigned int shift = mc->shift; + int mid = mc->max / 2; + int uv; + + uv = (val >> shift) & mask; + sign &= RK730_DDAC_SR_LMT_VOLL_POL_MASK; + if (sign) + uv = mid + uv; + else + uv = mid - uv; + + ucontrol->value.integer.value[0] = uv; + ucontrol->value.integer.value[1] = uv; + + return 0; +} + +static int rk730_dac_vol_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int reg = mc->reg; + unsigned int rreg = mc->rreg; + unsigned int shift = mc->shift; + unsigned int mask = (1 << fls(mc->max)) - 1; + unsigned int val, val_mask, sign, sign_mask; + int uv = ucontrol->value.integer.value[0]; + int min = mc->min; + int mid = mc->max / 2; + + sign_mask = RK730_DDAC_SR_LMT_VOLL_POL_MASK | RK730_DDAC_SR_LMT_VOLR_POL_MASK; + + if (uv > mid) { + sign = RK730_DDAC_SR_LMT_VOLL_POS | RK730_DDAC_SR_LMT_VOLR_POS; + uv = uv - mid; + } else { + sign = RK730_DDAC_SR_LMT_VOLL_NEG | RK730_DDAC_SR_LMT_VOLR_NEG; + uv = mid - uv; + } + + val = ((uv + min) & mask); + val_mask = mask << shift; + val = val << shift; + + snd_soc_component_update_bits(component, reg, val_mask, val); + snd_soc_component_update_bits(component, rreg, val_mask, val); + snd_soc_component_update_bits(component, RK730_DDAC_SR_LMT, sign_mask, sign); + + return 1; +} + +static int rk730_cp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + if (SND_SOC_DAPM_EVENT_ON(event)) + usleep_range(5000, 5100); + + return 0; +} + +static int rk730_pll_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + if (SND_SOC_DAPM_EVENT_ON(event)) + snd_soc_component_write(component, RK730_SYSPLL_0, 0x00); + else + snd_soc_component_write(component, RK730_SYSPLL_0, 0xff); + + return 0; +} + +static int rk730_adc_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + if (SND_SOC_DAPM_EVENT_ON(event)) { + snd_soc_component_update_bits(component, RK730_ADC_0, + RK730_ADC_0_DEM_EN_MASK, + RK730_ADC_0_DEM_EN); + snd_soc_component_update_bits(component, RK730_DTOP_DIGEN_CLKE, + RK730_DTOP_DIGEN_CLKE_ADC_CKE_MASK | + RK730_DTOP_DIGEN_CLKE_I2STX_CKE_MASK | + RK730_DTOP_DIGEN_CLKE_ADC_EN_MASK | + RK730_DTOP_DIGEN_CLKE_I2STX_EN_MASK, + RK730_DTOP_DIGEN_CLKE_ADC_CKE_EN | + RK730_DTOP_DIGEN_CLKE_I2STX_CKE_EN | + RK730_DTOP_DIGEN_CLKE_ADC_EN | + RK730_DTOP_DIGEN_CLKE_I2STX_EN); + usleep_range(20000, 21000); + snd_soc_component_update_bits(component, RK730_DI2S_TXCR_3_TXCMD, + RK730_DI2S_TXCR_3_TXCMD_TXS_MASK, + RK730_DI2S_TXCR_3_TXCMD_TXS_EN); + } else { + snd_soc_component_update_bits(component, RK730_DI2S_TXCR_3_TXCMD, + RK730_DI2S_TXCR_3_TXCMD_TXS_MASK, + RK730_DI2S_TXCR_3_TXCMD_TXS_DIS); + snd_soc_component_update_bits(component, RK730_ADC_0, + RK730_ADC_0_DEM_EN_MASK, + RK730_ADC_0_DEM_DIS); + snd_soc_component_update_bits(component, RK730_DTOP_DIGEN_CLKE, + RK730_DTOP_DIGEN_CLKE_ADC_CKE_MASK | + RK730_DTOP_DIGEN_CLKE_I2STX_CKE_MASK | + RK730_DTOP_DIGEN_CLKE_ADC_EN_MASK | + RK730_DTOP_DIGEN_CLKE_I2STX_EN_MASK, + RK730_DTOP_DIGEN_CLKE_ADC_CKE_DIS | + RK730_DTOP_DIGEN_CLKE_I2STX_CKE_DIS | + RK730_DTOP_DIGEN_CLKE_ADC_DIS | + RK730_DTOP_DIGEN_CLKE_I2STX_DIS); + } + + return 0; +} + +static int rk730_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + + if (SND_SOC_DAPM_EVENT_ON(event)) { + snd_soc_component_update_bits(component, RK730_DTOP_DIGEN_CLKE, + RK730_DTOP_DIGEN_CLKE_DAC_CKE_MASK | + RK730_DTOP_DIGEN_CLKE_I2SRX_CKE_MASK | + RK730_DTOP_DIGEN_CLKE_DAC_EN_MASK | + RK730_DTOP_DIGEN_CLKE_I2SRX_EN_MASK, + RK730_DTOP_DIGEN_CLKE_DAC_CKE_EN | + RK730_DTOP_DIGEN_CLKE_I2SRX_CKE_EN | + RK730_DTOP_DIGEN_CLKE_DAC_EN | + RK730_DTOP_DIGEN_CLKE_I2SRX_EN); + snd_soc_component_update_bits(component, RK730_DI2S_RXCMD_TSD, + RK730_DI2S_RXCMD_TSD_RXS_MASK, + RK730_DI2S_RXCMD_TSD_RXS_EN); + } else { + snd_soc_component_update_bits(component, RK730_DI2S_RXCMD_TSD, + RK730_DI2S_RXCMD_TSD_RXS_MASK, + RK730_DI2S_RXCMD_TSD_RXS_DIS); + snd_soc_component_update_bits(component, RK730_DTOP_DIGEN_CLKE, + RK730_DTOP_DIGEN_CLKE_DAC_CKE_MASK | + RK730_DTOP_DIGEN_CLKE_I2SRX_CKE_MASK | + RK730_DTOP_DIGEN_CLKE_DAC_EN_MASK | + RK730_DTOP_DIGEN_CLKE_I2SRX_EN_MASK, + RK730_DTOP_DIGEN_CLKE_DAC_CKE_DIS | + RK730_DTOP_DIGEN_CLKE_I2SRX_CKE_DIS | + RK730_DTOP_DIGEN_CLKE_DAC_DIS | + RK730_DTOP_DIGEN_CLKE_I2SRX_DIS); + } + + return 0; +} + +static int rk730_mux_out_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct rk730_priv *rk730 = snd_soc_component_get_drvdata(component); + unsigned int val; + + if (SND_SOC_DAPM_EVENT_ON(event)) + val = atomic_inc_return(&rk730->mix_mode); + else + val = atomic_dec_return(&rk730->mix_mode); + + snd_soc_component_update_bits(component, RK730_MIXER_2, + RK730_MIXER_2_MIX_R_MODE_MASK | + RK730_MIXER_2_MIX_L_MODE_MASK, + RK730_MIXER_2_MIX_R_MODE(val) | + RK730_MIXER_2_MIX_L_MODE(val)); + return 0; +} + +static const struct snd_kcontrol_new rk730_snd_controls[] = { + SOC_DOUBLE_R_TLV("ADC Volume", RK730_ADC_PGA_BLOCK_0, RK730_ADC_PGA_BLOCK_1, + 1, 0x7, 1, adc_tlv), + SOC_DOUBLE_R_TLV("D2S Volume", RK730_DAC_1, RK730_DAC_2, + 1, 0x6, 1, d2s_tlv), + + SOC_DOUBLE_R_TLV("MIC1 Boost Volume", RK730_MIC_BOOST_0, RK730_MIC_BOOST_1, + 1, 0x18, 0, micboost_tlv), + SOC_DOUBLE_R_TLV("MIC2 Boost Volume", RK730_MIC_BOOST_2, RK730_MIC_BOOST_3, + 1, 0x18, 0, micboost_tlv), + + SOC_DOUBLE_TLV("Out Mux Volume", RK730_MUXER_0, 1, 5, 0x1, 0, mux_tlv), + + SOC_SINGLE_TLV("Left Out Mux -> Left Out Mixer Volume", + RK730_MIXER_0, 1, 0x6, 1, mix_buf_tlv), + SOC_SINGLE_TLV("Left Out Mux -> Right Out Mixer Volume", + RK730_MIXER_0, 5, 0x6, 1, mix_buf_tlv), + SOC_SINGLE_TLV("Right Out Mux -> Left Out Mixer Volume", + RK730_MIXER_1, 1, 0x6, 1, mix_buf_tlv), + SOC_SINGLE_TLV("Right Out Mux -> Right Out Mixer Volume", + RK730_MIXER_1, 5, 0x6, 1, mix_buf_tlv), + + SOC_SINGLE_TLV("HP Volume", RK730_HP_0, 6, 0x3, 0, hp_tlv), + SOC_SINGLE_TLV("Line Out Volume", RK730_LINEOUT_1, 2, 0x3, 0, lineout_tlv), + + SOC_DOUBLE_R_EXT_TLV("ADC Digital Volume", + RK730_DADC_VOLL, RK730_DADC_VOLR, 0, 0x1fe, 0, + rk730_adc_vol_get, + rk730_adc_vol_put, + adc_dig_tlv), + SOC_DOUBLE_R_EXT_TLV("DAC Digital Volume", + RK730_DDAC_VOLL, RK730_DDAC_VOLR, 0, 0x1fe, 0, + rk730_dac_vol_get, + rk730_dac_vol_put, + dac_dig_tlv), + + SOC_ENUM("ADC HPF Cutoff", adc_hpf_cutoff_enum), + SOC_ENUM("DAC HPF Cutoff", dac_hpf_cutoff_enum), + SOC_ENUM("DAC Ref Buf Chop Freq", dac_ref_buf_chop_freq_enum), + SOC_ENUM("MIC Chop Freq", mic_chop_freq_enum), + SOC_ENUM("ADC PGA Chop Freq", adc_pga_chop_freq_enum), + SOC_ENUM("Out Mux Chop Freq", mux_out_chop_freq_enum), + SOC_ENUM("Mixer Chop Freq", mix_chop_freq_enum), + SOC_ENUM("HP / Lineout Chop Freq", hp_lo_chop_freq_enum), + SOC_ENUM("Mic Bias Volt", micbias_volt_enum), + SOC_ENUM("Charge Pump Volt", charge_pump_volt_enum), + + SOC_SINGLE("ADCL HPF Switch", RK730_DADC_HPF, 7, 1, 0), + SOC_SINGLE("ADCR HPF Switch", RK730_DADC_HPF, 6, 1, 0), + SOC_SINGLE("DAC HPF Switch", RK730_DDAC_MUTE_MIXCTL, 7, 1, 0), + SOC_SINGLE("ADC Volume Bypass Switch", RK730_DTOP_VUCTL, 7, 1, 0), + SOC_SINGLE("DAC Volume Bypass Switch", RK730_DTOP_VUCTL, 6, 1, 0), + SOC_SINGLE("ADC Fade Switch", RK730_DTOP_VUCTL, 5, 1, 0), + SOC_SINGLE("DAC Fade Switch", RK730_DTOP_VUCTL, 4, 1, 0), + SOC_SINGLE("ADC Zero Crossing Switch", RK730_DTOP_VUCTL, 1, 1, 0), + SOC_SINGLE("DAC Zero Crossing Switch", RK730_DTOP_VUCTL, 0, 1, 0), + SOC_SINGLE("MIC1N / MIC2P Exchanged Switch", RK730_MIC_BOOST_2, 7, 1, 0), +}; + +static const struct snd_soc_dapm_widget rk730_dapm_widgets[] = { + SND_SOC_DAPM_SUPPLY_S("ANA LDO", 0, RK730_LDO, 7, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("OSC CLK", 1, RK730_HK_TOP_2, 4, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("VAG BUF", 1, RK730_HK_TOP_2, 2, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("ADC BUF", 1, RK730_HK_TOP_2, 1, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("DAC BUF", 1, RK730_HK_TOP_2, 0, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("MICBIAS", 1, RK730_MIC_BIAS, 0, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("Charge Pump", 1, RK730_CHARGE_PUMP, 0, 0, + rk730_cp_event, SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_SUPPLY_S("PLL", 2, SND_SOC_NOPM, 0, 0, rk730_pll_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SUPPLY_S("DAC Bias", 2, RK730_DAC_0, 2, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("HP Bias", 2, RK730_HP_0, 5, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("Line Out Bias", 2, RK730_LINEOUT_1, 7, 1, NULL, 0), + + SND_SOC_DAPM_ADC_E("ADCL", "HiFi Capture", RK730_ADC_0, 0, 1, + rk730_adc_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("ADCR", "HiFi Capture", RK730_ADC_0, 1, 1, + rk730_adc_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_DAC_E("DACL", "HiFi Playback", RK730_DAC_0, 0, 1, + rk730_dac_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("DACR", "HiFi Playback", RK730_DAC_0, 1, 1, + rk730_dac_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_PGA("ADCL PGA", RK730_ADC_PGA_BLOCK_0, 0, 1, NULL, 0), + SND_SOC_DAPM_PGA("ADCR PGA", RK730_ADC_PGA_BLOCK_1, 0, 1, NULL, 0), + + SND_SOC_DAPM_PGA("D2SL", RK730_DAC_1, 0, 1, NULL, 0), + SND_SOC_DAPM_PGA("D2SR", RK730_DAC_2, 0, 1, NULL, 0), + + SND_SOC_DAPM_PGA("MIC1P", RK730_MIC_BOOST_0, 0, 1, NULL, 0), + SND_SOC_DAPM_PGA("MIC1N", RK730_MIC_BOOST_1, 0, 1, NULL, 0), + SND_SOC_DAPM_PGA("MIC2P", RK730_MIC_BOOST_2, 0, 1, NULL, 0), + SND_SOC_DAPM_PGA("MIC2N", RK730_MIC_BOOST_3, 0, 1, NULL, 0), + SND_SOC_DAPM_PGA("DIFFL", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("DIFFR", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_PGA("HP Out", RK730_HP_0, 2, 1, NULL, 0), + SND_SOC_DAPM_PGA("HP Out Stage", RK730_HP_0, 3, 1, NULL, 0), + SND_SOC_DAPM_PGA("Line Out", RK730_LINEOUT_0, 2, 1, NULL, 0), + SND_SOC_DAPM_PGA("Line Out Stage", RK730_LINEOUT_0, 3, 1, NULL, 0), + + SND_SOC_DAPM_MUX_E("Left Out Mux", SND_SOC_NOPM, 0, 0, &mux_out_l, + rk730_mux_out_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("Right Out Mux", SND_SOC_NOPM, 0, 0, &mux_out_r, + rk730_mux_out_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX("Left Input Mux", SND_SOC_NOPM, 0, 0, &mux_input_l), + SND_SOC_DAPM_MUX("Right Input Mux", SND_SOC_NOPM, 0, 0, &mux_input_r), + + SND_SOC_DAPM_MIXER("Left Out Mixer", RK730_MIXER_2, 0, 1, + mix_ctls, ARRAY_SIZE(mix_ctls)), + SND_SOC_DAPM_MIXER("Right Out Mixer", RK730_MIXER_2, 3, 1, + mix_ctls, ARRAY_SIZE(mix_ctls)), + + SND_SOC_DAPM_OUTPUT("HPL"), + SND_SOC_DAPM_OUTPUT("HPR"), + SND_SOC_DAPM_OUTPUT("LINEOUTL"), + SND_SOC_DAPM_OUTPUT("LINEOUTR"), + + SND_SOC_DAPM_INPUT("MIC1"), + SND_SOC_DAPM_INPUT("MIC2"), +}; + +static const struct snd_soc_dapm_route rk730_dapm_routes[] = { + { "DACL", NULL, "ANA LDO" }, + { "DACR", NULL, "ANA LDO" }, + { "DACL", NULL, "OSC CLK" }, + { "DACR", NULL, "OSC CLK" }, + { "DACL", NULL, "VAG BUF" }, + { "DACR", NULL, "VAG BUF" }, + { "DACL", NULL, "DAC BUF" }, + { "DACR", NULL, "DAC BUF" }, + { "DACL", NULL, "PLL" }, + { "DACR", NULL, "PLL" }, + { "DACL", NULL, "DAC Bias" }, + { "DACR", NULL, "DAC Bias" }, + + { "D2SL", NULL, "DACL" }, + { "D2SR", NULL, "DACR" }, + + { "Left Out Mixer", NULL, "D2SL" }, + { "Left Out Mixer", "Left Out Mux Switch", "Left Out Mux" }, + { "Left Out Mixer", "Right Out Mux Switch", "Right Out Mux" }, + { "Right Out Mixer", NULL, "D2SR" }, + { "Right Out Mixer", "Left Out Mux Switch", "Left Out Mux" }, + { "Right Out Mixer", "Right Out Mux Switch", "Right Out Mux" }, + + { "Left Out Mux", "DIFF", "DIFFL" }, + { "Left Out Mux", "MIC1N", "MIC1N" }, + { "Left Out Mux", "MIC2N", "MIC2N" }, + + { "Right Out Mux", "DIFF", "DIFFR" }, + { "Right Out Mux", "MIC1P", "MIC1P" }, + { "Right Out Mux", "MIC2P", "MIC2P" }, + + { "HP Out", NULL, "HP Bias" }, + { "HP Out", NULL, "HP Bias" }, + { "Line Out", NULL, "Line Out Bias" }, + + { "HP Out", NULL, "Left Out Mixer" }, + { "HP Out", NULL, "Right Out Mixer" }, + { "Line Out", NULL, "Left Out Mixer" }, + { "Line Out", NULL, "Right Out Mixer" }, + + { "HP Out Stage", NULL, "HP Out" }, + { "Line Out Stage", NULL, "Line Out" }, + + { "HPL", NULL, "HP Out Stage" }, + { "HPR", NULL, "HP Out Stage" }, + { "HPL", NULL, "Charge Pump" }, + { "HPR", NULL, "Charge Pump" }, + + { "LINEOUTL", NULL, "Line Out Stage" }, + { "LINEOUTR", NULL, "Line Out Stage" }, + { "LINEOUTL", NULL, "Charge Pump" }, + { "LINEOUTR", NULL, "Charge Pump" }, + + { "ADCL", NULL, "ANA LDO" }, + { "ADCR", NULL, "ANA LDO" }, + { "ADCL", NULL, "OSC CLK" }, + { "ADCR", NULL, "OSC CLK" }, + { "ADCL", NULL, "ADC BUF" }, + { "ADCR", NULL, "ADC BUF" }, + { "ADCL", NULL, "VAG BUF" }, + { "ADCR", NULL, "VAG BUF" }, + { "ADCL", NULL, "PLL" }, + { "ADCR", NULL, "PLL" }, + + { "ADCL", NULL, "ADCL PGA" }, + { "ADCR", NULL, "ADCR PGA" }, + { "ADCL PGA", NULL, "Left Input Mux" }, + { "ADCR PGA", NULL, "Right Input Mux" }, + + { "Left Input Mux", "DIFF", "DIFFL" }, + { "Left Input Mux", "VINP1", "MIC1P" }, + { "Left Input Mux", "VINN1", "MIC1N" }, + { "Right Input Mux", "DIFF", "DIFFR" }, + { "Right Input Mux", "VINP2", "MIC2P" }, + { "Right Input Mux", "VINN2", "MIC2N" }, + + { "DIFFL", NULL, "MIC1P" }, + { "DIFFL", NULL, "MIC1N" }, + { "DIFFR", NULL, "MIC2P" }, + { "DIFFR", NULL, "MIC2N" }, + + { "MIC1P", NULL, "MIC1" }, + { "MIC1N", NULL, "MIC1" }, + { "MIC2P", NULL, "MIC2" }, + { "MIC2N", NULL, "MIC2" }, + + { "MIC1", NULL, "MICBIAS" }, + { "MIC2", NULL, "MICBIAS" }, +}; + +static unsigned int samplerate_to_bit(unsigned int samplerate) +{ + switch (samplerate) { + case 8000: + case 11025: + case 12000: + return 0; + case 16000: + case 22050: + case 24000: + return 1; + case 32000: + case 44100: + case 48000: + return 2; + case 64000: + case 88200: + case 96000: + return 3; + case 128000: + case 176400: + case 192000: + return 4; + default: + return 2; + } +} + +static int rk730_dai_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; + unsigned int width, rate; + + width = min(params_width(params), 24); + rate = samplerate_to_bit(params_rate(params)); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + snd_soc_component_update_bits(component, RK730_DI2S_RXCR_2, + RK730_DI2S_XCR2_VDW_MASK, + RK730_DI2S_XCR2_VDW(width)); + snd_soc_component_update_bits(component, RK730_DDAC_SR_LMT, + RK730_DDAC_SR_LMT_SRT_MASK, + RK730_DDAC_SR_LMT_SRT(rate)); + } else { + snd_soc_component_update_bits(component, RK730_DI2S_TXCR_2, + RK730_DI2S_XCR2_VDW_MASK, + RK730_DI2S_XCR2_VDW(width)); + snd_soc_component_update_bits(component, RK730_DADC_SR_ACL, + RK730_DADC_SR_ACL_SRT_MASK, + RK730_DADC_SR_ACL_SRT(rate)); + } + + return 0; +} + +static int rk730_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + snd_soc_component_update_bits(component, RK730_DI2S_CKM, + RK730_DI2S_CKM_MST_MASK, + RK730_DI2S_CKM_MST_SLAVE); + break; + case SND_SOC_DAIFMT_CBM_CFM: + snd_soc_component_update_bits(component, RK730_DI2S_CKM, + RK730_DI2S_CKM_MST_MASK, + RK730_DI2S_CKM_MST_MASTER); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int rk730_dai_mute(struct snd_soc_dai *codec_dai, int mute, int direction) +{ + struct snd_soc_component *component = codec_dai->component; + + if (mute) + snd_soc_component_update_bits(component, RK730_DDAC_MUTE_MIXCTL, + RK730_DDAC_MUTE_MIXCTL_MUTE_MASK, + RK730_DDAC_MUTE_MIXCTL_MUTE); + else + snd_soc_component_update_bits(component, RK730_DDAC_MUTE_MIXCTL, + RK730_DDAC_MUTE_MIXCTL_MUTE_MASK, + RK730_DDAC_MUTE_MIXCTL_UNMUTE); + + return 0; +} + +static int rk730_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct rk730_priv *rk730 = snd_soc_component_get_drvdata(component); + + switch (level) { + case SND_SOC_BIAS_ON: + break; + + case SND_SOC_BIAS_PREPARE: + /* + * SND_SOC_BIAS_PREPARE is called while preparing for a + * transition to ON or away from ON. If current bias_level + * is SND_SOC_BIAS_ON, then it is preparing for a transition + * away from ON. Disable the clock in that case, otherwise + * enable it. + */ + if (!IS_ERR(rk730->mclk)) { + if (snd_soc_component_get_bias_level(component) == + SND_SOC_BIAS_ON) + clk_disable_unprepare(rk730->mclk); + else + clk_prepare_enable(rk730->mclk); + } + break; + + case SND_SOC_BIAS_STANDBY: + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) + regcache_sync(rk730->regmap); + break; + + case SND_SOC_BIAS_OFF: + regcache_mark_dirty(rk730->regmap); + break; + } + return 0; +} + +#define RK730_RATES SNDRV_PCM_RATE_8000_192000 +#define RK730_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +static const struct snd_soc_dai_ops rk730_dai_ops = { + .set_fmt = rk730_dai_set_fmt, + .hw_params = rk730_dai_hw_params, + .mute_stream = rk730_dai_mute, + .no_capture_mute = 1, +}; + +static struct snd_soc_dai_driver rk730_dai = { + .name = "HiFi", + .playback = { + .stream_name = "HiFi Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RK730_RATES, + .formats = RK730_FORMATS, + }, + .capture = { + .stream_name = "HiFi Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RK730_RATES, + .formats = RK730_FORMATS, + }, + .ops = &rk730_dai_ops, +}; + +static int rk730_reset(struct snd_soc_component *component) +{ + struct rk730_priv *rk730 = snd_soc_component_get_drvdata(component); + + clk_prepare_enable(rk730->mclk); + snd_soc_component_write(component, RK730_DTOP_LPT_SRST, 0x40); + udelay(10); + /* WA: Initial micbias default, ADC stopped with micbias(>2.5v) */ + snd_soc_component_update_bits(component, RK730_MIC_BIAS, + RK730_MIC_BIAS_VOLT_MASK, + RK730_MIC_BIAS_VOLT_2_2V); + /* PF: Use the maximum bias current for better performance */ + snd_soc_component_update_bits(component, RK730_HK_TOP_1, + RK730_HK_TOP_1_IBIAS_STD_SEL_MASK | + RK730_HK_TOP_1_IBIAS_GAIN_SEL_MASK, + RK730_HK_TOP_1_IBIAS_STD_SEL_27_5UA | + RK730_HK_TOP_1_IBIAS_GAIN_SEL_200); + /* PF: Use the chop 400kHz for better ADC noise performance */ + snd_soc_component_update_bits(component, RK730_MIC_BOOST_3, + RK730_MIC_BOOST_3_MIC_CHOP_MASK, + RK730_MIC_BOOST_3_MIC_CHOP(RK730_CHOP_FREQ_400KHZ)); + snd_soc_component_update_bits(component, RK730_ADC_PGA_BLOCK_1, + RK730_ADC_PGA_BLOCK_1_PGA_CHOP_MASK, + RK730_ADC_PGA_BLOCK_1_PGA_CHOP(RK730_CHOP_FREQ_400KHZ)); + + clk_disable_unprepare(rk730->mclk); + + return 0; +} + +static int rk730_probe(struct snd_soc_component *component) +{ + struct rk730_priv *rk730 = snd_soc_component_get_drvdata(component); + int ret = 0; + + regcache_mark_dirty(rk730->regmap); + + /* initialize private data */ + atomic_set(&rk730->mix_mode, RK730_MIX_MODE_1_PATH); + + ret = snd_soc_component_read(component, RK730_HK_TOP_0); + if (ret < 0) { + dev_err(component->dev, "Failed to read register: %d\n", ret); + return ret; + } + + rk730_reset(component); + + return ret; +} + +static const struct snd_soc_component_driver rk730_component_driver = { + .probe = rk730_probe, + .set_bias_level = rk730_set_bias_level, + .controls = rk730_snd_controls, + .num_controls = ARRAY_SIZE(rk730_snd_controls), + .dapm_widgets = rk730_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rk730_dapm_widgets), + .dapm_routes = rk730_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(rk730_dapm_routes), + .suspend_bias_off = 1, + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, + .non_legacy_dai_naming = 1, +}; + +static const struct reg_default rk730_reg_defaults[] = { + { 0x00, 0x40 }, + { 0x02, 0x17 }, + { 0x05, 0x03 }, + { 0x06, 0x22 }, + { 0x07, 0x02 }, + { 0x08, 0x07 }, + { 0x09, 0x01 }, + { 0x0a, 0x01 }, + { 0x0b, 0x01 }, + { 0x0c, 0x01 }, + { 0x0d, 0x01 }, + { 0x0e, 0x01 }, + { 0x0f, 0x07 }, + { 0x10, 0x07 }, + { 0x11, 0xff }, + { 0x12, 0x07 }, + { 0x13, 0x54 }, + { 0x14, 0x04 }, + { 0x15, 0x23 }, + { 0x16, 0x35 }, + { 0x17, 0x67 }, + { 0x18, 0x1e }, + { 0x19, 0xc0 }, + { 0x1a, 0x13 }, + { 0x1b, 0x04 }, + { 0x1c, 0x20 }, + { 0x1f, 0x90 }, + { 0x20, 0x11 }, + { 0x21, 0x09 }, + { 0x22, 0x33 }, + { 0x24, 0x11 }, + { 0x25, 0x11 }, + { 0x26, 0x09 }, + { 0x27, 0x02 }, + { 0x28, 0x2c }, + { 0x2a, 0x0c }, + { 0x2b, 0x80 }, + { 0x40, 0x03 }, + { 0x42, 0x20 }, + { 0x47, 0xe6 }, + { 0x48, 0xd0 }, + { 0x49, 0x17 }, + { 0x4a, 0x26 }, + { 0x4b, 0x01 }, + { 0x4c, 0x05 }, + { 0x4d, 0x0e }, + { 0x4e, 0x09 }, + { 0x4f, 0x02 }, + { 0x5b, 0xe6 }, + { 0x5c, 0xd0 }, + { 0x5d, 0x17 }, + { 0x5e, 0x26 }, + { 0x5f, 0x01 }, + { 0x60, 0x05 }, + { 0x61, 0x0e }, + { 0x62, 0x09 }, + { 0x63, 0x20 }, + { 0x66, 0x01 }, + { 0x69, 0x17 }, + { 0x6c, 0x17 }, +}; + +static bool rk730_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case RK730_DTOP_LPT_SRST: + return true; + default: + return false; + } +} + +static const struct regmap_config rk730_regmap = { + .reg_bits = 8, + .val_bits = 8, + .volatile_reg = rk730_volatile_register, + .max_register = RK730_DAC_ATTN, + .reg_defaults = rk730_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(rk730_reg_defaults), + .cache_type = REGCACHE_RBTREE, +}; + +static int rk730_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct rk730_priv *rk730; + int ret; + + rk730 = devm_kzalloc(&i2c->dev, sizeof(struct rk730_priv), GFP_KERNEL); + if (!rk730) + return -ENOMEM; + + rk730->regmap = devm_regmap_init_i2c(i2c, &rk730_regmap); + if (IS_ERR(rk730->regmap)) + return PTR_ERR(rk730->regmap); + + rk730->mclk = devm_clk_get(&i2c->dev, "mclk"); + if (IS_ERR(rk730->mclk)) + return PTR_ERR(rk730->mclk); + + i2c_set_clientdata(i2c, rk730); + + ret = devm_snd_soc_register_component(&i2c->dev, + &rk730_component_driver, &rk730_dai, 1); + return ret; +} + +static const struct i2c_device_id rk730_i2c_id[] = { + { "rk730", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, rk730_i2c_id); + +#if defined(CONFIG_OF) +static const struct of_device_id rk730_of_match[] = { + { .compatible = "rockchip,rk730" }, + { } +}; +MODULE_DEVICE_TABLE(of, rk730_of_match); +#endif + +static struct i2c_driver rk730_i2c_driver = { + .driver = { + .name = "rk730", + .of_match_table = of_match_ptr(rk730_of_match), + }, + .probe = rk730_i2c_probe, + .id_table = rk730_i2c_id, +}; + +module_i2c_driver(rk730_i2c_driver); + +MODULE_DESCRIPTION("ASoC RK730 driver"); +MODULE_AUTHOR("Sugar Zhang "); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/rk730.h b/sound/soc/codecs/rk730.h new file mode 100644 index 000000000000..b06f81828bb4 --- /dev/null +++ b/sound/soc/codecs/rk730.h @@ -0,0 +1,239 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * rk730.h -- RK730 ALSA SoC Audio driver + * + * Copyright (C) 2022 Rockchip Electronics Co.,Ltd + */ + +#ifndef _RK730_H +#define _RK730_H + +/* RK730 Analog Registers Definition */ +#define RK730_HK_TOP_0 0x00 +#define RK730_HK_TOP_1 0x01 +#define RK730_HK_TOP_2 0x02 +#define RK730_HK_TRIM_0 0x03 +#define RK730_HK_TRIM_1 0x04 +#define RK730_ADC_0 0x05 +#define RK730_ADC_1 0x06 +#define RK730_ADC_2 0x07 +#define RK730_DAC_0 0x08 +#define RK730_DAC_1 0x09 +#define RK730_DAC_2 0x0a +#define RK730_MIC_BOOST_0 0x0b +#define RK730_MIC_BOOST_1 0x0c +#define RK730_MIC_BOOST_2 0x0d +#define RK730_MIC_BOOST_3 0x0e +#define RK730_ADC_PGA_BLOCK_0 0x0f +#define RK730_ADC_PGA_BLOCK_1 0x10 +#define RK730_SYSPLL_0 0x11 +#define RK730_SYSPLL_1 0x12 +#define RK730_SYSPLL_2 0x13 +#define RK730_SYSPLL_3 0x14 +#define RK730_SYSPLL_LOOP_0 0x15 +#define RK730_SYSPLL_LOOP_1 0x16 +#define RK730_SYSPLL_LOOP_2 0x17 +#define RK730_SYSPLL_LOOP_3 0x18 +#define RK730_SYSPLL_RVCO_0 0x19 +#define RK730_SYSPLL_RVCO_1 0x1a +#define RK730_SYSPLL_RVCO_2 0x1b +#define RK730_SYSPLL_RVCO_3 0x1c +#define RK730_SYSPLL_FRACT_0 0x1d +#define RK730_SYSPLL_FRACT_1 0x1e +#define RK730_SYSPLL_FRACT_2 0x1f +#define RK730_LDO 0x20 +#define RK730_MIC_BIAS 0x21 +#define RK730_MUXER_0 0x22 +#define RK730_MUXER_1 0x23 +#define RK730_MIXER_0 0x24 +#define RK730_MIXER_1 0x25 +#define RK730_MIXER_2 0x26 +#define RK730_CHARGE_PUMP 0x27 +#define RK730_HP_0 0x28 +#define RK730_HP_1 0x29 +#define RK730_LINEOUT_0 0x2a +#define RK730_LINEOUT_1 0x2b + +/* RK730 Digital Registers Definition */ +#define RK730_DTOP_VUCTL 0x40 +#define RK730_DTOP_VUCTIME 0x41 +#define RK730_DTOP_LPT_SRST 0x42 +#define RK730_DTOP_DIGEN_CLKE 0x43 +#define RK730_DADC_VOLL 0x44 +#define RK730_DADC_VOLR 0x45 +#define RK730_DADC_SR_ACL 0x46 +#define RK730_DADC_PR_0 0x47 +#define RK730_DADC_PR_1 0x48 +#define RK730_DADC_PR_2 0x49 +#define RK730_DADC_PR_3 0x4a +#define RK730_DADC_NG_0 0x4b +#define RK730_DADC_NG_1 0x4c +#define RK730_DADC_NG_2 0x4d +#define RK730_DADC_NG_3 0x4e +#define RK730_DADC_CICCOMP 0x4f +#define RK730_DADC_HPF 0x50 +#define RK730_DADC_RVOLL 0x51 +#define RK730_DADC_RVOLR 0x52 +#define RK730_DMIC_LMT_1 0x53 +#define RK730_DMIC_LMT_2 0x54 +#define RK730_DMIC_NG_1 0x55 +#define RK730_DMIC_NG_2 0x56 +#define RK730_DDAC_POPD_DACST 0x57 +#define RK730_DDAC_VOLL 0x58 +#define RK730_DDAC_VOLR 0x59 +#define RK730_DDAC_SR_LMT 0x5a +#define RK730_DDAC_PR_0 0x5b +#define RK730_DDAC_PR_1 0x5c +#define RK730_DDAC_PR_2 0x5d +#define RK730_DDAC_PR_3 0x5e +#define RK730_DDAC_NG_0 0x5f +#define RK730_DDAC_NG_1 0x60 +#define RK730_DDAC_NG_2 0x61 +#define RK730_DDAC_NG_3 0x62 +#define RK730_DDAC_MUTE_MIXCTL 0x63 +#define RK730_DDAC_RVOLL 0x64 +#define RK730_DDAC_RVOLR 0x65 +#define RK730_DI2S_CKM 0x66 +#define RK730_DI2S_RSD 0x67 +#define RK730_DI2S_RXCR_1 0x68 +#define RK730_DI2S_RXCR_2 0x69 +#define RK730_DI2S_RXCMD_TSD 0x6a +#define RK730_DI2S_TXCR_1 0x6b +#define RK730_DI2S_TXCR_2 0x6c +#define RK730_DI2S_TXCR_3_TXCMD 0x6d +#define RK730_DAC_ATTN 0x6e + +/* RK730_HK_TOP_1 */ +#define RK730_HK_TOP_1_DAC_REF_BUF_CHOP_MASK GENMASK(7, 6) +#define RK730_HK_TOP_1_DAC_REF_BUF_CHOP(x) ((x) << 6) +#define RK730_HK_TOP_1_IBIAS_STD_SEL_MASK GENMASK(5, 4) +#define RK730_HK_TOP_1_IBIAS_STD_SEL_27_5UA (3 << 4) +#define RK730_HK_TOP_1_IBIAS_STD_SEL_23_5UA (2 << 4) +#define RK730_HK_TOP_1_IBIAS_STD_SEL_20UA (1 << 4) +#define RK730_HK_TOP_1_IBIAS_STD_SEL_16_5UA (0 << 4) +#define RK730_HK_TOP_1_IBIAS_GAIN_SEL_MASK GENMASK(3, 0) +#define RK730_HK_TOP_1_IBIAS_GAIN_SEL_200 8 +#define RK730_HK_TOP_1_IBIAS_GAIN_SEL_143 9 +#define RK730_HK_TOP_1_IBIAS_GAIN_SEL_120 10 +#define RK730_HK_TOP_1_IBIAS_GAIN_SEL_100 0 +#define RK730_HK_TOP_1_IBIAS_GAIN_SEL_71_5 1 +#define RK730_HK_TOP_1_IBIAS_GAIN_SEL_62_5 2 +#define RK730_HK_TOP_1_IBIAS_GAIN_SEL_50 3 +#define RK730_HK_TOP_1_IBIAS_GAIN_SEL_38_2 6 +#define RK730_HK_TOP_1_IBIAS_GAIN_SEL_32 7 + +/* RK730_ADC_0 */ +#define RK730_ADC_0_DEM_EN_MASK BIT(3) +#define RK730_ADC_0_DEM_EN BIT(3) +#define RK730_ADC_0_DEM_DIS 0 + +/* RK730_MIC_BOOST_3 */ +#define RK730_MIC_BOOST_3_MIC_CHOP_MASK GENMASK(7, 6) +#define RK730_MIC_BOOST_3_MIC_CHOP(x) ((x) << 6) + +/* RK730_ADC_PGA_BLOCK_1 */ +#define RK730_ADC_PGA_BLOCK_1_PGA_CHOP_MASK GENMASK(7, 6) +#define RK730_ADC_PGA_BLOCK_1_PGA_CHOP(x) ((x) << 6) + +/* RK730_MIC_BIAS */ +#define RK730_MIC_BIAS_VOLT_MASK GENMASK(3, 2) +#define RK730_MIC_BIAS_VOLT_2_8V (3 << 2) +#define RK730_MIC_BIAS_VOLT_2_5V (2 << 2) +#define RK730_MIC_BIAS_VOLT_2_2V (1 << 2) +#define RK730_MIC_BIAS_VOLT_2_0V (0 << 2) + +/* RK730_MUXER_1 */ +#define RK730_MUXER_1_MUX_OUT_CHOP_MASK GENMASK(1, 0) +#define RK730_MUXER_1_MUX_OUT_CHOP(x) ((x) << 0) + +/* RK730_MIXER_2 */ +#define RK730_MIXER_2_MIX_CHOP_MASK GENMASK(7, 6) +#define RK730_MIXER_2_MIX_CHOP(x) ((x) << 6) +#define RK730_MIXER_2_MIX_R_MODE_MASK GENMASK(5, 4) +#define RK730_MIXER_2_MIX_R_MODE(x) ((x) << 4) +#define RK730_MIXER_2_MIX_L_MODE_MASK GENMASK(2, 1) +#define RK730_MIXER_2_MIX_L_MODE(x) ((x) << 1) + +/* RK730_HP_1 */ +#define RK730_HP_1_HP_LO_CHOP_MASK GENMASK(6, 5) +#define RK730_HP_1_HP_LO_CHOP(x) ((x) << 5) + +/* RK730_DTOP_DIGEN_CLKE */ +#define RK730_DTOP_DIGEN_CLKE_ADC_CKE_MASK BIT(7) +#define RK730_DTOP_DIGEN_CLKE_ADC_CKE_EN BIT(7) +#define RK730_DTOP_DIGEN_CLKE_ADC_CKE_DIS 0 +#define RK730_DTOP_DIGEN_CLKE_I2STX_CKE_MASK BIT(6) +#define RK730_DTOP_DIGEN_CLKE_I2STX_CKE_EN BIT(6) +#define RK730_DTOP_DIGEN_CLKE_I2STX_CKE_DIS 0 +#define RK730_DTOP_DIGEN_CLKE_ADC_EN_MASK BIT(5) +#define RK730_DTOP_DIGEN_CLKE_ADC_EN BIT(5) +#define RK730_DTOP_DIGEN_CLKE_ADC_DIS 0 +#define RK730_DTOP_DIGEN_CLKE_I2STX_EN_MASK BIT(4) +#define RK730_DTOP_DIGEN_CLKE_I2STX_EN BIT(4) +#define RK730_DTOP_DIGEN_CLKE_I2STX_DIS 0 +#define RK730_DTOP_DIGEN_CLKE_DAC_CKE_MASK BIT(3) +#define RK730_DTOP_DIGEN_CLKE_DAC_CKE_EN BIT(3) +#define RK730_DTOP_DIGEN_CLKE_DAC_CKE_DIS 0 +#define RK730_DTOP_DIGEN_CLKE_I2SRX_CKE_MASK BIT(2) +#define RK730_DTOP_DIGEN_CLKE_I2SRX_CKE_EN BIT(2) +#define RK730_DTOP_DIGEN_CLKE_I2SRX_CKE_DIS 0 +#define RK730_DTOP_DIGEN_CLKE_DAC_EN_MASK BIT(1) +#define RK730_DTOP_DIGEN_CLKE_DAC_EN BIT(1) +#define RK730_DTOP_DIGEN_CLKE_DAC_DIS 0 +#define RK730_DTOP_DIGEN_CLKE_I2SRX_EN_MASK BIT(0) +#define RK730_DTOP_DIGEN_CLKE_I2SRX_EN BIT(0) +#define RK730_DTOP_DIGEN_CLKE_I2SRX_DIS 0 + +/* RK730_DADC_SR_ACL */ +#define RK730_DADC_SR_ACL_VOLL_POL_MASK BIT(5) +#define RK730_DADC_SR_ACL_VOLL_POS BIT(5) +#define RK730_DADC_SR_ACL_VOLL_NEG 0 +#define RK730_DADC_SR_ACL_VOLR_POL_MASK BIT(4) +#define RK730_DADC_SR_ACL_VOLR_POS BIT(4) +#define RK730_DADC_SR_ACL_VOLR_NEG 0 +#define RK730_DADC_SR_ACL_SRT_MASK GENMASK(2, 0) +#define RK730_DADC_SR_ACL_SRT(x) (x) + +/* RK730_DDAC_SR_LMT */ +#define RK730_DDAC_SR_LMT_VOLL_POL_MASK BIT(5) +#define RK730_DDAC_SR_LMT_VOLL_POS BIT(5) +#define RK730_DDAC_SR_LMT_VOLL_NEG 0 +#define RK730_DDAC_SR_LMT_VOLR_POL_MASK BIT(4) +#define RK730_DDAC_SR_LMT_VOLR_POS BIT(4) +#define RK730_DDAC_SR_LMT_VOLR_NEG 0 +#define RK730_DDAC_SR_LMT_SRT_MASK GENMASK(2, 0) +#define RK730_DDAC_SR_LMT_SRT(x) (x) + +/* RK730_DDAC_MUTE_MIXCTL */ +#define RK730_DDAC_MUTE_MIXCTL_MUTE_MASK BIT(0) +#define RK730_DDAC_MUTE_MIXCTL_MUTE BIT(0) +#define RK730_DDAC_MUTE_MIXCTL_UNMUTE 0 + +/* RK730_DI2S_CKM */ +#define RK730_DI2S_CKM_SCLK_DIV_MASK GENMASK(7, 4) +#define RK730_DI2S_CKM_SCLK_DIV(x) ((x - 1) << 4) +#define RK730_DI2S_CKM_SCLK_EN_MASK BIT(2) +#define RK730_DI2S_CKM_SCLK_EN BIT(2) +#define RK730_DI2S_CKM_SCLK_DIS 0 +#define RK730_DI2S_CKM_SCLK_POL_MASK BIT(1) +#define RK730_DI2S_CKM_SCLK_INVERTED BIT(1) +#define RK730_DI2S_CKM_SCLK_NORMAL 0 +#define RK730_DI2S_CKM_MST_MASK BIT(0) +#define RK730_DI2S_CKM_MST_MASTER BIT(0) +#define RK730_DI2S_CKM_MST_SLAVE 0 + +/* RK730_DI2S_XCR2 */ +#define RK730_DI2S_XCR2_VDW_MASK GENMASK(4, 0) +#define RK730_DI2S_XCR2_VDW(x) (x - 1) + +/* RK730_DI2S_RXCMD_TSD */ +#define RK730_DI2S_RXCMD_TSD_RXS_MASK BIT(5) +#define RK730_DI2S_RXCMD_TSD_RXS_EN BIT(5) +#define RK730_DI2S_RXCMD_TSD_RXS_DIS 0 + +/* RK730_DI2S_TXCR_3_TXCMD */ +#define RK730_DI2S_TXCR_3_TXCMD_TXS_MASK BIT(7) +#define RK730_DI2S_TXCR_3_TXCMD_TXS_EN BIT(7) +#define RK730_DI2S_TXCR_3_TXCMD_TXS_DIS 0 + +#endif diff --git a/sound/soc/rockchip/rockchip_i2s_tdm.c b/sound/soc/rockchip/rockchip_i2s_tdm.c index 19691b6d5a39..de31da12f611 100644 --- a/sound/soc/rockchip/rockchip_i2s_tdm.c +++ b/sound/soc/rockchip/rockchip_i2s_tdm.c @@ -2282,6 +2282,16 @@ static int rockchip_i2s_tdm_remove(struct platform_device *pdev) return 0; } +static void rockchip_i2s_tdm_platform_shutdown(struct platform_device *pdev) +{ + struct rk_i2s_tdm_dev *i2s_tdm = dev_get_drvdata(&pdev->dev); + + pm_runtime_get_sync(i2s_tdm->dev); + rockchip_i2s_tdm_stop(i2s_tdm, SNDRV_PCM_STREAM_PLAYBACK); + rockchip_i2s_tdm_stop(i2s_tdm, SNDRV_PCM_STREAM_CAPTURE); + pm_runtime_put(i2s_tdm->dev); +} + #ifdef CONFIG_PM_SLEEP static int rockchip_i2s_tdm_suspend(struct device *dev) { @@ -2317,6 +2327,7 @@ static const struct dev_pm_ops rockchip_i2s_tdm_pm_ops = { static struct platform_driver rockchip_i2s_tdm_driver = { .probe = rockchip_i2s_tdm_probe, .remove = rockchip_i2s_tdm_remove, + .shutdown = rockchip_i2s_tdm_platform_shutdown, .driver = { .name = DRV_NAME, .of_match_table = of_match_ptr(rockchip_i2s_tdm_match), diff --git a/sound/soc/rockchip/rockchip_pdm.c b/sound/soc/rockchip/rockchip_pdm.c index f4719f271793..703219e9c42c 100644 --- a/sound/soc/rockchip/rockchip_pdm.c +++ b/sound/soc/rockchip/rockchip_pdm.c @@ -28,6 +28,7 @@ #define PDM_START_DELAY_MS_MAX (1000) #define PDM_FILTER_DELAY_MS_MIN (20) #define PDM_FILTER_DELAY_MS_MAX (1000) +#define PDM_CLK_SHIFT_PPM_MAX (1000000) /* 1 ppm */ enum rk_pdm_version { RK_PDM_RK3229, @@ -89,7 +90,7 @@ static unsigned int get_pdm_clk(struct rk_pdm_dev *pdm, unsigned int sr, unsigned int *clk_src, unsigned int *clk_out, unsigned int signoff) { - unsigned int i, count, clk, div, rate; + unsigned int i, count, clk, div, rate, delta; clk = 0; if (!sr) @@ -103,7 +104,9 @@ static unsigned int get_pdm_clk(struct rk_pdm_dev *pdm, unsigned int sr, if ((div & (div - 1)) == 0) { *clk_out = clkref[i].clk_out; rate = clk_round_rate(pdm->clk, clkref[i].clk); - if (rate != clkref[i].clk) + delta = clkref[i].clk / PDM_CLK_SHIFT_PPM_MAX; + if (rate < clkref[i].clk - delta || + rate > clkref[i].clk + delta) continue; clk = clkref[i].clk; *clk_src = clkref[i].clk; @@ -610,6 +613,7 @@ static int rockchip_pdm_runtime_resume(struct device *dev) return ret; } + rockchip_pdm_rxctrl(pdm, 0); regcache_cache_only(pdm->regmap, false); regcache_mark_dirty(pdm->regmap); ret = regcache_sync(pdm->regmap);