From f2ac8cf7af698b3c847f8f70476f081f54467411 Mon Sep 17 00:00:00 2001 From: "shengyang.chen" Date: Sun, 12 Jun 2022 23:12:24 +0800 Subject: [PATCH] riscv:linux:driver:drm:mipi-dsi update mipi-dsi driver support Signed-off-by:shengyang.chen --- arch/riscv/boot/dts/starfive/codecs/sf_wm8960.dtsi | 0 arch/riscv/boot/dts/starfive/jh7110-common.dtsi | 48 ++- arch/riscv/boot/dts/starfive/jh7110-evb.dts | 0 arch/riscv/boot/dts/starfive/jh7110-fpga.dts | 0 .../boot/dts/starfive/jh7110-visionfive-v2.dts | 0 arch/riscv/boot/dts/starfive/jh7110.dtsi | 20 +- arch/riscv/boot/dts/starfive/jh7110_pinctrl.dtsi | 28 ++ arch/riscv/configs/starfive_jh7110_defconfig | 7 +- drivers/gpu/drm/verisilicon/Kconfig | 11 +- drivers/gpu/drm/verisilicon/README | 30 ++ drivers/gpu/drm/verisilicon/starfive_drm_dsi.c | 338 +++++++++++++++---- .../gpu/drm/verisilicon/starfive_drm_seeedpanel.c | 15 + drivers/gpu/drm/verisilicon/vs_dc.c | 59 ++++ drivers/gpu/drm/verisilicon/vs_dc.h | 10 + drivers/gpu/drm/verisilicon/vs_dc_hw.c | 21 ++ drivers/gpu/drm/verisilicon/vs_drv.c | 2 +- drivers/gpu/drm/verisilicon/vs_simple_enc.c | 20 +- drivers/phy/m31/7110-m31-dphy.h | 359 +++++++++++++++++++++ drivers/phy/m31/Kconfig | 0 drivers/phy/m31/Makefile | 0 drivers/phy/m31/phy-m31-dphy-tx0.c | 330 ++++++++++++++++++- 21 files changed, 1209 insertions(+), 89 deletions(-) mode change 100755 => 100644 arch/riscv/boot/dts/starfive/codecs/sf_wm8960.dtsi mode change 100755 => 100644 arch/riscv/boot/dts/starfive/jh7110-evb.dts mode change 100755 => 100644 arch/riscv/boot/dts/starfive/jh7110-fpga.dts mode change 100755 => 100644 arch/riscv/boot/dts/starfive/jh7110-visionfive-v2.dts create mode 100644 drivers/phy/m31/7110-m31-dphy.h mode change 100755 => 100644 drivers/phy/m31/Kconfig mode change 100755 => 100644 drivers/phy/m31/Makefile mode change 100755 => 100644 drivers/phy/m31/phy-m31-dphy-tx0.c diff --git a/arch/riscv/boot/dts/starfive/codecs/sf_wm8960.dtsi b/arch/riscv/boot/dts/starfive/codecs/sf_wm8960.dtsi old mode 100755 new mode 100644 diff --git a/arch/riscv/boot/dts/starfive/jh7110-common.dtsi b/arch/riscv/boot/dts/starfive/jh7110-common.dtsi index 365aae9..77d657c 100644 --- a/arch/riscv/boot/dts/starfive/jh7110-common.dtsi +++ b/arch/riscv/boot/dts/starfive/jh7110-common.dtsi @@ -140,6 +140,18 @@ pinctrl-names = "default"; pinctrl-0 = <&i2c2_pins>; status = "okay"; + + seeed_plane_i2c@45 { + compatible = "seeed_panel"; + reg = <0x45>; + + port { + panel_dsi_port: endpoint { + remote-endpoint = <&dsi_out_port>; + }; + }; + }; + }; &i2c3 { @@ -614,7 +626,7 @@ }; &hdmi { - status = "okay"; + status = "okay";//okay pinctrl-names = "default"; pinctrl-0 = <&inno_hdmi_pins>; @@ -644,6 +656,11 @@ reg = <1>; remote-endpoint = <&hdmi_in_lcdc>; }; + + dc_out_dpi2: endpoint@2 { + reg = <2>; + remote-endpoint = <&mipi_in>; + }; }; }; @@ -671,6 +688,35 @@ }; }; +&encoder { + status = "okay"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + mipi_in: endpoint { + remote-endpoint = <&dc_out_dpi2>; + }; + }; + + port@1 { + reg = <1>; + mipi_out: endpoint { + remote-endpoint = <&dsi_in_port>; + }; + }; + }; +}; + +&mipi_dsi { + pinctrl-names = "default"; + pinctrl-0 = <&mipitx_pins>; + status = "okay"; +}; + &co_process { status = "okay"; }; diff --git a/arch/riscv/boot/dts/starfive/jh7110-evb.dts b/arch/riscv/boot/dts/starfive/jh7110-evb.dts old mode 100755 new mode 100644 diff --git a/arch/riscv/boot/dts/starfive/jh7110-fpga.dts b/arch/riscv/boot/dts/starfive/jh7110-fpga.dts old mode 100755 new mode 100644 diff --git a/arch/riscv/boot/dts/starfive/jh7110-visionfive-v2.dts b/arch/riscv/boot/dts/starfive/jh7110-visionfive-v2.dts old mode 100755 new mode 100644 diff --git a/arch/riscv/boot/dts/starfive/jh7110.dtsi b/arch/riscv/boot/dts/starfive/jh7110.dtsi index c38fbf9..a5bcb5c 100644 --- a/arch/riscv/boot/dts/starfive/jh7110.dtsi +++ b/arch/riscv/boot/dts/starfive/jh7110.dtsi @@ -1477,12 +1477,15 @@ <&clkvout JH7110_U0_DC8200_CLK_PIX1>, <&clkvout JH7110_U0_DC8200_CLK_AXI>, <&clkvout JH7110_U0_DC8200_CLK_CORE>, - <&clkvout JH7110_U0_DC8200_CLK_AHB>; + <&clkvout JH7110_U0_DC8200_CLK_AHB>, + <&clkgen JH7110_VOUT_TOP_CLK_VOUT_AXI>, + <&clkgen JH7110_DOM_VOUT_TOP_LCD_CLK>; clock-names = "noc_cpu","noc_cfg0","noc_gpu","noc_vdec","noc_venc", "noc_disp","noc_isp","noc_stg","vout_src", "top_vout_axi","ahb1","top_vout_ahb", "top_vout_hdmiTX0","i2stx","pix_clk","vout_pix1", - "axi_clk","core_clk","vout_ahb"; + "axi_clk","core_clk","vout_ahb", + "vout_top_axi","vout_top_lcd"; resets = <&rstgen RSTN_U0_DOM_VOUT_TOP_SRC>, <&rstgen RSTN_U0_DC8200_AXI>, @@ -1504,6 +1507,11 @@ "rst_noc_disp","rst_noc_isp","rst_noc_stg","rst_noc_ddrc"; }; + encoder: display-encoder { + compatible = "verisilicon,dsi-encoder"; + status = "disabled"; + }; + mipi_dphy: mipi-dphy@295e0000{ compatible = "starfive,jh7100-mipi-dphy-tx"; reg = <0x0 0x295e0000 0x0 0x10000>; @@ -1519,6 +1527,7 @@ mipi_dsi: mipi@295d0000 { compatible = "cdns,dsi"; reg = <0x0 0x295d0000 0x0 0x10000>; + interrupts = <98>; reg-names = "dsi"; clocks = <&clkvout JH7110_U0_CDNS_DSITX_CLK_SYS>, <&clkvout JH7110_U0_CDNS_DSITX_CLK_APB>, @@ -1538,8 +1547,11 @@ status = "disabled"; port { - dsi_out_port: endpoint { - /*remote-endpoint = <&panel_dsi_port>;*/ + dsi_out_port: endpoint@0 { + remote-endpoint = <&panel_dsi_port>; + }; + dsi_in_port: endpoint@1 { + remote-endpoint = <&mipi_out>; }; }; diff --git a/arch/riscv/boot/dts/starfive/jh7110_pinctrl.dtsi b/arch/riscv/boot/dts/starfive/jh7110_pinctrl.dtsi index c67495a..3426ddc 100644 --- a/arch/riscv/boot/dts/starfive/jh7110_pinctrl.dtsi +++ b/arch/riscv/boot/dts/starfive/jh7110_pinctrl.dtsi @@ -1133,6 +1133,34 @@ }; }; + mipitx_pins: mipitx-pins { + mipitx-1-pins { + sf,pins = ; + sf,pinmux = ; + sf,pin-ioconfig = ; + }; + mipitx-2-pins { + sf,pins = ; + sf,pinmux = ; + sf,pin-ioconfig = ; + }; + mipitx-3-pins { + sf,pins = ; + sf,pinmux = ; + sf,pin-ioconfig = ; + }; + mipitx-4-pins { + sf,pins = ; + sf,pinmux = ; + sf,pin-ioconfig = ; + }; + mipitx-5-pins { + sf,pins = ; + sf,pinmux = ; + sf,pin-ioconfig = ; + }; + }; + pcie0_perst_default: pcie0_perst_default { perst-pins { sf,pins = ; diff --git a/arch/riscv/configs/starfive_jh7110_defconfig b/arch/riscv/configs/starfive_jh7110_defconfig index 8031f95..a6d37d2 100644 --- a/arch/riscv/configs/starfive_jh7110_defconfig +++ b/arch/riscv/configs/starfive_jh7110_defconfig @@ -172,6 +172,8 @@ CONFIG_POWER_RESET_SYSCON_POWEROFF=y CONFIG_SENSORS_SFCTEMP=y CONFIG_WATCHDOG=y CONFIG_STARFIVE_WATCHDOG=y +CONFIG_REGULATOR=y +CONFIG_REGULATOR_STF7110=y # CONFIG_MEDIA_CEC_SUPPORT is not set CONFIG_MEDIA_SUPPORT=y CONFIG_V4L_PLATFORM_DRIVERS=y @@ -180,7 +182,7 @@ CONFIG_VIN_SENSOR_SC2235=y CONFIG_VIN_SENSOR_OV4689=y CONFIG_DRM=y CONFIG_DRM_VERISILICON=y -CONFIG_STARFIVE_INNO_HDMI=y +CONFIG_STARFIVE_DSI=y # CONFIG_DRM_IMG_NULLDISP is not set CONFIG_DRM_LEGACY=y CONFIG_FB=y @@ -228,6 +230,7 @@ CONFIG_RPMSG_VIRTIO=y CONFIG_SIFIVE_L2_FLUSH_START=0x40000000 CONFIG_SIFIVE_L2_FLUSH_SIZE=0x400000000 CONFIG_PWM=y +CONFIG_PHY_M31_DPHY_RX0=y CONFIG_RAS=y CONFIG_EXT4_FS=y CONFIG_EXT4_FS_POSIX_ACL=y @@ -277,5 +280,3 @@ CONFIG_RCU_EQS_DEBUG=y # CONFIG_FTRACE is not set # CONFIG_RUNTIME_TESTING_MENU is not set CONFIG_MEMTEST=y -CONFIG_REGULATOR=y -CONFIG_REGULATOR_STF7110=y diff --git a/drivers/gpu/drm/verisilicon/Kconfig b/drivers/gpu/drm/verisilicon/Kconfig index cbed5e0..1b3ce97 100644 --- a/drivers/gpu/drm/verisilicon/Kconfig +++ b/drivers/gpu/drm/verisilicon/Kconfig @@ -55,8 +55,15 @@ config STARFIVE_INNO_HDMI for the Innosilicon HDMI driver. If you want to enable HDMI on RK3036 based SoC, you should select this option. config STARFIVE_DSI - bool "DSI" + bool "Starfive MIPI DSI Select" + depends on DRM_VERISILICON + select DRM_MIPI_DSI + select GENERIC_PHY + select GENERIC_PHY_MIPI_DPHY help - enable dsi, you should select this option. + This selects support for starfive SoC specific extensions + for the Synopsys DesignWare MIPI driver. If you want to + enable MIPI DSI on VIC7110 based SoC, you should + select this option. source "drivers/gpu/drm/verisilicon/adv7511/Kconfig" diff --git a/drivers/gpu/drm/verisilicon/README b/drivers/gpu/drm/verisilicon/README index 70cc40a..b8225f4 100644 --- a/drivers/gpu/drm/verisilicon/README +++ b/drivers/gpu/drm/verisilicon/README @@ -13,4 +13,34 @@ notes: 2、evb板子当前与hdmi冲突的gpio有 i2c0 i2c1 i2c2 i2c3 sdio1 tdm 调试hdmi的时候建议在jh7100-common.dtsi文件 +将这些模块的pin的结点进行注释或者将node disabled + + + +build drm mipi-dsi need to config: +# CONFIG_DRM_I2C_ADV7511_CEC is not set +CONFIG_DRM_VERISILICON=y +#CONFIG_DRM_I2C_ADV7513 is not set +CONFIG_DRM_LEGACY=y +CONFIG_STARFIVE_DSI=y +CONFIG_PHY_M31_DPHY_RX0=y +#STARFIVE_INNO_HDMI is not set +# CONFIG_DRM_VGEM is not set +# CONFIG_DRM_VKMS is not set + +notes: +1. +测试mipi-dsi时,在编译前需要打开的dts节点有: +jh7110.dtsi: +dc8200, encoder: display-encoder, mipi_dphy, mipi_dsi, mipi_panel + +jh7100-common.dtsi: +&i2c2 + + +2. +evb板子当前与mipi-dsi冲突的gpio有: +i2c0, i2c3, hdmi(inno), hdmi_output(rgb), sdio1(mmc1) + +调试mipi-dsi的时候建议在jh7100-common.dtsi文件 将这些模块的pin的结点进行注释或者将node disabled \ No newline at end of file diff --git a/drivers/gpu/drm/verisilicon/starfive_drm_dsi.c b/drivers/gpu/drm/verisilicon/starfive_drm_dsi.c index 3d6c43e..453bfd5 100644 --- a/drivers/gpu/drm/verisilicon/starfive_drm_dsi.c +++ b/drivers/gpu/drm/verisilicon/starfive_drm_dsi.c @@ -26,6 +26,8 @@ #include #include "vs_drv.h" +//#include //20220520 pmic support +//#include"vs_clock.h" //sysrst registers #define SRST_ASSERT0 0x00 @@ -430,6 +432,11 @@ #define DSI_NULL_FRAME_OVERHEAD 6 #define DSI_EOT_PKT_SIZE 4 +#if 1 //7110 fw +#define ARRAY_SIZE_DSI(x) (sizeof(x) / sizeof((x)[0])) +#define TEST_CTRL(x) (x) +#endif + struct cdns_dsi_output { struct mipi_dsi_device *dev; struct drm_panel *panel; @@ -484,6 +491,7 @@ struct cdns_dsi { //clk op func----------// static int cdns_dsi_clock_enable(struct cdns_dsi *dsi, struct device *dev) { + //dev_info(dev,"==cdns_dsi_clock_enable begin\n"); int ret; /*clk_prepare_enable(dsi->sys_clk);*/ ret = clk_prepare_enable(dsi->dsi_sys_clk); @@ -506,7 +514,8 @@ static int cdns_dsi_clock_enable(struct cdns_dsi *dsi, struct device *dev) dev_err(dev, "failed to prepare/enable dpi_clk\n"); return ret; } - + + //dev_info(dev,"==cdns_dsi_clock_enable successful\n"); return ret; } @@ -522,6 +531,7 @@ static void cdns_dsi_clock_disable(struct cdns_dsi *dsi) //rst op func--------// static int cdns_dsi_resets_deassert(struct cdns_dsi *dsi, struct device *dev) { + //dev_info(dev,"==cdns_dsi_resets_deassert begin\n"); int ret; ret = reset_control_deassert(dsi->sys_rst); @@ -529,11 +539,11 @@ static int cdns_dsi_resets_deassert(struct cdns_dsi *dsi, struct device *dev) dev_err(dev, "failed to deassert sys_rst\n"); return ret; } - ret = reset_control_deassert(dsi->txbytehs_rst); - if (ret < 0) { - dev_err(dev, "failed to deassert txbytehs_rst\n"); - return ret; - } + //ret = reset_control_deassert(dsi->txbytehs_rst); + //if (ret < 0) { + // dev_err(dev, "failed to deassert txbytehs_rst\n"); + // return ret; + //} ret = reset_control_deassert(dsi->apb_rst); if (ret < 0) { dev_err(dev, "failed to deassert apb_rst\n"); @@ -554,12 +564,13 @@ static int cdns_dsi_resets_deassert(struct cdns_dsi *dsi, struct device *dev) dev_err(dev, "failed to deassert rxesc_rst\n"); return ret; } - + //dev_info(dev,"==cdns_dsi_resets_deassert successful\n"); return ret; } static int cdns_dsi_resets_assert(struct cdns_dsi *dsi, struct device *dev) { + //dev_info(dev,"==cdns_dsi_resets_assert begin\n"); int ret; ret = reset_control_assert(dsi->sys_rst); @@ -567,11 +578,11 @@ static int cdns_dsi_resets_assert(struct cdns_dsi *dsi, struct device *dev) dev_err(dev, "failed to assert sys_rst\n"); return ret; } - ret = reset_control_assert(dsi->txbytehs_rst); - if (ret < 0) { - dev_err(dev, "failed to assert txbytehs_rst\n"); - return ret; - } + //ret = reset_control_assert(dsi->txbytehs_rst); + //if (ret < 0) { + // dev_err(dev, "failed to assert txbytehs_rst\n"); + // return ret; + //} ret = reset_control_assert(dsi->apb_rst); if (ret < 0) { dev_err(dev, "failed to assert apb_rst\n"); @@ -592,55 +603,87 @@ static int cdns_dsi_resets_assert(struct cdns_dsi *dsi, struct device *dev) dev_err(dev, "failed to assert dpi_rst\n"); return ret; } - + //dev_info(dev,"==cdns_dsi_resets_assert successful\n"); return ret; } //get clock func -static int cdns_dsi_get_clock(struct platform_device *pdev, struct cdns_dsi *dsi) +static int cdns_dsi_get_clock(struct device *dev, struct cdns_dsi *dsi) { + //dev_info(dev,"==cdns_dsi_get_clock begin\n"); int ret; - dsi->dsi_sys_clk = devm_clk_get(&pdev->dev, "sys"); - if (IS_ERR(dsi->dsi_sys_clk)) + dsi->dsi_sys_clk = devm_clk_get(dev, "sys"); + if (IS_ERR(dsi->dsi_sys_clk)){ + dev_err(dev, "failed to get dsi_sys_clk\n"); return PTR_ERR(dsi->dsi_sys_clk); - dsi->apb_clk = devm_clk_get(&pdev->dev, "apb"); - if (IS_ERR(dsi->apb_clk)) + } + + dsi->apb_clk = devm_clk_get(dev, "apb"); + if (IS_ERR(dsi->apb_clk)){ + dev_err(dev, "failed to get apb_clk\n"); return PTR_ERR(dsi->apb_clk); - dsi->txesc_clk = devm_clk_get(&pdev->dev, "txesc"); - if (IS_ERR(dsi->txesc_clk)) + } + + dsi->txesc_clk = devm_clk_get(dev, "txesc"); + if (IS_ERR(dsi->txesc_clk)){ + dev_err(dev, "failed to get txesc_clk\n"); return PTR_ERR(dsi->txesc_clk); - dsi->dpi_clk = devm_clk_get(&pdev->dev, "dpi"); - if (IS_ERR(dsi->dpi_clk)) + } + + dsi->dpi_clk = devm_clk_get(dev, "dpi"); + if (IS_ERR(dsi->dpi_clk)){ + dev_err(dev, "failed to get dpi_clk\n"); return PTR_ERR(dsi->dpi_clk); + } + //dev_info(dev,"==cdns_dsi_get_clock successful\n"); return ret; } //get reset func -static int cdns_dsi_get_reset(struct platform_device *pdev, struct cdns_dsi *dsi) +static int cdns_dsi_get_reset(struct device *dev, struct cdns_dsi *dsi) { + //dev_info(dev,"==cdns_dsi_get_reset begin\n"); int ret; - dsi->dpi_rst = reset_control_get_exclusive(&pdev->dev, "dsi_dpi"); - if (IS_ERR(dsi->dpi_rst)) + dsi->dpi_rst = reset_control_get_exclusive(dev, "dsi_dpi"); + if (IS_ERR(dsi->dpi_rst)){ + dev_err(dev, "failed to get dpi_rst\n"); return PTR_ERR(dsi->dpi_rst); - dsi->apb_rst = reset_control_get_exclusive(&pdev->dev, "dsi_apb"); - if (IS_ERR(dsi->apb_rst)) + } + + dsi->apb_rst = reset_control_get_exclusive(dev, "dsi_apb"); + if (IS_ERR(dsi->apb_rst)){ + dev_err(dev, "failed to get apb_rst\n"); return PTR_ERR(dsi->apb_rst); - dsi->rxesc_rst = reset_control_get_exclusive(&pdev->dev, "dsi_rxesc"); - if (IS_ERR(dsi->rxesc_rst)) + } + + dsi->rxesc_rst = reset_control_get_exclusive(dev, "dsi_rxesc"); + if (IS_ERR(dsi->rxesc_rst)){ + dev_err(dev, "failed to get rxesc_rst\n"); return PTR_ERR(dsi->rxesc_rst); - dsi->sys_rst = reset_control_get_exclusive(&pdev->dev, "dsi_sys"); - if (IS_ERR(dsi->sys_rst)) + } + + dsi->sys_rst = reset_control_get_exclusive(dev, "dsi_sys"); + if (IS_ERR(dsi->sys_rst)){ + dev_err(dev, "failed to get sys_rst\n"); return PTR_ERR(dsi->sys_rst); - dsi->txbytehs_rst = reset_control_get_exclusive(&pdev->dev, "dsi_txbytehs"); - if (IS_ERR(dsi->txbytehs_rst)) + } + + dsi->txbytehs_rst = reset_control_get_exclusive(dev, "dsi_txbytehs"); + if (IS_ERR(dsi->txbytehs_rst)){ + dev_err(dev, "failed to get txbytehs_rst\n"); return PTR_ERR(dsi->txbytehs_rst); - dsi->txesc_rst = reset_control_get_exclusive(&pdev->dev, "dsi_txesc"); - if (IS_ERR(dsi->txesc_rst)) + } + + dsi->txesc_rst = reset_control_get_exclusive(dev, "dsi_txesc"); + if (IS_ERR(dsi->txesc_rst)){ + dev_err(dev, "failed to get txesc_rst\n"); return PTR_ERR(dsi->txesc_rst); + } + //dev_info(dev,"==cdns_dsi_get_reset successful\n"); return ret; } @@ -726,15 +769,25 @@ static int cdns_dsi_mode2cfg(struct cdns_dsi *dsi, dsi_cfg->hfp = dpi_to_dsi_timing(mode_to_dpi_hfp(mode, mode_valid_check), bpp, DSI_HFP_FRAME_OVERHEAD); //dpi to dsi transfer can not match , reconfig those parms + #if 0 if (mode->hdisplay == 800) { dsi_cfg->hsa = 16; //30-14 dsi_cfg->hbp = 73; //85-12 dsi_cfg->hfp = 146; //152-6 } + #endif + #if 1 + if (mode->hdisplay == 800) { + dsi_cfg->hsa = 5; //19-14 + dsi_cfg->hbp = 5; //17-12 + dsi_cfg->hfp = 102; //108-6 + } + #endif return 0; } +#if 1//original static int cdns_dsi_adjust_phy_config(struct cdns_dsi *dsi, struct cdns_dsi_cfg *dsi_cfg, struct phy_configure_opts_mipi_dphy *phy_cfg, @@ -785,7 +838,8 @@ static int cdns_dsi_adjust_phy_config(struct cdns_dsi *dsi, return 0; } - +#endif +#if 1 //original static int cdns_dsi_check_conf(struct cdns_dsi *dsi, const struct drm_display_mode *mode, struct cdns_dsi_cfg *dsi_cfg, @@ -830,6 +884,7 @@ static int cdns_dsi_check_conf(struct cdns_dsi *dsi, return 0; } +#endif static int cdns_dsi_bridge_attach(struct drm_bridge *bridge, enum drm_bridge_attach_flags flags) @@ -838,12 +893,13 @@ static int cdns_dsi_bridge_attach(struct drm_bridge *bridge, struct cdns_dsi *dsi = input_to_dsi(input); struct cdns_dsi_output *output = &dsi->output; + dev_info(dsi->base.dev, "===>cdns_dsi_bridge_attach begin\n"); if (!drm_core_check_feature(bridge->dev, DRIVER_ATOMIC)) { dev_err(dsi->base.dev, "cdns-dsi driver is only compatible with DRM devices supporting atomic updates"); return -EOPNOTSUPP; } - + dev_info(dsi->base.dev, "===>cdns_dsi_bridge_attach end\n"); return drm_bridge_attach(bridge->encoder, output->bridge, bridge, flags); } @@ -875,13 +931,52 @@ cdns_dsi_bridge_mode_valid(struct drm_bridge *bridge, if ((mode->hdisplay * bpp) % 32) return MODE_H_ILLEGAL; - ret = cdns_dsi_check_conf(dsi, mode, &dsi_cfg, true); - if (ret) - return MODE_BAD; + ret = cdns_dsi_check_conf(dsi, mode, &dsi_cfg, true); //original + //if (ret) + // return MODE_BAD; return MODE_OK; } +#if 1 //20220602 +static inline void sys_delay(int cycles) +{ + int i = 0; + + while (i++ < cycles); +} +int sys_mipi_dsi_set_ppi_txbyte_hs(int enable, struct cdns_dsi *dsi) +//int sys_mipi_dsi_set_ppi_txbyte_hs(int enable) +{ + static int status = 0; + int ret; + if (!enable && status) { + status = 0; + //_ASSERT_RESET_RSTGEN_RSTN_U0_CDNS_DSITX_RSTN_TXBYTEHS_; + ret = reset_control_assert(dsi->txbytehs_rst); + if (ret < 0) { + dev_err(dsi->base.dev, "failed to assert txbytehs_rst\n"); + return ret; + } + dev_info(dsi->base.dev, "STOP dsi ppi_txbyte_hs\n"); + } else if (enable && !status) { + status = 1; + //_CLEAR_RESET_RSTGEN_RSTN_U0_CDNS_DSITX_RSTN_TXBYTEHS_; + ret = reset_control_deassert(dsi->txbytehs_rst); + if (ret < 0) { + dev_err(dsi->base.dev, "failed to deassert txbytehs_rst\n"); + return ret; + } + + dev_info(dsi->base.dev, "START dsi ppi_txbyte_hs\n"); + } + + dev_info(dsi->base.dev, "sys_mipi_dsi_set_ppi_txbyte_hs\n"); + sys_delay(100); + return 0; +} +#endif + static void cdns_dsi_bridge_disable(struct drm_bridge *bridge) { struct cdns_dsi_input *input = bridge_to_cdns_dsi_input(bridge); @@ -897,10 +992,12 @@ static void cdns_dsi_bridge_disable(struct drm_bridge *bridge) val = readl(dsi->regs + MCTL_MAIN_EN) & ~IF_EN(input->id); writel(val, dsi->regs + MCTL_MAIN_EN); pm_runtime_put(dsi->base.dev); + sys_mipi_dsi_set_ppi_txbyte_hs(0, dsi);//20220609 phy_power_off(dsi->dphy); phy_exit(dsi->dphy); } +#if 0 //original static void release_txbyte_rst(void) { void __iomem *regs = ioremap(0x12250000, 0x10000); @@ -918,12 +1015,13 @@ static void release_txbyte_rst(void) } while (temp != 0x1); //udelay(1); } +#endif static void cdns_dsi_hs_init(struct cdns_dsi *dsi) { struct cdns_dsi_output *output = &dsi->output; - u32 dpi_fifo_int = 0; - + //u32 dpi_fifo_int = 0; + int ret; /* * Power all internal DPHY blocks down and maintain their reset line * asserted before changing the DPHY config. @@ -936,7 +1034,12 @@ static void cdns_dsi_hs_init(struct cdns_dsi *dsi) phy_set_mode(dsi->dphy, PHY_MODE_MIPI_DPHY); phy_configure(dsi->dphy, &output->phy_opts); phy_power_on(dsi->dphy); - release_txbyte_rst(); + //release_txbyte_rst();//20220602 disabled + #if 1 + + //ret = sys_mipi_dsi_set_ppi_txbyte_hs(1); + ret = sys_mipi_dsi_set_ppi_txbyte_hs(1, dsi); + #endif writel(PLL_LOCKED, dsi->regs + MCTL_MAIN_STS_CLR); writel(DPHY_CMN_PSO | DPHY_ALL_D_PDN | DPHY_C_PDN | DPHY_CMN_PDN, @@ -947,9 +1050,12 @@ static void cdns_dsi_hs_init(struct cdns_dsi *dsi) DPHY_D_RSTB(output->dev->lanes) | DPHY_C_RSTB, dsi->regs + MCTL_DPHY_CFG0); +//20220602 disabled +/* dpi_fifo_int = readl(dsi->regs + DPI_IRQ_CLR); if (dpi_fifo_int) writel(1, dsi->regs + DPI_IRQ_CLR); +*/ } static void cdns_dsi_init_link(struct cdns_dsi *dsi) @@ -962,7 +1068,8 @@ static void cdns_dsi_init_link(struct cdns_dsi *dsi) if (dsi->link_initialized) return; - val = 0; + //val = 0;//original + val = WAIT_BURST_TIME(0xf); for (i = 1; i < output->dev->lanes; i++) val |= DATA_LANE_EN(i); @@ -976,6 +1083,7 @@ static void cdns_dsi_init_link(struct cdns_dsi *dsi) ulpout = DIV_ROUND_UP(NSEC_PER_MSEC, sysclk_period); writel(CLK_LANE_ULPOUT_TIME(ulpout) | DATA_LANE_ULPOUT_TIME(ulpout), dsi->regs + MCTL_ULPOUT_TIME); + //dev_info(dsi->base.dev, "sysclk_period = %ldns, ulpout: 0x%08x\n", sysclk_period, ulpout); writel(LINK_EN, dsi->regs + MCTL_MAIN_DATA_CTL); @@ -986,6 +1094,7 @@ static void cdns_dsi_init_link(struct cdns_dsi *dsi) writel(val, dsi->regs + MCTL_MAIN_EN); dsi->link_initialized = true; + } static void cdns_dsi_bridge_enable(struct drm_bridge *bridge) @@ -999,6 +1108,8 @@ static void cdns_dsi_bridge_enable(struct drm_bridge *bridge) struct cdns_dsi_cfg dsi_cfg; u32 tmp, reg_wakeup; int nlanes; + int vrefresh; + u32 div; if (WARN_ON(pm_runtime_get_sync(dsi->base.dev) < 0)) return; @@ -1006,22 +1117,56 @@ static void cdns_dsi_bridge_enable(struct drm_bridge *bridge) mode = &bridge->encoder->crtc->state->adjusted_mode; nlanes = output->dev->lanes; - WARN_ON_ONCE(cdns_dsi_check_conf(dsi, mode, &dsi_cfg, false)); + //WARN_ON_ONCE(cdns_dsi_check_conf(dsi, mode, &dsi_cfg, false));//original //7110 mode illegal,need confirm //cannot disable + cdns_dsi_check_conf(dsi, mode, &dsi_cfg, false); cdns_dsi_hs_init(dsi); cdns_dsi_init_link(dsi); +#if 0 + dev_info(dsi->base.dev, "DSI HSA: %d, HBP: %d, HFP: %d, HACT: %d, HTOTAL: %d\n", + dsi_cfg.hsa, dsi_cfg.hbp, dsi_cfg.hfp, dsi_cfg.hact, dsi_cfg.htotal); +#endif + +#if 1 + dsi_cfg.htotal=2544;//7110,0 while testing, must + //dev_info(dsi->base.dev, "change HTOTAL: %d\n",dsi_cfg.htotal); +#endif + writel(HBP_LEN(dsi_cfg.hbp) | HSA_LEN(dsi_cfg.hsa), dsi->regs + VID_HSIZE1); writel(HFP_LEN(dsi_cfg.hfp) | HACT_LEN(dsi_cfg.hact), dsi->regs + VID_HSIZE2); +#if 0 + dev_info(dsi->base.dev, "DPI HSA: %d, HBP: %d, HFP: %d, HACT: %d, HTOTAL: %d\n", + mode->crtc_htotal - mode->crtc_hsync_end, + mode->crtc_hsync_end - mode->crtc_hsync_start, + mode->crtc_hsync_start - mode->crtc_hdisplay, + mode->crtc_hdisplay, + mode->crtc_htotal); + dev_info(dsi->base.dev, "DPI VSA: %d, VBP: %d, VFP: %d, VACT: %d, VTOTAL: %d\n", + mode->crtc_vtotal - mode->crtc_vsync_end, + mode->crtc_vsync_end - mode->crtc_vsync_start, + mode->crtc_vsync_start - mode->crtc_vdisplay-1, + mode->crtc_vdisplay, + mode->crtc_vtotal); +#endif + #if 0//original writel(VBP_LEN(mode->crtc_vtotal - mode->crtc_vsync_end - 1) | VFP_LEN(mode->crtc_vsync_start - mode->crtc_vdisplay) | VSA_LEN(mode->crtc_vsync_end - mode->crtc_vsync_start + 1), dsi->regs + VID_VSIZE1); + #endif + + #if 1//jh7110 + writel(VBP_LEN(mode->crtc_vtotal - mode->crtc_vsync_end) | + VFP_LEN(mode->crtc_vsync_start - mode->crtc_vdisplay - 1) | + VSA_LEN(mode->crtc_vsync_end - mode->crtc_vsync_start), + dsi->regs + VID_VSIZE1); + + #endif writel(mode->crtc_vdisplay, dsi->regs + VID_VSIZE2); - tmp = dsi_cfg.htotal - (dsi_cfg.hsa + DSI_BLANKING_FRAME_OVERHEAD + DSI_HSA_FRAME_OVERHEAD); @@ -1046,12 +1191,36 @@ static void cdns_dsi_bridge_enable(struct drm_bridge *bridge) tx_byte_period = DIV_ROUND_DOWN_ULL((u64)NSEC_PER_SEC * 8, phy_cfg->hs_clk_rate); reg_wakeup = (phy_cfg->hs_prepare + phy_cfg->hs_zero) / tx_byte_period; + //dev_info(dsi->base.dev, "tx_byte_period = %dns, reg_wakeup: 0x%08x\n", tx_byte_period, reg_wakeup); writel(REG_WAKEUP_TIME(reg_wakeup) | REG_LINE_DURATION(tmp), dsi->regs + VID_DPHY_TIME); +#if 0//original writel(0xafffb, dsi->regs + MCTL_DPHY_TIMEOUT1); writel(0x3ffff, dsi->regs + MCTL_DPHY_TIMEOUT2); writel(0x3ab05, dsi->regs + MCTL_ULPOUT_TIME); +#endif + +#if 1 //7110 + vrefresh = 49;//display_timing_vrefresh(dpi); + tmp = NSEC_PER_SEC / vrefresh; + tmp /= tx_byte_period; + for (div = 0; div <= CLK_DIV_MAX; div++) { + if (tmp <= HSTX_TIMEOUT_MAX) + break; + + tmp >>= 1; + } + + if (tmp > HSTX_TIMEOUT_MAX) + tmp = HSTX_TIMEOUT_MAX; + + writel(CLK_DIV(div) | HSTX_TIMEOUT(tmp), + dsi->regs + MCTL_DPHY_TIMEOUT1); + //dev_info(dsi->base.dev, "vrefresh %dHz, HSTX_TIMEOUT %ld, div %d\n", vrefresh, tmp, div); + + writel(LPRX_TIMEOUT(tmp), dsi->regs + MCTL_DPHY_TIMEOUT2); +#endif if (output->dev->mode_flags & MIPI_DSI_MODE_VIDEO) { switch (output->dev->format) { @@ -1094,7 +1263,7 @@ static void cdns_dsi_bridge_enable(struct drm_bridge *bridge) tmp = readl(dsi->regs + MCTL_MAIN_DATA_CTL); tmp &= ~(IF_VID_SELECT_MASK | HOST_EOT_GEN | IF_VID_MODE); - if (!(output->dev->mode_flags & MIPI_DSI_MODE_NO_EOT_PACKET)) + if (!(output->dev->mode_flags & MIPI_DSI_MODE_NO_EOT_PACKET)) //MIPI_DSI_MODE_EOT_PACKET" bit(9) tmp |= HOST_EOT_GEN; if (output->dev->mode_flags & MIPI_DSI_MODE_VIDEO) @@ -1176,7 +1345,6 @@ static int cdns_dsi_attach(struct mipi_dsi_host *host, * in a display pipeline. */ drm_bridge_add(&input->bridge); - return 0; } @@ -1343,6 +1511,7 @@ static int __maybe_unused cdns_dsi_suspend(struct device *dev) static UNIVERSAL_DEV_PM_OPS(cdns_dsi_pm_ops, cdns_dsi_suspend, cdns_dsi_resume, NULL); +#if 0 static int cdns_dsi_drm_remove(struct platform_device *pdev) { struct cdns_dsi *dsi = platform_get_drvdata(pdev); @@ -1352,12 +1521,36 @@ static int cdns_dsi_drm_remove(struct platform_device *pdev) return 0; } +#endif static const struct of_device_id cdns_dsi_of_match[] = { { .compatible = "cdns,dsi" }, { }, }; +#if 1 +static int cdns_check_register_access(struct cdns_dsi* dsi) +{ + const uint16_t CTRL_PATTERNS[] = {0x0000, 0xffff, 0xa5a5, 0x5a5a}; + int i; + uint32_t rd_val; + + for (i = 0; i < ARRAY_SIZE_DSI(CTRL_PATTERNS); i++) { + uint32_t temp = readl(dsi->regs + TEST_GENERIC); + temp &= ~0xffff; + temp |= TEST_CTRL(CTRL_PATTERNS[i]); + writel(temp, dsi->regs + TEST_GENERIC); + + rd_val = readl(dsi->regs + TEST_GENERIC); + if (rd_val != temp) { + return 1; + } + } + + return 0; +} +#endif + static int starfive_dsi_bind(struct device *dev, struct device *master, void *data) { struct cdns_dsi *dsi; @@ -1367,6 +1560,7 @@ static int starfive_dsi_bind(struct device *dev, struct device *master, void *da //struct drm_device *drm_dev = data; //struct starfive_drm_private *private = drm_dev->dev_private; int ret; + int irq; u32 val; dsi = devm_kzalloc(&pdev->dev, sizeof(*dsi), GFP_KERNEL); @@ -1381,32 +1575,33 @@ static int starfive_dsi_bind(struct device *dev, struct device *master, void *da if (IS_ERR(dsi->regs)) return PTR_ERR(dsi->regs); - ret = cdns_dsi_get_clock(pdev, dsi);//get clock res - if (ret) { - dev_err(&pdev->dev, "failed to get clock\n"); - return ret; - } - ret = cdns_dsi_get_reset(pdev, dsi);//get reset res - if (ret) { - dev_err(&pdev->dev, "failed to get reset\n"); - return ret; - } + ret = cdns_dsi_get_clock(dev, dsi);//get clock res - dsi->dphy = devm_phy_get(&pdev->dev, "dphy"); - if (IS_ERR(dsi->dphy)) - return PTR_ERR(dsi->dphy); + dev_info(dev, "dsi_sys_clk = %ld\n",clk_get_rate(dsi->dsi_sys_clk)); + + ret = cdns_dsi_get_reset(dev, dsi);//get reset res ret = cdns_dsi_clock_enable(dsi, dev); if (ret) { - dev_err(&pdev->dev, "failed to enable clock\n"); + dev_err(dev, "failed to enable clock\n"); return ret; } ret = cdns_dsi_resets_deassert(dsi, dev); if (ret < 0) { - dev_err(&pdev->dev, "failed to deassert reset\n"); + dev_err(dev, "failed to deassert reset\n"); return ret; } + irq = platform_get_irq(pdev, 0); + if (irq < 0){ + dev_err(dev, "---get irq error\n"); + return irq; + } + + dsi->dphy = devm_phy_get(&pdev->dev, "dphy"); + if (IS_ERR(dsi->dphy)) + return PTR_ERR(dsi->dphy); + val = readl(dsi->regs + ID_REG); if (REV_VENDOR_ID(val) != 0xcad) { @@ -1415,6 +1610,12 @@ static int starfive_dsi_bind(struct device *dev, struct device *master, void *da goto err_disable_pclk; } + ret = cdns_check_register_access(dsi); + if (ret) { + dev_err(dev, "error: r/w test generic reg failed\n"); + goto ERROR; + } + val = readl(dsi->regs + IP_CONF); dsi->direct_cmd_fifo_depth = 1 << (DIRCMD_FIFO_DEPTH(val) + 2); dsi->rx_fifo_depth = RX_FIFO_DEPTH(val); @@ -1440,6 +1641,13 @@ static int starfive_dsi_bind(struct device *dev, struct device *master, void *da writel(0, dsi->regs + TVG_STS_CTL); writel(0, dsi->regs + DPI_IRQ_EN); + ret = devm_request_irq(&pdev->dev, irq, cdns_dsi_interrupt, 0, + dev_name(&pdev->dev), dsi); + if (ret){ + dev_err(dev, "---devm_request_irq error\n"); + goto err_disable_pclk; + } + pm_runtime_enable(&pdev->dev); dsi->base.dev = &pdev->dev; dsi->base.ops = &cdns_dsi_ops; @@ -1449,6 +1657,7 @@ static int starfive_dsi_bind(struct device *dev, struct device *master, void *da goto err_disable_runtime_pm; init_seeed_panel(); + dev_err(dev, "====starfive_dsi_bind end\n"); return 0; err_disable_runtime_pm: @@ -1457,6 +1666,7 @@ err_disable_runtime_pm: err_disable_pclk: //clk_disable_unprepare(dsi->dsi_p_clk); +ERROR: return ret; } @@ -1467,11 +1677,13 @@ static void starfive_dsi_unbind(struct device *dev, struct device *master, void int ret; +#if 1 ret = cdns_dsi_resets_assert(dsi, dev); if (ret < 0) dev_err(dev, "failed to assert reset\n"); cdns_dsi_clock_disable(dsi); +#endif exit_seeed_panel(); mipi_dsi_host_unregister(&dsi->base); diff --git a/drivers/gpu/drm/verisilicon/starfive_drm_seeedpanel.c b/drivers/gpu/drm/verisilicon/starfive_drm_seeedpanel.c index 9ccf8b4..8bae58e 100644 --- a/drivers/gpu/drm/verisilicon/starfive_drm_seeedpanel.c +++ b/drivers/gpu/drm/verisilicon/starfive_drm_seeedpanel.c @@ -219,6 +219,7 @@ static struct seeed_panel_dev *panel_to_seeed(struct drm_panel *panel) } static const struct drm_display_mode seeed_panel_modes[] = { + #if 0 { .clock = 33000000 / 1000, .hdisplay = 800, @@ -230,6 +231,20 @@ static const struct drm_display_mode seeed_panel_modes[] = { .vsync_end = 480 + 135 + 5, .vtotal = 480 + 135 + 5 + 5, }, + #endif + #if 1 + { + .clock = 20144262 / 1000, + .hdisplay = 800, + .hsync_start = 800 + 10, + .hsync_end = 800 + 10 + 5, + .htotal = 800 + 10 + 5 + 5, + .vdisplay = 480, + .vsync_start = 480 + 4, + .vsync_end = 480 + 4 + 5, + .vtotal = 480 + 4 + 5 + 5, + }, + #endif }; static int seeed_panel_disable(struct drm_panel *panel) diff --git a/drivers/gpu/drm/verisilicon/vs_dc.c b/drivers/gpu/drm/verisilicon/vs_dc.c index 2e07223..8866578 100644 --- a/drivers/gpu/drm/verisilicon/vs_dc.c +++ b/drivers/gpu/drm/verisilicon/vs_dc.c @@ -827,6 +827,25 @@ int sys_dispctrl_clk(void) return 0; } +int drv_config_dc_4_dsi(struct vs_dc *dc, struct device *dev)//for dc_dsi config //only for dc_init +{ + int ret; + + dev_info(dev, "====> %s, %d.\n", __func__, __LINE__); + /*---------------------mux config------------*/ + SET_U0_DSITX_DATA_MAPPING_DPI_DP_SEL(0); + //SET_U0_DSITX_DATA_MAPPING_DP_MODE(vout_sys->vout_dsitx.dp_color_mode); + SET_U1_DISPLAY_PANEL_MUX_PANEL_SEL(0); + //_ENABLE_CLOCK_CLK_DOM_VOUT_TOP_LCD_CLK_; + ret = clk_prepare_enable(dc->vout_top_lcd); + if (ret) { + dev_err(dev, "failed to prepare/enable vout_top_lcd\n"); + return ret; + } + /*----------mux config------------*/ + dev_info(dev, "====> %s, %d.\n", __func__, __LINE__); + return 0; +} int sys_dispctrl_init(void) { @@ -874,6 +893,24 @@ static int dc_init(struct device *dev) dev_err(dev, "failed to init vout clk reset: %d\n", ret); return ret; } + #ifdef CONFIG_STARFIVE_DSI + dc->vout_src = devm_clk_get(dev, "vout_src"); + if (IS_ERR(dc->vout_src)){ + dev_err(dev,"failed to get dc->vout_src\n"); + return PTR_ERR(dc->vout_src); + } + dc->vout_top_lcd = devm_clk_get(dev, "vout_top_lcd"); + if (IS_ERR(dc->vout_top_lcd)){ + dev_err(dev,"failed to get dc->vout_top_lcd\n"); + return PTR_ERR(dc->vout_top_lcd); + } + ret = drv_config_dc_4_dsi(dc,dev); + if (ret < 0) { + dev_err(dev, "failed to drv_config_dc_4_dsi: %d\n", ret); + return ret; + } + #endif + printk("====> %s, %d.\n", __func__, __LINE__); ret = dc_hw_init(&dc->hw); if (ret) { @@ -906,6 +943,10 @@ static void vs_dc_enable(struct device *dev, struct drm_crtc *crtc) struct vs_crtc_state *crtc_state = to_vs_crtc_state(crtc->state); struct drm_display_mode *mode = &crtc->state->adjusted_mode; struct dc_hw_display display; + uint32_t vout_clock; + uint32_t div; + uint32_t div_new; + const uint32_t wanted_pxclk = mode->clock * 1000; display.bus_format = crtc_state->output_fmt; display.h_active = mode->hdisplay; @@ -960,6 +1001,24 @@ static void vs_dc_enable(struct device *dev, struct drm_crtc *crtc) } #endif +#ifdef CONFIG_STARFIVE_DSI//7110 mipi + /*-----------------div freq clk sys_dispctrl_clk()----------*/ + //const uint32_t wanted_pxclk = 20144262;//dpi->pixelclock; + //wanted_pxclk = mode->clock * 1000; + dev_info(dev, "wanted_pxclk = %d\n",wanted_pxclk); + //uint32_t vout_clock = 614400000; + vout_clock = clk_get_rate(dc->vout_src); + dev_info(dev, "vout_clock = %d\n", vout_clock); + div = _GET_CLOCK_DIVIDE_STATUS_CLK_DC8200_PIX0_; + div_new = vout_clock / wanted_pxclk; + if (div != div_new) { + div = div_new; + _DIVIDE_CLOCK_CLK_DC8200_PIX0_(div); + _SWITCH_CLOCK_CLK_U0_DC8200_CLK_PIX0_SOURCE_CLK_DC8200_PIX0_; + } + /*-----------------div freq clk sys_dispctrl_clk()----------*/ +#endif + if (crtc_state->encoder_type == DRM_MODE_ENCODER_DSI) dc_hw_set_out(&dc->hw, OUT_DPI, display.id); else diff --git a/drivers/gpu/drm/verisilicon/vs_dc.h b/drivers/gpu/drm/verisilicon/vs_dc.h index 994e4e5..2e21d16 100644 --- a/drivers/gpu/drm/verisilicon/vs_dc.h +++ b/drivers/gpu/drm/verisilicon/vs_dc.h @@ -93,8 +93,18 @@ struct vs_dc { struct reset_control *rstn_dc8200_ahb; //dc8200 rst +//7110 mp +struct clk *vout_top_axi;//20220530 for vout_clock +struct clk *vout_top_lcd;//7110 mp +//7110 mp }; extern struct platform_driver dc_platform_driver; +//20220530 +//extern struct platform_driver simple_encoder_driver; +extern struct platform_driver starfive_dsi_platform_driver; +extern int init_seeed_panel(void); +extern void exit_seeed_panel(void); + #endif /* __VS_DC_H__ */ diff --git a/drivers/gpu/drm/verisilicon/vs_dc_hw.c b/drivers/gpu/drm/verisilicon/vs_dc_hw.c index d745a95e..e36132b 100644 --- a/drivers/gpu/drm/verisilicon/vs_dc_hw.c +++ b/drivers/gpu/drm/verisilicon/vs_dc_hw.c @@ -1891,6 +1891,26 @@ static void setup_display(struct dc_hw *hw, struct dc_hw_display *display) else dc_set_clear(hw, DC_DISPLAY_PANEL_START, 0, BIT(1) | BIT(2)); +#ifdef CONFIG_STARFIVE_DSI + dc_write(hw, DC_DISPLAY_H + offset, hw->display[id].h_active | + (hw->display[id].h_total << 16)); + + dc_write(hw, DC_DISPLAY_H_SYNC + offset, + hw->display[id].h_sync_start | + (hw->display[id].h_sync_end << 15) | + BIT(31) | + BIT(30)); + + dc_write(hw, DC_DISPLAY_V + offset, hw->display[id].v_active | + (hw->display[id].v_total << 16)); + + dc_write(hw, DC_DISPLAY_V_SYNC + offset, + hw->display[id].v_sync_start | + (hw->display[id].v_sync_end << 15) | + (hw->display[id].v_sync_polarity ? 0 : BIT(31)) | + BIT(30)); + +#else dc_write(hw, DC_DISPLAY_H + offset, hw->display[id].h_active | (hw->display[id].h_total << 16)); dc_write(hw, DC_DISPLAY_H_SYNC + offset, @@ -1905,6 +1925,7 @@ static void setup_display(struct dc_hw *hw, struct dc_hw_display *display) (hw->display[id].v_sync_end << 15) | (hw->display[id].v_sync_polarity ? 0 : BIT(31)) | BIT(30)); +#endif if (hw->info->pipe_sync) { switch (display->sync_mode) { diff --git a/drivers/gpu/drm/verisilicon/vs_drv.c b/drivers/gpu/drm/verisilicon/vs_drv.c index 3e64608..d4f7c32 100644 --- a/drivers/gpu/drm/verisilicon/vs_drv.c +++ b/drivers/gpu/drm/verisilicon/vs_drv.c @@ -311,7 +311,7 @@ static struct platform_driver *drm_sub_drivers[] = { &starfive_dsi_platform_driver, #endif /* encoder */ - //&simple_encoder_driver, + &simple_encoder_driver, //&starfive_encoder_driver, #ifdef CONFIG_VERISILICON_VIRTUAL_DISPLAY diff --git a/drivers/gpu/drm/verisilicon/vs_simple_enc.c b/drivers/gpu/drm/verisilicon/vs_simple_enc.c index 25b1a83..99f3ef0 100644 --- a/drivers/gpu/drm/verisilicon/vs_simple_enc.c +++ b/drivers/gpu/drm/verisilicon/vs_simple_enc.c @@ -200,15 +200,18 @@ static const struct drm_encoder_helper_funcs encoder_helper_funcs = { static int encoder_bind(struct device *dev, struct device *master, void *data) { + struct drm_device *drm_dev = data; struct simple_encoder *simple = dev_get_drvdata(dev); struct drm_encoder *encoder; struct drm_bridge *bridge; + struct drm_panel *tmp_panel;//20220530 int ret; encoder = &simple->encoder; /* Encoder. */ + ret = drm_encoder_init(drm_dev, encoder, &encoder_funcs, simple->priv->encoder_type, NULL); if (ret) @@ -220,10 +223,21 @@ static int encoder_bind(struct device *dev, struct device *master, void *data) drm_of_find_possible_crtcs(drm_dev, dev->of_node); /* output port is port1*/ - ret = drm_of_find_panel_or_bridge(dev->of_node, 1, -1, NULL, &bridge); - if (ret) - goto err; +#ifdef CONFIG_STARFIVE_DSI + ret = drm_of_find_panel_or_bridge(dev->of_node, 1, 0,&tmp_panel, &bridge); + if (ret){ + printk("==no panel, %d\n",ret); + //dev_err_probe(dev, ret, "endpoint returns %d\n", ret); + goto err; + } + if (tmp_panel) + dev_err(dev, "found panel on endpoint\n"); +#else + ret = drm_of_find_panel_or_bridge(dev->of_node, 1, -1, NULL, &bridge); + if (ret) + goto err; +#endif #if KERNEL_VERSION(5, 7, 0) <= LINUX_VERSION_CODE ret = drm_bridge_attach(encoder, bridge, NULL, 0); #else diff --git a/drivers/phy/m31/7110-m31-dphy.h b/drivers/phy/m31/7110-m31-dphy.h new file mode 100644 index 0000000..9ed496e --- /dev/null +++ b/drivers/phy/m31/7110-m31-dphy.h @@ -0,0 +1,359 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2022 StarFive Technology Co., Ltd. + */ + +#ifndef __7110_M31_DPHY_H__ +#define __7110_M31_DPHY_H__ + +#define AON_POWER_READY_N_WIDTH 0x1U +#define AON_POWER_READY_N_SHIFT 0x0U +#define AON_POWER_READY_N_MASK 0x1U +#define CFG_CKLANE_SET_WIDTH 0x5U +#define CFG_CKLANE_SET_SHIFT 0x1U +#define CFG_CKLANE_SET_MASK 0x3EU +#define CFG_DATABUS16_SEL_WIDTH 0x1U +#define CFG_DATABUS16_SEL_SHIFT 0x6U +#define CFG_DATABUS16_SEL_MASK 0x40U +#define CFG_DPDN_SWAP_WIDTH 0x5U +#define CFG_DPDN_SWAP_SHIFT 0x7U +#define CFG_DPDN_SWAP_MASK 0xF80U +#define CFG_L0_SWAP_SEL_WIDTH 0x3U +#define CFG_L0_SWAP_SEL_SHIFT 0xCU +#define CFG_L0_SWAP_SEL_MASK 0x7000U +#define CFG_L1_SWAP_SEL_WIDTH 0x3U +#define CFG_L1_SWAP_SEL_SHIFT 0xFU +#define CFG_L1_SWAP_SEL_MASK 0x38000U +#define CFG_L2_SWAP_SEL_WIDTH 0x3U +#define CFG_L2_SWAP_SEL_SHIFT 0x12U +#define CFG_L2_SWAP_SEL_MASK 0x1C0000U +#define CFG_L3_SWAP_SEL_WIDTH 0x3U +#define CFG_L3_SWAP_SEL_SHIFT 0x15U +#define CFG_L3_SWAP_SEL_MASK 0xE00000U +#define CFG_L4_SWAP_SEL_WIDTH 0x3U +#define CFG_L4_SWAP_SEL_SHIFT 0x18U +#define CFG_L4_SWAP_SEL_MASK 0x7000000U +#define MPOSV_31_0__WIDTH 0x20U +#define MPOSV_31_0__SHIFT 0x0U +#define MPOSV_31_0__MASK 0xFFFFFFFFU +#define MPOSV_46_32__WIDTH 0xFU +#define MPOSV_46_32__SHIFT 0x0U +#define MPOSV_46_32__MASK 0x7FFFU +#define RGS_CDTX_PLL_FM_CPLT_WIDTH 0x1U +#define RGS_CDTX_PLL_FM_CPLT_SHIFT 0xFU +#define RGS_CDTX_PLL_FM_CPLT_MASK 0x8000U +#define RGS_CDTX_PLL_FM_OVER_WIDTH 0x1U +#define RGS_CDTX_PLL_FM_OVER_SHIFT 0x10U +#define RGS_CDTX_PLL_FM_OVER_MASK 0x10000U +#define RGS_CDTX_PLL_FM_UNDER_WIDTH 0x1U +#define RGS_CDTX_PLL_FM_UNDER_SHIFT 0x11U +#define RGS_CDTX_PLL_FM_UNDER_MASK 0x20000U +#define RGS_CDTX_PLL_UNLOCK_WIDTH 0x1U +#define RGS_CDTX_PLL_UNLOCK_SHIFT 0x12U +#define RGS_CDTX_PLL_UNLOCK_MASK 0x40000U +#define RG_CDTX_L0N_HSTX_RES_WIDTH 0x5U +#define RG_CDTX_L0N_HSTX_RES_SHIFT 0x13U +#define RG_CDTX_L0N_HSTX_RES_MASK 0xF80000U +#define RG_CDTX_L0P_HSTX_RES_WIDTH 0x5U +#define RG_CDTX_L0P_HSTX_RES_SHIFT 0x18U +#define RG_CDTX_L0P_HSTX_RES_MASK 0x1F000000U + +#define RG_CDTX_L1N_HSTX_RES_WIDTH 0x5U +#define RG_CDTX_L1N_HSTX_RES_SHIFT 0x0U +#define RG_CDTX_L1N_HSTX_RES_MASK 0x1FU +#define RG_CDTX_L1P_HSTX_RES_WIDTH 0x5U +#define RG_CDTX_L1P_HSTX_RES_SHIFT 0x5U +#define RG_CDTX_L1P_HSTX_RES_MASK 0x3E0U +#define RG_CDTX_L2N_HSTX_RES_WIDTH 0x5U +#define RG_CDTX_L2N_HSTX_RES_SHIFT 0xAU +#define RG_CDTX_L2N_HSTX_RES_MASK 0x7C00U +#define RG_CDTX_L2P_HSTX_RES_WIDTH 0x5U +#define RG_CDTX_L2P_HSTX_RES_SHIFT 0xFU +#define RG_CDTX_L2P_HSTX_RES_MASK 0xF8000U +#define RG_CDTX_L3N_HSTX_RES_WIDTH 0x5U +#define RG_CDTX_L3N_HSTX_RES_SHIFT 0x14U +#define RG_CDTX_L3N_HSTX_RES_MASK 0x1F00000U +#define RG_CDTX_L3P_HSTX_RES_WIDTH 0x5U +#define RG_CDTX_L3P_HSTX_RES_SHIFT 0x19U +#define RG_CDTX_L3P_HSTX_RES_MASK 0x3E000000U + +#define RG_CDTX_L4N_HSTX_RES_WIDTH 0x5U +#define RG_CDTX_L4N_HSTX_RES_SHIFT 0x0U +#define RG_CDTX_L4N_HSTX_RES_MASK 0x1FU +#define RG_CDTX_L4P_HSTX_RES_WIDTH 0x5U +#define RG_CDTX_L4P_HSTX_RES_SHIFT 0x5U +#define RG_CDTX_L4P_HSTX_RES_MASK 0x3E0U + +#define RG_CDTX_PLL_FBK_FRA_WIDTH 0x18U +#define RG_CDTX_PLL_FBK_FRA_SHIFT 0x0U +#define RG_CDTX_PLL_FBK_FRA_MASK 0xFFFFFFU + +#define RG_CDTX_PLL_FBK_INT_WIDTH 0x9U +#define RG_CDTX_PLL_FBK_INT_SHIFT 0x0U +#define RG_CDTX_PLL_FBK_INT_MASK 0x1FFU +#define RG_CDTX_PLL_FM_EN_WIDTH 0x1U +#define RG_CDTX_PLL_FM_EN_SHIFT 0x9U +#define RG_CDTX_PLL_FM_EN_MASK 0x200U +#define RG_CDTX_PLL_LDO_STB_X2_EN_WIDTH 0x1U +#define RG_CDTX_PLL_LDO_STB_X2_EN_SHIFT 0xAU +#define RG_CDTX_PLL_LDO_STB_X2_EN_MASK 0x400U +#define RG_CDTX_PLL_PRE_DIV_WIDTH 0x2U +#define RG_CDTX_PLL_PRE_DIV_SHIFT 0xBU +#define RG_CDTX_PLL_PRE_DIV_MASK 0x1800U +#define RG_CDTX_PLL_SSC_DELTA_WIDTH 0x12U +#define RG_CDTX_PLL_SSC_DELTA_SHIFT 0xDU +#define RG_CDTX_PLL_SSC_DELTA_MASK 0x7FFFE000U + +#define RG_CDTX_PLL_SSC_DELTA_INIT_WIDTH 0x12U +#define RG_CDTX_PLL_SSC_DELTA_INIT_SHIFT 0x0U +#define RG_CDTX_PLL_SSC_DELTA_INIT_MASK 0x3FFFFU +#define RG_CDTX_PLL_SSC_EN_WIDTH 0x1U +#define RG_CDTX_PLL_SSC_EN_SHIFT 0x12U +#define RG_CDTX_PLL_SSC_EN_MASK 0x40000U +#define RG_CDTX_PLL_SSC_PRD_WIDTH 0xAU +#define RG_CDTX_PLL_SSC_PRD_SHIFT 0x13U +#define RG_CDTX_PLL_SSC_PRD_MASK 0x1FF80000U + +#define RG_CLANE_HS_CLK_POST_TIME_WIDTH 0x8U +#define RG_CLANE_HS_CLK_POST_TIME_SHIFT 0x0U +#define RG_CLANE_HS_CLK_POST_TIME_MASK 0xFFU +#define RG_CLANE_HS_CLK_PRE_TIME_WIDTH 0x8U +#define RG_CLANE_HS_CLK_PRE_TIME_SHIFT 0x8U +#define RG_CLANE_HS_CLK_PRE_TIME_MASK 0xFF00U +#define RG_CLANE_HS_PRE_TIME_WIDTH 0x8U +#define RG_CLANE_HS_PRE_TIME_SHIFT 0x10U +#define RG_CLANE_HS_PRE_TIME_MASK 0xFF0000U +#define RG_CLANE_HS_TRAIL_TIME_WIDTH 0x8U +#define RG_CLANE_HS_TRAIL_TIME_SHIFT 0x18U +#define RG_CLANE_HS_TRAIL_TIME_MASK 0xFF000000U + +#define RG_CLANE_HS_ZERO_TIME_WIDTH 0x8U +#define RG_CLANE_HS_ZERO_TIME_SHIFT 0x0U +#define RG_CLANE_HS_ZERO_TIME_MASK 0xFFU +#define RG_DLANE_HS_PRE_TIME_WIDTH 0x8U +#define RG_DLANE_HS_PRE_TIME_SHIFT 0x8U +#define RG_DLANE_HS_PRE_TIME_MASK 0xFF00U +#define RG_DLANE_HS_TRAIL_TIME_WIDTH 0x8U +#define RG_DLANE_HS_TRAIL_TIME_SHIFT 0x10U +#define RG_DLANE_HS_TRAIL_TIME_MASK 0xFF0000U +#define RG_DLANE_HS_ZERO_TIME_WIDTH 0x8U +#define RG_DLANE_HS_ZERO_TIME_SHIFT 0x18U +#define RG_DLANE_HS_ZERO_TIME_MASK 0xFF000000U + +#define RG_EXTD_CYCLE_SEL_WIDTH 0x3U +#define RG_EXTD_CYCLE_SEL_SHIFT 0x0U +#define RG_EXTD_CYCLE_SEL_MASK 0x7U + +#define SCFG_C_HS_PRE_ZERO_TIME_WIDTH 0x20U +#define SCFG_C_HS_PRE_ZERO_TIME_SHIFT 0x0U +#define SCFG_C_HS_PRE_ZERO_TIME_MASK 0xFFFFFFFFU + +#define SCFG_DPHY_SRC_SEL_WIDTH 0x1U +#define SCFG_DPHY_SRC_SEL_SHIFT 0x0U +#define SCFG_DPHY_SRC_SEL_MASK 0x1U +#define SCFG_DSI_TXREADY_ESC_SEL_WIDTH 0x2U +#define SCFG_DSI_TXREADY_ESC_SEL_SHIFT 0x1U +#define SCFG_DSI_TXREADY_ESC_SEL_MASK 0x6U +#define SCFG_PPI_C_READY_SEL_WIDTH 0x2U +#define SCFG_PPI_C_READY_SEL_SHIFT 0x3U +#define SCFG_PPI_C_READY_SEL_MASK 0x18U +#define VCONTROL_WIDTH 0x5U +#define VCONTROL_SHIFT 0x5U +#define VCONTROL_MASK 0x3E0U + +#define XCFGI_DW00_WIDTH 0x20U +#define XCFGI_DW00_SHIFT 0x0U +#define XCFGI_DW00_MASK 0xFFFFFFFFU + +#define XCFGI_DW01_WIDTH 0x20U +#define XCFGI_DW01_SHIFT 0x0U +#define XCFGI_DW01_MASK 0xFFFFFFFFU + +#define XCFGI_DW02_WIDTH 0x20U +#define XCFGI_DW02_SHIFT 0x0U +#define XCFGI_DW02_MASK 0xFFFFFFFFU + +#define XCFGI_DW03_WIDTH 0x20U +#define XCFGI_DW03_SHIFT 0x0U +#define XCFGI_DW03_MASK 0xFFFFFFFFU + +#define XCFGI_DW04_WIDTH 0x20U +#define XCFGI_DW04_SHIFT 0x0U +#define XCFGI_DW04_MASK 0xFFFFFFFFU + +#define XCFGI_DW05_WIDTH 0x20U +#define XCFGI_DW05_SHIFT 0x0U +#define XCFGI_DW05_MASK 0xFFFFFFFFU + +#define XCFGI_DW06_WIDTH 0x20U +#define XCFGI_DW06_SHIFT 0x0U +#define XCFGI_DW06_MASK 0xFFFFFFFFU + +#define XCFGI_DW07_WIDTH 0x20U +#define XCFGI_DW07_SHIFT 0x0U +#define XCFGI_DW07_MASK 0xFFFFFFFFU + +#define XCFGI_DW08_WIDTH 0x20U +#define XCFGI_DW08_SHIFT 0x0U +#define XCFGI_DW08_MASK 0xFFFFFFFFU + +#define XCFGI_DW09_WIDTH 0x20U +#define XCFGI_DW09_SHIFT 0x0U +#define XCFGI_DW09_MASK 0xFFFFFFFFU + +#define XCFGI_DW0A_WIDTH 0x20U +#define XCFGI_DW0A_SHIFT 0x0U +#define XCFGI_DW0A_MASK 0xFFFFFFFFU + +#define XCFGI_DW0B_WIDTH 0x20U +#define XCFGI_DW0B_SHIFT 0x0U +#define XCFGI_DW0B_MASK 0xFFFFFFFFU + +#define DBG1_MUX_DOUT_WIDTH 0x8U +#define DBG1_MUX_DOUT_SHIFT 0x0U +#define DBG1_MUX_DOUT_MASK 0xFFU +#define DBG1_MUX_SEL_WIDTH 0x5U +#define DBG1_MUX_SEL_SHIFT 0x8U +#define DBG1_MUX_SEL_MASK 0x1F00U +#define DBG2_MUX_DOUT_WIDTH 0x8U +#define DBG2_MUX_DOUT_SHIFT 0xDU +#define DBG2_MUX_DOUT_MASK 0x1FE000U +#define DBG2_MUX_SEL_WIDTH 0x5U +#define DBG2_MUX_SEL_SHIFT 0x15U +#define DBG2_MUX_SEL_MASK 0x3E00000U +#define REFCLK_IN_SEL_WIDTH 0x3U +#define REFCLK_IN_SEL_SHIFT 0x1AU +#define REFCLK_IN_SEL_MASK 0x1C000000U +#define RESETB_WIDTH 0x1U +#define RESETB_SHIFT 0x1DU +#define RESETB_MASK 0x20000000U + +//aonsys con +#define AON_GP_REG_WIDTH 0x20U +#define AON_GP_REG_SHIFT 0x0U +#define AON_GP_REG_MASK 0xFFFFFFFFU + + +#define M31_DPHY_REFCLK_RESERVED 0 +#define M31_DPHY_REFCLK_12M 1 +#define M31_DPHY_REFCLK_19_2M 2 +#define M31_DPHY_REFCLK_25M 3 +#define M31_DPHY_REFCLK_26M 4 +#define M31_DPHY_REFCLK_27M 5 +#define M31_DPHY_REFCLK_38_4M 6 +#define M31_DPHY_REFCLK_52M 7 +#define M31_DPHY_REFCLK_BUTT 8 + +#define DPHY_TX_PSW_EN_MASK (1<<30) + +struct m31_dphy_config { + int ref_clk; + unsigned long bitrate; + uint32_t pll_prev_div, pll_fbk_int, pll_fbk_fra, extd_cycle_sel; + uint32_t dlane_hs_pre_time, dlane_hs_zero_time, dlane_hs_trail_time; + uint32_t clane_hs_pre_time, clane_hs_zero_time, clane_hs_trail_time; + uint32_t clane_hs_clk_pre_time, clane_hs_clk_post_time; +}; + +#define M31_DPHY_REFCLK M31_DPHY_REFCLK_12M +#define M31_DPHY_BITRATE_ALIGN 10000000 + + + +static const struct m31_dphy_config m31_dphy_configs[] = { +#if (M31_DPHY_REFCLK == M31_DPHY_REFCLK_25M) + //25000000,M31_DPHY_HS_RATE_80M,0x1,0x66,0x666666,0x4,0x0E,0x1D,0x15,0x05,0x2B,0x0D,0x0F,0x71, //comment unusual config for simplicity + {25000000, 100000000, 0x1, 0x80, 0x000000, 0x4, 0x10, 0x21, 0x17, 0x07, 0x35, 0x0F, 0x0F, 0x73,}, + {25000000, 200000000, 0x1, 0x80, 0x000000, 0x3, 0x0C, 0x1B, 0x13, 0x07, 0x35, 0x0F, 0x07, 0x3F,}, + {25000000, 300000000, 0x1, 0xC0, 0x000000, 0x3, 0x11, 0x25, 0x19, 0x0A, 0x50, 0x15, 0x07, 0x45,}, + {25000000, 400000000, 0x1, 0x80, 0x000000, 0x2, 0x0A, 0x18, 0x11, 0x07, 0x35, 0x0F, 0x03, 0x25,}, + {25000000, 500000000, 0x1, 0xA0, 0x000000, 0x2, 0x0C, 0x1D, 0x14, 0x09, 0x42, 0x12, 0x03, 0x28,}, + {25000000, 600000000, 0x1, 0xC0, 0x000000, 0x2, 0x0E, 0x23, 0x17, 0x0A, 0x50, 0x15, 0x03, 0x2B,}, + {25000000, 700000000, 0x1, 0x70, 0x000000, 0x1, 0x08, 0x14, 0x0F, 0x06, 0x2F, 0x0E, 0x01, 0x16,}, + {25000000, 800000000, 0x1, 0x80, 0x000000, 0x1, 0x09, 0x17, 0x10, 0x07, 0x35, 0x0F, 0x01, 0x18,}, + {25000000, 900000000, 0x1, 0x90, 0x000000, 0x1, 0x0A, 0x19, 0x12, 0x08, 0x3C, 0x10, 0x01, 0x19,}, + {25000000, 1000000000, 0x1, 0xA0, 0x000000, 0x1, 0x0B, 0x1C, 0x13, 0x09, 0x42, 0x12, 0x01, 0x1B,}, + {25000000, 1100000000, 0x1, 0xB0, 0x000000, 0x1, 0x0C, 0x1E, 0x15, 0x09, 0x4A, 0x14, 0x01, 0x1D,}, + {25000000, 1200000000, 0x1, 0xC0, 0x000000, 0x1, 0x0E, 0x20, 0x16, 0x0A, 0x50, 0x15, 0x01, 0x1E,}, + {25000000, 1300000000, 0x1, 0x68, 0x000000, 0x0, 0x07, 0x12, 0x0D, 0x05, 0x2C, 0x0D, 0x00, 0x0F,}, + {25000000, 1400000000, 0x1, 0x70, 0x000000, 0x0, 0x07, 0x14, 0x0E, 0x06, 0x2F, 0x0E, 0x00, 0x10,}, + {25000000, 1500000000, 0x1, 0x78, 0x000000, 0x0, 0x08, 0x14, 0x0F, 0x06, 0x32, 0x0E, 0x00, 0x11,}, + {25000000, 1600000000, 0x1, 0x80, 0x000000, 0x0, 0x09, 0x15, 0x10, 0x07, 0x35, 0x0F, 0x00, 0x12,}, + {25000000, 1700000000, 0x1, 0x88, 0x000000, 0x0, 0x09, 0x17, 0x10, 0x07, 0x39, 0x10, 0x00, 0x12,}, + {25000000, 1800000000, 0x1, 0x90, 0x000000, 0x0, 0x0A, 0x18, 0x11, 0x08, 0x3C, 0x10, 0x00, 0x13,}, + {25000000, 1900000000, 0x1, 0x98, 0x000000, 0x0, 0x0A, 0x1A, 0x12, 0x08, 0x3F, 0x11, 0x00, 0x14,}, + {25000000, 2000000000, 0x1, 0xA0, 0x000000, 0x0, 0x0B, 0x1B, 0x13, 0x09, 0x42, 0x12, 0x00, 0x15,}, + {25000000, 2100000000, 0x1, 0xA8, 0x000000, 0x0, 0x0B, 0x1C, 0x13, 0x09, 0x46, 0x13, 0x00, 0x15,}, + {25000000, 2200000000, 0x1, 0xB0, 0x000000, 0x0, 0x0C, 0x1D, 0x14, 0x09, 0x4A, 0x14, 0x00, 0x16,}, + {25000000, 2300000000, 0x1, 0xB8, 0x000000, 0x0, 0x0C, 0x1F, 0x15, 0x0A, 0x4C, 0x14, 0x00, 0x17,}, + {25000000, 2400000000, 0x1, 0xC0, 0x000000, 0x0, 0x0D, 0x20, 0x16, 0x0A, 0x50, 0x15, 0x00, 0x18,}, + {25000000, 2500000000, 0x1, 0xC8, 0x000000, 0x0, 0x0E, 0x21, 0x16, 0x0B, 0x53, 0x16, 0x00, 0x18,}, +#elif (M31_DPHY_REFCLK == M31_DPHY_REFCLK_12M) + + {12000000, 180000000, 0x0, 0x78, 0x0 << 16 | 0x0<<8 | 0x0, 0x3, 0xb, 0x19, 0x12, 0x6, 0x30, 0xe, 0x7, 0x3e,}, + + {12000000, 500000000, 0x0, 0xa6, 0xaa << 16 | 0xaa << 8 | 0xaa, 0x2, 0xc, 0x1d, 0x14, 0x9, 0x42, 0x12, 0x3, 0x28,}, + {12000000, 510000000, 0x0, 0xaa, 0x0 << 16 | 0x0 << 8 | 0x0, 0x2, 0xc, 0x1e, 0x14, 0x9, 0x44, 0x12, 0x3, 0x28,}, + + {12000000, 590000000, 0x0, 0xc4, 0xaa << 16 | 0xaa << 8 | 0xaa, 0x2, 0xe, 0x22, 0x17, 0xa, 0x4f, 0x15, 0x3, 0x2b,}, + + {12000000, 690000000, 0x0, 0x73, 0x0 << 16 | 0x0 << 8 | 0x0, 0x1, 0x8, 0x14, 0xe, 0x6, 0x2e, 0xd, 0x1, 0x16,}, + + {12000000, 720000000, 0x0, 0x78, 0x0<<16 | 0x0 << 8 | 0x0, 0x1, 0x8, 0x15, 0xf, 0x6, 0x30, 0xe, 0x1, 0x17,}, + + {12000000, 840000000, 0x0, 0x8c, 0x0 << 16 | 0x0<<8 | 0x0, 0x1, 0x9, 0x18, 0x11, 0x7, 0x38, 0x10, 0x1, 0x19,}, + +#endif +}; + +static inline u32 sf_dphy_get_reg(void* io_addr, u32 shift, u32 mask) +{ + //void __iomem *io_addr = ioremap(addr, 0x10000); + u32 tmp; + tmp = readl(io_addr); + tmp = (tmp & mask) >> shift; + return tmp; +} + +static inline void sf_dphy_set_reg(void* io_addr, u32 data, u32 shift, u32 mask) +{ + //void __iomem *io_addr = ioremap(addr, 0x10000); + + u32 tmp; + tmp = readl(io_addr); + tmp &= ~mask; + tmp |= (data << shift) & mask; + writel(tmp, io_addr); +} + +static inline void sf_dphy_assert_rst(void* io_addr, u32 addr_status, u32 mask) +{ + //void __iomem *io_addr = ioremap(addr, 0x4); + + void __iomem *io_addr_status = ioremap(addr_status, 0x4); + + u32 tmp; + tmp = readl(io_addr); + tmp |= mask; + writel(tmp,io_addr); + do{ + tmp = readl(io_addr_status); + }while((tmp & mask)!=0); +} + +static inline void sf_dphy_clear_rst (void* io_addr, u32 addr_status, u32 mask) +{ + //void __iomem *io_addr = ioremap(addr, 0x4); + + void __iomem *io_addr_status = ioremap(addr_status, 0x4); + + u32 tmp; + tmp = readl(io_addr); + tmp &= ~mask; + writel(tmp, io_addr); + do{ + tmp = readl(io_addr_status); + }while((tmp & mask) != mask); +} + +#endif /* __7110_M31_DPHY_H__ */ diff --git a/drivers/phy/m31/Kconfig b/drivers/phy/m31/Kconfig old mode 100755 new mode 100644 diff --git a/drivers/phy/m31/Makefile b/drivers/phy/m31/Makefile old mode 100755 new mode 100644 diff --git a/drivers/phy/m31/phy-m31-dphy-tx0.c b/drivers/phy/m31/phy-m31-dphy-tx0.c old mode 100755 new mode 100644 index e82e1da..0c73306 --- a/drivers/phy/m31/phy-m31-dphy-tx0.c +++ b/drivers/phy/m31/phy-m31-dphy-tx0.c @@ -26,6 +26,10 @@ #include #include #include +#include +//#include //20220602 pmic support +#include +#include "7110-m31-dphy.h" //syscfg registers #define SCFG_DSI_CSI_SEL 0x2c @@ -129,14 +133,98 @@ struct sf_dphy { void __iomem *topsys; struct clk_bulk_data *clks; + //20220601 clk support + struct clk * txesc_clk; + struct reset_control *sys_rst; + struct reset_control *txbytehs_rst; + //20220601 clk support + void __iomem *aonsys;//7110 aonsys con struct phy_configure_opts_mipi_dphy config; u8 hsfreq; + struct regulator *mipitx_1p8; + struct regulator *mipitx_0p9; + struct phy *phy; }; +static int sf_dphy_clkrst_get(struct device *dev, struct sf_dphy *dphy) +{ + //dev_info(dev,"===sf_dphy_clkrst_get begin\n"); + int ret; + + dphy->txesc_clk = devm_clk_get(dev, "dphy_txesc"); + if (IS_ERR(dphy->txesc_clk)){ + dev_err(dev, "===txesc_clk get error\n"); + return PTR_ERR(dphy->txesc_clk); + } + dphy->sys_rst = reset_control_get_exclusive(dev, "dphy_sys"); + if (IS_ERR(dphy->sys_rst)){ + dev_err(dev, "===sys_rst get error\n"); + return PTR_ERR(dphy->sys_rst); + } + //dphy->txbytehs_rst = reset_control_get_exclusive(dev,"dphy_txbytehs"); + //if (IS_ERR(dphy->txbytehs_rst)){ + // dev_err(dev,"===txbytehs_rst get error\n"); + // return PTR_ERR(dphy->txbytehs_rst); + //} + //dev_info(dev,"===sf_dphy_clkrst_get begin\n"); + return ret; +} +static int sf_dphy_clkrst_ena_deas(struct device *dev, struct sf_dphy *dphy) +{ + //dev_info(dev,"===sf_dphy_clkrst_ena_deas begin\n"); + int ret; + + ret = clk_prepare_enable(dphy->txesc_clk); + if (ret) { + dev_err(dev, "failed to prepare/enable txesc_clk\n"); + return ret; + } + ret = reset_control_deassert(dphy->sys_rst); + if (ret < 0) { + dev_err(dev, "failed to deassert sys_rst\n"); + return ret; + } + //ret = reset_control_deassert(dphy->txbytehs_rst); + //if (ret < 0) { + // dev_err(dev, "failed to deassert txbytehs_rst\n"); + // return ret; + //} + //dev_info(dev,"===sf_dphy_clkrst_ena_deas successful\n"); + return ret; +} + +static int sf_dphy_clkrst_disa_assert(struct device *dev, struct sf_dphy *dphy) +{ + //dev_info(dev,"===sf_dphy_clkrst_disa_assert begin\n"); + int ret; + ret = reset_control_assert(dphy->sys_rst); + if (ret < 0) { + dev_err(dev, "failed to assert sys_rst\n"); + return ret; + } + //reset_control_assert(dphy->txbytehs_rst); + clk_disable_unprepare(dphy->txesc_clk); + + //dev_info(dev,"===sf_dphy_clkrst_disa_assert successful\n"); + return ret; +} + +/* +*static int sf_dphy_remove(struct platform_device *pdev) +*{ +* struct sf_dphy *dphy = dev_get_drvdata(&pdev->dev); +* reset_control_assert(dphy->sys_rst); +* //reset_control_assert(dphy->txbytehs_rst); +* clk_disable_unprepare(dphy->txesc_clk); +* return 0; +*} +*/ + +#if 0//original static u32 top_sys_read32(struct sf_dphy *priv, u32 reg) { return ioread32(priv->topsys + reg); @@ -411,9 +499,10 @@ static void polling_dphy_lock(struct sf_dphy *priv) } static int sf_dphy_configure(struct phy *phy, union phy_configure_opts *opts) -{ +{ //dev_info(dphy->dev,"--->sf_dphy_configure\n"); struct sf_dphy *dphy = phy_get_drvdata(phy); uint32_t bit_rate = 800000000/1000000UL;//new mipi panel clock setting + //uint32_t bit_rate = 500000000/1000000UL;//7110 mipi panel clock setting dphy_config(dphy, bit_rate); @@ -421,26 +510,180 @@ static int sf_dphy_configure(struct phy *phy, union phy_configure_opts *opts) mdelay(10); polling_dphy_lock(dphy); + //dev_info(dphy->dev,"--->sf_dphy_configure\n"); return 0; } +#endif -static int sf_dphy_power_on(struct phy *phy) +static int is_pll_locked(struct sf_dphy *dphy) { - return 0; + //int tmp = GET_U0_MIPITX_DPHY_RGS_CDTX_PLL_UNLOCK; + int tmp = sf_dphy_get_reg(dphy->topsys + 0x8, + RGS_CDTX_PLL_UNLOCK_SHIFT, RGS_CDTX_PLL_UNLOCK_MASK); + return !tmp; +} +static void reset(int assert, struct sf_dphy *dphy) +{ + dev_info(dphy->dev, "1 SET_U0_MIPITX_DPHY_RESETB\n"); + //SET_U0_MIPITX_DPHY_RESETB((!assert)); + sf_dphy_set_reg(dphy->topsys + 0x64, (!assert), RESETB_SHIFT, RESETB_MASK); + dev_info(dphy->dev, "2 SET_U0_MIPITX_DPHY_RESETB\n"); + + if (!assert) { + while(!is_pll_locked(dphy)); + dev_info(dphy->dev, "MIPI dphy-tx # PLL Locked\n"); + } } -static int sf_dphy_power_off(struct phy *phy) +static int sys_m31_dphy_tx_configure(struct phy *phy, union phy_configure_opts *opts) { + //dev_info(dphy->dev,"---sys_m31_dphy_tx_configure begin\n"); + struct sf_dphy *dphy; + uint32_t bitrate; + unsigned long alignment; + int i; + const struct m31_dphy_config *p; + const uint32_t AON_POWER_READY_N_active = 0; + dphy = phy_get_drvdata(phy); + bitrate = 500000000; + + sf_dphy_set_reg(dphy->topsys + 0x8, 0x10, + RG_CDTX_L0N_HSTX_RES_SHIFT, RG_CDTX_L0N_HSTX_RES_MASK); + sf_dphy_set_reg(dphy->topsys + 0xC, 0x10, + RG_CDTX_L0N_HSTX_RES_SHIFT, RG_CDTX_L0N_HSTX_RES_MASK); + sf_dphy_set_reg(dphy->topsys + 0xC, 0x10, + RG_CDTX_L2N_HSTX_RES_SHIFT, RG_CDTX_L2N_HSTX_RES_MASK); + sf_dphy_set_reg(dphy->topsys + 0xC, 0x10, + RG_CDTX_L3N_HSTX_RES_SHIFT, RG_CDTX_L3N_HSTX_RES_MASK); + sf_dphy_set_reg(dphy->topsys + 0x10, 0x10, + RG_CDTX_L4N_HSTX_RES_SHIFT, RG_CDTX_L4N_HSTX_RES_MASK); + sf_dphy_set_reg(dphy->topsys + 0x8, 0x10, + RG_CDTX_L0P_HSTX_RES_SHIFT, RG_CDTX_L0P_HSTX_RES_MASK); + sf_dphy_set_reg(dphy->topsys + 0xC, 0x10, + RG_CDTX_L1P_HSTX_RES_SHIFT, RG_CDTX_L1P_HSTX_RES_MASK); + sf_dphy_set_reg(dphy->topsys + 0xC, 0x10, + RG_CDTX_L2P_HSTX_RES_SHIFT, RG_CDTX_L2P_HSTX_RES_MASK); + sf_dphy_set_reg(dphy->topsys + 0xC, 0x10, + RG_CDTX_L3P_HSTX_RES_SHIFT, RG_CDTX_L3P_HSTX_RES_MASK); + sf_dphy_set_reg(dphy->topsys + 0x10, 0x10, + RG_CDTX_L4P_HSTX_RES_SHIFT, RG_CDTX_L4P_HSTX_RES_MASK); + + dev_info(dphy->dev,"request dphy hs_rate %dMbps\n", bitrate/1000000); + //if (is_pll_locked()) { + if (is_pll_locked(dphy)) + dev_info(dphy->dev, "Error: MIPI dphy-tx # PLL is not supposed to be LOCKED\n"); + else + dev_info(dphy->dev, "MIPI dphy-tx # PLL is not LOCKED\n"); + + //unsigned long alignment = (bitrate <= M31_DPHY_HS_RATE_80M) ? M31_DPHY_HS_RATE_80M : M31_DPHY_BITRATE_ALIGN; + alignment = M31_DPHY_BITRATE_ALIGN; + if (bitrate % alignment) { + bitrate += alignment - (bitrate % alignment); + } + + dev_info(dphy->dev, "want dphy hs_rate %dMbps\n", bitrate/1000000); + + p = m31_dphy_configs; + for (i = 0; i < ARRAY_SIZE(m31_dphy_configs); i++, p++) { + if (p->bitrate == bitrate) { + dev_info(dphy->dev, "config dphy hs_rate %dMbps\n", bitrate/1000000); + + //Clock source and lane setting + //SET_U0_MIPITX_DPHY_REFCLK_IN_SEL(M31_DPHY_REFCLK); + sf_dphy_set_reg(dphy->topsys + 0x64, M31_DPHY_REFCLK, REFCLK_IN_SEL_SHIFT, REFCLK_IN_SEL_MASK); + //const uint32_t AON_POWER_READY_N_active = 0; + dev_info(dphy->dev, "MIPI dphy-tx # AON_POWER_READY_N active(%d)\n", AON_POWER_READY_N_active); + + + sf_dphy_set_reg(dphy->topsys, AON_POWER_READY_N_active, + AON_POWER_READY_N_SHIFT, AON_POWER_READY_N_MASK); + + sf_dphy_set_reg(dphy->topsys, 0x0, + CFG_L0_SWAP_SEL_SHIFT, CFG_L0_SWAP_SEL_MASK);//Lane setting + sf_dphy_set_reg(dphy->topsys, 0x1, + CFG_L1_SWAP_SEL_SHIFT, CFG_L1_SWAP_SEL_MASK); + sf_dphy_set_reg(dphy->topsys, 0x4, + CFG_L2_SWAP_SEL_SHIFT, CFG_L2_SWAP_SEL_MASK); + sf_dphy_set_reg(dphy->topsys, 0x2, + CFG_L3_SWAP_SEL_SHIFT, CFG_L3_SWAP_SEL_MASK); + sf_dphy_set_reg(dphy->topsys, 0x3, + CFG_L4_SWAP_SEL_SHIFT, CFG_L4_SWAP_SEL_MASK); + //PLL setting + sf_dphy_set_reg(dphy->topsys + 0x1c, 0x0, + RG_CDTX_PLL_SSC_EN_SHIFT, RG_CDTX_PLL_SSC_EN_MASK); + sf_dphy_set_reg(dphy->topsys + 0x18, 0x1, + RG_CDTX_PLL_LDO_STB_X2_EN_SHIFT, RG_CDTX_PLL_LDO_STB_X2_EN_MASK); + sf_dphy_set_reg(dphy->topsys + 0x18, 0x1, + RG_CDTX_PLL_FM_EN_SHIFT, RG_CDTX_PLL_FM_EN_MASK); + + sf_dphy_set_reg(dphy->topsys + 0x18, p->pll_prev_div, + RG_CDTX_PLL_PRE_DIV_SHIFT, RG_CDTX_PLL_PRE_DIV_MASK); + sf_dphy_set_reg(dphy->topsys + 0x18, p->pll_fbk_int, + RG_CDTX_PLL_FBK_INT_SHIFT, RG_CDTX_PLL_FBK_INT_MASK); + sf_dphy_set_reg(dphy->topsys + 0x14, p->pll_fbk_fra, + RG_CDTX_PLL_FBK_FRA_SHIFT, RG_CDTX_PLL_FBK_FRA_MASK); + sf_dphy_set_reg(dphy->topsys + 0x28, p->extd_cycle_sel, + RG_EXTD_CYCLE_SEL_SHIFT, RG_EXTD_CYCLE_SEL_MASK); + sf_dphy_set_reg(dphy->topsys + 0x24, p->dlane_hs_pre_time, + RG_DLANE_HS_PRE_TIME_SHIFT, RG_DLANE_HS_PRE_TIME_MASK); + sf_dphy_set_reg(dphy->topsys + 0x24, p->dlane_hs_pre_time, + RG_DLANE_HS_PRE_TIME_SHIFT, RG_DLANE_HS_PRE_TIME_MASK); + sf_dphy_set_reg(dphy->topsys + 0x24, p->dlane_hs_zero_time, + RG_DLANE_HS_ZERO_TIME_SHIFT, RG_DLANE_HS_ZERO_TIME_MASK); + sf_dphy_set_reg(dphy->topsys + 0x24, p->dlane_hs_trail_time, + RG_DLANE_HS_TRAIL_TIME_SHIFT, RG_DLANE_HS_TRAIL_TIME_MASK); + sf_dphy_set_reg(dphy->topsys + 0x20, p->clane_hs_pre_time, + RG_CLANE_HS_PRE_TIME_SHIFT, RG_CLANE_HS_PRE_TIME_MASK); + sf_dphy_set_reg(dphy->topsys + 0x24, p->clane_hs_zero_time, + RG_CLANE_HS_ZERO_TIME_SHIFT, RG_CLANE_HS_ZERO_TIME_MASK); + sf_dphy_set_reg(dphy->topsys + 0x20, p->clane_hs_trail_time, + RG_CLANE_HS_TRAIL_TIME_SHIFT, RG_CLANE_HS_TRAIL_TIME_MASK); + sf_dphy_set_reg(dphy->topsys + 0x20, p->clane_hs_clk_pre_time, + RG_CLANE_HS_CLK_PRE_TIME_SHIFT, RG_CLANE_HS_CLK_PRE_TIME_MASK); + sf_dphy_set_reg(dphy->topsys + 0x20, p->clane_hs_clk_post_time, + RG_CLANE_HS_CLK_POST_TIME_SHIFT, RG_CLANE_HS_CLK_POST_TIME_MASK); + + break; + } + } + + //dev_info(dphy->dev,"---sys_m31_dphy_tx_configure end\n"); + return -ENOTSUPP; +} + +static int sf_dphy_power_on(struct phy *phy) +{ //dev_info(dphy->dev,"--->sf_dphy_power_on begin\n"); + + struct sf_dphy *dphy = phy_get_drvdata(phy); + int ret; + + reset(0, dphy); + sf_dphy_set_reg(dphy->topsys + 0x30, 0, + SCFG_PPI_C_READY_SEL_SHIFT, SCFG_PPI_C_READY_SEL_MASK); + sf_dphy_set_reg(dphy->topsys + 0x30, 0, + SCFG_DSI_TXREADY_ESC_SEL_SHIFT, SCFG_DSI_TXREADY_ESC_SEL_MASK); + sf_dphy_set_reg(dphy->topsys + 0x2c, 0x30, + SCFG_C_HS_PRE_ZERO_TIME_SHIFT, SCFG_C_HS_PRE_ZERO_TIME_MASK); + //ret = sf_dphy_clkrst_init(&pdev->dev, dphy);//clk rst interface + ret = sf_dphy_clkrst_ena_deas(dphy->dev, dphy);//clk rst interface enable and deassert + + //dev_info(dphy->dev,"--->sf_dphy_power_on end\n"); return 0; } -static int sf_dphy_init(struct phy *phy) +static int sf_dphy_power_off(struct phy *phy) { + //dev_info(dphy->dev,"--->sf_dphy_power_off begin\n"); struct sf_dphy *dphy = phy_get_drvdata(phy); - dsi_csi2tx_sel(dphy, 0); - dphy_clane_hs_txready_sel(dphy, 0x1); + sf_dphy_clkrst_disa_assert(dphy->dev, dphy); + reset(1, dphy); + //dev_info(dphy->dev,"--->sf_dphy_power_off end\n"); + return 0; +} +static int sf_dphy_init(struct phy *phy) +{ return 0; } @@ -451,7 +694,10 @@ static int sf_dphy_validate(struct phy *phy, enum phy_mode mode, int submode, } static int sf_dphy_set_mode(struct phy *phy, enum phy_mode mode, int submode) -{ +{ + struct sf_dphy *dphy = phy_get_drvdata(phy); + + dev_info(dphy->dev, "--->sf_dphy_set_mode\n"); return 0; } @@ -466,7 +712,8 @@ static const struct phy_ops sf_dphy_ops = { .power_off = sf_dphy_power_off, .init = sf_dphy_init, .exit = sf_dphy_exit, - .configure = sf_dphy_configure, + //.configure = sf_dphy_configure, + .configure = sys_m31_dphy_tx_configure, .validate = sf_dphy_validate, .set_mode = sf_dphy_set_mode, .owner = THIS_MODULE, @@ -481,19 +728,28 @@ static const struct of_device_id sf_dphy_dt_ids[] = { MODULE_DEVICE_TABLE(of, sf_dphy_dt_ids); static int sf_dphy_probe(struct platform_device *pdev) -{ +{//dev_info(dphy->dev,"====sf_dphy_probe begin\n"); struct phy_provider *phy_provider; struct sf_dphy *dphy; struct resource *res; int ret; + uint32_t temp; + + dev_info(&pdev->dev, "sf_dphy_probe begin\n"); dphy = devm_kzalloc(&pdev->dev, sizeof(*dphy), GFP_KERNEL); if (!dphy) return -ENOMEM; dev_set_drvdata(&pdev->dev, dphy); - dev_info(&pdev->dev,"===> %s enter, %d \n", __func__, __LINE__); + dev_info(&pdev->dev, "===> %s enter, %d \n", __func__, __LINE__); + + //dphy->topsys = ioremap(0x12260000, 0x10000); + //dphy->topsys = ioremap(0x295e0000, 0x10000); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + dphy->topsys = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(dphy->topsys)) + return PTR_ERR(dphy->topsys); - dphy->topsys = ioremap(0x12260000, 0x10000); dphy->phy = devm_phy_create(&pdev->dev, NULL, &sf_dphy_ops); if (IS_ERR(dphy->phy)) { @@ -502,8 +758,58 @@ static int sf_dphy_probe(struct platform_device *pdev) } phy_set_drvdata(dphy->phy, dphy); + //dont know need or not. will crash in starup + // this power switch control bit was added in ECO, check ECO item "aon psw_en" for detail + dev_info(dphy->dev, "control ECO\n"); + dphy->aonsys = ioremap(0x17010000, 0x10000); + temp = 0; + //temp = GET_AON_GP_REG; + temp = sf_dphy_get_reg(dphy->aonsys, AON_GP_REG_SHIFT,AON_GP_REG_MASK); + dev_info(dphy->dev, "GET_AON_GP_REG\n"); + /* + if (!(temp & DPHY_TX_PSW_EN_MASK)) { + temp |= DPHY_TX_PSW_EN_MASK; + SET_AON_GP_REG(temp); + } + */ + if (!(temp & DPHY_TX_PSW_EN_MASK)) { + temp |= DPHY_TX_PSW_EN_MASK; + //SET_AON_GP_REG(temp); + sf_dphy_set_reg(dphy->aonsys, temp,AON_GP_REG_SHIFT,AON_GP_REG_MASK); + } + dev_info(dphy->dev, "control ECO\n"); + + //mipi_pmic setting + dphy->mipitx_1p8 = devm_regulator_get(&pdev->dev, "mipitx_1p8"); + if (IS_ERR(dphy->mipitx_1p8)) + return PTR_ERR(dphy->mipitx_1p8); + + dphy->mipitx_0p9 = devm_regulator_get(&pdev->dev, "mipitx_0p9"); + if (IS_ERR(dphy->mipitx_0p9)) + return PTR_ERR(dphy->mipitx_0p9); + + //pmic turn on + ret = regulator_enable(dphy->mipitx_0p9); + if (ret) { + dev_err(&pdev->dev, "Cannot enable mipitx_0p9 regulator\n"); + //goto err_reg_0p9; + } + udelay(100); + ret = regulator_enable(dphy->mipitx_1p8); + if (ret) { + dev_err(&pdev->dev, "Cannot enable mipitx_1p8 regulator\n"); + //goto err_reg_1p8; + } + udelay(100); + //mipi_pmic setting + + //ret = sf_dphy_clkrst_init(&pdev->dev, dphy);//clk rst interface + ret = sf_dphy_clkrst_get(&pdev->dev, dphy); + phy_provider = devm_of_phy_provider_register(&pdev->dev, of_phy_simple_xlate); + dev_info(&pdev->dev, "sf_dphy_probe end\n"); + return PTR_ERR_OR_ZERO(phy_provider); } -- 2.7.4