[PATCH v7 5/5] drm/rockchip: support dp training outside dp firmware
DP firmware uses fixed phy config values to do training, but some boards need to adjust these values to fit for their unique hardware design. So get phy config values from dts and use software link training instead of relying on firmware, if software training fail, keep firmware training as a fallback if sw training fails. Signed-off-by: Chris Zhong <z...@rock-chips.com> Signed-off-by: Lin Huang <h...@rock-chips.com> Reviewed-by: Sean Paul <seanp...@chromium.org> --- Changes in v2: - update patch following Enric suggest Changes in v3: - use variable fw_training instead sw_training_success - base on DP SPCE, if training fail use lower link rate to retry training Changes in v4: - improve cdn_dp_get_lower_link_rate() and cdn_dp_software_train_link() follow Sean suggest Changes in v5: - fix some whitespcae issue Changes in v6: - None Changes in v7: - None drivers/gpu/drm/rockchip/Makefile | 3 +- drivers/gpu/drm/rockchip/cdn-dp-core.c | 24 +- drivers/gpu/drm/rockchip/cdn-dp-core.h | 2 + drivers/gpu/drm/rockchip/cdn-dp-link-training.c | 420 drivers/gpu/drm/rockchip/cdn-dp-reg.c | 31 +- drivers/gpu/drm/rockchip/cdn-dp-reg.h | 38 ++- 6 files changed, 505 insertions(+), 13 deletions(-) create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-link-training.c diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile index a314e21..b932f62 100644 --- a/drivers/gpu/drm/rockchip/Makefile +++ b/drivers/gpu/drm/rockchip/Makefile @@ -9,7 +9,8 @@ rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o \ rockchipdrm-$(CONFIG_DRM_FBDEV_EMULATION) += rockchip_drm_fbdev.o rockchipdrm-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o -rockchipdrm-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o cdn-dp-reg.o +rockchipdrm-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o cdn-dp-reg.o \ + cdn-dp-link-training.o rockchipdrm-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o rockchipdrm-$(CONFIG_ROCKCHIP_DW_MIPI_DSI) += dw-mipi-dsi.o rockchipdrm-$(CONFIG_ROCKCHIP_INNO_HDMI) += inno_hdmi.o diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.c b/drivers/gpu/drm/rockchip/cdn-dp-core.c index cce64c1..783d57a 100644 --- a/drivers/gpu/drm/rockchip/cdn-dp-core.c +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.c @@ -629,11 +629,13 @@ static void cdn_dp_encoder_enable(struct drm_encoder *encoder) goto out; } } - - ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_IDLE); - if (ret) { - DRM_DEV_ERROR(dp->dev, "Failed to idle video %d\n", ret); - goto out; + if (dp->use_fw_training) { + ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_IDLE); + if (ret) { + DRM_DEV_ERROR(dp->dev, + "Failed to idle video %d\n", ret); + goto out; + } } ret = cdn_dp_config_video(dp); @@ -642,11 +644,15 @@ static void cdn_dp_encoder_enable(struct drm_encoder *encoder) goto out; } - ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_VALID); - if (ret) { - DRM_DEV_ERROR(dp->dev, "Failed to valid video %d\n", ret); - goto out; + if (dp->use_fw_training) { + ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_VALID); + if (ret) { + DRM_DEV_ERROR(dp->dev, + "Failed to valid video %d\n", ret); + goto out; + } } + out: mutex_unlock(>lock); } diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.h b/drivers/gpu/drm/rockchip/cdn-dp-core.h index 46159b2..77a9793 100644 --- a/drivers/gpu/drm/rockchip/cdn-dp-core.h +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.h @@ -84,6 +84,7 @@ struct cdn_dp_device { bool connected; bool active; bool suspended; + bool use_fw_training; const struct firmware *fw; /* cdn dp firmware */ unsigned int fw_version;/* cdn fw version */ @@ -106,6 +107,7 @@ struct cdn_dp_device { u8 ports; u8 lanes; int active_port; + u8 train_set[4]; u8 dpcd[DP_RECEIVER_CAP_SIZE]; bool sink_has_audio; diff --git a/drivers/gpu/drm/rockchip/cdn-dp-link-training.c b/drivers/gpu/drm/rockchip/cdn-dp-link-training.c new file mode 100644 index 000..73c3290 --- /dev/null +++ b/drivers/gpu/drm/rockchip/cdn-dp-link-training.c @@ -0,0 +1,420 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd + * Author: Chris Zhong <z...@rock-chips.com> + */ + +#include +#include +#include +#include + +#include "cdn-dp-core.h" +#include "cdn-dp-reg.h" + +static
[PATCH v7 4/5] phy: rockchip-typec: support variable phy config value
the phy config values used to fix in dp firmware, but some boards need change these values to do training and get the better eye diagram result. So support that in phy driver. Signed-off-by: Chris Zhong <z...@rock-chips.com> Signed-off-by: Lin Huang <h...@rock-chips.com> --- Changes in v2: - update patch following Enric suggest Changes in v3: - delete need_software_training variable - add default phy config value, if dts do not define phy config value, use these value Changes in v4: - rename variable config to tcphy_default_config Changes in v5: - None Changes in v6: - split the header file to new patch Changes in v7: - add default case when check link rate - move struct rockchip_typec_phy new element to this patch drivers/phy/rockchip/phy-rockchip-typec.c | 263 -- include/soc/rockchip/rockchip_phy_typec.h | 8 + 2 files changed, 218 insertions(+), 53 deletions(-) diff --git a/drivers/phy/rockchip/phy-rockchip-typec.c b/drivers/phy/rockchip/phy-rockchip-typec.c index 795055f..69af90e 100644 --- a/drivers/phy/rockchip/phy-rockchip-typec.c +++ b/drivers/phy/rockchip/phy-rockchip-typec.c @@ -324,21 +324,29 @@ * clock 0: PLL 0 div 1 * clock 1: PLL 1 div 2 */ -#define CLK_PLL_CONFIG 0X30 +#define CLK_PLL1_DIV1 0x20 +#define CLK_PLL1_DIV2 0x30 #define CLK_PLL_MASK 0x33 #define CMN_READY BIT(0) +#define DP_PLL_CLOCK_ENABLE_ACKBIT(3) #define DP_PLL_CLOCK_ENABLEBIT(2) +#define DP_PLL_ENABLE_ACK BIT(1) #define DP_PLL_ENABLE BIT(0) #define DP_PLL_DATA_RATE_RBR ((2 << 12) | (4 << 8)) #define DP_PLL_DATA_RATE_HBR ((2 << 12) | (4 << 8)) #define DP_PLL_DATA_RATE_HBR2 ((1 << 12) | (2 << 8)) +#define DP_PLL_DATA_RATE_MASK 0xff00 -#define DP_MODE_A0 BIT(4) -#define DP_MODE_A2 BIT(6) -#define DP_MODE_ENTER_A0 0xc101 -#define DP_MODE_ENTER_A2 0xc104 +#define DP_MODE_MASK 0xf +#define DP_MODE_ENTER_A0 BIT(0) +#define DP_MODE_ENTER_A2 BIT(2) +#define DP_MODE_ENTER_A3 BIT(3) +#define DP_MODE_A0_ACK BIT(4) +#define DP_MODE_A2_ACK BIT(6) +#define DP_MODE_A3_ACK BIT(7) +#define DP_LINK_RESET_DEASSERTED BIT(8) #define PHY_MODE_SET_TIMEOUT 10 @@ -350,6 +358,8 @@ #define MODE_DFP_USB BIT(1) #define MODE_DFP_DPBIT(2) +#define DP_DEFAULT_RATE162000 + struct phy_reg { u16 value; u32 addr; @@ -372,15 +382,15 @@ struct phy_reg usb3_pll_cfg[] = { { 0x8, CMN_DIAG_PLL0_LF_PROG }, }; -struct phy_reg dp_pll_cfg[] = { +struct phy_reg dp_pll_rbr_cfg[] = { { 0xf0, CMN_PLL1_VCOCAL_INIT }, { 0x18, CMN_PLL1_VCOCAL_ITER }, { 0x30b9, CMN_PLL1_VCOCAL_START }, - { 0x21c,CMN_PLL1_INTDIV }, + { 0x87, CMN_PLL1_INTDIV }, { 0,CMN_PLL1_FRACDIV }, - { 0x5, CMN_PLL1_HIGH_THR }, - { 0x35, CMN_PLL1_SS_CTRL1 }, - { 0x7f1e, CMN_PLL1_SS_CTRL2 }, + { 0x22, CMN_PLL1_HIGH_THR }, + { 0x8000, CMN_PLL1_SS_CTRL1 }, + { 0,CMN_PLL1_SS_CTRL2 }, { 0x20, CMN_PLL1_DSM_DIAG }, { 0,CMN_PLLSM1_USER_DEF_CTRL }, { 0,CMN_DIAG_PLL1_OVRD }, @@ -391,9 +401,52 @@ struct phy_reg dp_pll_cfg[] = { { 0x8, CMN_DIAG_PLL1_LF_PROG }, { 0x100,CMN_DIAG_PLL1_PTATIS_TUNE1 }, { 0x7, CMN_DIAG_PLL1_PTATIS_TUNE2 }, - { 0x4, CMN_DIAG_PLL1_INCLK_CTRL }, + { 0x1, CMN_DIAG_PLL1_INCLK_CTRL }, }; +struct phy_reg dp_pll_hbr_cfg[] = { + { 0xf0, CMN_PLL1_VCOCAL_INIT }, + { 0x18, CMN_PLL1_VCOCAL_ITER }, + { 0x30b4, CMN_PLL1_VCOCAL_START }, + { 0xe1, CMN_PLL1_INTDIV }, + { 0,CMN_PLL1_FRACDIV }, + { 0x5, CMN_PLL1_HIGH_THR }, + { 0x8000, CMN_PLL1_SS_CTRL1 }, + { 0,CMN_PLL1_SS_CTRL2 }, + { 0x20, CMN_PLL1_DSM_DIAG }, + { 0x1000, CMN_PLLSM1_USER_DEF_CTRL }, + { 0,CMN_DIAG_PLL1_OVRD }, + { 0,CMN_DIAG_PLL1_FBH_OVRD }, + { 0,CMN_DIAG_PLL1_FBL_OVRD }, + { 0x7, CMN_DIAG_PLL1_V2I_TUNE }, + { 0x45, CMN_DIAG_PLL1_CP_TUNE }, + { 0x8, CMN_DIAG_PLL1_LF_PROG }, + { 0x1, CMN_DIAG_PLL1_PTATIS_TUNE1 }, + { 0x1, CMN_DIAG_PLL1_PTATIS_TUNE2 }, + { 0x1, CMN_DIAG_PLL1_INCLK_CTRL }, +}; + +struct phy_reg dp_pll_hbr2_cfg[] = { + { 0xf0, CMN_PLL1_VCOCAL_INIT
[PATCH v7 2/5] Documentation: dt-bindings: phy: add phy_config for Rockchip USB Type-C PHY
If want to do training outside DP Firmware, need phy voltage swing and pre_emphasis value. Signed-off-by: Lin Huang <h...@rock-chips.com> Reviewed-by: Rob Herring <r...@kernel.org> --- Changes in v2: - None Changes in v3: - modify property description and add this property to Example Changes in v4: - None Changes in v5: - None Changes in v6: - change rockchip,phy_config to rockchip,phy-config and descript it in detail. Changes in v7: - None .../devicetree/bindings/phy/phy-rockchip-typec.txt | 36 +- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/phy/phy-rockchip-typec.txt b/Documentation/devicetree/bindings/phy/phy-rockchip-typec.txt index 960da7f..40d5e7a 100644 --- a/Documentation/devicetree/bindings/phy/phy-rockchip-typec.txt +++ b/Documentation/devicetree/bindings/phy/phy-rockchip-typec.txt @@ -17,7 +17,11 @@ Required properties: Optional properties: - extcon : extcon specifier for the Power Delivery - + - rockchip,phy-config : A list of voltage swing(mV) and pre-emphasis + (dB) pairs. They are 3 blocks of 4 entries and + correspond to s0p0 ~ s0p3, s1p0 ~ s1p3, + s2p0 ~ s2p3, s3p0 ~ s2p3 swing and pre-emphasis + values. Required nodes : a sub-node is required for each port the phy provides. The sub-node name is used to identify dp or usb3 port, and shall be the following entries: @@ -50,6 +54,21 @@ Example: < SRST_P_UPHY0_TCPHY>; reset-names = "uphy", "uphy-pipe", "uphy-tcphy"; + rockchip,phy-config = <0x2a 0x00>, + <0x1f 0x15>, + <0x14 0x22>, + <0x02 0x2b>, + + <0x21 0x00>, + <0x12 0x15>, + <0x02 0x22>, + <0 0>, + + <0x15 0x00>, + <0x00 0x15>, + <0 0>, + <0 0>; + tcphy0_dp: dp-port { #phy-cells = <0>; }; @@ -74,6 +93,21 @@ Example: < SRST_P_UPHY1_TCPHY>; reset-names = "uphy", "uphy-pipe", "uphy-tcphy"; + rockchip,phy-config = <0x2a 0x00>, + <0x1f 0x15>, + <0x14 0x22>, + <0x02 0x2b>, + + <0x21 0x00>, + <0x12 0x15>, + <0x02 0x22>, + <0 0>, + + <0x15 0x00>, + <0x00 0x15>, + <0 0>, + <0 0>; + tcphy1_dp: dp-port { #phy-cells = <0>; }; -- 2.7.4 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH v7 1/5] drm/rockchip: add transfer function for cdn-dp
From: Chris Zhong <z...@rock-chips.com> We may support training outside firmware, so we need support dpcd read/write to get the message or do some setting with display. Signed-off-by: Chris Zhong <z...@rock-chips.com> Signed-off-by: Lin Huang <h...@rock-chips.com> Reviewed-by: Sean Paul <seanp...@chromium.org> Reviewed-by: Enric Balletbo <enric.balle...@collabora.com> --- Changes in v2: - update patch following Enric suggest Changes in v3: - None Changes in v4: - None Changes in v5: - None Changes in v6: - None Changes in v7: - None drivers/gpu/drm/rockchip/cdn-dp-core.c | 55 +++ drivers/gpu/drm/rockchip/cdn-dp-core.h | 1 + drivers/gpu/drm/rockchip/cdn-dp-reg.c | 69 ++ drivers/gpu/drm/rockchip/cdn-dp-reg.h | 14 ++- 4 files changed, 122 insertions(+), 17 deletions(-) diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.c b/drivers/gpu/drm/rockchip/cdn-dp-core.c index c6fbdcd..cce64c1 100644 --- a/drivers/gpu/drm/rockchip/cdn-dp-core.c +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.c @@ -176,8 +176,8 @@ static int cdn_dp_get_sink_count(struct cdn_dp_device *dp, u8 *sink_count) u8 value; *sink_count = 0; - ret = cdn_dp_dpcd_read(dp, DP_SINK_COUNT, , 1); - if (ret) + ret = drm_dp_dpcd_read(>aux, DP_SINK_COUNT, , 1); + if (ret < 0) return ret; *sink_count = DP_GET_SINK_COUNT(value); @@ -374,9 +374,9 @@ static int cdn_dp_get_sink_capability(struct cdn_dp_device *dp) if (!cdn_dp_check_sink_connection(dp)) return -ENODEV; - ret = cdn_dp_dpcd_read(dp, DP_DPCD_REV, dp->dpcd, - DP_RECEIVER_CAP_SIZE); - if (ret) { + ret = drm_dp_dpcd_read(>aux, DP_DPCD_REV, dp->dpcd, + sizeof(dp->dpcd)); + if (ret < 0) { DRM_DEV_ERROR(dp->dev, "Failed to get caps %d\n", ret); return ret; } @@ -582,8 +582,8 @@ static bool cdn_dp_check_link_status(struct cdn_dp_device *dp) if (!port || !dp->link.rate || !dp->link.num_lanes) return false; - if (cdn_dp_dpcd_read(dp, DP_LANE0_1_STATUS, link_status, -DP_LINK_STATUS_SIZE)) { + if (drm_dp_dpcd_read_link_status(>aux, link_status) != + DP_LINK_STATUS_SIZE) { DRM_ERROR("Failed to get link status\n"); return false; } @@ -1012,6 +1012,40 @@ static int cdn_dp_pd_event(struct notifier_block *nb, return NOTIFY_DONE; } +static ssize_t cdn_dp_aux_transfer(struct drm_dp_aux *aux, + struct drm_dp_aux_msg *msg) +{ + struct cdn_dp_device *dp = container_of(aux, struct cdn_dp_device, aux); + int ret; + u8 status; + + switch (msg->request & ~DP_AUX_I2C_MOT) { + case DP_AUX_NATIVE_WRITE: + case DP_AUX_I2C_WRITE: + case DP_AUX_I2C_WRITE_STATUS_UPDATE: + ret = cdn_dp_dpcd_write(dp, msg->address, msg->buffer, + msg->size); + break; + case DP_AUX_NATIVE_READ: + case DP_AUX_I2C_READ: + ret = cdn_dp_dpcd_read(dp, msg->address, msg->buffer, + msg->size); + break; + default: + return -EINVAL; + } + + status = cdn_dp_get_aux_status(dp); + if (status == AUX_STATUS_ACK) + msg->reply = DP_AUX_NATIVE_REPLY_ACK; + else if (status == AUX_STATUS_NACK) + msg->reply = DP_AUX_NATIVE_REPLY_NACK; + else if (status == AUX_STATUS_DEFER) + msg->reply = DP_AUX_NATIVE_REPLY_DEFER; + + return ret; +} + static int cdn_dp_bind(struct device *dev, struct device *master, void *data) { struct cdn_dp_device *dp = dev_get_drvdata(dev); @@ -1030,6 +1064,13 @@ static int cdn_dp_bind(struct device *dev, struct device *master, void *data) dp->active = false; dp->active_port = -1; dp->fw_loaded = false; + dp->aux.name = "DP-AUX"; + dp->aux.transfer = cdn_dp_aux_transfer; + dp->aux.dev = dev; + + ret = drm_dp_aux_register(>aux); + if (ret) + return ret; INIT_WORK(>event_work, cdn_dp_pd_event_work); diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.h b/drivers/gpu/drm/rockchip/cdn-dp-core.h index f57e296..46159b2 100644 --- a/drivers/gpu/drm/rockchip/cdn-dp-core.h +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.h @@ -78,6 +78,7 @@ struct cdn_dp_device { struct platform_device *audio_pdev; struct work_struct event_work; struct edid *edid; + struct drm_dp_aux aux; struct mutex lock; bool connected; diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.c b/driv
[PATCH v7 3/5] soc: rockchip: split rockchip_typec_phy struct to separate header
we may use rockchip_phy_typec struct in other driver, so split it to separate header. Signed-off-by: Lin Huang <h...@rock-chips.com> --- Changes in v2: - None Changes in v3: - None Changes in v4: - None Changes in v5: - None Changes in v6: - new patch here Changes in v7: - move new element to next patch drivers/phy/rockchip/phy-rockchip-typec.c | 47 +- include/soc/rockchip/rockchip_phy_typec.h | 55 +++ 2 files changed, 56 insertions(+), 46 deletions(-) create mode 100644 include/soc/rockchip/rockchip_phy_typec.h diff --git a/drivers/phy/rockchip/phy-rockchip-typec.c b/drivers/phy/rockchip/phy-rockchip-typec.c index 76a4b58..795055f 100644 --- a/drivers/phy/rockchip/phy-rockchip-typec.c +++ b/drivers/phy/rockchip/phy-rockchip-typec.c @@ -63,6 +63,7 @@ #include #include +#include #define CMN_SSM_BANDGAP(0x21 << 2) #define CMN_SSM_BIAS (0x22 << 2) @@ -349,52 +350,6 @@ #define MODE_DFP_USB BIT(1) #define MODE_DFP_DPBIT(2) -struct usb3phy_reg { - u32 offset; - u32 enable_bit; - u32 write_enable; -}; - -/** - * struct rockchip_usb3phy_port_cfg: usb3-phy port configuration. - * @reg: the base address for usb3-phy config. - * @typec_conn_dir: the register of type-c connector direction. - * @usb3tousb2_en: the register of type-c force usb2 to usb2 enable. - * @external_psm: the register of type-c phy external psm clock. - * @pipe_status: the register of type-c phy pipe status. - * @usb3_host_disable: the register of type-c usb3 host disable. - * @usb3_host_port: the register of type-c usb3 host port. - * @uphy_dp_sel: the register of type-c phy DP select control. - */ -struct rockchip_usb3phy_port_cfg { - unsigned int reg; - struct usb3phy_reg typec_conn_dir; - struct usb3phy_reg usb3tousb2_en; - struct usb3phy_reg external_psm; - struct usb3phy_reg pipe_status; - struct usb3phy_reg usb3_host_disable; - struct usb3phy_reg usb3_host_port; - struct usb3phy_reg uphy_dp_sel; -}; - -struct rockchip_typec_phy { - struct device *dev; - void __iomem *base; - struct extcon_dev *extcon; - struct regmap *grf_regs; - struct clk *clk_core; - struct clk *clk_ref; - struct reset_control *uphy_rst; - struct reset_control *pipe_rst; - struct reset_control *tcphy_rst; - const struct rockchip_usb3phy_port_cfg *port_cfgs; - /* mutex to protect access to individual PHYs */ - struct mutex lock; - - bool flip; - u8 mode; -}; - struct phy_reg { u16 value; u32 addr; diff --git a/include/soc/rockchip/rockchip_phy_typec.h b/include/soc/rockchip/rockchip_phy_typec.h new file mode 100644 index 000..4afe039 --- /dev/null +++ b/include/soc/rockchip/rockchip_phy_typec.h @@ -0,0 +1,55 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd + * Author: Lin Huang <h...@rock-chips.com> + */ + +#ifndef __SOC_ROCKCHIP_PHY_TYPEC_H +#define __SOC_ROCKCHIP_PHY_TYPEC_H + +struct usb3phy_reg { + u32 offset; + u32 enable_bit; + u32 write_enable; +}; + +/** + * struct rockchip_usb3phy_port_cfg: usb3-phy port configuration. + * @reg: the base address for usb3-phy config. + * @typec_conn_dir: the register of type-c connector direction. + * @usb3tousb2_en: the register of type-c force usb2 to usb2 enable. + * @external_psm: the register of type-c phy external psm clock. + * @pipe_status: the register of type-c phy pipe status. + * @usb3_host_disable: the register of type-c usb3 host disable. + * @usb3_host_port: the register of type-c usb3 host port. + * @uphy_dp_sel: the register of type-c phy DP select control. + */ +struct rockchip_usb3phy_port_cfg { + unsigned int reg; + struct usb3phy_reg typec_conn_dir; + struct usb3phy_reg usb3tousb2_en; + struct usb3phy_reg external_psm; + struct usb3phy_reg pipe_status; + struct usb3phy_reg usb3_host_disable; + struct usb3phy_reg usb3_host_port; + struct usb3phy_reg uphy_dp_sel; +}; + +struct rockchip_typec_phy { + struct device *dev; + void __iomem *base; + struct extcon_dev *extcon; + struct regmap *grf_regs; + struct clk *clk_core; + struct clk *clk_ref; + struct reset_control *uphy_rst; + struct reset_control *pipe_rst; + struct reset_control *tcphy_rst; + const struct rockchip_usb3phy_port_cfg *port_cfgs; + /* mutex to protect access to individual PHYs */ + struct mutex lock; + bool flip; + u8 mode; +}; + +#endif -- 2.7.4 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH 2/2] ASoC: rockchip: cdn-dp sound output use spdif
some monitors care about the parity bit in the sub-frame of I2S, but the cdn-dp always set this bit to "1", so these monitors do not have sound output if use i2s, use spdif can fix this issue. Signed-off-by: Chris Zhong <z...@rock-chips.com> Signed-off-by: Lin Huang <h...@rock-chips.com> --- sound/soc/rockchip/rk3399_gru_sound.c | 46 +-- 1 file changed, 1 insertion(+), 45 deletions(-) diff --git a/sound/soc/rockchip/rk3399_gru_sound.c b/sound/soc/rockchip/rk3399_gru_sound.c index 9a10181..f184168 100644 --- a/sound/soc/rockchip/rk3399_gru_sound.c +++ b/sound/soc/rockchip/rk3399_gru_sound.c @@ -220,45 +220,6 @@ static int rockchip_sound_da7219_init(struct snd_soc_pcm_runtime *rtd) return 0; } -static int rockchip_sound_cdndp_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - int mclk, ret; - - /* in bypass mode, the mclk has to be one of the frequencies below */ - switch (params_rate(params)) { - case 8000: - case 16000: - case 24000: - case 32000: - case 48000: - case 64000: - case 96000: - mclk = 12288000; - break; - case 11025: - case 22050: - case 44100: - case 88200: - mclk = 11289600; - break; - default: - return -EINVAL; - } - - ret = snd_soc_dai_set_sysclk(cpu_dai, 0, mclk, -SND_SOC_CLOCK_OUT); - if (ret < 0) { - dev_err(codec_dai->dev, "Can't set cpu clock out %d\n", ret); - return ret; - } - - return 0; -} - static int rockchip_sound_dmic_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { @@ -293,10 +254,6 @@ static const struct snd_soc_ops rockchip_sound_da7219_ops = { .hw_params = rockchip_sound_da7219_hw_params, }; -static const struct snd_soc_ops rockchip_sound_cdndp_ops = { - .hw_params = rockchip_sound_cdndp_hw_params, -}; - static const struct snd_soc_ops rockchip_sound_dmic_ops = { .hw_params = rockchip_sound_dmic_hw_params, }; @@ -323,8 +280,7 @@ static const struct snd_soc_dai_link rockchip_dais[] = { [DAILINK_CDNDP] = { .name = "DP", .stream_name = "DP PCM", - .codec_dai_name = "i2s-hifi", - .ops = _sound_cdndp_ops, + .codec_dai_name = "spdif-hifi", .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS, }, -- 2.7.4 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH 1/2] drm/rockchip: cnd-dp: adjust spdif register setting
We use jitter bypass mode for spdif, so do not need to set jitter mode related bit in SPDIF_CTRL_ADDR register. Also, we need to enable SPDIF_ENABLE bit. Signed-off-by: Chris Zhong <z...@rock-chips.com> Signed-off-by: Lin Huang <h...@rock-chips.com> --- drivers/gpu/drm/rockchip/cdn-dp-reg.c | 16 +--- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.c b/drivers/gpu/drm/rockchip/cdn-dp-reg.c index eb3042c..3105965 100644 --- a/drivers/gpu/drm/rockchip/cdn-dp-reg.c +++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.c @@ -792,7 +792,6 @@ int cdn_dp_config_video(struct cdn_dp_device *dp) int cdn_dp_audio_stop(struct cdn_dp_device *dp, struct audio_info *audio) { - u32 val; int ret; ret = cdn_dp_reg_write(dp, AUDIO_PACK_CONTROL, 0); @@ -801,11 +800,7 @@ int cdn_dp_audio_stop(struct cdn_dp_device *dp, struct audio_info *audio) return ret; } - val = SPDIF_AVG_SEL | SPDIF_JITTER_BYPASS; - val |= SPDIF_FIFO_MID_RANGE(0xe0); - val |= SPDIF_JITTER_THRSH(0xe0); - val |= SPDIF_JITTER_AVG_WIN(7); - writel(val, dp->regs + SPDIF_CTRL_ADDR); + writel(0, dp->regs + SPDIF_CTRL_ADDR); /* clearn the audio config and reset */ writel(0, dp->regs + AUDIO_SRC_CNTL); @@ -929,12 +924,6 @@ static void cdn_dp_audio_config_spdif(struct cdn_dp_device *dp) { u32 val; - val = SPDIF_AVG_SEL | SPDIF_JITTER_BYPASS; - val |= SPDIF_FIFO_MID_RANGE(0xe0); - val |= SPDIF_JITTER_THRSH(0xe0); - val |= SPDIF_JITTER_AVG_WIN(7); - writel(val, dp->regs + SPDIF_CTRL_ADDR); - writel(SYNC_WR_TO_CH_ZERO, dp->regs + FIFO_CNTL); val = MAX_NUM_CH(2) | AUDIO_TYPE_LPCM | CFG_SUB_PCKT_NUM(4); @@ -942,9 +931,6 @@ static void cdn_dp_audio_config_spdif(struct cdn_dp_device *dp) writel(SMPL2PKT_EN, dp->regs + SMPL2PKT_CNTL); val = SPDIF_ENABLE | SPDIF_AVG_SEL | SPDIF_JITTER_BYPASS; - val |= SPDIF_FIFO_MID_RANGE(0xe0); - val |= SPDIF_JITTER_THRSH(0xe0); - val |= SPDIF_JITTER_AVG_WIN(7); writel(val, dp->regs + SPDIF_CTRL_ADDR); clk_prepare_enable(dp->spdif_clk); -- 2.7.4 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH v6 5/5] drm/rockchip: support dp training outside dp firmware
DP firmware uses fixed phy config values to do training, but some boards need to adjust these values to fit for their unique hardware design. So get phy config values from dts and use software link training instead of relying on firmware, if software training fail, keep firmware training as a fallback if sw training fails. Signed-off-by: Chris Zhong <z...@rock-chips.com> Signed-off-by: Lin Huang <h...@rock-chips.com> Reviewed-by: Sean Paul <seanp...@chromium.org> --- Changes in v2: - update patch following Enric suggest Changes in v3: - use variable fw_training instead sw_training_success - base on DP SPCE, if training fail use lower link rate to retry training Changes in v4: - improve cdn_dp_get_lower_link_rate() and cdn_dp_software_train_link() follow Sean suggest Changes in v5: - fix some whitespcae issue Changes in v6: - None drivers/gpu/drm/rockchip/Makefile | 3 +- drivers/gpu/drm/rockchip/cdn-dp-core.c | 24 +- drivers/gpu/drm/rockchip/cdn-dp-core.h | 2 + drivers/gpu/drm/rockchip/cdn-dp-link-training.c | 420 drivers/gpu/drm/rockchip/cdn-dp-reg.c | 31 +- drivers/gpu/drm/rockchip/cdn-dp-reg.h | 38 ++- 6 files changed, 505 insertions(+), 13 deletions(-) create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-link-training.c diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile index a314e21..b932f62 100644 --- a/drivers/gpu/drm/rockchip/Makefile +++ b/drivers/gpu/drm/rockchip/Makefile @@ -9,7 +9,8 @@ rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o \ rockchipdrm-$(CONFIG_DRM_FBDEV_EMULATION) += rockchip_drm_fbdev.o rockchipdrm-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o -rockchipdrm-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o cdn-dp-reg.o +rockchipdrm-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o cdn-dp-reg.o \ + cdn-dp-link-training.o rockchipdrm-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o rockchipdrm-$(CONFIG_ROCKCHIP_DW_MIPI_DSI) += dw-mipi-dsi.o rockchipdrm-$(CONFIG_ROCKCHIP_INNO_HDMI) += inno_hdmi.o diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.c b/drivers/gpu/drm/rockchip/cdn-dp-core.c index cce64c1..d9d0d4d 100644 --- a/drivers/gpu/drm/rockchip/cdn-dp-core.c +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.c @@ -629,11 +629,13 @@ static void cdn_dp_encoder_enable(struct drm_encoder *encoder) goto out; } } - - ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_IDLE); - if (ret) { - DRM_DEV_ERROR(dp->dev, "Failed to idle video %d\n", ret); - goto out; + if (dp->use_fw_training == true) { + ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_IDLE); + if (ret) { + DRM_DEV_ERROR(dp->dev, + "Failed to idle video %d\n", ret); + goto out; + } } ret = cdn_dp_config_video(dp); @@ -642,11 +644,15 @@ static void cdn_dp_encoder_enable(struct drm_encoder *encoder) goto out; } - ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_VALID); - if (ret) { - DRM_DEV_ERROR(dp->dev, "Failed to valid video %d\n", ret); - goto out; + if (dp->use_fw_training == true) { + ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_VALID); + if (ret) { + DRM_DEV_ERROR(dp->dev, + "Failed to valid video %d\n", ret); + goto out; + } } + out: mutex_unlock(>lock); } diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.h b/drivers/gpu/drm/rockchip/cdn-dp-core.h index 46159b2..77a9793 100644 --- a/drivers/gpu/drm/rockchip/cdn-dp-core.h +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.h @@ -84,6 +84,7 @@ struct cdn_dp_device { bool connected; bool active; bool suspended; + bool use_fw_training; const struct firmware *fw; /* cdn dp firmware */ unsigned int fw_version;/* cdn fw version */ @@ -106,6 +107,7 @@ struct cdn_dp_device { u8 ports; u8 lanes; int active_port; + u8 train_set[4]; u8 dpcd[DP_RECEIVER_CAP_SIZE]; bool sink_has_audio; diff --git a/drivers/gpu/drm/rockchip/cdn-dp-link-training.c b/drivers/gpu/drm/rockchip/cdn-dp-link-training.c new file mode 100644 index 000..73c3290 --- /dev/null +++ b/drivers/gpu/drm/rockchip/cdn-dp-link-training.c @@ -0,0 +1,420 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd + * Author: Chris Zhong <z...@rock-chips.com> + */ + +#include +#include +#include +#include + +#include "cdn-dp-core.h" +#include "cdn-dp-reg.h" + +static void cdn_dp_s
[PATCH v6 3/5] soc: rockchip: split rockchip_typec_phy struct to separate header
we may use rockchip_phy_typec struct in other driver, so split it to separate header. Signed-off-by: Lin Huang <h...@rock-chips.com> --- Changes in v2: - None Changes in v3: - None Changes in v4: - None Changes in v5: - None Changes in v6: - new patch here drivers/phy/rockchip/phy-rockchip-typec.c | 47 +-- include/soc/rockchip/rockchip_phy_typec.h | 63 +++ 2 files changed, 64 insertions(+), 46 deletions(-) create mode 100644 include/soc/rockchip/rockchip_phy_typec.h diff --git a/drivers/phy/rockchip/phy-rockchip-typec.c b/drivers/phy/rockchip/phy-rockchip-typec.c index 76a4b58..795055f 100644 --- a/drivers/phy/rockchip/phy-rockchip-typec.c +++ b/drivers/phy/rockchip/phy-rockchip-typec.c @@ -63,6 +63,7 @@ #include #include +#include #define CMN_SSM_BANDGAP(0x21 << 2) #define CMN_SSM_BIAS (0x22 << 2) @@ -349,52 +350,6 @@ #define MODE_DFP_USB BIT(1) #define MODE_DFP_DPBIT(2) -struct usb3phy_reg { - u32 offset; - u32 enable_bit; - u32 write_enable; -}; - -/** - * struct rockchip_usb3phy_port_cfg: usb3-phy port configuration. - * @reg: the base address for usb3-phy config. - * @typec_conn_dir: the register of type-c connector direction. - * @usb3tousb2_en: the register of type-c force usb2 to usb2 enable. - * @external_psm: the register of type-c phy external psm clock. - * @pipe_status: the register of type-c phy pipe status. - * @usb3_host_disable: the register of type-c usb3 host disable. - * @usb3_host_port: the register of type-c usb3 host port. - * @uphy_dp_sel: the register of type-c phy DP select control. - */ -struct rockchip_usb3phy_port_cfg { - unsigned int reg; - struct usb3phy_reg typec_conn_dir; - struct usb3phy_reg usb3tousb2_en; - struct usb3phy_reg external_psm; - struct usb3phy_reg pipe_status; - struct usb3phy_reg usb3_host_disable; - struct usb3phy_reg usb3_host_port; - struct usb3phy_reg uphy_dp_sel; -}; - -struct rockchip_typec_phy { - struct device *dev; - void __iomem *base; - struct extcon_dev *extcon; - struct regmap *grf_regs; - struct clk *clk_core; - struct clk *clk_ref; - struct reset_control *uphy_rst; - struct reset_control *pipe_rst; - struct reset_control *tcphy_rst; - const struct rockchip_usb3phy_port_cfg *port_cfgs; - /* mutex to protect access to individual PHYs */ - struct mutex lock; - - bool flip; - u8 mode; -}; - struct phy_reg { u16 value; u32 addr; diff --git a/include/soc/rockchip/rockchip_phy_typec.h b/include/soc/rockchip/rockchip_phy_typec.h new file mode 100644 index 000..be6af0e --- /dev/null +++ b/include/soc/rockchip/rockchip_phy_typec.h @@ -0,0 +1,63 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd + * Author: Lin Huang <h...@rock-chips.com> + */ + +#ifndef __SOC_ROCKCHIP_PHY_TYPEC_H +#define __SOC_ROCKCHIP_PHY_TYPEC_H + +struct usb3phy_reg { + u32 offset; + u32 enable_bit; + u32 write_enable; +}; + +/** + * struct rockchip_usb3phy_port_cfg: usb3-phy port configuration. + * @reg: the base address for usb3-phy config. + * @typec_conn_dir: the register of type-c connector direction. + * @usb3tousb2_en: the register of type-c force usb2 to usb2 enable. + * @external_psm: the register of type-c phy external psm clock. + * @pipe_status: the register of type-c phy pipe status. + * @usb3_host_disable: the register of type-c usb3 host disable. + * @usb3_host_port: the register of type-c usb3 host port. + * @uphy_dp_sel: the register of type-c phy DP select control. + */ +struct rockchip_usb3phy_port_cfg { + unsigned int reg; + struct usb3phy_reg typec_conn_dir; + struct usb3phy_reg usb3tousb2_en; + struct usb3phy_reg external_psm; + struct usb3phy_reg pipe_status; + struct usb3phy_reg usb3_host_disable; + struct usb3phy_reg usb3_host_port; + struct usb3phy_reg uphy_dp_sel; +}; + +struct phy_config { + int swing; + int pe; +}; + +struct rockchip_typec_phy { + struct device *dev; + void __iomem *base; + struct extcon_dev *extcon; + struct regmap *grf_regs; + struct clk *clk_core; + struct clk *clk_ref; + struct reset_control *uphy_rst; + struct reset_control *pipe_rst; + struct reset_control *tcphy_rst; + const struct rockchip_usb3phy_port_cfg *port_cfgs; + /* mutex to protect access to individual PHYs */ + struct mutex lock; + struct phy_config config[3][4]; + bool flip; + u8 mode; + int (*typec_phy_config)(struct phy *phy, int link_rate, + int lanes, u8 swing, u8 pre_emp); +}; + +#endif -- 2.7.4 ___ dri-devel mailing list dri-dev
[PATCH v6 1/5] drm/rockchip: add transfer function for cdn-dp
From: Chris Zhong <z...@rock-chips.com> We may support training outside firmware, so we need support dpcd read/write to get the message or do some setting with display. Signed-off-by: Chris Zhong <z...@rock-chips.com> Signed-off-by: Lin Huang <h...@rock-chips.com> Reviewed-by: Sean Paul <seanp...@chromium.org> Reviewed-by: Enric Balletbo <enric.balle...@collabora.com> --- Changes in v2: - update patch following Enric suggest Changes in v3: - None Changes in v4: - None Changes in v5: - None Changes in v6: - None drivers/gpu/drm/rockchip/cdn-dp-core.c | 55 +++ drivers/gpu/drm/rockchip/cdn-dp-core.h | 1 + drivers/gpu/drm/rockchip/cdn-dp-reg.c | 69 ++ drivers/gpu/drm/rockchip/cdn-dp-reg.h | 14 ++- 4 files changed, 122 insertions(+), 17 deletions(-) diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.c b/drivers/gpu/drm/rockchip/cdn-dp-core.c index c6fbdcd..cce64c1 100644 --- a/drivers/gpu/drm/rockchip/cdn-dp-core.c +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.c @@ -176,8 +176,8 @@ static int cdn_dp_get_sink_count(struct cdn_dp_device *dp, u8 *sink_count) u8 value; *sink_count = 0; - ret = cdn_dp_dpcd_read(dp, DP_SINK_COUNT, , 1); - if (ret) + ret = drm_dp_dpcd_read(>aux, DP_SINK_COUNT, , 1); + if (ret < 0) return ret; *sink_count = DP_GET_SINK_COUNT(value); @@ -374,9 +374,9 @@ static int cdn_dp_get_sink_capability(struct cdn_dp_device *dp) if (!cdn_dp_check_sink_connection(dp)) return -ENODEV; - ret = cdn_dp_dpcd_read(dp, DP_DPCD_REV, dp->dpcd, - DP_RECEIVER_CAP_SIZE); - if (ret) { + ret = drm_dp_dpcd_read(>aux, DP_DPCD_REV, dp->dpcd, + sizeof(dp->dpcd)); + if (ret < 0) { DRM_DEV_ERROR(dp->dev, "Failed to get caps %d\n", ret); return ret; } @@ -582,8 +582,8 @@ static bool cdn_dp_check_link_status(struct cdn_dp_device *dp) if (!port || !dp->link.rate || !dp->link.num_lanes) return false; - if (cdn_dp_dpcd_read(dp, DP_LANE0_1_STATUS, link_status, -DP_LINK_STATUS_SIZE)) { + if (drm_dp_dpcd_read_link_status(>aux, link_status) != + DP_LINK_STATUS_SIZE) { DRM_ERROR("Failed to get link status\n"); return false; } @@ -1012,6 +1012,40 @@ static int cdn_dp_pd_event(struct notifier_block *nb, return NOTIFY_DONE; } +static ssize_t cdn_dp_aux_transfer(struct drm_dp_aux *aux, + struct drm_dp_aux_msg *msg) +{ + struct cdn_dp_device *dp = container_of(aux, struct cdn_dp_device, aux); + int ret; + u8 status; + + switch (msg->request & ~DP_AUX_I2C_MOT) { + case DP_AUX_NATIVE_WRITE: + case DP_AUX_I2C_WRITE: + case DP_AUX_I2C_WRITE_STATUS_UPDATE: + ret = cdn_dp_dpcd_write(dp, msg->address, msg->buffer, + msg->size); + break; + case DP_AUX_NATIVE_READ: + case DP_AUX_I2C_READ: + ret = cdn_dp_dpcd_read(dp, msg->address, msg->buffer, + msg->size); + break; + default: + return -EINVAL; + } + + status = cdn_dp_get_aux_status(dp); + if (status == AUX_STATUS_ACK) + msg->reply = DP_AUX_NATIVE_REPLY_ACK; + else if (status == AUX_STATUS_NACK) + msg->reply = DP_AUX_NATIVE_REPLY_NACK; + else if (status == AUX_STATUS_DEFER) + msg->reply = DP_AUX_NATIVE_REPLY_DEFER; + + return ret; +} + static int cdn_dp_bind(struct device *dev, struct device *master, void *data) { struct cdn_dp_device *dp = dev_get_drvdata(dev); @@ -1030,6 +1064,13 @@ static int cdn_dp_bind(struct device *dev, struct device *master, void *data) dp->active = false; dp->active_port = -1; dp->fw_loaded = false; + dp->aux.name = "DP-AUX"; + dp->aux.transfer = cdn_dp_aux_transfer; + dp->aux.dev = dev; + + ret = drm_dp_aux_register(>aux); + if (ret) + return ret; INIT_WORK(>event_work, cdn_dp_pd_event_work); diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.h b/drivers/gpu/drm/rockchip/cdn-dp-core.h index f57e296..46159b2 100644 --- a/drivers/gpu/drm/rockchip/cdn-dp-core.h +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.h @@ -78,6 +78,7 @@ struct cdn_dp_device { struct platform_device *audio_pdev; struct work_struct event_work; struct edid *edid; + struct drm_dp_aux aux; struct mutex lock; bool connected; diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.c b/drivers/gpu/drm/rockch
[PATCH v6 2/5] Documentation: dt-bindings: phy: add phy_config for Rockchip USB Type-C PHY
If want to do training outside DP Firmware, need phy voltage swing and pre_emphasis value. Signed-off-by: Lin Huang <h...@rock-chips.com> --- Changes in v2: - None Changes in v3: - modify property description and add this property to Example Changes in v4: - None Changes in v5: - None Changes in v6: - change rockchip,phy_config to rockchip,phy-config and descript it in detail. .../devicetree/bindings/phy/phy-rockchip-typec.txt | 36 +- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/phy/phy-rockchip-typec.txt b/Documentation/devicetree/bindings/phy/phy-rockchip-typec.txt index 960da7f..40d5e7a 100644 --- a/Documentation/devicetree/bindings/phy/phy-rockchip-typec.txt +++ b/Documentation/devicetree/bindings/phy/phy-rockchip-typec.txt @@ -17,7 +17,11 @@ Required properties: Optional properties: - extcon : extcon specifier for the Power Delivery - + - rockchip,phy-config : A list of voltage swing(mV) and pre-emphasis + (dB) pairs. They are 3 blocks of 4 entries and + correspond to s0p0 ~ s0p3, s1p0 ~ s1p3, + s2p0 ~ s2p3, s3p0 ~ s2p3 swing and pre-emphasis + values. Required nodes : a sub-node is required for each port the phy provides. The sub-node name is used to identify dp or usb3 port, and shall be the following entries: @@ -50,6 +54,21 @@ Example: < SRST_P_UPHY0_TCPHY>; reset-names = "uphy", "uphy-pipe", "uphy-tcphy"; + rockchip,phy-config = <0x2a 0x00>, + <0x1f 0x15>, + <0x14 0x22>, + <0x02 0x2b>, + + <0x21 0x00>, + <0x12 0x15>, + <0x02 0x22>, + <0 0>, + + <0x15 0x00>, + <0x00 0x15>, + <0 0>, + <0 0>; + tcphy0_dp: dp-port { #phy-cells = <0>; }; @@ -74,6 +93,21 @@ Example: < SRST_P_UPHY1_TCPHY>; reset-names = "uphy", "uphy-pipe", "uphy-tcphy"; + rockchip,phy-config = <0x2a 0x00>, + <0x1f 0x15>, + <0x14 0x22>, + <0x02 0x2b>, + + <0x21 0x00>, + <0x12 0x15>, + <0x02 0x22>, + <0 0>, + + <0x15 0x00>, + <0x00 0x15>, + <0 0>, + <0 0>; + tcphy1_dp: dp-port { #phy-cells = <0>; }; -- 2.7.4 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH v6 4/5] phy: rockchip-typec: support variable phy config value
the phy config values used to fix in dp firmware, but some boards need change these values to do training and get the better eye diagram result. So support that in phy driver. Signed-off-by: Chris Zhong <z...@rock-chips.com> Signed-off-by: Lin Huang <h...@rock-chips.com> --- Changes in v2: - update patch following Enric suggest Changes in v3: - delete need_software_training variable - add default phy config value, if dts do not define phy config value, use these value Changes in v4: - rename variable config to tcphy_default_config Changes in v5: - None Changes in v6: - split the header file to new patch drivers/phy/rockchip/phy-rockchip-typec.c | 261 -- 1 file changed, 208 insertions(+), 53 deletions(-) diff --git a/drivers/phy/rockchip/phy-rockchip-typec.c b/drivers/phy/rockchip/phy-rockchip-typec.c index 795055f..4c4b925 100644 --- a/drivers/phy/rockchip/phy-rockchip-typec.c +++ b/drivers/phy/rockchip/phy-rockchip-typec.c @@ -324,21 +324,29 @@ * clock 0: PLL 0 div 1 * clock 1: PLL 1 div 2 */ -#define CLK_PLL_CONFIG 0X30 +#define CLK_PLL1_DIV1 0x20 +#define CLK_PLL1_DIV2 0x30 #define CLK_PLL_MASK 0x33 #define CMN_READY BIT(0) +#define DP_PLL_CLOCK_ENABLE_ACKBIT(3) #define DP_PLL_CLOCK_ENABLEBIT(2) +#define DP_PLL_ENABLE_ACK BIT(1) #define DP_PLL_ENABLE BIT(0) #define DP_PLL_DATA_RATE_RBR ((2 << 12) | (4 << 8)) #define DP_PLL_DATA_RATE_HBR ((2 << 12) | (4 << 8)) #define DP_PLL_DATA_RATE_HBR2 ((1 << 12) | (2 << 8)) +#define DP_PLL_DATA_RATE_MASK 0xff00 -#define DP_MODE_A0 BIT(4) -#define DP_MODE_A2 BIT(6) -#define DP_MODE_ENTER_A0 0xc101 -#define DP_MODE_ENTER_A2 0xc104 +#define DP_MODE_MASK 0xf +#define DP_MODE_ENTER_A0 BIT(0) +#define DP_MODE_ENTER_A2 BIT(2) +#define DP_MODE_ENTER_A3 BIT(3) +#define DP_MODE_A0_ACK BIT(4) +#define DP_MODE_A2_ACK BIT(6) +#define DP_MODE_A3_ACK BIT(7) +#define DP_LINK_RESET_DEASSERTED BIT(8) #define PHY_MODE_SET_TIMEOUT 10 @@ -350,6 +358,8 @@ #define MODE_DFP_USB BIT(1) #define MODE_DFP_DPBIT(2) +#define DP_DEFAULT_RATE162000 + struct phy_reg { u16 value; u32 addr; @@ -372,15 +382,15 @@ struct phy_reg usb3_pll_cfg[] = { { 0x8, CMN_DIAG_PLL0_LF_PROG }, }; -struct phy_reg dp_pll_cfg[] = { +struct phy_reg dp_pll_rbr_cfg[] = { { 0xf0, CMN_PLL1_VCOCAL_INIT }, { 0x18, CMN_PLL1_VCOCAL_ITER }, { 0x30b9, CMN_PLL1_VCOCAL_START }, - { 0x21c,CMN_PLL1_INTDIV }, + { 0x87, CMN_PLL1_INTDIV }, { 0,CMN_PLL1_FRACDIV }, - { 0x5, CMN_PLL1_HIGH_THR }, - { 0x35, CMN_PLL1_SS_CTRL1 }, - { 0x7f1e, CMN_PLL1_SS_CTRL2 }, + { 0x22, CMN_PLL1_HIGH_THR }, + { 0x8000, CMN_PLL1_SS_CTRL1 }, + { 0,CMN_PLL1_SS_CTRL2 }, { 0x20, CMN_PLL1_DSM_DIAG }, { 0,CMN_PLLSM1_USER_DEF_CTRL }, { 0,CMN_DIAG_PLL1_OVRD }, @@ -391,9 +401,52 @@ struct phy_reg dp_pll_cfg[] = { { 0x8, CMN_DIAG_PLL1_LF_PROG }, { 0x100,CMN_DIAG_PLL1_PTATIS_TUNE1 }, { 0x7, CMN_DIAG_PLL1_PTATIS_TUNE2 }, - { 0x4, CMN_DIAG_PLL1_INCLK_CTRL }, + { 0x1, CMN_DIAG_PLL1_INCLK_CTRL }, }; +struct phy_reg dp_pll_hbr_cfg[] = { + { 0xf0, CMN_PLL1_VCOCAL_INIT }, + { 0x18, CMN_PLL1_VCOCAL_ITER }, + { 0x30b4, CMN_PLL1_VCOCAL_START }, + { 0xe1, CMN_PLL1_INTDIV }, + { 0,CMN_PLL1_FRACDIV }, + { 0x5, CMN_PLL1_HIGH_THR }, + { 0x8000, CMN_PLL1_SS_CTRL1 }, + { 0,CMN_PLL1_SS_CTRL2 }, + { 0x20, CMN_PLL1_DSM_DIAG }, + { 0x1000, CMN_PLLSM1_USER_DEF_CTRL }, + { 0,CMN_DIAG_PLL1_OVRD }, + { 0,CMN_DIAG_PLL1_FBH_OVRD }, + { 0,CMN_DIAG_PLL1_FBL_OVRD }, + { 0x7, CMN_DIAG_PLL1_V2I_TUNE }, + { 0x45, CMN_DIAG_PLL1_CP_TUNE }, + { 0x8, CMN_DIAG_PLL1_LF_PROG }, + { 0x1, CMN_DIAG_PLL1_PTATIS_TUNE1 }, + { 0x1, CMN_DIAG_PLL1_PTATIS_TUNE2 }, + { 0x1, CMN_DIAG_PLL1_INCLK_CTRL }, +}; + +struct phy_reg dp_pll_hbr2_cfg[] = { + { 0xf0, CMN_PLL1_VCOCAL_INIT }, + { 0x18, CMN_PLL1_VCOCAL_ITER }, + { 0x30b4, CMN_PLL1_VCOCAL_START }, + { 0xe1, CMN_PLL1_INTDIV }, + {
[PATCH v5 4/4] drm/rockchip: support dp training outside dp firmware
DP firmware uses fixed phy config values to do training, but some boards need to adjust these values to fit for their unique hardware design. So get phy config values from dts and use software link training instead of relying on firmware, if software training fail, keep firmware training as a fallback if sw training fails. Signed-off-by: Chris Zhong <z...@rock-chips.com> Signed-off-by: Lin Huang <h...@rock-chips.com> --- Changes in v2: - update patch following Enric suggest Changes in v3: - use variable fw_training instead sw_training_success - base on DP SPCE, if training fail use lower link rate to retry training Changes in v4: - improve cdn_dp_get_lower_link_rate() and cdn_dp_software_train_link() follow Sean suggest Changes in v5: - fix some whitespcae issue drivers/gpu/drm/rockchip/Makefile | 3 +- drivers/gpu/drm/rockchip/cdn-dp-core.c | 24 +- drivers/gpu/drm/rockchip/cdn-dp-core.h | 2 + drivers/gpu/drm/rockchip/cdn-dp-link-training.c | 420 drivers/gpu/drm/rockchip/cdn-dp-reg.c | 31 +- drivers/gpu/drm/rockchip/cdn-dp-reg.h | 38 ++- 6 files changed, 505 insertions(+), 13 deletions(-) create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-link-training.c diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile index a314e21..b932f62 100644 --- a/drivers/gpu/drm/rockchip/Makefile +++ b/drivers/gpu/drm/rockchip/Makefile @@ -9,7 +9,8 @@ rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o \ rockchipdrm-$(CONFIG_DRM_FBDEV_EMULATION) += rockchip_drm_fbdev.o rockchipdrm-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o -rockchipdrm-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o cdn-dp-reg.o +rockchipdrm-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o cdn-dp-reg.o \ + cdn-dp-link-training.o rockchipdrm-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o rockchipdrm-$(CONFIG_ROCKCHIP_DW_MIPI_DSI) += dw-mipi-dsi.o rockchipdrm-$(CONFIG_ROCKCHIP_INNO_HDMI) += inno_hdmi.o diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.c b/drivers/gpu/drm/rockchip/cdn-dp-core.c index cce64c1..d9d0d4d 100644 --- a/drivers/gpu/drm/rockchip/cdn-dp-core.c +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.c @@ -629,11 +629,13 @@ static void cdn_dp_encoder_enable(struct drm_encoder *encoder) goto out; } } - - ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_IDLE); - if (ret) { - DRM_DEV_ERROR(dp->dev, "Failed to idle video %d\n", ret); - goto out; + if (dp->use_fw_training == true) { + ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_IDLE); + if (ret) { + DRM_DEV_ERROR(dp->dev, + "Failed to idle video %d\n", ret); + goto out; + } } ret = cdn_dp_config_video(dp); @@ -642,11 +644,15 @@ static void cdn_dp_encoder_enable(struct drm_encoder *encoder) goto out; } - ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_VALID); - if (ret) { - DRM_DEV_ERROR(dp->dev, "Failed to valid video %d\n", ret); - goto out; + if (dp->use_fw_training == true) { + ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_VALID); + if (ret) { + DRM_DEV_ERROR(dp->dev, + "Failed to valid video %d\n", ret); + goto out; + } } + out: mutex_unlock(>lock); } diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.h b/drivers/gpu/drm/rockchip/cdn-dp-core.h index 46159b2..77a9793 100644 --- a/drivers/gpu/drm/rockchip/cdn-dp-core.h +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.h @@ -84,6 +84,7 @@ struct cdn_dp_device { bool connected; bool active; bool suspended; + bool use_fw_training; const struct firmware *fw; /* cdn dp firmware */ unsigned int fw_version;/* cdn fw version */ @@ -106,6 +107,7 @@ struct cdn_dp_device { u8 ports; u8 lanes; int active_port; + u8 train_set[4]; u8 dpcd[DP_RECEIVER_CAP_SIZE]; bool sink_has_audio; diff --git a/drivers/gpu/drm/rockchip/cdn-dp-link-training.c b/drivers/gpu/drm/rockchip/cdn-dp-link-training.c new file mode 100644 index 000..73c3290 --- /dev/null +++ b/drivers/gpu/drm/rockchip/cdn-dp-link-training.c @@ -0,0 +1,420 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd + * Author: Chris Zhong <z...@rock-chips.com> + */ + +#include +#include +#include +#include + +#include "cdn-dp-core.h" +#include "cdn-dp-reg.h" + +static void cdn_dp_set_signal_levels(struct cdn_dp_device *dp) +{ + struct cdn_dp_port *por
[PATCH v5 3/4] phy: rockchip-typec: support variable phy config value
the phy config values used to fix in dp firmware, but some boards need change these values to do training and get the better eye diagram result. So support that in phy driver. Signed-off-by: Chris Zhong <z...@rock-chips.com> Signed-off-by: Lin Huang <h...@rock-chips.com> --- Changes in v2: - update patch following Enric suggest Changes in v3: - delete need_software_training variable - add default phy config value, if dts do not define phy config value, use these value Changes in v4: - rename variable config to tcphy_default_config Changes in v5: - None drivers/phy/rockchip/phy-rockchip-typec.c | 306 -- include/soc/rockchip/rockchip_phy_typec.h | 63 ++ 2 files changed, 271 insertions(+), 98 deletions(-) create mode 100644 include/soc/rockchip/rockchip_phy_typec.h diff --git a/drivers/phy/rockchip/phy-rockchip-typec.c b/drivers/phy/rockchip/phy-rockchip-typec.c index 76a4b58..5d8692d 100644 --- a/drivers/phy/rockchip/phy-rockchip-typec.c +++ b/drivers/phy/rockchip/phy-rockchip-typec.c @@ -63,6 +63,7 @@ #include #include +#include #define CMN_SSM_BANDGAP(0x21 << 2) #define CMN_SSM_BIAS (0x22 << 2) @@ -323,21 +324,29 @@ * clock 0: PLL 0 div 1 * clock 1: PLL 1 div 2 */ -#define CLK_PLL_CONFIG 0X30 +#define CLK_PLL1_DIV1 0x20 +#define CLK_PLL1_DIV2 0x30 #define CLK_PLL_MASK 0x33 #define CMN_READY BIT(0) +#define DP_PLL_CLOCK_ENABLE_ACKBIT(3) #define DP_PLL_CLOCK_ENABLEBIT(2) +#define DP_PLL_ENABLE_ACK BIT(1) #define DP_PLL_ENABLE BIT(0) #define DP_PLL_DATA_RATE_RBR ((2 << 12) | (4 << 8)) #define DP_PLL_DATA_RATE_HBR ((2 << 12) | (4 << 8)) #define DP_PLL_DATA_RATE_HBR2 ((1 << 12) | (2 << 8)) +#define DP_PLL_DATA_RATE_MASK 0xff00 -#define DP_MODE_A0 BIT(4) -#define DP_MODE_A2 BIT(6) -#define DP_MODE_ENTER_A0 0xc101 -#define DP_MODE_ENTER_A2 0xc104 +#define DP_MODE_MASK 0xf +#define DP_MODE_ENTER_A0 BIT(0) +#define DP_MODE_ENTER_A2 BIT(2) +#define DP_MODE_ENTER_A3 BIT(3) +#define DP_MODE_A0_ACK BIT(4) +#define DP_MODE_A2_ACK BIT(6) +#define DP_MODE_A3_ACK BIT(7) +#define DP_LINK_RESET_DEASSERTED BIT(8) #define PHY_MODE_SET_TIMEOUT 10 @@ -349,51 +358,7 @@ #define MODE_DFP_USB BIT(1) #define MODE_DFP_DPBIT(2) -struct usb3phy_reg { - u32 offset; - u32 enable_bit; - u32 write_enable; -}; - -/** - * struct rockchip_usb3phy_port_cfg: usb3-phy port configuration. - * @reg: the base address for usb3-phy config. - * @typec_conn_dir: the register of type-c connector direction. - * @usb3tousb2_en: the register of type-c force usb2 to usb2 enable. - * @external_psm: the register of type-c phy external psm clock. - * @pipe_status: the register of type-c phy pipe status. - * @usb3_host_disable: the register of type-c usb3 host disable. - * @usb3_host_port: the register of type-c usb3 host port. - * @uphy_dp_sel: the register of type-c phy DP select control. - */ -struct rockchip_usb3phy_port_cfg { - unsigned int reg; - struct usb3phy_reg typec_conn_dir; - struct usb3phy_reg usb3tousb2_en; - struct usb3phy_reg external_psm; - struct usb3phy_reg pipe_status; - struct usb3phy_reg usb3_host_disable; - struct usb3phy_reg usb3_host_port; - struct usb3phy_reg uphy_dp_sel; -}; - -struct rockchip_typec_phy { - struct device *dev; - void __iomem *base; - struct extcon_dev *extcon; - struct regmap *grf_regs; - struct clk *clk_core; - struct clk *clk_ref; - struct reset_control *uphy_rst; - struct reset_control *pipe_rst; - struct reset_control *tcphy_rst; - const struct rockchip_usb3phy_port_cfg *port_cfgs; - /* mutex to protect access to individual PHYs */ - struct mutex lock; - - bool flip; - u8 mode; -}; +#define DP_DEFAULT_RATE162000 struct phy_reg { u16 value; @@ -417,15 +382,15 @@ struct phy_reg usb3_pll_cfg[] = { { 0x8, CMN_DIAG_PLL0_LF_PROG }, }; -struct phy_reg dp_pll_cfg[] = { +struct phy_reg dp_pll_rbr_cfg[] = { { 0xf0, CMN_PLL1_VCOCAL_INIT }, { 0x18, CMN_PLL1_VCOCAL_ITER }, { 0x30b9, CMN_PLL1_VCOCAL_START }, - { 0x21c,CMN_PLL1_INTDIV }, + { 0x87, CMN_PLL1_INTDIV }, { 0,CMN_PLL1_FRACDIV }, - { 0x5, CMN_PLL1_HIGH_THR }, - { 0x35, CMN_PLL1_SS_CTRL1 }, - { 0x7f1e, CMN_PLL1_SS_CTRL2 }, + { 0x22, CM
[PATCH v5 1/4] drm/rockchip: add transfer function for cdn-dp
From: Chris Zhong <z...@rock-chips.com> We may support training outside firmware, so we need support dpcd read/write to get the message or do some setting with display. Signed-off-by: Chris Zhong <z...@rock-chips.com> Signed-off-by: Lin Huang <h...@rock-chips.com> Reviewed-by: Sean Paul <seanp...@chromium.org> Reviewed-by: Enric Balletbo <enric.balle...@collabora.com> --- Changes in v2: - update patch following Enric suggest Changes in v3: - None Changes in v4: - None Changes in v5: - None drivers/gpu/drm/rockchip/cdn-dp-core.c | 55 +++ drivers/gpu/drm/rockchip/cdn-dp-core.h | 1 + drivers/gpu/drm/rockchip/cdn-dp-reg.c | 69 ++ drivers/gpu/drm/rockchip/cdn-dp-reg.h | 14 ++- 4 files changed, 122 insertions(+), 17 deletions(-) diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.c b/drivers/gpu/drm/rockchip/cdn-dp-core.c index c6fbdcd..cce64c1 100644 --- a/drivers/gpu/drm/rockchip/cdn-dp-core.c +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.c @@ -176,8 +176,8 @@ static int cdn_dp_get_sink_count(struct cdn_dp_device *dp, u8 *sink_count) u8 value; *sink_count = 0; - ret = cdn_dp_dpcd_read(dp, DP_SINK_COUNT, , 1); - if (ret) + ret = drm_dp_dpcd_read(>aux, DP_SINK_COUNT, , 1); + if (ret < 0) return ret; *sink_count = DP_GET_SINK_COUNT(value); @@ -374,9 +374,9 @@ static int cdn_dp_get_sink_capability(struct cdn_dp_device *dp) if (!cdn_dp_check_sink_connection(dp)) return -ENODEV; - ret = cdn_dp_dpcd_read(dp, DP_DPCD_REV, dp->dpcd, - DP_RECEIVER_CAP_SIZE); - if (ret) { + ret = drm_dp_dpcd_read(>aux, DP_DPCD_REV, dp->dpcd, + sizeof(dp->dpcd)); + if (ret < 0) { DRM_DEV_ERROR(dp->dev, "Failed to get caps %d\n", ret); return ret; } @@ -582,8 +582,8 @@ static bool cdn_dp_check_link_status(struct cdn_dp_device *dp) if (!port || !dp->link.rate || !dp->link.num_lanes) return false; - if (cdn_dp_dpcd_read(dp, DP_LANE0_1_STATUS, link_status, -DP_LINK_STATUS_SIZE)) { + if (drm_dp_dpcd_read_link_status(>aux, link_status) != + DP_LINK_STATUS_SIZE) { DRM_ERROR("Failed to get link status\n"); return false; } @@ -1012,6 +1012,40 @@ static int cdn_dp_pd_event(struct notifier_block *nb, return NOTIFY_DONE; } +static ssize_t cdn_dp_aux_transfer(struct drm_dp_aux *aux, + struct drm_dp_aux_msg *msg) +{ + struct cdn_dp_device *dp = container_of(aux, struct cdn_dp_device, aux); + int ret; + u8 status; + + switch (msg->request & ~DP_AUX_I2C_MOT) { + case DP_AUX_NATIVE_WRITE: + case DP_AUX_I2C_WRITE: + case DP_AUX_I2C_WRITE_STATUS_UPDATE: + ret = cdn_dp_dpcd_write(dp, msg->address, msg->buffer, + msg->size); + break; + case DP_AUX_NATIVE_READ: + case DP_AUX_I2C_READ: + ret = cdn_dp_dpcd_read(dp, msg->address, msg->buffer, + msg->size); + break; + default: + return -EINVAL; + } + + status = cdn_dp_get_aux_status(dp); + if (status == AUX_STATUS_ACK) + msg->reply = DP_AUX_NATIVE_REPLY_ACK; + else if (status == AUX_STATUS_NACK) + msg->reply = DP_AUX_NATIVE_REPLY_NACK; + else if (status == AUX_STATUS_DEFER) + msg->reply = DP_AUX_NATIVE_REPLY_DEFER; + + return ret; +} + static int cdn_dp_bind(struct device *dev, struct device *master, void *data) { struct cdn_dp_device *dp = dev_get_drvdata(dev); @@ -1030,6 +1064,13 @@ static int cdn_dp_bind(struct device *dev, struct device *master, void *data) dp->active = false; dp->active_port = -1; dp->fw_loaded = false; + dp->aux.name = "DP-AUX"; + dp->aux.transfer = cdn_dp_aux_transfer; + dp->aux.dev = dev; + + ret = drm_dp_aux_register(>aux); + if (ret) + return ret; INIT_WORK(>event_work, cdn_dp_pd_event_work); diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.h b/drivers/gpu/drm/rockchip/cdn-dp-core.h index f57e296..46159b2 100644 --- a/drivers/gpu/drm/rockchip/cdn-dp-core.h +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.h @@ -78,6 +78,7 @@ struct cdn_dp_device { struct platform_device *audio_pdev; struct work_struct event_work; struct edid *edid; + struct drm_dp_aux aux; struct mutex lock; bool connected; diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.c b/drivers/gpu/drm/rockchip/cdn-dp-reg.
[PATCH v5 2/4] Documentation: bindings: add phy_config for Rockchip USB Type-C PHY
If want to do training outside DP Firmware, need phy voltage swing and pre_emphasis value. Signed-off-by: Lin Huang <h...@rock-chips.com> --- Changes in v2: - None Changes in v3: - modify property description and add this property to Example Change in v4: - None Change in v5: - None .../devicetree/bindings/phy/phy-rockchip-typec.txt | 29 +- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/phy/phy-rockchip-typec.txt b/Documentation/devicetree/bindings/phy/phy-rockchip-typec.txt index 960da7f..af298f2 100644 --- a/Documentation/devicetree/bindings/phy/phy-rockchip-typec.txt +++ b/Documentation/devicetree/bindings/phy/phy-rockchip-typec.txt @@ -17,7 +17,8 @@ Required properties: Optional properties: - extcon : extcon specifier for the Power Delivery - + - rockchip,phy_config : A list of voltage swing(mv) and pre-emphasis + (dB) pairs. Required nodes : a sub-node is required for each port the phy provides. The sub-node name is used to identify dp or usb3 port, and shall be the following entries: @@ -50,6 +51,19 @@ Example: < SRST_P_UPHY0_TCPHY>; reset-names = "uphy", "uphy-pipe", "uphy-tcphy"; + rockchip,phy_config =<0x2a 0x00 + 0x1f 0x15 + 0x14 0x22 + 0x02 0x2b + 0x21 0x00 + 0x12 0x15 + 0x02 0x22 + 0 0 + 0x15 0x00 + 0x00 0x15 + 0 0 + 0 0>; + tcphy0_dp: dp-port { #phy-cells = <0>; }; @@ -74,6 +88,19 @@ Example: < SRST_P_UPHY1_TCPHY>; reset-names = "uphy", "uphy-pipe", "uphy-tcphy"; + rockchip,phy_config =<0x2a 0x00 + 0x1f 0x15 + 0x14 0x22 + 0x02 0x2b + 0x21 0x00 + 0x12 0x15 + 0x02 0x22 + 0 0 + 0x15 0x00 + 0x00 0x15 + 0 0 + 0 0>; + tcphy1_dp: dp-port { #phy-cells = <0>; }; -- 2.7.4 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH v4 3/4] phy: rockchip-typec: support variable phy config value
the phy config values used to fix in dp firmware, but some boards need change these values to do training and get the better eye diagram result. So support that in phy driver. Signed-off-by: Chris Zhong <z...@rock-chips.com> Signed-off-by: Lin Huang <h...@rock-chips.com> --- Changes in v2: - update patch following Enric suggest Changes in v3: - delete need_software_training variable - add default phy config value, if dts do not define phy config value, use these value Changes in v4: - rename variable config to tcphy_default_config drivers/phy/rockchip/phy-rockchip-typec.c | 306 -- include/soc/rockchip/rockchip_phy_typec.h | 63 ++ 2 files changed, 271 insertions(+), 98 deletions(-) create mode 100644 include/soc/rockchip/rockchip_phy_typec.h diff --git a/drivers/phy/rockchip/phy-rockchip-typec.c b/drivers/phy/rockchip/phy-rockchip-typec.c index 76a4b58..5d8692d 100644 --- a/drivers/phy/rockchip/phy-rockchip-typec.c +++ b/drivers/phy/rockchip/phy-rockchip-typec.c @@ -63,6 +63,7 @@ #include #include +#include #define CMN_SSM_BANDGAP(0x21 << 2) #define CMN_SSM_BIAS (0x22 << 2) @@ -323,21 +324,29 @@ * clock 0: PLL 0 div 1 * clock 1: PLL 1 div 2 */ -#define CLK_PLL_CONFIG 0X30 +#define CLK_PLL1_DIV1 0x20 +#define CLK_PLL1_DIV2 0x30 #define CLK_PLL_MASK 0x33 #define CMN_READY BIT(0) +#define DP_PLL_CLOCK_ENABLE_ACKBIT(3) #define DP_PLL_CLOCK_ENABLEBIT(2) +#define DP_PLL_ENABLE_ACK BIT(1) #define DP_PLL_ENABLE BIT(0) #define DP_PLL_DATA_RATE_RBR ((2 << 12) | (4 << 8)) #define DP_PLL_DATA_RATE_HBR ((2 << 12) | (4 << 8)) #define DP_PLL_DATA_RATE_HBR2 ((1 << 12) | (2 << 8)) +#define DP_PLL_DATA_RATE_MASK 0xff00 -#define DP_MODE_A0 BIT(4) -#define DP_MODE_A2 BIT(6) -#define DP_MODE_ENTER_A0 0xc101 -#define DP_MODE_ENTER_A2 0xc104 +#define DP_MODE_MASK 0xf +#define DP_MODE_ENTER_A0 BIT(0) +#define DP_MODE_ENTER_A2 BIT(2) +#define DP_MODE_ENTER_A3 BIT(3) +#define DP_MODE_A0_ACK BIT(4) +#define DP_MODE_A2_ACK BIT(6) +#define DP_MODE_A3_ACK BIT(7) +#define DP_LINK_RESET_DEASSERTED BIT(8) #define PHY_MODE_SET_TIMEOUT 10 @@ -349,51 +358,7 @@ #define MODE_DFP_USB BIT(1) #define MODE_DFP_DPBIT(2) -struct usb3phy_reg { - u32 offset; - u32 enable_bit; - u32 write_enable; -}; - -/** - * struct rockchip_usb3phy_port_cfg: usb3-phy port configuration. - * @reg: the base address for usb3-phy config. - * @typec_conn_dir: the register of type-c connector direction. - * @usb3tousb2_en: the register of type-c force usb2 to usb2 enable. - * @external_psm: the register of type-c phy external psm clock. - * @pipe_status: the register of type-c phy pipe status. - * @usb3_host_disable: the register of type-c usb3 host disable. - * @usb3_host_port: the register of type-c usb3 host port. - * @uphy_dp_sel: the register of type-c phy DP select control. - */ -struct rockchip_usb3phy_port_cfg { - unsigned int reg; - struct usb3phy_reg typec_conn_dir; - struct usb3phy_reg usb3tousb2_en; - struct usb3phy_reg external_psm; - struct usb3phy_reg pipe_status; - struct usb3phy_reg usb3_host_disable; - struct usb3phy_reg usb3_host_port; - struct usb3phy_reg uphy_dp_sel; -}; - -struct rockchip_typec_phy { - struct device *dev; - void __iomem *base; - struct extcon_dev *extcon; - struct regmap *grf_regs; - struct clk *clk_core; - struct clk *clk_ref; - struct reset_control *uphy_rst; - struct reset_control *pipe_rst; - struct reset_control *tcphy_rst; - const struct rockchip_usb3phy_port_cfg *port_cfgs; - /* mutex to protect access to individual PHYs */ - struct mutex lock; - - bool flip; - u8 mode; -}; +#define DP_DEFAULT_RATE162000 struct phy_reg { u16 value; @@ -417,15 +382,15 @@ struct phy_reg usb3_pll_cfg[] = { { 0x8, CMN_DIAG_PLL0_LF_PROG }, }; -struct phy_reg dp_pll_cfg[] = { +struct phy_reg dp_pll_rbr_cfg[] = { { 0xf0, CMN_PLL1_VCOCAL_INIT }, { 0x18, CMN_PLL1_VCOCAL_ITER }, { 0x30b9, CMN_PLL1_VCOCAL_START }, - { 0x21c,CMN_PLL1_INTDIV }, + { 0x87, CMN_PLL1_INTDIV }, { 0,CMN_PLL1_FRACDIV }, - { 0x5, CMN_PLL1_HIGH_THR }, - { 0x35, CMN_PLL1_SS_CTRL1 }, - { 0x7f1e, CMN_PLL1_SS_CTRL2 }, + { 0x22, CMN_PLL1_HIGH_THR
[PATCH v3 1/4] drm/rockchip: add transfer function for cdn-dp
From: Chris Zhong <z...@rock-chips.com> We may support training outside firmware, so we need support dpcd read/write to get the message or do some setting with display. Signed-off-by: Chris Zhong <z...@rock-chips.com> Signed-off-by: Lin Huang <h...@rock-chips.com> Reviewed-by: Sean Paul <seanp...@chromium.org> Reviewed-by: Enric Balletbo <eballe...@gmail.com> --- Changes in v2: - update patch following Enric suggest - None drivers/gpu/drm/rockchip/cdn-dp-core.c | 55 +++ drivers/gpu/drm/rockchip/cdn-dp-core.h | 1 + drivers/gpu/drm/rockchip/cdn-dp-reg.c | 69 ++ drivers/gpu/drm/rockchip/cdn-dp-reg.h | 14 ++- 4 files changed, 122 insertions(+), 17 deletions(-) diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.c b/drivers/gpu/drm/rockchip/cdn-dp-core.c index c6fbdcd..cce64c1 100644 --- a/drivers/gpu/drm/rockchip/cdn-dp-core.c +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.c @@ -176,8 +176,8 @@ static int cdn_dp_get_sink_count(struct cdn_dp_device *dp, u8 *sink_count) u8 value; *sink_count = 0; - ret = cdn_dp_dpcd_read(dp, DP_SINK_COUNT, , 1); - if (ret) + ret = drm_dp_dpcd_read(>aux, DP_SINK_COUNT, , 1); + if (ret < 0) return ret; *sink_count = DP_GET_SINK_COUNT(value); @@ -374,9 +374,9 @@ static int cdn_dp_get_sink_capability(struct cdn_dp_device *dp) if (!cdn_dp_check_sink_connection(dp)) return -ENODEV; - ret = cdn_dp_dpcd_read(dp, DP_DPCD_REV, dp->dpcd, - DP_RECEIVER_CAP_SIZE); - if (ret) { + ret = drm_dp_dpcd_read(>aux, DP_DPCD_REV, dp->dpcd, + sizeof(dp->dpcd)); + if (ret < 0) { DRM_DEV_ERROR(dp->dev, "Failed to get caps %d\n", ret); return ret; } @@ -582,8 +582,8 @@ static bool cdn_dp_check_link_status(struct cdn_dp_device *dp) if (!port || !dp->link.rate || !dp->link.num_lanes) return false; - if (cdn_dp_dpcd_read(dp, DP_LANE0_1_STATUS, link_status, -DP_LINK_STATUS_SIZE)) { + if (drm_dp_dpcd_read_link_status(>aux, link_status) != + DP_LINK_STATUS_SIZE) { DRM_ERROR("Failed to get link status\n"); return false; } @@ -1012,6 +1012,40 @@ static int cdn_dp_pd_event(struct notifier_block *nb, return NOTIFY_DONE; } +static ssize_t cdn_dp_aux_transfer(struct drm_dp_aux *aux, + struct drm_dp_aux_msg *msg) +{ + struct cdn_dp_device *dp = container_of(aux, struct cdn_dp_device, aux); + int ret; + u8 status; + + switch (msg->request & ~DP_AUX_I2C_MOT) { + case DP_AUX_NATIVE_WRITE: + case DP_AUX_I2C_WRITE: + case DP_AUX_I2C_WRITE_STATUS_UPDATE: + ret = cdn_dp_dpcd_write(dp, msg->address, msg->buffer, + msg->size); + break; + case DP_AUX_NATIVE_READ: + case DP_AUX_I2C_READ: + ret = cdn_dp_dpcd_read(dp, msg->address, msg->buffer, + msg->size); + break; + default: + return -EINVAL; + } + + status = cdn_dp_get_aux_status(dp); + if (status == AUX_STATUS_ACK) + msg->reply = DP_AUX_NATIVE_REPLY_ACK; + else if (status == AUX_STATUS_NACK) + msg->reply = DP_AUX_NATIVE_REPLY_NACK; + else if (status == AUX_STATUS_DEFER) + msg->reply = DP_AUX_NATIVE_REPLY_DEFER; + + return ret; +} + static int cdn_dp_bind(struct device *dev, struct device *master, void *data) { struct cdn_dp_device *dp = dev_get_drvdata(dev); @@ -1030,6 +1064,13 @@ static int cdn_dp_bind(struct device *dev, struct device *master, void *data) dp->active = false; dp->active_port = -1; dp->fw_loaded = false; + dp->aux.name = "DP-AUX"; + dp->aux.transfer = cdn_dp_aux_transfer; + dp->aux.dev = dev; + + ret = drm_dp_aux_register(>aux); + if (ret) + return ret; INIT_WORK(>event_work, cdn_dp_pd_event_work); diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.h b/drivers/gpu/drm/rockchip/cdn-dp-core.h index f57e296..46159b2 100644 --- a/drivers/gpu/drm/rockchip/cdn-dp-core.h +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.h @@ -78,6 +78,7 @@ struct cdn_dp_device { struct platform_device *audio_pdev; struct work_struct event_work; struct edid *edid; + struct drm_dp_aux aux; struct mutex lock; bool connected; diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.c b/drivers/gpu/drm/rockchip/cdn-dp-reg.c index eb3042c..979355d 100644 --- a/drivers/gpu/drm/roc
[PATCH v3 2/4] Documentation: bindings: add phy_config for Rockchip USB Type-C PHY
If want to do training outside DP Firmware, need phy voltage swing and pre_emphasis value. Signed-off-by: Lin Huang <h...@rock-chips.com> --- Changes in v2: - rebase Changes in v3: - modify property description and add this property to example .../devicetree/bindings/phy/phy-rockchip-typec.txt | 29 +- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/phy/phy-rockchip-typec.txt b/Documentation/devicetree/bindings/phy/phy-rockchip-typec.txt index 960da7f..af298f2 100644 --- a/Documentation/devicetree/bindings/phy/phy-rockchip-typec.txt +++ b/Documentation/devicetree/bindings/phy/phy-rockchip-typec.txt @@ -17,7 +17,8 @@ Required properties: Optional properties: - extcon : extcon specifier for the Power Delivery - + - rockchip,phy_config : A list of voltage swing(mv) and pre-emphasis + (dB) pairs. Required nodes : a sub-node is required for each port the phy provides. The sub-node name is used to identify dp or usb3 port, and shall be the following entries: @@ -50,6 +51,19 @@ Example: < SRST_P_UPHY0_TCPHY>; reset-names = "uphy", "uphy-pipe", "uphy-tcphy"; + rockchip,phy_config =<0x2a 0x00 + 0x1f 0x15 + 0x14 0x22 + 0x02 0x2b + 0x21 0x00 + 0x12 0x15 + 0x02 0x22 + 0 0 + 0x15 0x00 + 0x00 0x15 + 0 0 + 0 0>; + tcphy0_dp: dp-port { #phy-cells = <0>; }; @@ -74,6 +88,19 @@ Example: < SRST_P_UPHY1_TCPHY>; reset-names = "uphy", "uphy-pipe", "uphy-tcphy"; + rockchip,phy_config =<0x2a 0x00 + 0x1f 0x15 + 0x14 0x22 + 0x02 0x2b + 0x21 0x00 + 0x12 0x15 + 0x02 0x22 + 0 0 + 0x15 0x00 + 0x00 0x15 + 0 0 + 0 0>; + tcphy1_dp: dp-port { #phy-cells = <0>; }; -- 2.7.4 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH v4 4/4] drm/rockchip: support dp training outside dp firmware
DP firmware uses fixed phy config values to do training, but some boards need to adjust these values to fit for their unique hardware design. So get phy config values from dts and use software link training instead of relying on firmware, if software training fail, keep firmware training as a fallback if sw training fails. Signed-off-by: Chris Zhong <z...@rock-chips.com> Signed-off-by: Lin Huang <h...@rock-chips.com> --- Changes in v2: - update patch following Enric suggest Changes in v3: - use variable fw_training instead sw_training_success - base on DP SPCE, if training fail use lower link rate to retry training Changes in v4: - improve cdn_dp_get_lower_link_rate() and cdn_dp_software_train_link() follow Sean suggest drivers/gpu/drm/rockchip/Makefile | 3 +- drivers/gpu/drm/rockchip/cdn-dp-core.c | 24 +- drivers/gpu/drm/rockchip/cdn-dp-core.h | 2 + drivers/gpu/drm/rockchip/cdn-dp-link-training.c | 420 drivers/gpu/drm/rockchip/cdn-dp-reg.c | 31 +- drivers/gpu/drm/rockchip/cdn-dp-reg.h | 38 ++- 6 files changed, 505 insertions(+), 13 deletions(-) create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-link-training.c diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile index a314e21..b932f62 100644 --- a/drivers/gpu/drm/rockchip/Makefile +++ b/drivers/gpu/drm/rockchip/Makefile @@ -9,7 +9,8 @@ rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o \ rockchipdrm-$(CONFIG_DRM_FBDEV_EMULATION) += rockchip_drm_fbdev.o rockchipdrm-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o -rockchipdrm-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o cdn-dp-reg.o +rockchipdrm-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o cdn-dp-reg.o \ + cdn-dp-link-training.o rockchipdrm-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o rockchipdrm-$(CONFIG_ROCKCHIP_DW_MIPI_DSI) += dw-mipi-dsi.o rockchipdrm-$(CONFIG_ROCKCHIP_INNO_HDMI) += inno_hdmi.o diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.c b/drivers/gpu/drm/rockchip/cdn-dp-core.c index cce64c1..d9d0d4d 100644 --- a/drivers/gpu/drm/rockchip/cdn-dp-core.c +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.c @@ -629,11 +629,13 @@ static void cdn_dp_encoder_enable(struct drm_encoder *encoder) goto out; } } - - ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_IDLE); - if (ret) { - DRM_DEV_ERROR(dp->dev, "Failed to idle video %d\n", ret); - goto out; + if (dp->use_fw_training == true) { + ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_IDLE); + if (ret) { + DRM_DEV_ERROR(dp->dev, + "Failed to idle video %d\n", ret); + goto out; + } } ret = cdn_dp_config_video(dp); @@ -642,11 +644,15 @@ static void cdn_dp_encoder_enable(struct drm_encoder *encoder) goto out; } - ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_VALID); - if (ret) { - DRM_DEV_ERROR(dp->dev, "Failed to valid video %d\n", ret); - goto out; + if (dp->use_fw_training == true) { + ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_VALID); + if (ret) { + DRM_DEV_ERROR(dp->dev, + "Failed to valid video %d\n", ret); + goto out; + } } + out: mutex_unlock(>lock); } diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.h b/drivers/gpu/drm/rockchip/cdn-dp-core.h index 46159b2..77a9793 100644 --- a/drivers/gpu/drm/rockchip/cdn-dp-core.h +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.h @@ -84,6 +84,7 @@ struct cdn_dp_device { bool connected; bool active; bool suspended; + bool use_fw_training; const struct firmware *fw; /* cdn dp firmware */ unsigned int fw_version;/* cdn fw version */ @@ -106,6 +107,7 @@ struct cdn_dp_device { u8 ports; u8 lanes; int active_port; + u8 train_set[4]; u8 dpcd[DP_RECEIVER_CAP_SIZE]; bool sink_has_audio; diff --git a/drivers/gpu/drm/rockchip/cdn-dp-link-training.c b/drivers/gpu/drm/rockchip/cdn-dp-link-training.c new file mode 100644 index 000..7efd070 --- /dev/null +++ b/drivers/gpu/drm/rockchip/cdn-dp-link-training.c @@ -0,0 +1,420 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd + * Author: Chris Zhong <z...@rock-chips.com> + */ + +#include +#include +#include +#include + +#include "cdn-dp-core.h" +#include "cdn-dp-reg.h" + +static void cdn_dp_set_signal_levels(struct cdn_dp_device *dp) +{ + struct cdn_dp_port *port = dp->port[dp-&g
[PATCH v3 4/4] drm/rockchip: support dp training outside dp firmware
DP firmware uses fixed phy config values to do training, but some boards need to adjust these values to fit for their unique hardware design. So if the phy is using custom config values, do software link training instead of relying on firmware, if software training fail, keep firmware training as a fallback if sw training fails. Signed-off-by: Chris Zhong <z...@rock-chips.com> Signed-off-by: Lin Huang <h...@rock-chips.com> --- Changes in v2: - update patch following Enric suggest Changes in v3: - use variable fw_training instead sw_training_success - base on DP SPCE, if training fail use lower link rate to retry training drivers/gpu/drm/rockchip/Makefile | 3 +- drivers/gpu/drm/rockchip/cdn-dp-core.c | 24 +- drivers/gpu/drm/rockchip/cdn-dp-core.h | 2 + drivers/gpu/drm/rockchip/cdn-dp-link-training.c | 416 drivers/gpu/drm/rockchip/cdn-dp-reg.c | 31 +- drivers/gpu/drm/rockchip/cdn-dp-reg.h | 38 ++- 6 files changed, 501 insertions(+), 13 deletions(-) create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-link-training.c diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile index a314e21..b932f62 100644 --- a/drivers/gpu/drm/rockchip/Makefile +++ b/drivers/gpu/drm/rockchip/Makefile @@ -9,7 +9,8 @@ rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o \ rockchipdrm-$(CONFIG_DRM_FBDEV_EMULATION) += rockchip_drm_fbdev.o rockchipdrm-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o -rockchipdrm-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o cdn-dp-reg.o +rockchipdrm-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o cdn-dp-reg.o \ + cdn-dp-link-training.o rockchipdrm-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o rockchipdrm-$(CONFIG_ROCKCHIP_DW_MIPI_DSI) += dw-mipi-dsi.o rockchipdrm-$(CONFIG_ROCKCHIP_INNO_HDMI) += inno_hdmi.o diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.c b/drivers/gpu/drm/rockchip/cdn-dp-core.c index cce64c1..d9d0d4d 100644 --- a/drivers/gpu/drm/rockchip/cdn-dp-core.c +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.c @@ -629,11 +629,13 @@ static void cdn_dp_encoder_enable(struct drm_encoder *encoder) goto out; } } - - ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_IDLE); - if (ret) { - DRM_DEV_ERROR(dp->dev, "Failed to idle video %d\n", ret); - goto out; + if (dp->use_fw_training == true) { + ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_IDLE); + if (ret) { + DRM_DEV_ERROR(dp->dev, + "Failed to idle video %d\n", ret); + goto out; + } } ret = cdn_dp_config_video(dp); @@ -642,11 +644,15 @@ static void cdn_dp_encoder_enable(struct drm_encoder *encoder) goto out; } - ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_VALID); - if (ret) { - DRM_DEV_ERROR(dp->dev, "Failed to valid video %d\n", ret); - goto out; + if (dp->use_fw_training == true) { + ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_VALID); + if (ret) { + DRM_DEV_ERROR(dp->dev, + "Failed to valid video %d\n", ret); + goto out; + } } + out: mutex_unlock(>lock); } diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.h b/drivers/gpu/drm/rockchip/cdn-dp-core.h index 46159b2..77a9793 100644 --- a/drivers/gpu/drm/rockchip/cdn-dp-core.h +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.h @@ -84,6 +84,7 @@ struct cdn_dp_device { bool connected; bool active; bool suspended; + bool use_fw_training; const struct firmware *fw; /* cdn dp firmware */ unsigned int fw_version;/* cdn fw version */ @@ -106,6 +107,7 @@ struct cdn_dp_device { u8 ports; u8 lanes; int active_port; + u8 train_set[4]; u8 dpcd[DP_RECEIVER_CAP_SIZE]; bool sink_has_audio; diff --git a/drivers/gpu/drm/rockchip/cdn-dp-link-training.c b/drivers/gpu/drm/rockchip/cdn-dp-link-training.c new file mode 100644 index 000..b8fd5bc --- /dev/null +++ b/drivers/gpu/drm/rockchip/cdn-dp-link-training.c @@ -0,0 +1,416 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd + * Author: Chris Zhong <z...@rock-chips.com> + */ + +#include +#include +#include +#include + +#include "cdn-dp-core.h" +#include "cdn-dp-reg.h" + +static void cdn_dp_set_signal_levels(struct cdn_dp_device *dp) +{ + struct cdn_dp_port *port = dp->port[dp->active_port]; + struct rockchip_typec_phy *tcphy = phy_get_drvdata(port->phy); + + int rate = dr
[PATCH v4 1/4] drm/rockchip: add transfer function for cdn-dp
From: Chris Zhong <z...@rock-chips.com> We may support training outside firmware, so we need support dpcd read/write to get the message or do some setting with display. Signed-off-by: Chris Zhong <z...@rock-chips.com> Signed-off-by: Lin Huang <h...@rock-chips.com> Reviewed-by: Sean Paul <seanp...@chromium.org> Reviewed-by: Enric Balletbo <enric.balle...@collabora.com> --- Changes in v2: - update patch following Enric suggest Changes in v3: - None Changes in v4: - None drivers/gpu/drm/rockchip/cdn-dp-core.c | 55 +++ drivers/gpu/drm/rockchip/cdn-dp-core.h | 1 + drivers/gpu/drm/rockchip/cdn-dp-reg.c | 69 ++ drivers/gpu/drm/rockchip/cdn-dp-reg.h | 14 ++- 4 files changed, 122 insertions(+), 17 deletions(-) diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.c b/drivers/gpu/drm/rockchip/cdn-dp-core.c index c6fbdcd..cce64c1 100644 --- a/drivers/gpu/drm/rockchip/cdn-dp-core.c +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.c @@ -176,8 +176,8 @@ static int cdn_dp_get_sink_count(struct cdn_dp_device *dp, u8 *sink_count) u8 value; *sink_count = 0; - ret = cdn_dp_dpcd_read(dp, DP_SINK_COUNT, , 1); - if (ret) + ret = drm_dp_dpcd_read(>aux, DP_SINK_COUNT, , 1); + if (ret < 0) return ret; *sink_count = DP_GET_SINK_COUNT(value); @@ -374,9 +374,9 @@ static int cdn_dp_get_sink_capability(struct cdn_dp_device *dp) if (!cdn_dp_check_sink_connection(dp)) return -ENODEV; - ret = cdn_dp_dpcd_read(dp, DP_DPCD_REV, dp->dpcd, - DP_RECEIVER_CAP_SIZE); - if (ret) { + ret = drm_dp_dpcd_read(>aux, DP_DPCD_REV, dp->dpcd, + sizeof(dp->dpcd)); + if (ret < 0) { DRM_DEV_ERROR(dp->dev, "Failed to get caps %d\n", ret); return ret; } @@ -582,8 +582,8 @@ static bool cdn_dp_check_link_status(struct cdn_dp_device *dp) if (!port || !dp->link.rate || !dp->link.num_lanes) return false; - if (cdn_dp_dpcd_read(dp, DP_LANE0_1_STATUS, link_status, -DP_LINK_STATUS_SIZE)) { + if (drm_dp_dpcd_read_link_status(>aux, link_status) != + DP_LINK_STATUS_SIZE) { DRM_ERROR("Failed to get link status\n"); return false; } @@ -1012,6 +1012,40 @@ static int cdn_dp_pd_event(struct notifier_block *nb, return NOTIFY_DONE; } +static ssize_t cdn_dp_aux_transfer(struct drm_dp_aux *aux, + struct drm_dp_aux_msg *msg) +{ + struct cdn_dp_device *dp = container_of(aux, struct cdn_dp_device, aux); + int ret; + u8 status; + + switch (msg->request & ~DP_AUX_I2C_MOT) { + case DP_AUX_NATIVE_WRITE: + case DP_AUX_I2C_WRITE: + case DP_AUX_I2C_WRITE_STATUS_UPDATE: + ret = cdn_dp_dpcd_write(dp, msg->address, msg->buffer, + msg->size); + break; + case DP_AUX_NATIVE_READ: + case DP_AUX_I2C_READ: + ret = cdn_dp_dpcd_read(dp, msg->address, msg->buffer, + msg->size); + break; + default: + return -EINVAL; + } + + status = cdn_dp_get_aux_status(dp); + if (status == AUX_STATUS_ACK) + msg->reply = DP_AUX_NATIVE_REPLY_ACK; + else if (status == AUX_STATUS_NACK) + msg->reply = DP_AUX_NATIVE_REPLY_NACK; + else if (status == AUX_STATUS_DEFER) + msg->reply = DP_AUX_NATIVE_REPLY_DEFER; + + return ret; +} + static int cdn_dp_bind(struct device *dev, struct device *master, void *data) { struct cdn_dp_device *dp = dev_get_drvdata(dev); @@ -1030,6 +1064,13 @@ static int cdn_dp_bind(struct device *dev, struct device *master, void *data) dp->active = false; dp->active_port = -1; dp->fw_loaded = false; + dp->aux.name = "DP-AUX"; + dp->aux.transfer = cdn_dp_aux_transfer; + dp->aux.dev = dev; + + ret = drm_dp_aux_register(>aux); + if (ret) + return ret; INIT_WORK(>event_work, cdn_dp_pd_event_work); diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.h b/drivers/gpu/drm/rockchip/cdn-dp-core.h index f57e296..46159b2 100644 --- a/drivers/gpu/drm/rockchip/cdn-dp-core.h +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.h @@ -78,6 +78,7 @@ struct cdn_dp_device { struct platform_device *audio_pdev; struct work_struct event_work; struct edid *edid; + struct drm_dp_aux aux; struct mutex lock; bool connected; diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.c b/drivers/gpu/drm/rockchip/cdn-dp-reg.c index eb3042c..979355d 100
[PATCH v4 2/4] Documentation: bindings: add phy_config for Rockchip USB Type-C PHY
If want to do training outside DP Firmware, need phy voltage swing and pre_emphasis value. Signed-off-by: Lin Huang <h...@rock-chips.com> --- Changes in v2: - None Changes in v3: - modify property description and add this property to Example Change in v4: - None .../devicetree/bindings/phy/phy-rockchip-typec.txt | 29 +- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/phy/phy-rockchip-typec.txt b/Documentation/devicetree/bindings/phy/phy-rockchip-typec.txt index 960da7f..af298f2 100644 --- a/Documentation/devicetree/bindings/phy/phy-rockchip-typec.txt +++ b/Documentation/devicetree/bindings/phy/phy-rockchip-typec.txt @@ -17,7 +17,8 @@ Required properties: Optional properties: - extcon : extcon specifier for the Power Delivery - + - rockchip,phy_config : A list of voltage swing(mv) and pre-emphasis + (dB) pairs. Required nodes : a sub-node is required for each port the phy provides. The sub-node name is used to identify dp or usb3 port, and shall be the following entries: @@ -50,6 +51,19 @@ Example: < SRST_P_UPHY0_TCPHY>; reset-names = "uphy", "uphy-pipe", "uphy-tcphy"; + rockchip,phy_config =<0x2a 0x00 + 0x1f 0x15 + 0x14 0x22 + 0x02 0x2b + 0x21 0x00 + 0x12 0x15 + 0x02 0x22 + 0 0 + 0x15 0x00 + 0x00 0x15 + 0 0 + 0 0>; + tcphy0_dp: dp-port { #phy-cells = <0>; }; @@ -74,6 +88,19 @@ Example: < SRST_P_UPHY1_TCPHY>; reset-names = "uphy", "uphy-pipe", "uphy-tcphy"; + rockchip,phy_config =<0x2a 0x00 + 0x1f 0x15 + 0x14 0x22 + 0x02 0x2b + 0x21 0x00 + 0x12 0x15 + 0x02 0x22 + 0 0 + 0x15 0x00 + 0x00 0x15 + 0 0 + 0 0>; + tcphy1_dp: dp-port { #phy-cells = <0>; }; -- 2.7.4 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH v3 3/4] phy: rockchip-typec: support variable phy config value
the phy config values used to fix in dp firmware, but some boards need change these values to do training and get the better eye diagram result. So support that in phy driver. Signed-off-by: Chris Zhong <z...@rock-chips.com> Signed-off-by: Lin Huang <h...@rock-chips.com> --- Changes in v2: - update patch following Enric suggest Changes in v3: - delete need_software_training variable - add default phy config value, if dts do not define phy config value, use these value drivers/phy/rockchip/phy-rockchip-typec.c | 305 -- include/soc/rockchip/rockchip_phy_typec.h | 63 ++ 2 files changed, 270 insertions(+), 98 deletions(-) create mode 100644 include/soc/rockchip/rockchip_phy_typec.h diff --git a/drivers/phy/rockchip/phy-rockchip-typec.c b/drivers/phy/rockchip/phy-rockchip-typec.c index 76a4b58..10253ad 100644 --- a/drivers/phy/rockchip/phy-rockchip-typec.c +++ b/drivers/phy/rockchip/phy-rockchip-typec.c @@ -63,6 +63,7 @@ #include #include +#include #define CMN_SSM_BANDGAP(0x21 << 2) #define CMN_SSM_BIAS (0x22 << 2) @@ -323,21 +324,29 @@ * clock 0: PLL 0 div 1 * clock 1: PLL 1 div 2 */ -#define CLK_PLL_CONFIG 0X30 +#define CLK_PLL1_DIV1 0x20 +#define CLK_PLL1_DIV2 0x30 #define CLK_PLL_MASK 0x33 #define CMN_READY BIT(0) +#define DP_PLL_CLOCK_ENABLE_ACKBIT(3) #define DP_PLL_CLOCK_ENABLEBIT(2) +#define DP_PLL_ENABLE_ACK BIT(1) #define DP_PLL_ENABLE BIT(0) #define DP_PLL_DATA_RATE_RBR ((2 << 12) | (4 << 8)) #define DP_PLL_DATA_RATE_HBR ((2 << 12) | (4 << 8)) #define DP_PLL_DATA_RATE_HBR2 ((1 << 12) | (2 << 8)) +#define DP_PLL_DATA_RATE_MASK 0xff00 -#define DP_MODE_A0 BIT(4) -#define DP_MODE_A2 BIT(6) -#define DP_MODE_ENTER_A0 0xc101 -#define DP_MODE_ENTER_A2 0xc104 +#define DP_MODE_MASK 0xf +#define DP_MODE_ENTER_A0 BIT(0) +#define DP_MODE_ENTER_A2 BIT(2) +#define DP_MODE_ENTER_A3 BIT(3) +#define DP_MODE_A0_ACK BIT(4) +#define DP_MODE_A2_ACK BIT(6) +#define DP_MODE_A3_ACK BIT(7) +#define DP_LINK_RESET_DEASSERTED BIT(8) #define PHY_MODE_SET_TIMEOUT 10 @@ -349,51 +358,7 @@ #define MODE_DFP_USB BIT(1) #define MODE_DFP_DPBIT(2) -struct usb3phy_reg { - u32 offset; - u32 enable_bit; - u32 write_enable; -}; - -/** - * struct rockchip_usb3phy_port_cfg: usb3-phy port configuration. - * @reg: the base address for usb3-phy config. - * @typec_conn_dir: the register of type-c connector direction. - * @usb3tousb2_en: the register of type-c force usb2 to usb2 enable. - * @external_psm: the register of type-c phy external psm clock. - * @pipe_status: the register of type-c phy pipe status. - * @usb3_host_disable: the register of type-c usb3 host disable. - * @usb3_host_port: the register of type-c usb3 host port. - * @uphy_dp_sel: the register of type-c phy DP select control. - */ -struct rockchip_usb3phy_port_cfg { - unsigned int reg; - struct usb3phy_reg typec_conn_dir; - struct usb3phy_reg usb3tousb2_en; - struct usb3phy_reg external_psm; - struct usb3phy_reg pipe_status; - struct usb3phy_reg usb3_host_disable; - struct usb3phy_reg usb3_host_port; - struct usb3phy_reg uphy_dp_sel; -}; - -struct rockchip_typec_phy { - struct device *dev; - void __iomem *base; - struct extcon_dev *extcon; - struct regmap *grf_regs; - struct clk *clk_core; - struct clk *clk_ref; - struct reset_control *uphy_rst; - struct reset_control *pipe_rst; - struct reset_control *tcphy_rst; - const struct rockchip_usb3phy_port_cfg *port_cfgs; - /* mutex to protect access to individual PHYs */ - struct mutex lock; - - bool flip; - u8 mode; -}; +#define DP_DEFAULT_RATE162000 struct phy_reg { u16 value; @@ -417,15 +382,15 @@ struct phy_reg usb3_pll_cfg[] = { { 0x8, CMN_DIAG_PLL0_LF_PROG }, }; -struct phy_reg dp_pll_cfg[] = { +struct phy_reg dp_pll_rbr_cfg[] = { { 0xf0, CMN_PLL1_VCOCAL_INIT }, { 0x18, CMN_PLL1_VCOCAL_ITER }, { 0x30b9, CMN_PLL1_VCOCAL_START }, - { 0x21c,CMN_PLL1_INTDIV }, + { 0x87, CMN_PLL1_INTDIV }, { 0,CMN_PLL1_FRACDIV }, - { 0x5, CMN_PLL1_HIGH_THR }, - { 0x35, CMN_PLL1_SS_CTRL1 }, - { 0x7f1e, CMN_PLL1_SS_CTRL2 }, + { 0x22, CMN_PLL1_HIGH_THR }, + { 0x8000, CMN_PLL1_SS_CTRL1 }, + { 0,CM
[PATCH v2 4/4] drm/rockchip: support dp training outside dp firmware
DP firmware uses fixed phy config values to do training, but some boards need to adjust these values to fit for their unique hardware design. So if the phy is using custom config values, do software link training instead of relying on firmware, if software training fail, keep firmware training as a fallback if sw training fails. Signed-off-by: Chris Zhong <z...@rock-chips.com> Signed-off-by: Lin Huang <h...@rock-chips.com> --- Changes in v2: - update patch following Enric suggest drivers/gpu/drm/rockchip/Makefile | 3 +- drivers/gpu/drm/rockchip/cdn-dp-core.c | 24 +- drivers/gpu/drm/rockchip/cdn-dp-core.h | 2 + drivers/gpu/drm/rockchip/cdn-dp-link-training.c | 391 drivers/gpu/drm/rockchip/cdn-dp-reg.c | 34 ++- drivers/gpu/drm/rockchip/cdn-dp-reg.h | 38 ++- 6 files changed, 479 insertions(+), 13 deletions(-) create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-link-training.c diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile index a314e21..b932f62 100644 --- a/drivers/gpu/drm/rockchip/Makefile +++ b/drivers/gpu/drm/rockchip/Makefile @@ -9,7 +9,8 @@ rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o \ rockchipdrm-$(CONFIG_DRM_FBDEV_EMULATION) += rockchip_drm_fbdev.o rockchipdrm-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o -rockchipdrm-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o cdn-dp-reg.o +rockchipdrm-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o cdn-dp-reg.o \ + cdn-dp-link-training.o rockchipdrm-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o rockchipdrm-$(CONFIG_ROCKCHIP_DW_MIPI_DSI) += dw-mipi-dsi.o rockchipdrm-$(CONFIG_ROCKCHIP_INNO_HDMI) += inno_hdmi.o diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.c b/drivers/gpu/drm/rockchip/cdn-dp-core.c index cce64c1..2359d82 100644 --- a/drivers/gpu/drm/rockchip/cdn-dp-core.c +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.c @@ -629,11 +629,13 @@ static void cdn_dp_encoder_enable(struct drm_encoder *encoder) goto out; } } - - ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_IDLE); - if (ret) { - DRM_DEV_ERROR(dp->dev, "Failed to idle video %d\n", ret); - goto out; + if (dp->sw_training_success == false) { + ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_IDLE); + if (ret) { + DRM_DEV_ERROR(dp->dev, + "Failed to idle video %d\n", ret); + goto out; + } } ret = cdn_dp_config_video(dp); @@ -642,11 +644,15 @@ static void cdn_dp_encoder_enable(struct drm_encoder *encoder) goto out; } - ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_VALID); - if (ret) { - DRM_DEV_ERROR(dp->dev, "Failed to valid video %d\n", ret); - goto out; + if (dp->sw_training_success == false) { + ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_VALID); + if (ret) { + DRM_DEV_ERROR(dp->dev, + "Failed to valid video %d\n", ret); + goto out; + } } + out: mutex_unlock(>lock); } diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.h b/drivers/gpu/drm/rockchip/cdn-dp-core.h index 46159b2..c6050ab 100644 --- a/drivers/gpu/drm/rockchip/cdn-dp-core.h +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.h @@ -84,6 +84,7 @@ struct cdn_dp_device { bool connected; bool active; bool suspended; + bool sw_training_success; const struct firmware *fw; /* cdn dp firmware */ unsigned int fw_version;/* cdn fw version */ @@ -106,6 +107,7 @@ struct cdn_dp_device { u8 ports; u8 lanes; int active_port; + u8 train_set[4]; u8 dpcd[DP_RECEIVER_CAP_SIZE]; bool sink_has_audio; diff --git a/drivers/gpu/drm/rockchip/cdn-dp-link-training.c b/drivers/gpu/drm/rockchip/cdn-dp-link-training.c new file mode 100644 index 000..25282de --- /dev/null +++ b/drivers/gpu/drm/rockchip/cdn-dp-link-training.c @@ -0,0 +1,391 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd + * Author: Chris Zhong <z...@rock-chips.com> + */ + +#include +#include +#include +#include + +#include "cdn-dp-core.h" +#include "cdn-dp-reg.h" + +static void cdn_dp_set_signal_levels(struct cdn_dp_device *dp) +{ + struct cdn_dp_port *port = dp->port[dp->active_port]; + struct rockchip_typec_phy *tcphy = phy_get_drvdata(port->phy); + + int rate = drm_dp_bw_code_to_link_rate(dp->link.rate); + u8 swing = (dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK) >&g
[PATCH v2 1/4] drm/rockchip: add transfer function for cdn-dp
From: Chris Zhong <z...@rock-chips.com> We may support training outside firmware, so we need support dpcd read/write to get the message or do some setting with display. Signed-off-by: Chris Zhong <z...@rock-chips.com> Signed-off-by: Lin Huang <h...@rock-chips.com> --- Changes in v2: - update patch following Enric suggest drivers/gpu/drm/rockchip/cdn-dp-core.c | 55 drivers/gpu/drm/rockchip/cdn-dp-core.h | 1 + drivers/gpu/drm/rockchip/cdn-dp-reg.c | 67 ++ drivers/gpu/drm/rockchip/cdn-dp-reg.h | 14 ++- 4 files changed, 120 insertions(+), 17 deletions(-) diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.c b/drivers/gpu/drm/rockchip/cdn-dp-core.c index c6fbdcd..cce64c1 100644 --- a/drivers/gpu/drm/rockchip/cdn-dp-core.c +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.c @@ -176,8 +176,8 @@ static int cdn_dp_get_sink_count(struct cdn_dp_device *dp, u8 *sink_count) u8 value; *sink_count = 0; - ret = cdn_dp_dpcd_read(dp, DP_SINK_COUNT, , 1); - if (ret) + ret = drm_dp_dpcd_read(>aux, DP_SINK_COUNT, , 1); + if (ret < 0) return ret; *sink_count = DP_GET_SINK_COUNT(value); @@ -374,9 +374,9 @@ static int cdn_dp_get_sink_capability(struct cdn_dp_device *dp) if (!cdn_dp_check_sink_connection(dp)) return -ENODEV; - ret = cdn_dp_dpcd_read(dp, DP_DPCD_REV, dp->dpcd, - DP_RECEIVER_CAP_SIZE); - if (ret) { + ret = drm_dp_dpcd_read(>aux, DP_DPCD_REV, dp->dpcd, + sizeof(dp->dpcd)); + if (ret < 0) { DRM_DEV_ERROR(dp->dev, "Failed to get caps %d\n", ret); return ret; } @@ -582,8 +582,8 @@ static bool cdn_dp_check_link_status(struct cdn_dp_device *dp) if (!port || !dp->link.rate || !dp->link.num_lanes) return false; - if (cdn_dp_dpcd_read(dp, DP_LANE0_1_STATUS, link_status, -DP_LINK_STATUS_SIZE)) { + if (drm_dp_dpcd_read_link_status(>aux, link_status) != + DP_LINK_STATUS_SIZE) { DRM_ERROR("Failed to get link status\n"); return false; } @@ -1012,6 +1012,40 @@ static int cdn_dp_pd_event(struct notifier_block *nb, return NOTIFY_DONE; } +static ssize_t cdn_dp_aux_transfer(struct drm_dp_aux *aux, + struct drm_dp_aux_msg *msg) +{ + struct cdn_dp_device *dp = container_of(aux, struct cdn_dp_device, aux); + int ret; + u8 status; + + switch (msg->request & ~DP_AUX_I2C_MOT) { + case DP_AUX_NATIVE_WRITE: + case DP_AUX_I2C_WRITE: + case DP_AUX_I2C_WRITE_STATUS_UPDATE: + ret = cdn_dp_dpcd_write(dp, msg->address, msg->buffer, + msg->size); + break; + case DP_AUX_NATIVE_READ: + case DP_AUX_I2C_READ: + ret = cdn_dp_dpcd_read(dp, msg->address, msg->buffer, + msg->size); + break; + default: + return -EINVAL; + } + + status = cdn_dp_get_aux_status(dp); + if (status == AUX_STATUS_ACK) + msg->reply = DP_AUX_NATIVE_REPLY_ACK; + else if (status == AUX_STATUS_NACK) + msg->reply = DP_AUX_NATIVE_REPLY_NACK; + else if (status == AUX_STATUS_DEFER) + msg->reply = DP_AUX_NATIVE_REPLY_DEFER; + + return ret; +} + static int cdn_dp_bind(struct device *dev, struct device *master, void *data) { struct cdn_dp_device *dp = dev_get_drvdata(dev); @@ -1030,6 +1064,13 @@ static int cdn_dp_bind(struct device *dev, struct device *master, void *data) dp->active = false; dp->active_port = -1; dp->fw_loaded = false; + dp->aux.name = "DP-AUX"; + dp->aux.transfer = cdn_dp_aux_transfer; + dp->aux.dev = dev; + + ret = drm_dp_aux_register(>aux); + if (ret) + return ret; INIT_WORK(>event_work, cdn_dp_pd_event_work); diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.h b/drivers/gpu/drm/rockchip/cdn-dp-core.h index f57e296..46159b2 100644 --- a/drivers/gpu/drm/rockchip/cdn-dp-core.h +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.h @@ -78,6 +78,7 @@ struct cdn_dp_device { struct platform_device *audio_pdev; struct work_struct event_work; struct edid *edid; + struct drm_dp_aux aux; struct mutex lock; bool connected; diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.c b/drivers/gpu/drm/rockchip/cdn-dp-reg.c index eb3042c..afdfda0 100644 --- a/drivers/gpu/drm/rockchip/cdn-dp-reg.c +++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.c @@ -221,7 +221,11 @@ static int cdn_dp_reg_write_bit(struct cdn_dp_dev
[PATCH v2 2/4] phy: rockchip-typec: support variable phy config value
the phy config values used to fix in dp firmware, but some boards need change these values to do training and get the better eye diagram result. So support that in phy driver. Signed-off-by: Chris Zhong <z...@rock-chips.com> Signed-off-by: Lin Huang <h...@rock-chips.com> --- Changes in v2: - update patch following Enric suggest drivers/phy/rockchip/phy-rockchip-typec.c | 284 +++--- include/soc/rockchip/rockchip_phy_typec.h | 64 +++ 2 files changed, 250 insertions(+), 98 deletions(-) create mode 100644 include/soc/rockchip/rockchip_phy_typec.h diff --git a/drivers/phy/rockchip/phy-rockchip-typec.c b/drivers/phy/rockchip/phy-rockchip-typec.c index 76a4b58..86cbd6c 100644 --- a/drivers/phy/rockchip/phy-rockchip-typec.c +++ b/drivers/phy/rockchip/phy-rockchip-typec.c @@ -63,6 +63,7 @@ #include #include +#include #define CMN_SSM_BANDGAP(0x21 << 2) #define CMN_SSM_BIAS (0x22 << 2) @@ -323,21 +324,29 @@ * clock 0: PLL 0 div 1 * clock 1: PLL 1 div 2 */ -#define CLK_PLL_CONFIG 0X30 +#define CLK_PLL1_DIV1 0x20 +#define CLK_PLL1_DIV2 0x30 #define CLK_PLL_MASK 0x33 #define CMN_READY BIT(0) +#define DP_PLL_CLOCK_ENABLE_ACKBIT(3) #define DP_PLL_CLOCK_ENABLEBIT(2) +#define DP_PLL_ENABLE_ACK BIT(1) #define DP_PLL_ENABLE BIT(0) #define DP_PLL_DATA_RATE_RBR ((2 << 12) | (4 << 8)) #define DP_PLL_DATA_RATE_HBR ((2 << 12) | (4 << 8)) #define DP_PLL_DATA_RATE_HBR2 ((1 << 12) | (2 << 8)) +#define DP_PLL_DATA_RATE_MASK 0xff00 -#define DP_MODE_A0 BIT(4) -#define DP_MODE_A2 BIT(6) -#define DP_MODE_ENTER_A0 0xc101 -#define DP_MODE_ENTER_A2 0xc104 +#define DP_MODE_MASK 0xf +#define DP_MODE_ENTER_A0 BIT(0) +#define DP_MODE_ENTER_A2 BIT(2) +#define DP_MODE_ENTER_A3 BIT(3) +#define DP_MODE_A0_ACK BIT(4) +#define DP_MODE_A2_ACK BIT(6) +#define DP_MODE_A3_ACK BIT(7) +#define DP_LINK_RESET_DEASSERTED BIT(8) #define PHY_MODE_SET_TIMEOUT 10 @@ -349,51 +358,7 @@ #define MODE_DFP_USB BIT(1) #define MODE_DFP_DPBIT(2) -struct usb3phy_reg { - u32 offset; - u32 enable_bit; - u32 write_enable; -}; - -/** - * struct rockchip_usb3phy_port_cfg: usb3-phy port configuration. - * @reg: the base address for usb3-phy config. - * @typec_conn_dir: the register of type-c connector direction. - * @usb3tousb2_en: the register of type-c force usb2 to usb2 enable. - * @external_psm: the register of type-c phy external psm clock. - * @pipe_status: the register of type-c phy pipe status. - * @usb3_host_disable: the register of type-c usb3 host disable. - * @usb3_host_port: the register of type-c usb3 host port. - * @uphy_dp_sel: the register of type-c phy DP select control. - */ -struct rockchip_usb3phy_port_cfg { - unsigned int reg; - struct usb3phy_reg typec_conn_dir; - struct usb3phy_reg usb3tousb2_en; - struct usb3phy_reg external_psm; - struct usb3phy_reg pipe_status; - struct usb3phy_reg usb3_host_disable; - struct usb3phy_reg usb3_host_port; - struct usb3phy_reg uphy_dp_sel; -}; - -struct rockchip_typec_phy { - struct device *dev; - void __iomem *base; - struct extcon_dev *extcon; - struct regmap *grf_regs; - struct clk *clk_core; - struct clk *clk_ref; - struct reset_control *uphy_rst; - struct reset_control *pipe_rst; - struct reset_control *tcphy_rst; - const struct rockchip_usb3phy_port_cfg *port_cfgs; - /* mutex to protect access to individual PHYs */ - struct mutex lock; - - bool flip; - u8 mode; -}; +#define DP_DEFAULT_RATE162000 struct phy_reg { u16 value; @@ -417,15 +382,15 @@ struct phy_reg usb3_pll_cfg[] = { { 0x8, CMN_DIAG_PLL0_LF_PROG }, }; -struct phy_reg dp_pll_cfg[] = { +struct phy_reg dp_pll_rbr_cfg[] = { { 0xf0, CMN_PLL1_VCOCAL_INIT }, { 0x18, CMN_PLL1_VCOCAL_ITER }, { 0x30b9, CMN_PLL1_VCOCAL_START }, - { 0x21c,CMN_PLL1_INTDIV }, + { 0x87, CMN_PLL1_INTDIV }, { 0,CMN_PLL1_FRACDIV }, - { 0x5, CMN_PLL1_HIGH_THR }, - { 0x35, CMN_PLL1_SS_CTRL1 }, - { 0x7f1e, CMN_PLL1_SS_CTRL2 }, + { 0x22, CMN_PLL1_HIGH_THR }, + { 0x8000, CMN_PLL1_SS_CTRL1 }, + { 0,CMN_PLL1_SS_CTRL2 }, { 0x20, CMN_PLL1_DSM_DIAG }, { 0,CMN_PLLSM1_USER_DEF_CTRL }, { 0,
[PATCH v2 3/4] Documentation: bindings: add phy_config for Rockchip USB Type-C PHY
If want to do training outside DP Firmware, need phy voltage swing and pre_emphasis value. Signed-off-by: Lin Huang <h...@rock-chips.com> --- Changes in v2: - rebase Documentation/devicetree/bindings/phy/phy-rockchip-typec.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/phy/phy-rockchip-typec.txt b/Documentation/devicetree/bindings/phy/phy-rockchip-typec.txt index 960da7f..eda26dd 100644 --- a/Documentation/devicetree/bindings/phy/phy-rockchip-typec.txt +++ b/Documentation/devicetree/bindings/phy/phy-rockchip-typec.txt @@ -17,7 +17,9 @@ Required properties: Optional properties: - extcon : extcon specifier for the Power Delivery - + - rockchip,phy_config : That's phy voltage swing and pre_emphasis +setting, if want to do dp training outside +dp firmware, need to add these value. Required nodes : a sub-node is required for each port the phy provides. The sub-node name is used to identify dp or usb3 port, and shall be the following entries: -- 2.7.4 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH 1/4] drm/rockchip: add transfer function for cdn-dp
From: Chris Zhong <z...@rock-chips.com> We may support training outside firmware, so we need support dpcd read/write to get the message or do some setting with display. Signed-off-by: Chris Zhong <z...@rock-chips.com> Signed-off-by: Lin Huang <h...@rock-chips.com> --- drivers/gpu/drm/rockchip/cdn-dp-core.c | 55 drivers/gpu/drm/rockchip/cdn-dp-core.h | 1 + drivers/gpu/drm/rockchip/cdn-dp-reg.c | 66 +- drivers/gpu/drm/rockchip/cdn-dp-reg.h | 14 ++-- 4 files changed, 119 insertions(+), 17 deletions(-) diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.c b/drivers/gpu/drm/rockchip/cdn-dp-core.c index c6fbdcd..268c190 100644 --- a/drivers/gpu/drm/rockchip/cdn-dp-core.c +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.c @@ -176,8 +176,8 @@ static int cdn_dp_get_sink_count(struct cdn_dp_device *dp, u8 *sink_count) u8 value; *sink_count = 0; - ret = cdn_dp_dpcd_read(dp, DP_SINK_COUNT, , 1); - if (ret) + ret = drm_dp_dpcd_read(>aux, DP_SINK_COUNT, , 1); + if (ret < 0) return ret; *sink_count = DP_GET_SINK_COUNT(value); @@ -374,9 +374,9 @@ static int cdn_dp_get_sink_capability(struct cdn_dp_device *dp) if (!cdn_dp_check_sink_connection(dp)) return -ENODEV; - ret = cdn_dp_dpcd_read(dp, DP_DPCD_REV, dp->dpcd, - DP_RECEIVER_CAP_SIZE); - if (ret) { + ret = drm_dp_dpcd_read(>aux, DP_DPCD_REV, dp->dpcd, + sizeof(dp->dpcd)); + if (ret < 0) { DRM_DEV_ERROR(dp->dev, "Failed to get caps %d\n", ret); return ret; } @@ -582,8 +582,8 @@ static bool cdn_dp_check_link_status(struct cdn_dp_device *dp) if (!port || !dp->link.rate || !dp->link.num_lanes) return false; - if (cdn_dp_dpcd_read(dp, DP_LANE0_1_STATUS, link_status, -DP_LINK_STATUS_SIZE)) { + if (drm_dp_dpcd_read_link_status(>aux, link_status) != + DP_LINK_STATUS_SIZE) { DRM_ERROR("Failed to get link status\n"); return false; } @@ -1012,6 +1012,40 @@ static int cdn_dp_pd_event(struct notifier_block *nb, return NOTIFY_DONE; } +static ssize_t cdn_dp_aux_transfer(struct drm_dp_aux *aux, + struct drm_dp_aux_msg *msg) +{ + struct cdn_dp_device *dp = container_of(aux, struct cdn_dp_device, aux); + int ret; + u8 status; + + switch (msg->request & ~DP_AUX_I2C_MOT) { + case DP_AUX_NATIVE_WRITE: + case DP_AUX_I2C_WRITE: + case DP_AUX_I2C_WRITE_STATUS_UPDATE: + ret = cdn_dp_dpcd_write(dp, msg->address, msg->buffer, + msg->size); + break; + case DP_AUX_NATIVE_READ: + case DP_AUX_I2C_READ: + ret = cdn_dp_dpcd_read(dp, msg->address, msg->buffer, + msg->size); + break; + default: + return -EINVAL; + } + + status = cdn_dp_get_aux_status(dp); + if (status == AUX_STAUS_ACK) + msg->reply = DP_AUX_NATIVE_REPLY_ACK; + else if (status == AUX_STAUS_NACK) + msg->reply = DP_AUX_NATIVE_REPLY_NACK; + else if (status == AUX_STAUS_DEFER) + msg->reply = DP_AUX_NATIVE_REPLY_DEFER; + + return ret; +} + static int cdn_dp_bind(struct device *dev, struct device *master, void *data) { struct cdn_dp_device *dp = dev_get_drvdata(dev); @@ -1030,6 +1064,13 @@ static int cdn_dp_bind(struct device *dev, struct device *master, void *data) dp->active = false; dp->active_port = -1; dp->fw_loaded = false; + dp->aux.name = "DP-AUX"; + dp->aux.transfer = cdn_dp_aux_transfer; + dp->aux.dev = dev; + + ret = drm_dp_aux_register(>aux); + if (ret) + return ret; INIT_WORK(>event_work, cdn_dp_pd_event_work); diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.h b/drivers/gpu/drm/rockchip/cdn-dp-core.h index f57e296..46159b2 100644 --- a/drivers/gpu/drm/rockchip/cdn-dp-core.h +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.h @@ -78,6 +78,7 @@ struct cdn_dp_device { struct platform_device *audio_pdev; struct work_struct event_work; struct edid *edid; + struct drm_dp_aux aux; struct mutex lock; bool connected; diff --git a/drivers/gpu/drm/rockchip/cdn-dp-reg.c b/drivers/gpu/drm/rockchip/cdn-dp-reg.c index eb3042c..b2f532a 100644 --- a/drivers/gpu/drm/rockchip/cdn-dp-reg.c +++ b/drivers/gpu/drm/rockchip/cdn-dp-reg.c @@ -221,7 +221,11 @@ static int cdn_dp_reg_write_bit(struct cdn_dp_device *dp, u16 addr, s
[PATCH 4/4] drm/rockchip: support dp training outside dp firmware
DP firware use fix phy config value to do training, but some board need to adjust these value to fit for their hardware design, so we use new phy config to do training outside firmware to meet this situation, if there have new phy config pass from dts, it will use training outside firmware. Signed-off-by: Chris Zhong <z...@rock-chips.com> Signed-off-by: Lin Huang <h...@rock-chips.com> --- drivers/gpu/drm/rockchip/Makefile | 3 +- drivers/gpu/drm/rockchip/cdn-dp-core.c | 23 +- drivers/gpu/drm/rockchip/cdn-dp-core.h | 2 + drivers/gpu/drm/rockchip/cdn-dp-link-training.c | 398 drivers/gpu/drm/rockchip/cdn-dp-reg.c | 33 +- drivers/gpu/drm/rockchip/cdn-dp-reg.h | 38 ++- 6 files changed, 480 insertions(+), 17 deletions(-) create mode 100644 drivers/gpu/drm/rockchip/cdn-dp-link-training.c diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile index a314e21..b932f62 100644 --- a/drivers/gpu/drm/rockchip/Makefile +++ b/drivers/gpu/drm/rockchip/Makefile @@ -9,7 +9,8 @@ rockchipdrm-y := rockchip_drm_drv.o rockchip_drm_fb.o \ rockchipdrm-$(CONFIG_DRM_FBDEV_EMULATION) += rockchip_drm_fbdev.o rockchipdrm-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o -rockchipdrm-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o cdn-dp-reg.o +rockchipdrm-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o cdn-dp-reg.o \ + cdn-dp-link-training.o rockchipdrm-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o rockchipdrm-$(CONFIG_ROCKCHIP_DW_MIPI_DSI) += dw-mipi-dsi.o rockchipdrm-$(CONFIG_ROCKCHIP_INNO_HDMI) += inno_hdmi.o diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.c b/drivers/gpu/drm/rockchip/cdn-dp-core.c index 268c190..a2a4208 100644 --- a/drivers/gpu/drm/rockchip/cdn-dp-core.c +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.c @@ -629,11 +629,13 @@ static void cdn_dp_encoder_enable(struct drm_encoder *encoder) goto out; } } - - ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_IDLE); - if (ret) { - DRM_DEV_ERROR(dp->dev, "Failed to idle video %d\n", ret); - goto out; + if (dp->sw_training_success == false) { + ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_IDLE); + if (ret) { + DRM_DEV_ERROR(dp->dev, + "Failed to idle video %d\n", ret); + goto out; + } } ret = cdn_dp_config_video(dp); @@ -642,11 +644,14 @@ static void cdn_dp_encoder_enable(struct drm_encoder *encoder) goto out; } - ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_VALID); - if (ret) { - DRM_DEV_ERROR(dp->dev, "Failed to valid video %d\n", ret); - goto out; + if (dp->sw_training_success == false) { + ret = cdn_dp_set_video_status(dp, CONTROL_VIDEO_VALID); + if (ret) { + DRM_DEV_ERROR(dp->dev, "Failed to valid video %d\n", ret); + goto out; + } } + out: mutex_unlock(>lock); } diff --git a/drivers/gpu/drm/rockchip/cdn-dp-core.h b/drivers/gpu/drm/rockchip/cdn-dp-core.h index 46159b2..c6050ab 100644 --- a/drivers/gpu/drm/rockchip/cdn-dp-core.h +++ b/drivers/gpu/drm/rockchip/cdn-dp-core.h @@ -84,6 +84,7 @@ struct cdn_dp_device { bool connected; bool active; bool suspended; + bool sw_training_success; const struct firmware *fw; /* cdn dp firmware */ unsigned int fw_version;/* cdn fw version */ @@ -106,6 +107,7 @@ struct cdn_dp_device { u8 ports; u8 lanes; int active_port; + u8 train_set[4]; u8 dpcd[DP_RECEIVER_CAP_SIZE]; bool sink_has_audio; diff --git a/drivers/gpu/drm/rockchip/cdn-dp-link-training.c b/drivers/gpu/drm/rockchip/cdn-dp-link-training.c new file mode 100644 index 000..558c945 --- /dev/null +++ b/drivers/gpu/drm/rockchip/cdn-dp-link-training.c @@ -0,0 +1,398 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd + * Author: Chris Zhong <z...@rock-chips.com> + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cdn-dp-core.h" +#include "cdn-dp-reg.h" + +static void cdn_dp_set_signal_levels(struct cdn_dp_device *dp) +{ + struct cdn_dp_port *port = dp->port[dp->active_port]; + struct rockchip_typec_phy *tcphy = phy_get_drvdata(port->phy); + + int rate = drm_dp_bw_code_to_link_rate(dp->link.rate); + u8 swing = (dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK) >> + DP_TRAIN_VOLTAGE_SWING_SHIFT; + u8 pre_emphasis
[PATCH 2/4] phy: rockchip-typec: support variable phy config value
the phy config values used to fix in dp firmware, but some boards need change these values to do training and get the better eye diagram result. So support that in phy driver. Signed-off-by: Chris Zhong <z...@rock-chips.com> Signed-off-by: Lin Huang <h...@rock-chips.com> --- drivers/phy/rockchip/phy-rockchip-typec.c | 286 +++--- include/soc/rockchip/rockchip_phy_typec.h | 72 2 files changed, 259 insertions(+), 99 deletions(-) create mode 100644 include/soc/rockchip/rockchip_phy_typec.h diff --git a/drivers/phy/rockchip/phy-rockchip-typec.c b/drivers/phy/rockchip/phy-rockchip-typec.c index 76a4b58..831a93b 100644 --- a/drivers/phy/rockchip/phy-rockchip-typec.c +++ b/drivers/phy/rockchip/phy-rockchip-typec.c @@ -63,6 +63,7 @@ #include #include +#include #define CMN_SSM_BANDGAP(0x21 << 2) #define CMN_SSM_BIAS (0x22 << 2) @@ -323,23 +324,31 @@ * clock 0: PLL 0 div 1 * clock 1: PLL 1 div 2 */ -#define CLK_PLL_CONFIG 0X30 +#define CLK_PLL1_DIV1 0x20 +#define CLK_PLL1_DIV2 0x30 #define CLK_PLL_MASK 0x33 #define CMN_READY BIT(0) +#define DP_PLL_CLOCK_ENABLE_ACKBIT(3) #define DP_PLL_CLOCK_ENABLEBIT(2) +#define DP_PLL_ENABLE_ACK BIT(1) #define DP_PLL_ENABLE BIT(0) #define DP_PLL_DATA_RATE_RBR ((2 << 12) | (4 << 8)) #define DP_PLL_DATA_RATE_HBR ((2 << 12) | (4 << 8)) #define DP_PLL_DATA_RATE_HBR2 ((1 << 12) | (2 << 8)) +#define DP_PLL_DATA_RATE_MASK 0xff00 -#define DP_MODE_A0 BIT(4) -#define DP_MODE_A2 BIT(6) -#define DP_MODE_ENTER_A0 0xc101 -#define DP_MODE_ENTER_A2 0xc104 +#define DP_MODE_MASK 0xf +#define DP_MODE_ENTER_A0 BIT(0) +#define DP_MODE_ENTER_A2 BIT(2) +#define DP_MODE_ENTER_A3 BIT(3) +#define DP_MODE_A0_ACK BIT(4) +#define DP_MODE_A2_ACK BIT(6) +#define DP_MODE_A3_ACK BIT(7) +#define DP_LINK_RESET_DEASSERTED BIT(8) -#define PHY_MODE_SET_TIMEOUT 10 +#define PHY_MODE_SET_TIMEOUT 100 #define PIN_ASSIGN_C_E 0x51d9 #define PIN_ASSIGN_D_F 0x5100 @@ -349,51 +358,7 @@ #define MODE_DFP_USB BIT(1) #define MODE_DFP_DPBIT(2) -struct usb3phy_reg { - u32 offset; - u32 enable_bit; - u32 write_enable; -}; - -/** - * struct rockchip_usb3phy_port_cfg: usb3-phy port configuration. - * @reg: the base address for usb3-phy config. - * @typec_conn_dir: the register of type-c connector direction. - * @usb3tousb2_en: the register of type-c force usb2 to usb2 enable. - * @external_psm: the register of type-c phy external psm clock. - * @pipe_status: the register of type-c phy pipe status. - * @usb3_host_disable: the register of type-c usb3 host disable. - * @usb3_host_port: the register of type-c usb3 host port. - * @uphy_dp_sel: the register of type-c phy DP select control. - */ -struct rockchip_usb3phy_port_cfg { - unsigned int reg; - struct usb3phy_reg typec_conn_dir; - struct usb3phy_reg usb3tousb2_en; - struct usb3phy_reg external_psm; - struct usb3phy_reg pipe_status; - struct usb3phy_reg usb3_host_disable; - struct usb3phy_reg usb3_host_port; - struct usb3phy_reg uphy_dp_sel; -}; - -struct rockchip_typec_phy { - struct device *dev; - void __iomem *base; - struct extcon_dev *extcon; - struct regmap *grf_regs; - struct clk *clk_core; - struct clk *clk_ref; - struct reset_control *uphy_rst; - struct reset_control *pipe_rst; - struct reset_control *tcphy_rst; - const struct rockchip_usb3phy_port_cfg *port_cfgs; - /* mutex to protect access to individual PHYs */ - struct mutex lock; - - bool flip; - u8 mode; -}; +#define DEFAULT_RATE 162000 struct phy_reg { u16 value; @@ -417,15 +382,15 @@ struct phy_reg usb3_pll_cfg[] = { { 0x8, CMN_DIAG_PLL0_LF_PROG }, }; -struct phy_reg dp_pll_cfg[] = { +struct phy_reg dp_pll_rbr_cfg[] = { { 0xf0, CMN_PLL1_VCOCAL_INIT }, { 0x18, CMN_PLL1_VCOCAL_ITER }, { 0x30b9, CMN_PLL1_VCOCAL_START }, - { 0x21c,CMN_PLL1_INTDIV }, + { 0x87, CMN_PLL1_INTDIV }, { 0,CMN_PLL1_FRACDIV }, - { 0x5, CMN_PLL1_HIGH_THR }, - { 0x35, CMN_PLL1_SS_CTRL1 }, - { 0x7f1e, CMN_PLL1_SS_CTRL2 }, + { 0x22, CMN_PLL1_HIGH_THR }, + { 0x8000, CMN_PLL1_SS_CTRL1 }, + { 0,CMN_PLL1_SS_CTRL2 }, { 0x20, CMN_PLL1_
[PATCH 3/4] Documentation: bindings: add phy_config for Rockchip USB Type-C PHY
If want to do training outside DP Firmware, need phy voltage swing and pre_emphasis value. Signed-off-by: Lin Huang <h...@rock-chips.com> --- Documentation/devicetree/bindings/phy/phy-rockchip-typec.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/phy/phy-rockchip-typec.txt b/Documentation/devicetree/bindings/phy/phy-rockchip-typec.txt index 960da7f..eda26dd 100644 --- a/Documentation/devicetree/bindings/phy/phy-rockchip-typec.txt +++ b/Documentation/devicetree/bindings/phy/phy-rockchip-typec.txt @@ -17,7 +17,9 @@ Required properties: Optional properties: - extcon : extcon specifier for the Power Delivery - + - rockchip,phy_config : That's phy voltage swing and pre_emphasis +setting, if want to do dp training outside +dp firmware, need to add these value. Required nodes : a sub-node is required for each port the phy provides. The sub-node name is used to identify dp or usb3 port, and shall be the following entries: -- 2.7.4 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH v4 1/3] drm/panel: refactor INNOLUX P079ZCA panel driver
From: huang lin <h...@rock-chips.com> Refactor Innolux P079ZCA panel driver, let it support multi panel. Change-Id: If89be5e56dba8cb498e2d50c1bbeb0e8016123a2 Signed-off-by: Lin Huang <h...@rock-chips.com> --- Changes in v2: - Change regulator property name to meet the panel datasheet Changes in v3: - this patch only refactor P079ZCA panel to support multi panel, support P097PFG panel in another patch Changes in v4: - Modify the patch which suggest by Thierry drivers/gpu/drm/panel/panel-innolux-p079zca.c | 142 ++ 1 file changed, 101 insertions(+), 41 deletions(-) diff --git a/drivers/gpu/drm/panel/panel-innolux-p079zca.c b/drivers/gpu/drm/panel/panel-innolux-p079zca.c index 57df39b..2075a9d 100644 --- a/drivers/gpu/drm/panel/panel-innolux-p079zca.c +++ b/drivers/gpu/drm/panel/panel-innolux-p079zca.c @@ -20,12 +20,28 @@ #include +struct panel_desc { + const struct drm_display_mode *modes; + unsigned int bpc; + struct { + unsigned int width; + unsigned int height; + } size; + + unsigned long flags; + enum mipi_dsi_pixel_format format; + unsigned int lanes; +}; + struct innolux_panel { struct drm_panel base; struct mipi_dsi_device *link; + const struct panel_desc *desc; struct backlight_device *backlight; - struct regulator *supply; + struct regulator *vddi; + struct regulator *avdd; + struct regulator *avee; struct gpio_desc *enable_gpio; bool prepared; @@ -77,9 +93,9 @@ static int innolux_panel_unprepare(struct drm_panel *panel) /* T8: 80ms - 1000ms */ msleep(80); - err = regulator_disable(innolux->supply); - if (err < 0) - return err; + regulator_disable(innolux->avee); + regulator_disable(innolux->avdd); + regulator_disable(innolux->vddi); innolux->prepared = false; @@ -89,17 +105,25 @@ static int innolux_panel_unprepare(struct drm_panel *panel) static int innolux_panel_prepare(struct drm_panel *panel) { struct innolux_panel *innolux = to_innolux_panel(panel); - int err, regulator_err; + int err; if (innolux->prepared) return 0; gpiod_set_value_cansleep(innolux->enable_gpio, 0); - err = regulator_enable(innolux->supply); + err = regulator_enable(innolux->vddi); if (err < 0) return err; + err = regulator_enable(innolux->avdd); + if (err < 0) + goto disable_vddi; + + err = regulator_enable(innolux->avee); + if (err < 0) + goto disable_avdd; + /* T2: 15ms - 1000ms */ usleep_range(15000, 16000); @@ -133,12 +157,13 @@ static int innolux_panel_prepare(struct drm_panel *panel) return 0; poweroff: - regulator_err = regulator_disable(innolux->supply); - if (regulator_err) - DRM_DEV_ERROR(panel->dev, "failed to disable regulator: %d\n", - regulator_err); - gpiod_set_value_cansleep(innolux->enable_gpio, 0); + regulator_disable(innolux->avee); +disable_avdd: + regulator_disable(innolux->avdd); +disable_vddi: + regulator_disable(innolux->vddi); + return err; } @@ -162,7 +187,7 @@ static int innolux_panel_enable(struct drm_panel *panel) return 0; } -static const struct drm_display_mode default_mode = { +static const struct drm_display_mode innolux_p079zca_mode = { .clock = 56900, .hdisplay = 768, .hsync_start = 768 + 40, @@ -175,15 +200,29 @@ static const struct drm_display_mode default_mode = { .vrefresh = 60, }; +static const struct panel_desc innolux_p079zca_panel_desc = { + .modes = _p079zca_mode, + .bpc = 8, + .size = { + .width = 120, + .height = 160, + }, + .flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE | +MIPI_DSI_MODE_LPM, + .format = MIPI_DSI_FMT_RGB888, + .lanes = 4, +}; + static int innolux_panel_get_modes(struct drm_panel *panel) { struct drm_display_mode *mode; + struct innolux_panel *innolux = to_innolux_panel(panel); + const struct drm_display_mode *m = innolux->desc->modes; - mode = drm_mode_duplicate(panel->drm, _mode); + mode = drm_mode_duplicate(panel->drm, m); if (!mode) { DRM_DEV_ERROR(panel->drm->dev, "failed to add mode %ux%ux@%u\n", - default_mode.hdisplay, default_mode.vdisplay, - default_mode.vrefresh); + m->hdisplay, m->vdisplay, m->vrefresh); return -ENOMEM; } @@ -191,9 +230,11 @@ static int innolux_panel_get_modes(struct drm_pa
[PATCH v4 2/3] drm/panel: support Innolux P097PFG panel
Support Innolux P097PFG 9.7" 1536x2048 TFT LCD panel, it reuse the Innolux P079ZCA panel driver. Change-Id: I97923aa3735f707332681691b0231c9421b427d0 Signed-off-by: Lin Huang <h...@rock-chips.com> --- Changes in v2: - None Changes in v3: - None Changes in v4: - download panel initial code drivers/gpu/drm/panel/panel-innolux-p079zca.c | 192 +- 1 file changed, 187 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/panel/panel-innolux-p079zca.c b/drivers/gpu/drm/panel/panel-innolux-p079zca.c index 2075a9d..883b279 100644 --- a/drivers/gpu/drm/panel/panel-innolux-p079zca.c +++ b/drivers/gpu/drm/panel/panel-innolux-p079zca.c @@ -20,6 +20,15 @@ #include +struct panel_init_cmd { + int len; + const char *data; +}; + +#define _INIT_CMD(...) { \ + .len = sizeof((char[]){__VA_ARGS__}), \ + .data = (char[]){__VA_ARGS__} } + struct panel_desc { const struct drm_display_mode *modes; unsigned int bpc; @@ -30,6 +39,7 @@ struct panel_desc { unsigned long flags; enum mipi_dsi_pixel_format format; + const struct panel_init_cmd *init_cmds; unsigned int lanes; }; @@ -88,9 +98,12 @@ static int innolux_panel_unprepare(struct drm_panel *panel) return err; } + /* p097pfg: t15 */ + msleep(100); + gpiod_set_value_cansleep(innolux->enable_gpio, 0); - /* T8: 80ms - 1000ms */ + /* p079zca: t8*/ msleep(80); regulator_disable(innolux->avee); @@ -124,13 +137,43 @@ static int innolux_panel_prepare(struct drm_panel *panel) if (err < 0) goto disable_avdd; - /* T2: 15ms - 1000ms */ - usleep_range(15000, 16000); + /* p079zca: t2 (20ms), p097pfg: t4 (15ms) */ + usleep_range(2, 21000); gpiod_set_value_cansleep(innolux->enable_gpio, 1); - /* T4: 15ms - 1000ms */ - usleep_range(15000, 16000); + /* p079zca: t4, p097pfg: t5 */ + usleep_range(2, 21000); + + if (innolux->desc->init_cmds) { + const struct panel_init_cmd *cmds = + innolux->desc->init_cmds; + int i; + + for (i = 0; cmds[i].len != 0; i++) { + const struct panel_init_cmd *cmd = [i]; + + err = mipi_dsi_generic_write(innolux->link, cmd->data, +cmd->len); + if (err < 0) { + dev_err(panel->dev, + "failed to write command %d\n", i); + goto poweroff; + } + + /* +* Included by random guessing, because without this +* (or at least, some delay), the panel sometimes +* didn't appear to pick up the command sequence. +*/ + err = mipi_dsi_dcs_nop(innolux->link); + if (err < 0) { + dev_err(panel->dev, + "failed to send DCS nop: %d\n", err); + goto poweroff; + } + } + } err = mipi_dsi_dcs_exit_sleep_mode(innolux->link); if (err < 0) { @@ -213,6 +256,142 @@ static const struct panel_desc innolux_p079zca_panel_desc = { .lanes = 4, }; +static const struct drm_display_mode innolux_p097pfg_mode = { + .clock = 229000, + .hdisplay = 1536, + .hsync_start = 1536 + 100, + .hsync_end = 1536 + 100 + 24, + .htotal = 1536 + 100 + 24 + 100, + .vdisplay = 2048, + .vsync_start = 2048 + 100, + .vsync_end = 2048 + 100 + 2, + .vtotal = 2048 + 100 + 2 + 18, + .vrefresh = 60, +}; + +static const struct panel_init_cmd innolux_p097pfg_init_cmds[] = { + /* page 0 */ + _INIT_CMD(0xF0, 0x55, 0xAA, 0x52, 0x08, 0x00), + _INIT_CMD(0xB1, 0xE8, 0x11), + _INIT_CMD(0xB2, 0x25, 0x02), + _INIT_CMD(0xB5, 0x08, 0x00), + _INIT_CMD(0xBC, 0x0F, 0x00), + _INIT_CMD(0xB8, 0x03, 0x06, 0x00, 0x00), + _INIT_CMD(0xBD, 0x01, 0x90, 0x14, 0x14), + _INIT_CMD(0x6F, 0x01), + _INIT_CMD(0xC0, 0x03), + _INIT_CMD(0x6F, 0x02), + _INIT_CMD(0xC1, 0x0D), + _INIT_CMD(0xD9, 0x01, 0x09, 0x70), + _INIT_CMD(0xC5, 0x12, 0x21, 0x00), + _INIT_CMD(0xBB, 0x93, 0x93), + + /* page 1 */ + _INIT_CMD(0xF0, 0x55, 0xAA, 0x52, 0x08, 0x01), + _INIT_CMD(0xB3, 0x3C, 0x3C), + _INIT_CMD(0xB4, 0x0F, 0x0F), + _INIT_CMD(0xB9, 0x45, 0x45), + _INIT_CMD(0xBA, 0x14, 0x14), + _INIT_CMD(0xCA, 0x02), + _INIT_CMD(0xCE, 0x04), + _INIT_CMD(0xC3, 0x9B, 0x9B), + _INIT_CMD(0xD8,
[PATCH v4 3/3] dt-bindings: Add INNOLUX P097PFG panel bindings
From: huang lin <h...@rock-chips.com> The Innolux P097PFG panel is 9.7" panel with 1536X2048 resolution, it reuse P079ZCA panel driver, so improve p079ZCA dt-binding to support P097PFG. Change-Id: I8704914898fe53b734d31fbe646df8aa5fd8b30d Signed-off-by: Lin Huang <h...@rock-chips.com> --- Changes in v2: - None Changes in v3: - None Changes in v4: - None .../devicetree/bindings/display/panel/innolux,p079zca.txt | 11 +-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/display/panel/innolux,p079zca.txt b/Documentation/devicetree/bindings/display/panel/innolux,p079zca.txt index d0f5516..8cadd8c 100644 --- a/Documentation/devicetree/bindings/display/panel/innolux,p079zca.txt +++ b/Documentation/devicetree/bindings/display/panel/innolux,p079zca.txt @@ -1,13 +1,18 @@ Innolux P079ZCA 7.85" 768x1024 TFT LCD panel +Innolux P097PFG 9.7" 1536x2048 TFT LCD panel Required properties: -- compatible: should be "innolux,p079zca" +- compatible: should be should be one of the following. +-"innolux,p079zca" for Innolux 7.9" P079ZCA 768*1024 panel +-"innolux,p097pfg" for Innolux 9.7" P097PFG 1536*2048 panel - reg: DSI virtual channel of the peripheral -- power-supply: phandle of the regulator that provides the supply voltage - enable-gpios: panel enable gpio Optional properties: - backlight: phandle of the backlight device attached to the panel +- power-supply: phandle of the regulator that provides the supply voltage +- avdd-supply: phandle of the regulator that provides positive voltage +- avee-supply: phandle of the regulator that provides negative voltage Example: @@ -16,6 +21,8 @@ Example: compatible = "innolux,p079zca"; reg = <0>; power-supply = <...>; + avdd-supply = <...>; + avee-supply = <...>; backlight = <>; enable-gpios = < 13 GPIO_ACTIVE_HIGH>; }; -- 2.7.4 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH v3 2/3] drm/panel: support Innolux P097PFG panel
Support Innolux P097PFG 9.7" 1536x2048 TFT LCD panel, it reuse the Innolux P079ZCA panel driver. Signed-off-by: Lin Huang <h...@rock-chips.com> --- drivers/gpu/drm/panel/Kconfig | 9 drivers/gpu/drm/panel/panel-innolux-p079zca.c | 31 +++ 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig index 726f3fb..429cf59 100644 --- a/drivers/gpu/drm/panel/Kconfig +++ b/drivers/gpu/drm/panel/Kconfig @@ -29,15 +29,14 @@ config DRM_PANEL_SIMPLE low power state. config DRM_PANEL_INNOLUX_P079ZCA - tristate "Innolux P079ZCA panel" + tristate "Innolux P079ZCA or P097PFG panel" depends on OF depends on DRM_MIPI_DSI depends on BACKLIGHT_CLASS_DEVICE help - Say Y here if you want to enable support for Innolux P079ZCA - TFT-LCD modules. The panel has a 1024x768 resolution and uses - 24 bit RGB per pixel. It provides a MIPI DSI interface to - the host and has a built-in LED backlight. + Say Y here if you want to enable support for Innolux P079ZCA or + Innolux P097PFG panel. They provide a MIPI DSI interface to + the host and have a built-in LED backlight. config DRM_PANEL_JDI_LT070ME05000 tristate "JDI LT070ME05000 WUXGA DSI panel" diff --git a/drivers/gpu/drm/panel/panel-innolux-p079zca.c b/drivers/gpu/drm/panel/panel-innolux-p079zca.c index 1597744..5d690b7 100644 --- a/drivers/gpu/drm/panel/panel-innolux-p079zca.c +++ b/drivers/gpu/drm/panel/panel-innolux-p079zca.c @@ -221,6 +221,34 @@ static const struct panel_desc_dsi innolux_p079zca_panel_desc = { .lanes = 4, }; +static const struct drm_display_mode innolux_p097pfg_mode = { + .clock = 22, + .hdisplay = 1536, + .hsync_start = 1536 + 100, + .hsync_end = 1536 + 100 + 24, + .htotal = 1536 + 100 + 24 + 100, + .vdisplay = 2048, + .vsync_start = 2048 + 18, + .vsync_end = 2048 + 18 + 2, + .vtotal = 2048 + 18 + 2 + 18, + .vrefresh = 60, +}; + +static const struct panel_desc_dsi innolux_p097pfg_panel_desc = { + .desc = { + .modes = _p097pfg_mode, + .bpc = 8, + .size = { + .width = 147, + .height = 196, + }, + }, + .flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE | +MIPI_DSI_MODE_LPM, + .format = MIPI_DSI_FMT_RGB888, + .lanes = 8, +}; + static int innolux_panel_get_modes(struct drm_panel *panel) { struct drm_display_mode *mode; @@ -259,6 +287,9 @@ static const struct of_device_id innolux_of_match[] = { { .compatible = "innolux,p079zca", .data = _p079zca_panel_desc }, + { .compatible = "innolux,p097pfg", + .data = _p097pfg_panel_desc + } }; MODULE_DEVICE_TABLE(of, innolux_of_match); -- 2.7.4 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[RESEND PATCH v3 2/3] drm/panel: support Innolux P097PFG panel
Support Innolux P097PFG 9.7" 1536x2048 TFT LCD panel, it reuse the Innolux P079ZCA panel driver. Signed-off-by: Lin Huang <h...@rock-chips.com> --- drivers/gpu/drm/panel/Kconfig | 9 drivers/gpu/drm/panel/panel-innolux-p079zca.c | 31 +++ 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig index 726f3fb..429cf59 100644 --- a/drivers/gpu/drm/panel/Kconfig +++ b/drivers/gpu/drm/panel/Kconfig @@ -29,15 +29,14 @@ config DRM_PANEL_SIMPLE low power state. config DRM_PANEL_INNOLUX_P079ZCA - tristate "Innolux P079ZCA panel" + tristate "Innolux P079ZCA or P097PFG panel" depends on OF depends on DRM_MIPI_DSI depends on BACKLIGHT_CLASS_DEVICE help - Say Y here if you want to enable support for Innolux P079ZCA - TFT-LCD modules. The panel has a 1024x768 resolution and uses - 24 bit RGB per pixel. It provides a MIPI DSI interface to - the host and has a built-in LED backlight. + Say Y here if you want to enable support for Innolux P079ZCA or + Innolux P097PFG panel. They provide a MIPI DSI interface to + the host and have a built-in LED backlight. config DRM_PANEL_JDI_LT070ME05000 tristate "JDI LT070ME05000 WUXGA DSI panel" diff --git a/drivers/gpu/drm/panel/panel-innolux-p079zca.c b/drivers/gpu/drm/panel/panel-innolux-p079zca.c index 1597744..5d690b7 100644 --- a/drivers/gpu/drm/panel/panel-innolux-p079zca.c +++ b/drivers/gpu/drm/panel/panel-innolux-p079zca.c @@ -221,6 +221,34 @@ static const struct panel_desc_dsi innolux_p079zca_panel_desc = { .lanes = 4, }; +static const struct drm_display_mode innolux_p097pfg_mode = { + .clock = 22, + .hdisplay = 1536, + .hsync_start = 1536 + 100, + .hsync_end = 1536 + 100 + 24, + .htotal = 1536 + 100 + 24 + 100, + .vdisplay = 2048, + .vsync_start = 2048 + 18, + .vsync_end = 2048 + 18 + 2, + .vtotal = 2048 + 18 + 2 + 18, + .vrefresh = 60, +}; + +static const struct panel_desc_dsi innolux_p097pfg_panel_desc = { + .desc = { + .modes = _p097pfg_mode, + .bpc = 8, + .size = { + .width = 147, + .height = 196, + }, + }, + .flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE | +MIPI_DSI_MODE_LPM, + .format = MIPI_DSI_FMT_RGB888, + .lanes = 8, +}; + static int innolux_panel_get_modes(struct drm_panel *panel) { struct drm_display_mode *mode; @@ -259,6 +287,9 @@ static const struct of_device_id innolux_of_match[] = { { .compatible = "innolux,p079zca", .data = _p079zca_panel_desc }, + { .compatible = "innolux,p097pfg", + .data = _p097pfg_panel_desc + } }; MODULE_DEVICE_TABLE(of, innolux_of_match); -- 2.7.4 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[RESEND PATCH v3 1/3] drm/panel: refactor INNOLUX P079ZCA panel driver
Refactor Innolux P079ZCA panel driver, let it support multi panel. Signed-off-by: Lin Huang <h...@rock-chips.com> --- Changes in v2: - Change regulator property name to meet the panel datasheet Changes in v3: - this patch only refactor P079ZCA panel to support multi panel, support P097PFG panel in another patch drivers/gpu/drm/panel/panel-innolux-p079zca.c | 147 ++ 1 file changed, 105 insertions(+), 42 deletions(-) diff --git a/drivers/gpu/drm/panel/panel-innolux-p079zca.c b/drivers/gpu/drm/panel/panel-innolux-p079zca.c index 6ba9344..1597744 100644 --- a/drivers/gpu/drm/panel/panel-innolux-p079zca.c +++ b/drivers/gpu/drm/panel/panel-innolux-p079zca.c @@ -20,12 +20,32 @@ #include +struct panel_desc { + const struct drm_display_mode *modes; + unsigned int bpc; + struct { + unsigned int width; + unsigned int height; + } size; +}; + +struct panel_desc_dsi { + struct panel_desc desc; + + unsigned long flags; + enum mipi_dsi_pixel_format format; + unsigned int lanes; +}; + struct innolux_panel { struct drm_panel base; struct mipi_dsi_device *link; + const struct panel_desc_dsi *dsi_desc; struct backlight_device *backlight; - struct regulator *supply; + struct regulator *vddi; + struct regulator *avdd; + struct regulator *avee; struct gpio_desc *enable_gpio; bool prepared; @@ -78,9 +98,9 @@ static int innolux_panel_unprepare(struct drm_panel *panel) /* T8: 80ms - 1000ms */ msleep(80); - err = regulator_disable(innolux->supply); - if (err < 0) - return err; + regulator_disable(innolux->avee); + regulator_disable(innolux->avdd); + regulator_disable(innolux->vddi); innolux->prepared = false; @@ -97,10 +117,18 @@ static int innolux_panel_prepare(struct drm_panel *panel) gpiod_set_value_cansleep(innolux->enable_gpio, 0); - err = regulator_enable(innolux->supply); + err = regulator_enable(innolux->vddi); if (err < 0) return err; + err = regulator_enable(innolux->avdd); + if (err < 0) + goto disable_vddi; + + err = regulator_enable(innolux->avee); + if (err < 0) + goto disable_avdd; + /* T2: 15ms - 1000ms */ usleep_range(15000, 16000); @@ -134,12 +162,13 @@ static int innolux_panel_prepare(struct drm_panel *panel) return 0; poweroff: - regulator_err = regulator_disable(innolux->supply); - if (regulator_err) - DRM_DEV_ERROR(panel->dev, "failed to disable regulator: %d\n", - regulator_err); - gpiod_set_value_cansleep(innolux->enable_gpio, 0); + regulator_disable(innolux->avee); +disable_avdd: + regulator_disable(innolux->avdd); +disable_vddi: + regulator_disable(innolux->vddi); + return err; } @@ -164,7 +193,7 @@ static int innolux_panel_enable(struct drm_panel *panel) return 0; } -static const struct drm_display_mode default_mode = { +static const struct drm_display_mode innolux_p079zca_mode = { .clock = 56900, .hdisplay = 768, .hsync_start = 768 + 40, @@ -177,15 +206,31 @@ static const struct drm_display_mode default_mode = { .vrefresh = 60, }; +static const struct panel_desc_dsi innolux_p079zca_panel_desc = { + .desc = { + .modes = _p079zca_mode, + .bpc = 8, + .size = { + .width = 120, + .height = 160, + }, + }, + .flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE | +MIPI_DSI_MODE_LPM, + .format = MIPI_DSI_FMT_RGB888, + .lanes = 4, +}; + static int innolux_panel_get_modes(struct drm_panel *panel) { struct drm_display_mode *mode; + struct innolux_panel *innolux = to_innolux_panel(panel); + const struct drm_display_mode *m = innolux->dsi_desc->desc.modes; - mode = drm_mode_duplicate(panel->drm, _mode); + mode = drm_mode_duplicate(panel->drm, m); if (!mode) { DRM_DEV_ERROR(panel->drm->dev, "failed to add mode %ux%ux@%u\n", - default_mode.hdisplay, default_mode.vdisplay, - default_mode.vrefresh); + m->hdisplay, m->vdisplay, m->vrefresh); return -ENOMEM; } @@ -193,9 +238,11 @@ static int innolux_panel_get_modes(struct drm_panel *panel) drm_mode_probed_add(panel->connector, mode); - panel->connector->display_info.width_mm = 120; - panel->connector->display_info.height_mm = 160; - panel->connector->display_info.bpc = 8;
[RESEND PATCH v3 3/3] dt-bindings: Add INNOLUX P097PFG panel bindings
The Innolux P097PFG panel is 9.7" panel with 1536X2048 resolution, it reuse P079ZCA panel driver, so improve p079ZCA dt-binding to support P097PFG. Signed-off-by: Lin Huang <h...@rock-chips.com> --- .../devicetree/bindings/display/panel/innolux,p079zca.txt | 11 +-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/display/panel/innolux,p079zca.txt b/Documentation/devicetree/bindings/display/panel/innolux,p079zca.txt index d0f5516..8cadd8c 100644 --- a/Documentation/devicetree/bindings/display/panel/innolux,p079zca.txt +++ b/Documentation/devicetree/bindings/display/panel/innolux,p079zca.txt @@ -1,13 +1,18 @@ Innolux P079ZCA 7.85" 768x1024 TFT LCD panel +Innolux P097PFG 9.7" 1536x2048 TFT LCD panel Required properties: -- compatible: should be "innolux,p079zca" +- compatible: should be should be one of the following. +-"innolux,p079zca" for Innolux 7.9" P079ZCA 768*1024 panel +-"innolux,p097pfg" for Innolux 9.7" P097PFG 1536*2048 panel - reg: DSI virtual channel of the peripheral -- power-supply: phandle of the regulator that provides the supply voltage - enable-gpios: panel enable gpio Optional properties: - backlight: phandle of the backlight device attached to the panel +- power-supply: phandle of the regulator that provides the supply voltage +- avdd-supply: phandle of the regulator that provides positive voltage +- avee-supply: phandle of the regulator that provides negative voltage Example: @@ -16,6 +21,8 @@ Example: compatible = "innolux,p079zca"; reg = <0>; power-supply = <...>; + avdd-supply = <...>; + avee-supply = <...>; backlight = <>; enable-gpios = < 13 GPIO_ACTIVE_HIGH>; }; -- 2.7.4 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH v3 3/3] dt-bindings: Add INNOLUX P097PFG panel bindings
The Innolux P097PFG panel is 9.7" panel with 1536X2048 resolution, it reuse P079ZCA panel driver, so improve p079ZCA dt-binding to support P097PFG. Change-Id: I8704914898fe53b734d31fbe646df8aa5fd8b30d Signed-off-by: Lin Huang <h...@rock-chips.com> --- .../devicetree/bindings/display/panel/innolux,p079zca.txt | 11 +-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/display/panel/innolux,p079zca.txt b/Documentation/devicetree/bindings/display/panel/innolux,p079zca.txt index d0f5516..8cadd8c 100644 --- a/Documentation/devicetree/bindings/display/panel/innolux,p079zca.txt +++ b/Documentation/devicetree/bindings/display/panel/innolux,p079zca.txt @@ -1,13 +1,18 @@ Innolux P079ZCA 7.85" 768x1024 TFT LCD panel +Innolux P097PFG 9.7" 1536x2048 TFT LCD panel Required properties: -- compatible: should be "innolux,p079zca" +- compatible: should be should be one of the following. +-"innolux,p079zca" for Innolux 7.9" P079ZCA 768*1024 panel +-"innolux,p097pfg" for Innolux 9.7" P097PFG 1536*2048 panel - reg: DSI virtual channel of the peripheral -- power-supply: phandle of the regulator that provides the supply voltage - enable-gpios: panel enable gpio Optional properties: - backlight: phandle of the backlight device attached to the panel +- power-supply: phandle of the regulator that provides the supply voltage +- avdd-supply: phandle of the regulator that provides positive voltage +- avee-supply: phandle of the regulator that provides negative voltage Example: @@ -16,6 +21,8 @@ Example: compatible = "innolux,p079zca"; reg = <0>; power-supply = <...>; + avdd-supply = <...>; + avee-supply = <...>; backlight = <>; enable-gpios = < 13 GPIO_ACTIVE_HIGH>; }; -- 2.7.4 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH v3 1/3] drm/panel: refactor INNOLUX P079ZCA panel driver
Refactor Innolux P079ZCA panel driver, let it support multi panel. Signed-off-by: Lin Huang <h...@rock-chips.com> --- Changes in v2: - Change regulator property name to meet the panel datasheet Changes in v3: - this patch only refactor P079ZCA panel to support multi panel, support P097PFG panel in another patch drivers/gpu/drm/panel/panel-innolux-p079zca.c | 147 ++ 1 file changed, 105 insertions(+), 42 deletions(-) diff --git a/drivers/gpu/drm/panel/panel-innolux-p079zca.c b/drivers/gpu/drm/panel/panel-innolux-p079zca.c index 6ba9344..1597744 100644 --- a/drivers/gpu/drm/panel/panel-innolux-p079zca.c +++ b/drivers/gpu/drm/panel/panel-innolux-p079zca.c @@ -20,12 +20,32 @@ #include +struct panel_desc { + const struct drm_display_mode *modes; + unsigned int bpc; + struct { + unsigned int width; + unsigned int height; + } size; +}; + +struct panel_desc_dsi { + struct panel_desc desc; + + unsigned long flags; + enum mipi_dsi_pixel_format format; + unsigned int lanes; +}; + struct innolux_panel { struct drm_panel base; struct mipi_dsi_device *link; + const struct panel_desc_dsi *dsi_desc; struct backlight_device *backlight; - struct regulator *supply; + struct regulator *vddi; + struct regulator *avdd; + struct regulator *avee; struct gpio_desc *enable_gpio; bool prepared; @@ -78,9 +98,9 @@ static int innolux_panel_unprepare(struct drm_panel *panel) /* T8: 80ms - 1000ms */ msleep(80); - err = regulator_disable(innolux->supply); - if (err < 0) - return err; + regulator_disable(innolux->avee); + regulator_disable(innolux->avdd); + regulator_disable(innolux->vddi); innolux->prepared = false; @@ -97,10 +117,18 @@ static int innolux_panel_prepare(struct drm_panel *panel) gpiod_set_value_cansleep(innolux->enable_gpio, 0); - err = regulator_enable(innolux->supply); + err = regulator_enable(innolux->vddi); if (err < 0) return err; + err = regulator_enable(innolux->avdd); + if (err < 0) + goto disable_vddi; + + err = regulator_enable(innolux->avee); + if (err < 0) + goto disable_avdd; + /* T2: 15ms - 1000ms */ usleep_range(15000, 16000); @@ -134,12 +162,13 @@ static int innolux_panel_prepare(struct drm_panel *panel) return 0; poweroff: - regulator_err = regulator_disable(innolux->supply); - if (regulator_err) - DRM_DEV_ERROR(panel->dev, "failed to disable regulator: %d\n", - regulator_err); - gpiod_set_value_cansleep(innolux->enable_gpio, 0); + regulator_disable(innolux->avee); +disable_avdd: + regulator_disable(innolux->avdd); +disable_vddi: + regulator_disable(innolux->vddi); + return err; } @@ -164,7 +193,7 @@ static int innolux_panel_enable(struct drm_panel *panel) return 0; } -static const struct drm_display_mode default_mode = { +static const struct drm_display_mode innolux_p079zca_mode = { .clock = 56900, .hdisplay = 768, .hsync_start = 768 + 40, @@ -177,15 +206,31 @@ static const struct drm_display_mode default_mode = { .vrefresh = 60, }; +static const struct panel_desc_dsi innolux_p079zca_panel_desc = { + .desc = { + .modes = _p079zca_mode, + .bpc = 8, + .size = { + .width = 120, + .height = 160, + }, + }, + .flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE | +MIPI_DSI_MODE_LPM, + .format = MIPI_DSI_FMT_RGB888, + .lanes = 4, +}; + static int innolux_panel_get_modes(struct drm_panel *panel) { struct drm_display_mode *mode; + struct innolux_panel *innolux = to_innolux_panel(panel); + const struct drm_display_mode *m = innolux->dsi_desc->desc.modes; - mode = drm_mode_duplicate(panel->drm, _mode); + mode = drm_mode_duplicate(panel->drm, m); if (!mode) { DRM_DEV_ERROR(panel->drm->dev, "failed to add mode %ux%ux@%u\n", - default_mode.hdisplay, default_mode.vdisplay, - default_mode.vrefresh); + m->hdisplay, m->vdisplay, m->vrefresh); return -ENOMEM; } @@ -193,9 +238,11 @@ static int innolux_panel_get_modes(struct drm_panel *panel) drm_mode_probed_add(panel->connector, mode); - panel->connector->display_info.width_mm = 120; - panel->connector->display_info.height_mm = 160; - panel->connector->display_info.bpc = 8;
[PATCH v2 2/2] dt-bindings: Add INNOLUX P097PFG panel bindings
The Innolux P097PFG panel is 9.7" panel with 1536X2048 resolution, it reuse P079ZCA panel driver, so improve p079ZCA dt-binding to support P097PFG. Signed-off-by: Lin Huang <h...@rock-chips.com> --- .../devicetree/bindings/display/panel/innolux,p079zca.txt | 11 +-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/display/panel/innolux,p079zca.txt b/Documentation/devicetree/bindings/display/panel/innolux,p079zca.txt index d0f5516..8cadd8c 100644 --- a/Documentation/devicetree/bindings/display/panel/innolux,p079zca.txt +++ b/Documentation/devicetree/bindings/display/panel/innolux,p079zca.txt @@ -1,13 +1,18 @@ Innolux P079ZCA 7.85" 768x1024 TFT LCD panel +Innolux P097PFG 9.7" 1536x2048 TFT LCD panel Required properties: -- compatible: should be "innolux,p079zca" +- compatible: should be should be one of the following. +-"innolux,p079zca" for Innolux 7.9" P079ZCA 768*1024 panel +-"innolux,p097pfg" for Innolux 9.7" P097PFG 1536*2048 panel - reg: DSI virtual channel of the peripheral -- power-supply: phandle of the regulator that provides the supply voltage - enable-gpios: panel enable gpio Optional properties: - backlight: phandle of the backlight device attached to the panel +- power-supply: phandle of the regulator that provides the supply voltage +- avdd-supply: phandle of the regulator that provides positive voltage +- avee-supply: phandle of the regulator that provides negative voltage Example: @@ -16,6 +21,8 @@ Example: compatible = "innolux,p079zca"; reg = <0>; power-supply = <...>; + avdd-supply = <...>; + avee-supply = <...>; backlight = <>; enable-gpios = < 13 GPIO_ACTIVE_HIGH>; }; -- 2.7.4 ___ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
[PATCH v2 1/2] drm/panel: support Innolux P097PFG panel
Support Innolux P097PFG 9.7" 1536x2048 TFT LCD panel, it refactor Innolux P079ZCA panel driver, let it support multi panel, and add support P097PFG panel in this driver. Signed-off-by: Lin Huang <h...@rock-chips.com> --- Changes in v2: - change regulator property name to meet the panel datasheet drivers/gpu/drm/panel/panel-innolux-p079zca.c | 178 -- 1 file changed, 136 insertions(+), 42 deletions(-) diff --git a/drivers/gpu/drm/panel/panel-innolux-p079zca.c b/drivers/gpu/drm/panel/panel-innolux-p079zca.c index 6ba9344..5d690b7 100644 --- a/drivers/gpu/drm/panel/panel-innolux-p079zca.c +++ b/drivers/gpu/drm/panel/panel-innolux-p079zca.c @@ -20,12 +20,32 @@ #include +struct panel_desc { + const struct drm_display_mode *modes; + unsigned int bpc; + struct { + unsigned int width; + unsigned int height; + } size; +}; + +struct panel_desc_dsi { + struct panel_desc desc; + + unsigned long flags; + enum mipi_dsi_pixel_format format; + unsigned int lanes; +}; + struct innolux_panel { struct drm_panel base; struct mipi_dsi_device *link; + const struct panel_desc_dsi *dsi_desc; struct backlight_device *backlight; - struct regulator *supply; + struct regulator *vddi; + struct regulator *avdd; + struct regulator *avee; struct gpio_desc *enable_gpio; bool prepared; @@ -78,9 +98,9 @@ static int innolux_panel_unprepare(struct drm_panel *panel) /* T8: 80ms - 1000ms */ msleep(80); - err = regulator_disable(innolux->supply); - if (err < 0) - return err; + regulator_disable(innolux->avee); + regulator_disable(innolux->avdd); + regulator_disable(innolux->vddi); innolux->prepared = false; @@ -97,10 +117,18 @@ static int innolux_panel_prepare(struct drm_panel *panel) gpiod_set_value_cansleep(innolux->enable_gpio, 0); - err = regulator_enable(innolux->supply); + err = regulator_enable(innolux->vddi); if (err < 0) return err; + err = regulator_enable(innolux->avdd); + if (err < 0) + goto disable_vddi; + + err = regulator_enable(innolux->avee); + if (err < 0) + goto disable_avdd; + /* T2: 15ms - 1000ms */ usleep_range(15000, 16000); @@ -134,12 +162,13 @@ static int innolux_panel_prepare(struct drm_panel *panel) return 0; poweroff: - regulator_err = regulator_disable(innolux->supply); - if (regulator_err) - DRM_DEV_ERROR(panel->dev, "failed to disable regulator: %d\n", - regulator_err); - gpiod_set_value_cansleep(innolux->enable_gpio, 0); + regulator_disable(innolux->avee); +disable_avdd: + regulator_disable(innolux->avdd); +disable_vddi: + regulator_disable(innolux->vddi); + return err; } @@ -164,7 +193,7 @@ static int innolux_panel_enable(struct drm_panel *panel) return 0; } -static const struct drm_display_mode default_mode = { +static const struct drm_display_mode innolux_p079zca_mode = { .clock = 56900, .hdisplay = 768, .hsync_start = 768 + 40, @@ -177,15 +206,59 @@ static const struct drm_display_mode default_mode = { .vrefresh = 60, }; +static const struct panel_desc_dsi innolux_p079zca_panel_desc = { + .desc = { + .modes = _p079zca_mode, + .bpc = 8, + .size = { + .width = 120, + .height = 160, + }, + }, + .flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE | +MIPI_DSI_MODE_LPM, + .format = MIPI_DSI_FMT_RGB888, + .lanes = 4, +}; + +static const struct drm_display_mode innolux_p097pfg_mode = { + .clock = 22, + .hdisplay = 1536, + .hsync_start = 1536 + 100, + .hsync_end = 1536 + 100 + 24, + .htotal = 1536 + 100 + 24 + 100, + .vdisplay = 2048, + .vsync_start = 2048 + 18, + .vsync_end = 2048 + 18 + 2, + .vtotal = 2048 + 18 + 2 + 18, + .vrefresh = 60, +}; + +static const struct panel_desc_dsi innolux_p097pfg_panel_desc = { + .desc = { + .modes = _p097pfg_mode, + .bpc = 8, + .size = { + .width = 147, + .height = 196, + }, + }, + .flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE | +MIPI_DSI_MODE_LPM, + .format = MIPI_DSI_FMT_RGB888, + .lanes = 8, +}; + static int innolux_panel_get_modes(struct drm_panel *panel) { struct drm_display_mode *mode; + struct innolux_panel *innolux = to_innolux_panel(panel); + const struct drm_display_mode *
[RESENT PATCH] drm/panel: support Innolux P097PFG panel
Support Innolux P097PFG 9.7" 1536x2048 TFT LCD panel, it refactor Innolux P079ZCA panel driver, let it support multi panel, and add support P097PFG panel in this driver. Signed-off-by: Lin Huang <h...@rock-chips.com> --- drivers/gpu/drm/panel/panel-innolux-p079zca.c | 178 -- 1 file changed, 136 insertions(+), 42 deletions(-) diff --git a/drivers/gpu/drm/panel/panel-innolux-p079zca.c b/drivers/gpu/drm/panel/panel-innolux-p079zca.c index 6ba9344..a40798f 100644 --- a/drivers/gpu/drm/panel/panel-innolux-p079zca.c +++ b/drivers/gpu/drm/panel/panel-innolux-p079zca.c @@ -20,12 +20,32 @@ #include +struct panel_desc { + const struct drm_display_mode *modes; + unsigned int bpc; + struct { + unsigned int width; + unsigned int height; + } size; +}; + +struct panel_desc_dsi { + struct panel_desc desc; + + unsigned long flags; + enum mipi_dsi_pixel_format format; + unsigned int lanes; +}; + struct innolux_panel { struct drm_panel base; struct mipi_dsi_device *link; + const struct panel_desc_dsi *dsi_desc; struct backlight_device *backlight; - struct regulator *supply; + struct regulator *vddi; + struct regulator *avdd; + struct regulator *avee; struct gpio_desc *enable_gpio; bool prepared; @@ -78,9 +98,9 @@ static int innolux_panel_unprepare(struct drm_panel *panel) /* T8: 80ms - 1000ms */ msleep(80); - err = regulator_disable(innolux->supply); - if (err < 0) - return err; + regulator_disable(innolux->avee); + regulator_disable(innolux->avdd); + regulator_disable(innolux->vddi); innolux->prepared = false; @@ -97,10 +117,18 @@ static int innolux_panel_prepare(struct drm_panel *panel) gpiod_set_value_cansleep(innolux->enable_gpio, 0); - err = regulator_enable(innolux->supply); + err = regulator_enable(innolux->vddi); if (err < 0) return err; + err = regulator_enable(innolux->avdd); + if (err < 0) + goto disable_vddi; + + err = regulator_enable(innolux->avee); + if (err < 0) + goto disable_avdd; + /* T2: 15ms - 1000ms */ usleep_range(15000, 16000); @@ -134,12 +162,13 @@ static int innolux_panel_prepare(struct drm_panel *panel) return 0; poweroff: - regulator_err = regulator_disable(innolux->supply); - if (regulator_err) - DRM_DEV_ERROR(panel->dev, "failed to disable regulator: %d\n", - regulator_err); - gpiod_set_value_cansleep(innolux->enable_gpio, 0); + regulator_disable(innolux->avee); +disable_avdd: + regulator_disable(innolux->avdd); +disable_vddi: + regulator_disable(innolux->vddi); + return err; } @@ -164,7 +193,7 @@ static int innolux_panel_enable(struct drm_panel *panel) return 0; } -static const struct drm_display_mode default_mode = { +static const struct drm_display_mode innolux_p079zca_mode = { .clock = 56900, .hdisplay = 768, .hsync_start = 768 + 40, @@ -177,15 +206,59 @@ static const struct drm_display_mode default_mode = { .vrefresh = 60, }; +static const struct panel_desc_dsi innolux_p079zca_panel_desc = { + .desc = { + .modes = _p079zca_mode, + .bpc = 8, + .size = { + .width = 120, + .height = 160, + }, + }, + .flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE | +MIPI_DSI_MODE_LPM, + .format = MIPI_DSI_FMT_RGB888, + .lanes = 4, +}; + +static const struct drm_display_mode innolux_p097pfg_mode = { + .clock = 22, + .hdisplay = 1536, + .hsync_start = 1536 + 100, + .hsync_end = 1536 + 100 + 24, + .htotal = 1536 + 100 + 24 + 100, + .vdisplay = 2048, + .vsync_start = 2048 + 18, + .vsync_end = 2048 + 18 + 2, + .vtotal = 2048 + 18 + 2 + 18, + .vrefresh = 60, +}; + +static const struct panel_desc_dsi innolux_p097pfg_panel_desc = { + .desc = { + .modes = _p097pfg_mode, + .bpc = 8, + .size = { + .width = 147, + .height = 196, + }, + }, + .flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE | +MIPI_DSI_MODE_LPM, + .format = MIPI_DSI_FMT_RGB888, + .lanes = 8, +}; + static int innolux_panel_get_modes(struct drm_panel *panel) { struct drm_display_mode *mode; + struct innolux_panel *innolux = to_innolux_panel(panel); + const struct drm_display_mode *m = innolux->dsi_desc->desc.modes; - mode = drm_mode_duplicate(
[PATCH] drm/panel: support Innolux P097PFG panel
Support Innolux P097PFG 9.7" 1536x2048 TFT LCD panel, it refactor Innolux P079ZCA panel driver, let it support multi panel, and add support P097PFG panel in this driver. Change-Id: If342e58a3de2861219b0b1313f402b6cb41ffa29 Signed-off-by: Lin Huang <h...@rock-chips.com> --- drivers/gpu/drm/panel/panel-innolux-p079zca.c | 178 -- 1 file changed, 136 insertions(+), 42 deletions(-) diff --git a/drivers/gpu/drm/panel/panel-innolux-p079zca.c b/drivers/gpu/drm/panel/panel-innolux-p079zca.c index 6ba9344..a40798f 100644 --- a/drivers/gpu/drm/panel/panel-innolux-p079zca.c +++ b/drivers/gpu/drm/panel/panel-innolux-p079zca.c @@ -20,12 +20,32 @@ #include +struct panel_desc { + const struct drm_display_mode *modes; + unsigned int bpc; + struct { + unsigned int width; + unsigned int height; + } size; +}; + +struct panel_desc_dsi { + struct panel_desc desc; + + unsigned long flags; + enum mipi_dsi_pixel_format format; + unsigned int lanes; +}; + struct innolux_panel { struct drm_panel base; struct mipi_dsi_device *link; + const struct panel_desc_dsi *dsi_desc; struct backlight_device *backlight; - struct regulator *supply; + struct regulator *vddi; + struct regulator *avdd; + struct regulator *avee; struct gpio_desc *enable_gpio; bool prepared; @@ -78,9 +98,9 @@ static int innolux_panel_unprepare(struct drm_panel *panel) /* T8: 80ms - 1000ms */ msleep(80); - err = regulator_disable(innolux->supply); - if (err < 0) - return err; + regulator_disable(innolux->avee); + regulator_disable(innolux->avdd); + regulator_disable(innolux->vddi); innolux->prepared = false; @@ -97,10 +117,18 @@ static int innolux_panel_prepare(struct drm_panel *panel) gpiod_set_value_cansleep(innolux->enable_gpio, 0); - err = regulator_enable(innolux->supply); + err = regulator_enable(innolux->vddi); if (err < 0) return err; + err = regulator_enable(innolux->avdd); + if (err < 0) + goto disable_vddi; + + err = regulator_enable(innolux->avee); + if (err < 0) + goto disable_avdd; + /* T2: 15ms - 1000ms */ usleep_range(15000, 16000); @@ -134,12 +162,13 @@ static int innolux_panel_prepare(struct drm_panel *panel) return 0; poweroff: - regulator_err = regulator_disable(innolux->supply); - if (regulator_err) - DRM_DEV_ERROR(panel->dev, "failed to disable regulator: %d\n", - regulator_err); - gpiod_set_value_cansleep(innolux->enable_gpio, 0); + regulator_disable(innolux->avee); +disable_avdd: + regulator_disable(innolux->avdd); +disable_vddi: + regulator_disable(innolux->vddi); + return err; } @@ -164,7 +193,7 @@ static int innolux_panel_enable(struct drm_panel *panel) return 0; } -static const struct drm_display_mode default_mode = { +static const struct drm_display_mode innolux_p079zca_mode = { .clock = 56900, .hdisplay = 768, .hsync_start = 768 + 40, @@ -177,15 +206,59 @@ static const struct drm_display_mode default_mode = { .vrefresh = 60, }; +static const struct panel_desc_dsi innolux_p079zca_panel_desc = { + .desc = { + .modes = _p079zca_mode, + .bpc = 8, + .size = { + .width = 120, + .height = 160, + }, + }, + .flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE | +MIPI_DSI_MODE_LPM, + .format = MIPI_DSI_FMT_RGB888, + .lanes = 4, +}; + +static const struct drm_display_mode innolux_p097pfg_mode = { + .clock = 22, + .hdisplay = 1536, + .hsync_start = 1536 + 100, + .hsync_end = 1536 + 100 + 24, + .htotal = 1536 + 100 + 24 + 100, + .vdisplay = 2048, + .vsync_start = 2048 + 18, + .vsync_end = 2048 + 18 + 2, + .vtotal = 2048 + 18 + 2 + 18, + .vrefresh = 60, +}; + +static const struct panel_desc_dsi innolux_p097pfg_panel_desc = { + .desc = { + .modes = _p097pfg_mode, + .bpc = 8, + .size = { + .width = 147, + .height = 196, + }, + }, + .flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_SYNC_PULSE | +MIPI_DSI_MODE_LPM, + .format = MIPI_DSI_FMT_RGB888, + .lanes = 8, +}; + static int innolux_panel_get_modes(struct drm_panel *panel) { struct drm_display_mode *mode; + struct innolux_panel *innolux = to_innolux_panel(panel); + const struct drm_display_mode *m = innolux->dsi_de
[PATCH v10 5/5] drm/rockchip: Add dmc notifier in vop driver
when in ddr frequency scaling process, vop can not do enable or disable operation, since in dcf we check vop clock to see whether vop work. If vop work, dcf do ddr frequency scaling when vop in vblank status, and we need to read vop register to check whether vop go into vblank status. If vop not work, dcf can do ddr frequency any time. So when do ddr frequency scaling, you disabled or enable vop, there may two bad thing happen: 1, the panel flicker(when vop from disable status change to enable). 2, kernel hang (when vop from enable status change to disable, dcf need to read vblank status, but if you disable vop clock, it can not get the status, it will lead soc dead) So we need register to devfreq notifier, and we can get the dmc status. Also, when there have two vop enabled, we need to disable dmc, since dcf only base on one vop vblank time, so the other panel will flicker when do ddr frequency scaling. Signed-off-by: Lin Huang Reviewed-by: Chanwoo Choi --- Changes in v10: - None Changes in v9: - None Changes in v8: - None Changes in v7: - None Changes in v6: - fix a build error Changes in v5: - improve some nits Changes in v4: - register notifier to devfreq_register_notifier - use DEVFREQ_PRECHANGE and DEVFREQ_POSTCHANGE to get dmc status - when two vop enable, disable dmc - when two vop back to one vop, enable dmc Changes in v3: - when do vop eanble/disable, dmc will wait until it finish Changes in v2: - None Changes in v1: - use wait_event instead usleep drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 116 1 file changed, 116 insertions(+) diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c index efbc41a..a73f3aa 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c @@ -19,6 +19,8 @@ #include #include +#include +#include #include #include #include @@ -118,6 +120,13 @@ struct vop { const struct vop_data *data; + struct devfreq *devfreq; + struct devfreq_event_dev *devfreq_event_dev; + struct notifier_block dmc_nb; + int dmc_in_process; + int vop_switch_status; + wait_queue_head_t wait_dmc_queue; + wait_queue_head_t wait_vop_switch_queue; uint32_t *regsbak; void __iomem *regs; @@ -428,11 +437,47 @@ static void vop_dsp_hold_valid_irq_disable(struct vop *vop) spin_unlock_irqrestore(>irq_lock, flags); } +static int dmc_notify(struct notifier_block *nb, unsigned long event, + void *data) +{ + struct vop *vop = container_of(nb, struct vop, dmc_nb); + + if (event == DEVFREQ_PRECHANGE) { + /* +* check if vop in enable or disable process, +* if yes, wait until it finishes, use 200ms as +* timeout. +*/ + if (!wait_event_timeout(vop->wait_vop_switch_queue, + !vop->vop_switch_status, HZ / 5)) + dev_warn(vop->dev, +"Timeout waiting for vop swtich status\n"); + vop->dmc_in_process = 1; + } else if (event == DEVFREQ_POSTCHANGE) { + vop->dmc_in_process = 0; + wake_up(>wait_dmc_queue); + } + + return NOTIFY_OK; +} + static int vop_enable(struct drm_crtc *crtc) { struct vop *vop = to_vop(crtc); + int num_enabled_crtc = 0; int ret; + /* +* if in dmc scaling frequency process, wait until it finishes +* use 200ms as timeout time. +*/ + if (!wait_event_timeout(vop->wait_dmc_queue, + !vop->dmc_in_process, HZ / 5)) + dev_warn(vop->dev, +"Timeout waiting for dmc when vop enable\n"); + + vop->vop_switch_status = 1; + ret = pm_runtime_get_sync(vop->dev); if (ret < 0) { dev_err(vop->dev, "failed to get pm runtime: %d\n", ret); @@ -479,6 +524,21 @@ static int vop_enable(struct drm_crtc *crtc) drm_crtc_vblank_on(crtc); + vop->vop_switch_status = 0; + wake_up(>wait_vop_switch_queue); + + /* check how many VOPs in use now */ + drm_for_each_crtc(crtc, vop->drm_dev) { + if (crtc->state->enable) + num_enabled_crtc++; + } + + /* if enable two vop, need to disable dmc */ + if ((num_enabled_crtc > 1) && vop->devfreq) { + if (vop->devfreq_event_dev) + devfreq_event_disable_edev(vop->devfreq_event_dev); + devfreq_suspend_device(vop->devfreq); + } return 0; err_disable_aclk: @@ -489,17 +549,31 @@ err_disable_hclk: clk_disable(vop->hclk); err_put_pm_runtime: pm_runtime_put_sync(vop->dev
[PATCH v10 4/5] PM / devfreq: rockchip: add devfreq driver for rk3399 dmc
base on dfi result, we do ddr frequency scaling, register dmc driver to devfreq framework, and use simple-ondemand policy. Signed-off-by: Lin Huang Signed-off-by: MyngJoo Ham Reviewed-by: Chanwoo Choi --- Changes in v10: - None Changes in v9: - None Changes in v8: - None Changes in v8: - do not use ddr_timing node, get ddr timing directly Changes in v7: - remove a blank line Changes in v6: - fix some nit suggest by Chanwoo Choi Changes in v5: - improve dmc driver suggest by Chanwoo Choi Changes in v4: - use arm_smccc_smc() function talk to bl31 - delete rockchip_dmc.c file and config - delete dmc_notify - adjust probe order Changes in v3: - operate dram setting through sip call - imporve set rate flow Changes in v2: - None Changes in v1: - move dfi controller to event - fix set voltage sequence when set rate fail - change Kconfig type from tristate to bool - move unuse EXPORT_SYMBOL_GPL() drivers/devfreq/Kconfig | 11 + drivers/devfreq/Makefile | 1 + drivers/devfreq/rk3399_dmc.c | 480 +++ 3 files changed, 492 insertions(+) create mode 100644 drivers/devfreq/rk3399_dmc.c diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig index a5be56e..e848121 100644 --- a/drivers/devfreq/Kconfig +++ b/drivers/devfreq/Kconfig @@ -100,6 +100,17 @@ config ARM_TEGRA_DEVFREQ It reads ACTMON counters of memory controllers and adjusts the operating frequencies and voltages with OPP support. +config ARM_RK3399_DMC_DEVFREQ + tristate "ARM RK3399 DMC DEVFREQ Driver" + depends on ARCH_ROCKCHIP + select DEVFREQ_EVENT_ROCKCHIP_DFI + select DEVFREQ_GOV_SIMPLE_ONDEMAND + select PM_OPP + help + This adds the DEVFREQ driver for the RK3399 DMC(Dynamic Memory Controller). + It sets the frequency for the memory controller and reads the usage counts + from hardware. + source "drivers/devfreq/event/Kconfig" endif # PM_DEVFREQ diff --git a/drivers/devfreq/Makefile b/drivers/devfreq/Makefile index 09f11d9..fbff40a 100644 --- a/drivers/devfreq/Makefile +++ b/drivers/devfreq/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_DEVFREQ_GOV_PASSIVE) += governor_passive.o # DEVFREQ Drivers obj-$(CONFIG_ARM_EXYNOS_BUS_DEVFREQ) += exynos-bus.o +obj-$(CONFIG_ARM_RK3399_DMC_DEVFREQ) += rk3399_dmc.o obj-$(CONFIG_ARM_TEGRA_DEVFREQ)+= tegra-devfreq.o # DEVFREQ Event Drivers diff --git a/drivers/devfreq/rk3399_dmc.c b/drivers/devfreq/rk3399_dmc.c new file mode 100644 index 000..54d65f2 --- /dev/null +++ b/drivers/devfreq/rk3399_dmc.c @@ -0,0 +1,480 @@ +/* + * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd. + * Author: Lin Huang + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +struct dram_timing { + unsigned int ddr3_speed_bin; + unsigned int pd_idle; + unsigned int sr_idle; + unsigned int sr_mc_gate_idle; + unsigned int srpd_lite_idle; + unsigned int standby_idle; + unsigned int auto_pd_dis_freq; + unsigned int dram_dll_dis_freq; + unsigned int phy_dll_dis_freq; + unsigned int ddr3_odt_dis_freq; + unsigned int ddr3_drv; + unsigned int ddr3_odt; + unsigned int phy_ddr3_ca_drv; + unsigned int phy_ddr3_dq_drv; + unsigned int phy_ddr3_odt; + unsigned int lpddr3_odt_dis_freq; + unsigned int lpddr3_drv; + unsigned int lpddr3_odt; + unsigned int phy_lpddr3_ca_drv; + unsigned int phy_lpddr3_dq_drv; + unsigned int phy_lpddr3_odt; + unsigned int lpddr4_odt_dis_freq; + unsigned int lpddr4_drv; + unsigned int lpddr4_dq_odt; + unsigned int lpddr4_ca_odt; + unsigned int phy_lpddr4_ca_drv; + unsigned int phy_lpddr4_ck_cs_drv; + unsigned int phy_lpddr4_dq_drv; + unsigned int phy_lpddr4_odt; +}; + +struct rk3399_dmcfreq { + struct device *dev; + struct devfreq *devfreq; + struct devfreq_simple_ondemand_data ondemand_data; + struct clk *dmc_clk; + struct devfreq_event_dev *edev; + struct mutex lock; + struct dram_timing timing; + + /* +* DDR Converser of Frequency (DCF) is used to implement DDR frequency +* conversion without the participation of CPU, we will implement and +* control it in arm trust firmware. +*/ + wait_queue_head_t
[PATCH v10 3/5] Documentation: bindings: add dt documentation for rk3399 dmc
This patch adds the documentation for rockchip rk3399 dmc driver. Signed-off-by: Lin Huang Reviewed-by: Chanwoo Choi --- Changes in v10: - add rockchip prefix in property describe Changes in v9: - add ddr timing property to node Changes in v8: - add ddr timing properties Changes in v7: - None Changes in v6: -Add more detail in Documentation Changes in v5: -None Changes in v4: -None Changes in v3: -None Changes in v2: -None Changes in v1: -None .../devicetree/bindings/devfreq/rk3399_dmc.txt | 202 + 1 file changed, 202 insertions(+) create mode 100644 Documentation/devicetree/bindings/devfreq/rk3399_dmc.txt diff --git a/Documentation/devicetree/bindings/devfreq/rk3399_dmc.txt b/Documentation/devicetree/bindings/devfreq/rk3399_dmc.txt new file mode 100644 index 000..84660a3 --- /dev/null +++ b/Documentation/devicetree/bindings/devfreq/rk3399_dmc.txt @@ -0,0 +1,210 @@ +* Rockchip rk3399 DMC(Dynamic Memory Controller) device + +Required properties: +- compatible: Must be "rockchip,rk3399-dmc". +- devfreq-events: Node to get DDR loading, Refer to +Documentation/devicetree/bindings/devfreq/ +rockchip-dfi.txt +- interrupts: The interrupt number to the CPU. The interrupt +specifier format depends on the interrupt controller. +It should be DCF interrupts, when DDR dvfs finish, +it will happen. +- clocks: Phandles for clock specified in "clock-names" property +- clock-names : The name of clock used by the DFI, must be +"pclk_ddr_mon"; +- operating-points-v2: Refer to Documentation/devicetree/bindings/power/opp.txt +for details. +- center-supply:DMC supply node. +- status: Marks the node enabled/disabled. + +Following properties are ddr timing: + +- rockchip,dram_speed_bin : Value reference include/dt-bindings/clock/ddr.h, + it select ddr3 cl-trp-trcd type, default value + "DDR3_DEFAULT".it must selected according to + "Speed Bin" in ddr3 datasheet, DO NOT use + smaller "Speed Bin" than ddr3 exactly is. + +- rockchip,pd_idle : Config the PD_IDLE value, defined the power-down + idle period, memories are places into power-down + mode if bus is idle for PD_IDLE DFI clocks. + +- rockchip,sr_idle : Configure the SR_IDLE value, defined the + selfrefresh idle period, memories are places + into self-refresh mode if bus is idle for + SR_IDLE*1024 DFI clocks (DFI clocks freq is + half of dram's clocks), defaule value is "0". + +- rockchip,sr_mc_gate_idle : Defined the self-refresh with memory and + controller clock gating idle period, memories + are places into self-refresh mode and memory + controller clock arg gating if bus is idle for + sr_mc_gate_idle*1024 DFI clocks. + +- rockchip,srpd_lite_idle : Defined the self-refresh power down idle + period, memories are places into self-refresh + power down mode if bus is idle for + srpd_lite_idle*1024 DFI clocks. This parameter + is for LPDDR4 only. + +- rockchip,standby_idle :Defined the standby idle period, memories are + places into self-refresh than controller, pi, + phy and dram clock will gating if bus is idle + for standby_idle * DFI clocks. + +- rockchip,dram_dll_disb_freq : It's defined the DDR3 dll bypass frequency in + MHz, when ddr freq less than DRAM_DLL_DISB_FREQ, + ddr3 dll will bypssed note: if dll was bypassed, + the odt also stop working. + +- rockchip,phy_dll_disb_freq : Defined the PHY dll bypass frequency in + MHz (Mega Hz), when ddr freq less than + DRAM_DLL_DISB_FREQ, phy dll will bypssed. + note: phy dll and phy odt are independent. + +- rockchip,ddr3_odt_disb_freq : When dram type is DDR3, this parameter defined + the odt disable frequency in MHz (Mega Hz), + when ddr frequency less then ddr3_odt_disb_freq, +
[PATCH v10 2/5] PM / devfreq: event: support rockchip dfi controller
on rk3399 platform, there is dfi conroller can monitor ddr load, base on this result, we can do ddr freqency scaling. Signed-off-by: Lin Huang Signed-off-by: MyungJoo Ham Acked-by: Chanwoo Choi --- Changes in v10: -None Changes in v9: -None Changes in v8: -None Changes in v7: -access need to *4 to get right DDR loading Changes in v6: -None Changes in v5: -None Changes in v4: -None Changes in v3: -None Changes in v2: -None Changes in v1: -None drivers/devfreq/event/Kconfig| 7 + drivers/devfreq/event/Makefile | 1 + drivers/devfreq/event/rockchip-dfi.c | 256 +++ 3 files changed, 264 insertions(+) create mode 100644 drivers/devfreq/event/rockchip-dfi.c diff --git a/drivers/devfreq/event/Kconfig b/drivers/devfreq/event/Kconfig index eb6f74a..20d82c2 100644 --- a/drivers/devfreq/event/Kconfig +++ b/drivers/devfreq/event/Kconfig @@ -30,4 +30,11 @@ config DEVFREQ_EVENT_EXYNOS_PPMU (Platform Performance Monitoring Unit) counters to estimate the utilization of each module. +config DEVFREQ_EVENT_ROCKCHIP_DFI + tristate "ROCKCHIP DFI DEVFREQ event Driver" + depends on ARCH_ROCKCHIP + help + This add the devfreq-event driver for Rockchip SoC. It provides DFI + (DDR Monitor Module) driver to count ddr load. + endif # PM_DEVFREQ_EVENT diff --git a/drivers/devfreq/event/Makefile b/drivers/devfreq/event/Makefile index 3d6afd3..dda7090 100644 --- a/drivers/devfreq/event/Makefile +++ b/drivers/devfreq/event/Makefile @@ -2,3 +2,4 @@ obj-$(CONFIG_DEVFREQ_EVENT_EXYNOS_NOCP) += exynos-nocp.o obj-$(CONFIG_DEVFREQ_EVENT_EXYNOS_PPMU) += exynos-ppmu.o +obj-$(CONFIG_DEVFREQ_EVENT_ROCKCHIP_DFI) += rockchip-dfi.o diff --git a/drivers/devfreq/event/rockchip-dfi.c b/drivers/devfreq/event/rockchip-dfi.c new file mode 100644 index 000..43fcc5a --- /dev/null +++ b/drivers/devfreq/event/rockchip-dfi.c @@ -0,0 +1,256 @@ +/* + * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd + * Author: Lin Huang + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RK3399_DMC_NUM_CH 2 + +/* DDRMON_CTRL */ +#define DDRMON_CTRL0x04 +#define CLR_DDRMON_CTRL(0x1f << 0) +#define LPDDR4_EN (0x10001 << 4) +#define HARDWARE_EN(0x10001 << 3) +#define LPDDR3_EN (0x10001 << 2) +#define SOFTWARE_EN(0x10001 << 1) +#define SOFTWARE_DIS (0x1 << 1) +#define TIME_CNT_EN(0x10001 << 0) + +#define DDRMON_CH0_COUNT_NUM 0x28 +#define DDRMON_CH0_DFI_ACCESS_NUM 0x2c +#define DDRMON_CH1_COUNT_NUM 0x3c +#define DDRMON_CH1_DFI_ACCESS_NUM 0x40 + +/* pmu grf */ +#define PMUGRF_OS_REG2 0x308 +#define DDRTYPE_SHIFT 13 +#define DDRTYPE_MASK 7 + +enum { + DDR3 = 3, + LPDDR3 = 6, + LPDDR4 = 7, + UNUSED = 0xFF +}; + +struct dmc_usage { + u32 access; + u32 total; +}; + +/* + * The dfi controller can monitor DDR load. It has an upper and lower threshold + * for the operating points. Whenever the usage leaves these bounds an event is + * generated to indicate the DDR frequency should be changed. + */ +struct rockchip_dfi { + struct devfreq_event_dev *edev; + struct devfreq_event_desc *desc; + struct dmc_usage ch_usage[RK3399_DMC_NUM_CH]; + struct device *dev; + void __iomem *regs; + struct regmap *regmap_pmu; + struct clk *clk; +}; + +static void rockchip_dfi_start_hardware_counter(struct devfreq_event_dev *edev) +{ + struct rockchip_dfi *info = devfreq_event_get_drvdata(edev); + void __iomem *dfi_regs = info->regs; + u32 val; + u32 ddr_type; + + /* get ddr type */ + regmap_read(info->regmap_pmu, PMUGRF_OS_REG2, ); + ddr_type = (val >> DDRTYPE_SHIFT) & DDRTYPE_MASK; + + /* clear DDRMON_CTRL setting */ + writel_relaxed(CLR_DDRMON_CTRL, dfi_regs + DDRMON_CTRL); + + /* set ddr type to dfi */ + if (ddr_type == LPDDR3) + writel_relaxed(LPDDR3_EN, dfi_regs + DDRMON_CTRL); + else if (ddr_type == LPDDR4) + writel_relaxed(LPDDR4_EN, dfi_regs + DDRMON_CTRL); + + /* enable count, use software mode */ + writel_relaxed(SOFTWARE_EN, dfi_regs + DDRMON_CTRL); +} + +static void rockchip_dfi_stop_hardware_counter(struct devfreq_event_dev *edev) +{ + struct rockchip_dfi *in
[PATCH v10 1/5] Documentation: bindings: add dt documentation for dfi controller
This patch adds the documentation for rockchip dfi devfreq-event driver. Signed-off-by: Lin Huang Acked-by: Chanwoo Choi --- Changes in v10: - None Changes in v9: - reorder compatible and reg Changes in v8: - delete a unuse blank line Changes in v7: - None Changes in v6: - None Changes in v5: - None Changes in v4: - None Changes in v3: - None Changes in v2: - None Changes in v1: - None .../bindings/devfreq/event/rockchip-dfi.txt | 19 +++ 1 file changed, 19 insertions(+) create mode 100644 Documentation/devicetree/bindings/devfreq/event/rockchip-dfi.txt diff --git a/Documentation/devicetree/bindings/devfreq/event/rockchip-dfi.txt b/Documentation/devicetree/bindings/devfreq/event/rockchip-dfi.txt new file mode 100644 index 000..f223313 --- /dev/null +++ b/Documentation/devicetree/bindings/devfreq/event/rockchip-dfi.txt @@ -0,0 +1,19 @@ + +* Rockchip rk3399 DFI device + +Required properties: +- compatible: Must be "rockchip,rk3399-dfi". +- reg: physical base address of each DFI and length of memory mapped region +- rockchip,pmu: phandle to the syscon managing the "pmu general register files" +- clocks: phandles for clock specified in "clock-names" property +- clock-names : the name of clock used by the DFI, must be "pclk_ddr_mon"; + +Example: + dfi: dfi at 0xff63 { + compatible = "rockchip,rk3399-dfi"; + reg = <0x00 0xff63 0x00 0x4000>; + rockchip,pmu = <>; + clocks = < PCLK_DDR_MON>; + clock-names = "pclk_ddr_mon"; + status = "disabled"; + }; -- 2.6.6
[PATCH v10 0/5] rk3399 support ddr frequency scaling
rk3399 platform have dfi controller can monitor ddr load, and dcf controller to handle ddr register so we can get the right ddr frequency and make ddr controller happy work(which will implement in bl31). So we do ddr frequency scaling with following flow: kernelbl31 monitor ddr load | | get_target_rate | | pass rate to bl31 clk_set_rate(ddr) ->run dcf flow | | | | wait dcf interrupt<---trigger dcf interrupt | | return Lin Huang (5): Documentation: bindings: add dt documentation for dfi controller PM / devfreq: event: support rockchip dfi controller Documentation: bindings: add dt documentation for rk3399 dmc PM / devfreq: rockchip: add devfreq driver for rk3399 dmc drm/rockchip: Add dmc notifier in vop driver Following patch: clk: rockchip: add new clock-type for the ddrclk clk: rockchip: rk3399: add SCLK_DDRCLK ID for ddrc clk: rockchip: rk3399: add ddrc clock support have applied to: http://git.kernel.org/pub/scm/linux/kernel/git/mmind/linux-rockchip.git/v4.9-clk/next .../bindings/devfreq/event/rockchip-dfi.txt| 19 + .../devicetree/bindings/devfreq/rk3399_dmc.txt | 210 + drivers/devfreq/Kconfig| 11 + drivers/devfreq/Makefile | 1 + drivers/devfreq/event/Kconfig | 7 + drivers/devfreq/event/Makefile | 1 + drivers/devfreq/event/rockchip-dfi.c | 256 +++ drivers/devfreq/rk3399_dmc.c | 480 + drivers/gpu/drm/rockchip/rockchip_drm_vop.c| 116 + 9 files changed, 1101 insertions(+) create mode 100644 Documentation/devicetree/bindings/devfreq/event/rockchip-dfi.txt create mode 100644 Documentation/devicetree/bindings/devfreq/rk3399_dmc.txt create mode 100644 drivers/devfreq/event/rockchip-dfi.c create mode 100644 drivers/devfreq/rk3399_dmc.c -- 2.6.6
[PATCH v9 5/5] drm/rockchip: Add dmc notifier in vop driver
when in ddr frequency scaling process, vop can not do enable or disable operation, since in dcf we check vop clock to see whether vop work. If vop work, dcf do ddr frequency scaling when vop in vblank status, and we need to read vop register to check whether vop go into vblank status. If vop not work, dcf can do ddr frequency any time. So when do ddr frequency scaling, you disabled or enable vop, there may two bad thing happen: 1, the panel flicker(when vop from disable status change to enable). 2, kernel hang (when vop from enable status change to disable, dcf need to read vblank status, but if you disable vop clock, it can not get the status, it will lead soc dead) So we need register to devfreq notifier, and we can get the dmc status. Also, when there have two vop enabled, we need to disable dmc, since dcf only base on one vop vblank time, so the other panel will flicker when do ddr frequency scaling. Signed-off-by: Lin Huang Reviewed-by: Chanwoo Choi --- Changes in v9: - None Changes in v8: - None Changes in v7: - None Changes in v6: - fix a build error Changes in v5: - improve some nits Changes in v4: - register notifier to devfreq_register_notifier - use DEVFREQ_PRECHANGE and DEVFREQ_POSTCHANGE to get dmc status - when two vop enable, disable dmc - when two vop back to one vop, enable dmc Changes in v3: - when do vop eanble/disable, dmc will wait until it finish Changes in v2: - None Changes in v1: - use wait_event instead usleep drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 116 1 file changed, 116 insertions(+) diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c index efbc41a..a73f3aa 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c @@ -19,6 +19,8 @@ #include #include +#include +#include #include #include #include @@ -118,6 +120,13 @@ struct vop { const struct vop_data *data; + struct devfreq *devfreq; + struct devfreq_event_dev *devfreq_event_dev; + struct notifier_block dmc_nb; + int dmc_in_process; + int vop_switch_status; + wait_queue_head_t wait_dmc_queue; + wait_queue_head_t wait_vop_switch_queue; uint32_t *regsbak; void __iomem *regs; @@ -428,11 +437,47 @@ static void vop_dsp_hold_valid_irq_disable(struct vop *vop) spin_unlock_irqrestore(>irq_lock, flags); } +static int dmc_notify(struct notifier_block *nb, unsigned long event, + void *data) +{ + struct vop *vop = container_of(nb, struct vop, dmc_nb); + + if (event == DEVFREQ_PRECHANGE) { + /* +* check if vop in enable or disable process, +* if yes, wait until it finishes, use 200ms as +* timeout. +*/ + if (!wait_event_timeout(vop->wait_vop_switch_queue, + !vop->vop_switch_status, HZ / 5)) + dev_warn(vop->dev, +"Timeout waiting for vop swtich status\n"); + vop->dmc_in_process = 1; + } else if (event == DEVFREQ_POSTCHANGE) { + vop->dmc_in_process = 0; + wake_up(>wait_dmc_queue); + } + + return NOTIFY_OK; +} + static int vop_enable(struct drm_crtc *crtc) { struct vop *vop = to_vop(crtc); + int num_enabled_crtc = 0; int ret; + /* +* if in dmc scaling frequency process, wait until it finishes +* use 200ms as timeout time. +*/ + if (!wait_event_timeout(vop->wait_dmc_queue, + !vop->dmc_in_process, HZ / 5)) + dev_warn(vop->dev, +"Timeout waiting for dmc when vop enable\n"); + + vop->vop_switch_status = 1; + ret = pm_runtime_get_sync(vop->dev); if (ret < 0) { dev_err(vop->dev, "failed to get pm runtime: %d\n", ret); @@ -479,6 +524,21 @@ static int vop_enable(struct drm_crtc *crtc) drm_crtc_vblank_on(crtc); + vop->vop_switch_status = 0; + wake_up(>wait_vop_switch_queue); + + /* check how many VOPs in use now */ + drm_for_each_crtc(crtc, vop->drm_dev) { + if (crtc->state->enable) + num_enabled_crtc++; + } + + /* if enable two vop, need to disable dmc */ + if ((num_enabled_crtc > 1) && vop->devfreq) { + if (vop->devfreq_event_dev) + devfreq_event_disable_edev(vop->devfreq_event_dev); + devfreq_suspend_device(vop->devfreq); + } return 0; err_disable_aclk: @@ -489,17 +549,31 @@ err_disable_hclk: clk_disable(vop->hclk); err_put_pm_runtime: pm_runtime_put_sync(vop->dev); + vop->vop_s
[PATCH v9 4/5] PM / devfreq: rockchip: add devfreq driver for rk3399 dmc
base on dfi result, we do ddr frequency scaling, register dmc driver to devfreq framework, and use simple-ondemand policy. Signed-off-by: Lin Huang Signed-off-by: MyngJoo Ham Reviewed-by: Chanwoo Choi --- Changes in v8: - None Changes in v8: - do not use ddr_timing node, get ddr timing directly Changes in v7: - remove a blank line Changes in v6: - fix some nit suggest by Chanwoo Choi Changes in v5: - improve dmc driver suggest by Chanwoo Choi Changes in v4: - use arm_smccc_smc() function talk to bl31 - delete rockchip_dmc.c file and config - delete dmc_notify - adjust probe order Changes in v3: - operate dram setting through sip call - imporve set rate flow Changes in v2: - None Changes in v1: - move dfi controller to event - fix set voltage sequence when set rate fail - change Kconfig type from tristate to bool - move unuse EXPORT_SYMBOL_GPL() drivers/devfreq/Kconfig | 11 + drivers/devfreq/Makefile | 1 + drivers/devfreq/rk3399_dmc.c | 480 +++ 3 files changed, 492 insertions(+) create mode 100644 drivers/devfreq/rk3399_dmc.c diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig index a5be56e..e848121 100644 --- a/drivers/devfreq/Kconfig +++ b/drivers/devfreq/Kconfig @@ -100,6 +100,17 @@ config ARM_TEGRA_DEVFREQ It reads ACTMON counters of memory controllers and adjusts the operating frequencies and voltages with OPP support. +config ARM_RK3399_DMC_DEVFREQ + tristate "ARM RK3399 DMC DEVFREQ Driver" + depends on ARCH_ROCKCHIP + select DEVFREQ_EVENT_ROCKCHIP_DFI + select DEVFREQ_GOV_SIMPLE_ONDEMAND + select PM_OPP + help + This adds the DEVFREQ driver for the RK3399 DMC(Dynamic Memory Controller). + It sets the frequency for the memory controller and reads the usage counts + from hardware. + source "drivers/devfreq/event/Kconfig" endif # PM_DEVFREQ diff --git a/drivers/devfreq/Makefile b/drivers/devfreq/Makefile index 09f11d9..fbff40a 100644 --- a/drivers/devfreq/Makefile +++ b/drivers/devfreq/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_DEVFREQ_GOV_PASSIVE) += governor_passive.o # DEVFREQ Drivers obj-$(CONFIG_ARM_EXYNOS_BUS_DEVFREQ) += exynos-bus.o +obj-$(CONFIG_ARM_RK3399_DMC_DEVFREQ) += rk3399_dmc.o obj-$(CONFIG_ARM_TEGRA_DEVFREQ)+= tegra-devfreq.o # DEVFREQ Event Drivers diff --git a/drivers/devfreq/rk3399_dmc.c b/drivers/devfreq/rk3399_dmc.c new file mode 100644 index 000..54d65f2 --- /dev/null +++ b/drivers/devfreq/rk3399_dmc.c @@ -0,0 +1,480 @@ +/* + * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd. + * Author: Lin Huang + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +struct dram_timing { + unsigned int ddr3_speed_bin; + unsigned int pd_idle; + unsigned int sr_idle; + unsigned int sr_mc_gate_idle; + unsigned int srpd_lite_idle; + unsigned int standby_idle; + unsigned int auto_pd_dis_freq; + unsigned int dram_dll_dis_freq; + unsigned int phy_dll_dis_freq; + unsigned int ddr3_odt_dis_freq; + unsigned int ddr3_drv; + unsigned int ddr3_odt; + unsigned int phy_ddr3_ca_drv; + unsigned int phy_ddr3_dq_drv; + unsigned int phy_ddr3_odt; + unsigned int lpddr3_odt_dis_freq; + unsigned int lpddr3_drv; + unsigned int lpddr3_odt; + unsigned int phy_lpddr3_ca_drv; + unsigned int phy_lpddr3_dq_drv; + unsigned int phy_lpddr3_odt; + unsigned int lpddr4_odt_dis_freq; + unsigned int lpddr4_drv; + unsigned int lpddr4_dq_odt; + unsigned int lpddr4_ca_odt; + unsigned int phy_lpddr4_ca_drv; + unsigned int phy_lpddr4_ck_cs_drv; + unsigned int phy_lpddr4_dq_drv; + unsigned int phy_lpddr4_odt; +}; + +struct rk3399_dmcfreq { + struct device *dev; + struct devfreq *devfreq; + struct devfreq_simple_ondemand_data ondemand_data; + struct clk *dmc_clk; + struct devfreq_event_dev *edev; + struct mutex lock; + struct dram_timing timing; + + /* +* DDR Converser of Frequency (DCF) is used to implement DDR frequency +* conversion without the participation of CPU, we will implement and +* control it in arm trust firmware. +*/ + wait_queue_head_t wait_dcf_queue; + int irq; +
[PATCH v9 3/5] Documentation: bindings: add dt documentation for rk3399 dmc
This patch adds the documentation for rockchip rk3399 dmc driver. Signed-off-by: Lin Huang --- Changes in v9: - add ddr timing property to node Changes in v8: - add ddr timing properties Changes in v7: - None Changes in v6: -Add more detail in Documentation Changes in v5: -None Changes in v4: -None Changes in v3: -None Changes in v2: -None Changes in v1: -None .../devicetree/bindings/devfreq/rk3399_dmc.txt | 202 + 1 file changed, 202 insertions(+) create mode 100644 Documentation/devicetree/bindings/devfreq/rk3399_dmc.txt diff --git a/Documentation/devicetree/bindings/devfreq/rk3399_dmc.txt b/Documentation/devicetree/bindings/devfreq/rk3399_dmc.txt new file mode 100644 index 000..f187c8fa --- /dev/null +++ b/Documentation/devicetree/bindings/devfreq/rk3399_dmc.txt @@ -0,0 +1,202 @@ +* Rockchip rk3399 DMC(Dynamic Memory Controller) device + +Required properties: +- compatible: Must be "rockchip,rk3399-dmc". +- devfreq-events: Node to get DDR loading, Refer to +Documentation/devicetree/bindings/devfreq/ +rockchip-dfi.txt +- interrupts: The interrupt number to the CPU. The interrupt +specifier format depends on the interrupt controller. +It should be DCF interrupts, when DDR dvfs finish, +it will happen. +- clocks: Phandles for clock specified in "clock-names" property +- clock-names : The name of clock used by the DFI, must be +"pclk_ddr_mon"; +- operating-points-v2: Refer to Documentation/devicetree/bindings/power/opp.txt +for details. +- center-supply:DMC supply node. +- status: Marks the node enabled/disabled. + +Following properties are ddr timing: + +- dram_speed_bin : Value is defined at include/dt-bindings/clock/ddr.h, +it select ddr3 cl-trp-trcd type, default value +"DDR3_DEFAULT".it must selected according to +"Speed Bin" in ddr3 datasheet, DO NOT use smaller +"Speed Bin" than ddr3 exactly is. + +- pd_idle : Config the PD_IDLE value, defined the power-down idle +period, memories are places into power-down mode if +bus is idle for PD_IDLE DFI clocks. + +- sr_idle : Configure the SR_IDLE value, defined the selfrefresh +idle period, memories are places into self-refresh +mode if bus is idle for SR_IDLE*1024 DFI clocks +(DFI clocks freq is half of dram's clocks), defaule +value is "0". + +- sr_mc_gate_idle : Defined the self-refresh with memory and controller +clock gating idle period, memories are places into +self-refresh mode and memory controller clock arg +gating if bus is idle for sr_mc_gate_idle*1024 DFI +clocks. + +- srpd_lite_idle : Defined the self-refresh power down idle period, +memories are places into self-refresh power down +mode if bus is idle for srpd_lite_idle*1024 DFI +clocks. This parameter is for LPDDR4 only. + +- standby_idle :Defined the standby idle period, memories are places +into self-refresh than controller, pi, phy and dram +clock will gating if bus is idle for +standby_idle * DFI clocks. + +- dram_dll_disb_freq : It's defined the DDR3 dll bypass frequency in MHz +when ddr freq less than DRAM_DLL_DISB_FREQ, ddr3 +dll will bypssed note: if dll was bypassed, the +odt also stop working. + +- phy_dll_disb_freq : Defined the PHY dll bypass frequency in MHz (Mega Hz), +when ddr freq less than DRAM_DLL_DISB_FREQ, phy dll +will bypssed. note: phy dll and phy odt are +independent + +- ddr3_odt_disb_freq : When dram type is DDR3, this parameter defined the +odt disable frequency in MHz (Mega Hz), when ddr +frequency less then ddr3_odt_disb_freq, the odt +on dram side and controller side are both disabled. + +- ddr3_drv :When dram type is DDR3, this parameter define the +dram side driver stength in ohm, default value is +DDR3_DS_40ohm. + +- ddr3_odt :When dram type is DDR3, this parameter define the +dram side ODT stength in ohm, default value is +DDR3_ODT_120ohm. + +- phy_d
[PATCH v9 2/5] PM / devfreq: event: support rockchip dfi controller
on rk3399 platform, there is dfi conroller can monitor ddr load, base on this result, we can do ddr freqency scaling. Signed-off-by: Lin Huang Signed-off-by: MyungJoo Ham Acked-by: Chanwoo Choi --- Changes in v9: -None Changes in v8: -None Changes in v7: -access need to *4 to get right DDR loading Changes in v6: -None Changes in v5: -None Changes in v4: -None Changes in v3: -None Changes in v2: -None Changes in v1: -None drivers/devfreq/event/Kconfig| 7 + drivers/devfreq/event/Makefile | 1 + drivers/devfreq/event/rockchip-dfi.c | 256 +++ 3 files changed, 264 insertions(+) create mode 100644 drivers/devfreq/event/rockchip-dfi.c diff --git a/drivers/devfreq/event/Kconfig b/drivers/devfreq/event/Kconfig index eb6f74a..20d82c2 100644 --- a/drivers/devfreq/event/Kconfig +++ b/drivers/devfreq/event/Kconfig @@ -30,4 +30,11 @@ config DEVFREQ_EVENT_EXYNOS_PPMU (Platform Performance Monitoring Unit) counters to estimate the utilization of each module. +config DEVFREQ_EVENT_ROCKCHIP_DFI + tristate "ROCKCHIP DFI DEVFREQ event Driver" + depends on ARCH_ROCKCHIP + help + This add the devfreq-event driver for Rockchip SoC. It provides DFI + (DDR Monitor Module) driver to count ddr load. + endif # PM_DEVFREQ_EVENT diff --git a/drivers/devfreq/event/Makefile b/drivers/devfreq/event/Makefile index 3d6afd3..dda7090 100644 --- a/drivers/devfreq/event/Makefile +++ b/drivers/devfreq/event/Makefile @@ -2,3 +2,4 @@ obj-$(CONFIG_DEVFREQ_EVENT_EXYNOS_NOCP) += exynos-nocp.o obj-$(CONFIG_DEVFREQ_EVENT_EXYNOS_PPMU) += exynos-ppmu.o +obj-$(CONFIG_DEVFREQ_EVENT_ROCKCHIP_DFI) += rockchip-dfi.o diff --git a/drivers/devfreq/event/rockchip-dfi.c b/drivers/devfreq/event/rockchip-dfi.c new file mode 100644 index 000..43fcc5a --- /dev/null +++ b/drivers/devfreq/event/rockchip-dfi.c @@ -0,0 +1,256 @@ +/* + * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd + * Author: Lin Huang + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RK3399_DMC_NUM_CH 2 + +/* DDRMON_CTRL */ +#define DDRMON_CTRL0x04 +#define CLR_DDRMON_CTRL(0x1f << 0) +#define LPDDR4_EN (0x10001 << 4) +#define HARDWARE_EN(0x10001 << 3) +#define LPDDR3_EN (0x10001 << 2) +#define SOFTWARE_EN(0x10001 << 1) +#define SOFTWARE_DIS (0x1 << 1) +#define TIME_CNT_EN(0x10001 << 0) + +#define DDRMON_CH0_COUNT_NUM 0x28 +#define DDRMON_CH0_DFI_ACCESS_NUM 0x2c +#define DDRMON_CH1_COUNT_NUM 0x3c +#define DDRMON_CH1_DFI_ACCESS_NUM 0x40 + +/* pmu grf */ +#define PMUGRF_OS_REG2 0x308 +#define DDRTYPE_SHIFT 13 +#define DDRTYPE_MASK 7 + +enum { + DDR3 = 3, + LPDDR3 = 6, + LPDDR4 = 7, + UNUSED = 0xFF +}; + +struct dmc_usage { + u32 access; + u32 total; +}; + +/* + * The dfi controller can monitor DDR load. It has an upper and lower threshold + * for the operating points. Whenever the usage leaves these bounds an event is + * generated to indicate the DDR frequency should be changed. + */ +struct rockchip_dfi { + struct devfreq_event_dev *edev; + struct devfreq_event_desc *desc; + struct dmc_usage ch_usage[RK3399_DMC_NUM_CH]; + struct device *dev; + void __iomem *regs; + struct regmap *regmap_pmu; + struct clk *clk; +}; + +static void rockchip_dfi_start_hardware_counter(struct devfreq_event_dev *edev) +{ + struct rockchip_dfi *info = devfreq_event_get_drvdata(edev); + void __iomem *dfi_regs = info->regs; + u32 val; + u32 ddr_type; + + /* get ddr type */ + regmap_read(info->regmap_pmu, PMUGRF_OS_REG2, ); + ddr_type = (val >> DDRTYPE_SHIFT) & DDRTYPE_MASK; + + /* clear DDRMON_CTRL setting */ + writel_relaxed(CLR_DDRMON_CTRL, dfi_regs + DDRMON_CTRL); + + /* set ddr type to dfi */ + if (ddr_type == LPDDR3) + writel_relaxed(LPDDR3_EN, dfi_regs + DDRMON_CTRL); + else if (ddr_type == LPDDR4) + writel_relaxed(LPDDR4_EN, dfi_regs + DDRMON_CTRL); + + /* enable count, use software mode */ + writel_relaxed(SOFTWARE_EN, dfi_regs + DDRMON_CTRL); +} + +static void rockchip_dfi_stop_hardware_counter(struct devfreq_event_dev *edev) +{ + struct rockchip_dfi *info = devfreq_
[PATCH v9 1/5] Documentation: bindings: add dt documentation for dfi controller
This patch adds the documentation for rockchip dfi devfreq-event driver. Signed-off-by: Lin Huang --- Changes in v9: - reorder compatible and reg Changes in v8: - delete a unuse blank line Changes in v7: - None Changes in v6: - None Changes in v5: - None Changes in v4: - None Changes in v3: - None Changes in v2: - None Changes in v1: - None .../bindings/devfreq/event/rockchip-dfi.txt | 19 +++ 1 file changed, 19 insertions(+) create mode 100644 Documentation/devicetree/bindings/devfreq/event/rockchip-dfi.txt diff --git a/Documentation/devicetree/bindings/devfreq/event/rockchip-dfi.txt b/Documentation/devicetree/bindings/devfreq/event/rockchip-dfi.txt new file mode 100644 index 000..f223313 --- /dev/null +++ b/Documentation/devicetree/bindings/devfreq/event/rockchip-dfi.txt @@ -0,0 +1,19 @@ + +* Rockchip rk3399 DFI device + +Required properties: +- compatible: Must be "rockchip,rk3399-dfi". +- reg: physical base address of each DFI and length of memory mapped region +- rockchip,pmu: phandle to the syscon managing the "pmu general register files" +- clocks: phandles for clock specified in "clock-names" property +- clock-names : the name of clock used by the DFI, must be "pclk_ddr_mon"; + +Example: + dfi: dfi at 0xff63 { + compatible = "rockchip,rk3399-dfi"; + reg = <0x00 0xff63 0x00 0x4000>; + rockchip,pmu = <>; + clocks = < PCLK_DDR_MON>; + clock-names = "pclk_ddr_mon"; + status = "disabled"; + }; -- 2.6.6
[PATCH v9 0/5] rk3399 support ddr frequency scaling
rk3399 platform have dfi controller can monitor ddr load, and dcf controller to handle ddr register so we can get the right ddr frequency and make ddr controller happy work(which will implement in bl31). So we do ddr frequency scaling with following flow: kernelbl31 monitor ddr load | | get_target_rate | | pass rate to bl31 clk_set_rate(ddr) ->run dcf flow | | | | wait dcf interrupt<---trigger dcf interrupt | | return Lin Huang (5): Documentation: bindings: add dt documentation for dfi controller PM / devfreq: event: support rockchip dfi controller Documentation: bindings: add dt documentation for rk3399 dmc PM / devfreq: rockchip: add devfreq driver for rk3399 dmc drm/rockchip: Add dmc notifier in vop driver Following patch: clk: rockchip: add new clock-type for the ddrclk clk: rockchip: rk3399: add SCLK_DDRCLK ID for ddrc clk: rockchip: rk3399: add ddrc clock support have applied to: http://git.kernel.org/pub/scm/linux/kernel/git/mmind/linux-rockchip.git/v4.9-clk/next .../bindings/devfreq/event/rockchip-dfi.txt| 19 + .../devicetree/bindings/devfreq/rk3399_dmc.txt | 202 + drivers/devfreq/Kconfig| 11 + drivers/devfreq/Makefile | 1 + drivers/devfreq/event/Kconfig | 7 + drivers/devfreq/event/Makefile | 1 + drivers/devfreq/event/rockchip-dfi.c | 256 +++ drivers/devfreq/rk3399_dmc.c | 480 + drivers/gpu/drm/rockchip/rockchip_drm_vop.c| 116 + 9 files changed, 1093 insertions(+) create mode 100644 Documentation/devicetree/bindings/devfreq/event/rockchip-dfi.txt create mode 100644 Documentation/devicetree/bindings/devfreq/rk3399_dmc.txt create mode 100644 drivers/devfreq/event/rockchip-dfi.c create mode 100644 drivers/devfreq/rk3399_dmc.c -- 2.6.6
[PATCH 1/5] Documentation: bindings: add dt documentation for dfi controller
This patch adds the documentation for rockchip dfi devfreq-event driver. Signed-off-by: Lin Huang --- Changes in v8: - delete a unuse blank line Changes in v7: - None Changes in v6: - None Changes in v5: - None Changes in v4: - None Changes in v3: - None Changes in v2: - None Changes in v1: - None .../bindings/devfreq/event/rockchip-dfi.txt | 19 +++ 1 file changed, 19 insertions(+) create mode 100644 Documentation/devicetree/bindings/devfreq/event/rockchip-dfi.txt diff --git a/Documentation/devicetree/bindings/devfreq/event/rockchip-dfi.txt b/Documentation/devicetree/bindings/devfreq/event/rockchip-dfi.txt new file mode 100644 index 000..d66aca6 --- /dev/null +++ b/Documentation/devicetree/bindings/devfreq/event/rockchip-dfi.txt @@ -0,0 +1,19 @@ + +* Rockchip rk3399 DFI device + +Required properties: +- compatible: Must be "rockchip,rk3399-dfi". +- reg: physical base address of each DFI and length of memory mapped region +- rockchip,pmu: phandle to the syscon managing the "pmu general register files" +- clocks: phandles for clock specified in "clock-names" property +- clock-names : the name of clock used by the DFI, must be "pclk_ddr_mon"; + +Example: + dfi: dfi at 0xff63 { + reg = <0x00 0xff63 0x00 0x4000>; + compatible = "rockchip,rk3399-dfi"; + rockchip,pmu = <>; + clocks = < PCLK_DDR_MON>; + clock-names = "pclk_ddr_mon"; + status = "disabled"; + }; -- 2.6.6
[PATCH 0/5] rk3399 support ddr frequency scaling
rk3399 platform have dfi controller can monitor ddr load, and dcf controller to handle ddr register so we can get the right ddr frequency and make ddr controller happy work(which will implement in bl31). So we do ddr frequency scaling with following flow: kernelbl31 monitor ddr load | | get_target_rate | | pass rate to bl31 clk_set_rate(ddr) ->run dcf flow | | | | wait dcf interrupt<---trigger dcf interrupt | | return Lin Huang (5): Documentation: bindings: add dt documentation for dfi controller PM / devfreq: event: support rockchip dfi controller Documentation: bindings: add dt documentation for rk3399 dmc PM / devfreq: rockchip: add devfreq driver for rk3399 dmc drm/rockchip: Add dmc notifier in vop driver Following patch: clk: rockchip: add new clock-type for the ddrclk clk: rockchip: rk3399: add SCLK_DDRCLK ID for ddrc clk: rockchip: rk3399: add ddrc clock support have applied to: http://git.kernel.org/pub/scm/linux/kernel/git/mmind/linux-rockchip.git/v4.9-clk/next .../bindings/devfreq/event/rockchip-dfi.txt| 19 + .../devicetree/bindings/devfreq/rk3399_dmc.txt | 173 drivers/devfreq/Kconfig| 11 + drivers/devfreq/Makefile | 1 + drivers/devfreq/event/Kconfig | 7 + drivers/devfreq/event/Makefile | 1 + drivers/devfreq/event/rockchip-dfi.c | 256 +++ drivers/devfreq/rk3399_dmc.c | 480 + drivers/gpu/drm/rockchip/rockchip_drm_vop.c| 116 + 9 files changed, 1064 insertions(+) create mode 100644 Documentation/devicetree/bindings/devfreq/event/rockchip-dfi.txt create mode 100644 Documentation/devicetree/bindings/devfreq/rk3399_dmc.txt create mode 100644 drivers/devfreq/event/rockchip-dfi.c create mode 100644 drivers/devfreq/rk3399_dmc.c -- 2.6.6
[PATCH 5/5] drm/rockchip: Add dmc notifier in vop driver
when in ddr frequency scaling process, vop can not do enable or disable operation, since in dcf we check vop clock to see whether vop work. If vop work, dcf do ddr frequency scaling when vop in vblank status, and we need to read vop register to check whether vop go into vblank status. If vop not work, dcf can do ddr frequency any time. So when do ddr frequency scaling, you disabled or enable vop, there may two bad thing happen: 1, the panel flicker(when vop from disable status change to enable). 2, kernel hang (when vop from enable status change to disable, dcf need to read vblank status, but if you disable vop clock, it can not get the status, it will lead soc dead) So we need register to devfreq notifier, and we can get the dmc status. Also, when there have two vop enabled, we need to disable dmc, since dcf only base on one vop vblank time, so the other panel will flicker when do ddr frequency scaling. Signed-off-by: Lin Huang Reviewed-by: Chanwoo Choi --- Changes in v8: - None Changes in v7: - None Changes in v6: - fix a build error Changes in v5: - improve some nits Changes in v4: - register notifier to devfreq_register_notifier - use DEVFREQ_PRECHANGE and DEVFREQ_POSTCHANGE to get dmc status - when two vop enable, disable dmc - when two vop back to one vop, enable dmc Changes in v3: - when do vop eanble/disable, dmc will wait until it finish Changes in v2: - None Changes in v1: - use wait_event instead usleep drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 116 1 file changed, 116 insertions(+) diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c index efbc41a..a73f3aa 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c @@ -19,6 +19,8 @@ #include #include +#include +#include #include #include #include @@ -118,6 +120,13 @@ struct vop { const struct vop_data *data; + struct devfreq *devfreq; + struct devfreq_event_dev *devfreq_event_dev; + struct notifier_block dmc_nb; + int dmc_in_process; + int vop_switch_status; + wait_queue_head_t wait_dmc_queue; + wait_queue_head_t wait_vop_switch_queue; uint32_t *regsbak; void __iomem *regs; @@ -428,11 +437,47 @@ static void vop_dsp_hold_valid_irq_disable(struct vop *vop) spin_unlock_irqrestore(>irq_lock, flags); } +static int dmc_notify(struct notifier_block *nb, unsigned long event, + void *data) +{ + struct vop *vop = container_of(nb, struct vop, dmc_nb); + + if (event == DEVFREQ_PRECHANGE) { + /* +* check if vop in enable or disable process, +* if yes, wait until it finishes, use 200ms as +* timeout. +*/ + if (!wait_event_timeout(vop->wait_vop_switch_queue, + !vop->vop_switch_status, HZ / 5)) + dev_warn(vop->dev, +"Timeout waiting for vop swtich status\n"); + vop->dmc_in_process = 1; + } else if (event == DEVFREQ_POSTCHANGE) { + vop->dmc_in_process = 0; + wake_up(>wait_dmc_queue); + } + + return NOTIFY_OK; +} + static int vop_enable(struct drm_crtc *crtc) { struct vop *vop = to_vop(crtc); + int num_enabled_crtc = 0; int ret; + /* +* if in dmc scaling frequency process, wait until it finishes +* use 200ms as timeout time. +*/ + if (!wait_event_timeout(vop->wait_dmc_queue, + !vop->dmc_in_process, HZ / 5)) + dev_warn(vop->dev, +"Timeout waiting for dmc when vop enable\n"); + + vop->vop_switch_status = 1; + ret = pm_runtime_get_sync(vop->dev); if (ret < 0) { dev_err(vop->dev, "failed to get pm runtime: %d\n", ret); @@ -479,6 +524,21 @@ static int vop_enable(struct drm_crtc *crtc) drm_crtc_vblank_on(crtc); + vop->vop_switch_status = 0; + wake_up(>wait_vop_switch_queue); + + /* check how many VOPs in use now */ + drm_for_each_crtc(crtc, vop->drm_dev) { + if (crtc->state->enable) + num_enabled_crtc++; + } + + /* if enable two vop, need to disable dmc */ + if ((num_enabled_crtc > 1) && vop->devfreq) { + if (vop->devfreq_event_dev) + devfreq_event_disable_edev(vop->devfreq_event_dev); + devfreq_suspend_device(vop->devfreq); + } return 0; err_disable_aclk: @@ -489,17 +549,31 @@ err_disable_hclk: clk_disable(vop->hclk); err_put_pm_runtime: pm_runtime_put_sync(vop->dev); + vop->vop_switch_status =
[PATCH 4/5] PM / devfreq: rockchip: add devfreq driver for rk3399 dmc
base on dfi result, we do ddr frequency scaling, register dmc driver to devfreq framework, and use simple-ondemand policy. Signed-off-by: Lin Huang Signed-off-by: MyngJoo Ham Reviewed-by: Chanwoo Choi --- Changes in v8: - do not use ddr_timing node, get ddr timing directly Changes in v7: - remove a blank line Changes in v6: - fix some nit suggest by Chanwoo Choi Changes in v5: - improve dmc driver suggest by Chanwoo Choi Changes in v4: - use arm_smccc_smc() function talk to bl31 - delete rockchip_dmc.c file and config - delete dmc_notify - adjust probe order Changes in v3: - operate dram setting through sip call - imporve set rate flow Changes in v2: - None Changes in v1: - move dfi controller to event - fix set voltage sequence when set rate fail - change Kconfig type from tristate to bool - move unuse EXPORT_SYMBOL_GPL() drivers/devfreq/Kconfig | 11 + drivers/devfreq/Makefile | 1 + drivers/devfreq/rk3399_dmc.c | 480 +++ 3 files changed, 492 insertions(+) create mode 100644 drivers/devfreq/rk3399_dmc.c diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig index a5be56e..e848121 100644 --- a/drivers/devfreq/Kconfig +++ b/drivers/devfreq/Kconfig @@ -100,6 +100,17 @@ config ARM_TEGRA_DEVFREQ It reads ACTMON counters of memory controllers and adjusts the operating frequencies and voltages with OPP support. +config ARM_RK3399_DMC_DEVFREQ + tristate "ARM RK3399 DMC DEVFREQ Driver" + depends on ARCH_ROCKCHIP + select DEVFREQ_EVENT_ROCKCHIP_DFI + select DEVFREQ_GOV_SIMPLE_ONDEMAND + select PM_OPP + help + This adds the DEVFREQ driver for the RK3399 DMC(Dynamic Memory Controller). + It sets the frequency for the memory controller and reads the usage counts + from hardware. + source "drivers/devfreq/event/Kconfig" endif # PM_DEVFREQ diff --git a/drivers/devfreq/Makefile b/drivers/devfreq/Makefile index 09f11d9..fbff40a 100644 --- a/drivers/devfreq/Makefile +++ b/drivers/devfreq/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_DEVFREQ_GOV_PASSIVE) += governor_passive.o # DEVFREQ Drivers obj-$(CONFIG_ARM_EXYNOS_BUS_DEVFREQ) += exynos-bus.o +obj-$(CONFIG_ARM_RK3399_DMC_DEVFREQ) += rk3399_dmc.o obj-$(CONFIG_ARM_TEGRA_DEVFREQ)+= tegra-devfreq.o # DEVFREQ Event Drivers diff --git a/drivers/devfreq/rk3399_dmc.c b/drivers/devfreq/rk3399_dmc.c new file mode 100644 index 000..54d65f2 --- /dev/null +++ b/drivers/devfreq/rk3399_dmc.c @@ -0,0 +1,480 @@ +/* + * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd. + * Author: Lin Huang + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +struct dram_timing { + unsigned int ddr3_speed_bin; + unsigned int pd_idle; + unsigned int sr_idle; + unsigned int sr_mc_gate_idle; + unsigned int srpd_lite_idle; + unsigned int standby_idle; + unsigned int auto_pd_dis_freq; + unsigned int dram_dll_dis_freq; + unsigned int phy_dll_dis_freq; + unsigned int ddr3_odt_dis_freq; + unsigned int ddr3_drv; + unsigned int ddr3_odt; + unsigned int phy_ddr3_ca_drv; + unsigned int phy_ddr3_dq_drv; + unsigned int phy_ddr3_odt; + unsigned int lpddr3_odt_dis_freq; + unsigned int lpddr3_drv; + unsigned int lpddr3_odt; + unsigned int phy_lpddr3_ca_drv; + unsigned int phy_lpddr3_dq_drv; + unsigned int phy_lpddr3_odt; + unsigned int lpddr4_odt_dis_freq; + unsigned int lpddr4_drv; + unsigned int lpddr4_dq_odt; + unsigned int lpddr4_ca_odt; + unsigned int phy_lpddr4_ca_drv; + unsigned int phy_lpddr4_ck_cs_drv; + unsigned int phy_lpddr4_dq_drv; + unsigned int phy_lpddr4_odt; +}; + +struct rk3399_dmcfreq { + struct device *dev; + struct devfreq *devfreq; + struct devfreq_simple_ondemand_data ondemand_data; + struct clk *dmc_clk; + struct devfreq_event_dev *edev; + struct mutex lock; + struct dram_timing timing; + + /* +* DDR Converser of Frequency (DCF) is used to implement DDR frequency +* conversion without the participation of CPU, we will implement and +* control it in arm trust firmware. +*/ + wait_queue_head_t wait_dcf_queue; + int irq; + int wait_dcf_flag; +
[PATCH 3/5] Documentation: bindings: add dt documentation for rk3399 dmc
This patch adds the documentation for rockchip rk3399 dmc driver. Signed-off-by: Lin Huang --- Changes in v8: - add ddr timing properties Changes in v7: -None Changes in v6: -Add more detail in Documentation Changes in v5: -None Changes in v4: -None Changes in v3: -None Changes in v2: -None Changes in v1: -None .../devicetree/bindings/devfreq/rk3399_dmc.txt | 173 + 1 file changed, 173 insertions(+) create mode 100644 Documentation/devicetree/bindings/devfreq/rk3399_dmc.txt diff --git a/Documentation/devicetree/bindings/devfreq/rk3399_dmc.txt b/Documentation/devicetree/bindings/devfreq/rk3399_dmc.txt new file mode 100644 index 000..1f39b5cb --- /dev/null +++ b/Documentation/devicetree/bindings/devfreq/rk3399_dmc.txt @@ -0,0 +1,173 @@ +* Rockchip rk3399 DMC(Dynamic Memory Controller) device + +Required properties: +- compatible: Must be "rockchip,rk3399-dmc". +- devfreq-events: Node to get DDR loading, Refer to +Documentation/devicetree/bindings/devfreq/ +rockchip-dfi.txt +- interrupts: The interrupt number to the CPU. The interrupt +specifier format depends on the interrupt controller. +It should be DCF interrupts, when DDR dvfs finish, +it will happen. +- clocks: Phandles for clock specified in "clock-names" property +- clock-names : The name of clock used by the DFI, must be +"pclk_ddr_mon"; +- operating-points-v2: Refer to Documentation/devicetree/bindings/power/opp.txt +for details. +- center-supply:DMC supply node. +- status: Marks the node enabled/disabled. + +Following properties are ddr timing: + +- dram_speed_bin : Value is defined at include/dt-bindings/clock/ddr.h, +it select ddr3 cl-trp-trcd type, default value +"DDR3_DEFAULT".it must selected according to +"Speed Bin" in ddr3 datasheet, DO NOT use smaller +"Speed Bin" than ddr3 exactly is. + +- pd_idle : Config the PD_IDLE value, defined the power-down idle +period, memories are places into power-down mode if +bus is idle for PD_IDLE DFI clocks. + +- sr_idle : Configure the SR_IDLE value, defined the selfrefresh +idle period, memories are places into self-refresh +mode if bus is idle for SR_IDLE*1024 DFI clocks +(DFI clocks freq is half of dram's clocks), defaule +value is "0". + +- sr_mc_gate_idle : Defined the self-refresh with memory and controller +clock gating idle period, memories are places into +self-refresh mode and memory controller clock arg +gating if bus is idle for sr_mc_gate_idle*1024 DFI +clocks. + +- srpd_lite_idle : Defined the self-refresh power down idle period, +memories are places into self-refresh power down +mode if bus is idle for srpd_lite_idle*1024 DFI +clocks. This parameter is for LPDDR4 only. + +- standby_idle :Defined the standby idle period, memories are places +into self-refresh than controller, pi, phy and dram +clock will gating if bus is idle for +standby_idle * DFI clocks. + +- dram_dll_disb_freq : It's defined the DDR3 dll bypass frequency in MHz +when ddr freq less than DRAM_DLL_DISB_FREQ, ddr3 +dll will bypssed note: if dll was bypassed, the +odt also stop working. + +- phy_dll_disb_freq : Defined the PHY dll bypass frequency in MHz (Mega Hz), +when ddr freq less than DRAM_DLL_DISB_FREQ, phy dll +will bypssed. note: phy dll and phy odt are +independent + +- ddr3_odt_disb_freq : When dram type is DDR3, this parameter defined the +odt disable frequency in MHz (Mega Hz), when ddr +frequency less then ddr3_odt_disb_freq, the odt +on dram side and controller side are both disabled. + +- ddr3_drv :When dram type is DDR3, this parameter define the +dram side driver stength in ohm, default value is +DDR3_DS_40ohm. + +- ddr3_odt :When dram type is DDR3, this parameter define the +dram side ODT stength in ohm, default value is +DDR3_ODT_120ohm. + +- phy_ddr3_ca_drv : When dram type is DDR3, this parameter
[PATCH 2/5] PM / devfreq: event: support rockchip dfi controller
on rk3399 platform, there is dfi conroller can monitor ddr load, base on this result, we can do ddr freqency scaling. Signed-off-by: Lin Huang Signed-off-by: MyungJoo Ham Acked-by: Chanwoo Choi --- Changes in v8: -None Changes in v7: -access need to *4 to get right DDR loading Changes in v6: -None Changes in v5: -None Changes in v4: -None Changes in v3: -None Changes in v2: -None Changes in v1: -None drivers/devfreq/event/Kconfig| 7 + drivers/devfreq/event/Makefile | 1 + drivers/devfreq/event/rockchip-dfi.c | 256 +++ 3 files changed, 264 insertions(+) create mode 100644 drivers/devfreq/event/rockchip-dfi.c diff --git a/drivers/devfreq/event/Kconfig b/drivers/devfreq/event/Kconfig index eb6f74a..20d82c2 100644 --- a/drivers/devfreq/event/Kconfig +++ b/drivers/devfreq/event/Kconfig @@ -30,4 +30,11 @@ config DEVFREQ_EVENT_EXYNOS_PPMU (Platform Performance Monitoring Unit) counters to estimate the utilization of each module. +config DEVFREQ_EVENT_ROCKCHIP_DFI + tristate "ROCKCHIP DFI DEVFREQ event Driver" + depends on ARCH_ROCKCHIP + help + This add the devfreq-event driver for Rockchip SoC. It provides DFI + (DDR Monitor Module) driver to count ddr load. + endif # PM_DEVFREQ_EVENT diff --git a/drivers/devfreq/event/Makefile b/drivers/devfreq/event/Makefile index 3d6afd3..dda7090 100644 --- a/drivers/devfreq/event/Makefile +++ b/drivers/devfreq/event/Makefile @@ -2,3 +2,4 @@ obj-$(CONFIG_DEVFREQ_EVENT_EXYNOS_NOCP) += exynos-nocp.o obj-$(CONFIG_DEVFREQ_EVENT_EXYNOS_PPMU) += exynos-ppmu.o +obj-$(CONFIG_DEVFREQ_EVENT_ROCKCHIP_DFI) += rockchip-dfi.o diff --git a/drivers/devfreq/event/rockchip-dfi.c b/drivers/devfreq/event/rockchip-dfi.c new file mode 100644 index 000..43fcc5a --- /dev/null +++ b/drivers/devfreq/event/rockchip-dfi.c @@ -0,0 +1,256 @@ +/* + * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd + * Author: Lin Huang + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RK3399_DMC_NUM_CH 2 + +/* DDRMON_CTRL */ +#define DDRMON_CTRL0x04 +#define CLR_DDRMON_CTRL(0x1f << 0) +#define LPDDR4_EN (0x10001 << 4) +#define HARDWARE_EN(0x10001 << 3) +#define LPDDR3_EN (0x10001 << 2) +#define SOFTWARE_EN(0x10001 << 1) +#define SOFTWARE_DIS (0x1 << 1) +#define TIME_CNT_EN(0x10001 << 0) + +#define DDRMON_CH0_COUNT_NUM 0x28 +#define DDRMON_CH0_DFI_ACCESS_NUM 0x2c +#define DDRMON_CH1_COUNT_NUM 0x3c +#define DDRMON_CH1_DFI_ACCESS_NUM 0x40 + +/* pmu grf */ +#define PMUGRF_OS_REG2 0x308 +#define DDRTYPE_SHIFT 13 +#define DDRTYPE_MASK 7 + +enum { + DDR3 = 3, + LPDDR3 = 6, + LPDDR4 = 7, + UNUSED = 0xFF +}; + +struct dmc_usage { + u32 access; + u32 total; +}; + +/* + * The dfi controller can monitor DDR load. It has an upper and lower threshold + * for the operating points. Whenever the usage leaves these bounds an event is + * generated to indicate the DDR frequency should be changed. + */ +struct rockchip_dfi { + struct devfreq_event_dev *edev; + struct devfreq_event_desc *desc; + struct dmc_usage ch_usage[RK3399_DMC_NUM_CH]; + struct device *dev; + void __iomem *regs; + struct regmap *regmap_pmu; + struct clk *clk; +}; + +static void rockchip_dfi_start_hardware_counter(struct devfreq_event_dev *edev) +{ + struct rockchip_dfi *info = devfreq_event_get_drvdata(edev); + void __iomem *dfi_regs = info->regs; + u32 val; + u32 ddr_type; + + /* get ddr type */ + regmap_read(info->regmap_pmu, PMUGRF_OS_REG2, ); + ddr_type = (val >> DDRTYPE_SHIFT) & DDRTYPE_MASK; + + /* clear DDRMON_CTRL setting */ + writel_relaxed(CLR_DDRMON_CTRL, dfi_regs + DDRMON_CTRL); + + /* set ddr type to dfi */ + if (ddr_type == LPDDR3) + writel_relaxed(LPDDR3_EN, dfi_regs + DDRMON_CTRL); + else if (ddr_type == LPDDR4) + writel_relaxed(LPDDR4_EN, dfi_regs + DDRMON_CTRL); + + /* enable count, use software mode */ + writel_relaxed(SOFTWARE_EN, dfi_regs + DDRMON_CTRL); +} + +static void rockchip_dfi_stop_hardware_counter(struct devfreq_event_dev *edev) +{ + struct rockchip_dfi *info = devfreq_event_get_drvdata(edev); +
[PATCH 1/5] Documentation: bindings: add dt documentation for dfi controller
This patch adds the documentation for rockchip dfi devfreq-event driver. Signed-off-by: Lin Huang --- Changes in v8: - delete a unuse blank line Changes in v7: - None Changes in v6: - None Changes in v5: - None Changes in v4: - None Changes in v3: - None Changes in v2: - None Changes in v1: - None .../bindings/devfreq/event/rockchip-dfi.txt | 19 +++ 1 file changed, 19 insertions(+) create mode 100644 Documentation/devicetree/bindings/devfreq/event/rockchip-dfi.txt diff --git a/Documentation/devicetree/bindings/devfreq/event/rockchip-dfi.txt b/Documentation/devicetree/bindings/devfreq/event/rockchip-dfi.txt new file mode 100644 index 000..d66aca6 --- /dev/null +++ b/Documentation/devicetree/bindings/devfreq/event/rockchip-dfi.txt @@ -0,0 +1,19 @@ + +* Rockchip rk3399 DFI device + +Required properties: +- compatible: Must be "rockchip,rk3399-dfi". +- reg: physical base address of each DFI and length of memory mapped region +- rockchip,pmu: phandle to the syscon managing the "pmu general register files" +- clocks: phandles for clock specified in "clock-names" property +- clock-names : the name of clock used by the DFI, must be "pclk_ddr_mon"; + +Example: + dfi: dfi at 0xff63 { + reg = <0x00 0xff63 0x00 0x4000>; + compatible = "rockchip,rk3399-dfi"; + rockchip,pmu = <>; + clocks = < PCLK_DDR_MON>; + clock-names = "pclk_ddr_mon"; + status = "disabled"; + }; -- 2.6.6
[PATCH 0/5] rk3399 support ddr frequency scaling
rk3399 platform have dfi controller can monitor ddr load, and dcf controller to handle ddr register so we can get the right ddr frequency and make ddr controller happy work(which will implement in bl31). So we do ddr frequency scaling with following flow: kernelbl31 monitor ddr load | | get_target_rate | | pass rate to bl31 clk_set_rate(ddr) ->run dcf flow | | | | wait dcf interrupt<---trigger dcf interrupt | | return Lin Huang (5): Documentation: bindings: add dt documentation for dfi controller PM / devfreq: event: support rockchip dfi controller Documentation: bindings: add dt documentation for rk3399 dmc PM / devfreq: rockchip: add devfreq driver for rk3399 dmc drm/rockchip: Add dmc notifier in vop driver Following patch: clk: rockchip: add new clock-type for the ddrclk clk: rockchip: rk3399: add SCLK_DDRCLK ID for ddrc clk: rockchip: rk3399: add ddrc clock support have applied to: http://git.kernel.org/pub/scm/linux/kernel/git/mmind/linux-rockchip.git/v4.9-clk/next .../bindings/devfreq/event/rockchip-dfi.txt| 19 + .../devicetree/bindings/devfreq/rk3399_dmc.txt | 173 drivers/devfreq/Kconfig| 11 + drivers/devfreq/Makefile | 1 + drivers/devfreq/event/Kconfig | 7 + drivers/devfreq/event/Makefile | 1 + drivers/devfreq/event/rockchip-dfi.c | 256 +++ drivers/devfreq/rk3399_dmc.c | 480 + drivers/gpu/drm/rockchip/rockchip_drm_vop.c| 116 + 9 files changed, 1064 insertions(+) create mode 100644 Documentation/devicetree/bindings/devfreq/event/rockchip-dfi.txt create mode 100644 Documentation/devicetree/bindings/devfreq/rk3399_dmc.txt create mode 100644 drivers/devfreq/event/rockchip-dfi.c create mode 100644 drivers/devfreq/rk3399_dmc.c -- 2.6.6
[PATCH v7 8/8] drm/rockchip: Add dmc notifier in vop driver
when in ddr frequency scaling process, vop can not do enable or disable operation, since in dcf we check vop clock to see whether vop work. If vop work, dcf do ddr frequency scaling when vop in vblank status, and we need to read vop register to check whether vop go into vblank status. If vop not work, dcf can do ddr frequency any time. So when do ddr frequency scaling, you disabled or enable vop, there may two bad thing happen: 1, the panel flicker(when vop from disable status change to enable). 2, kernel hang (when vop from enable status change to disable, dcf need to read vblank status, but if you disable vop clock, it can not get the status, it will lead soc dead) So we need register to devfreq notifier, and we can get the dmc status. Also, when there have two vop enabled, we need to disable dmc, since dcf only base on one vop vblank time, so the other panel will flicker when do ddr frequency scaling. Signed-off-by: Lin Huang Reviewed-by: Chanwoo Choi --- Changes in v7: - None Changes in v6: - fix a build error Changes in v5: - improve some nits Changes in v4: - register notifier to devfreq_register_notifier - use DEVFREQ_PRECHANGE and DEVFREQ_POSTCHANGE to get dmc status - when two vop enable, disable dmc - when two vop back to one vop, enable dmc Changes in v3: - when do vop eanble/disable, dmc will wait until it finish Changes in v2: - None Changes in v1: - use wait_event instead usleep drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 127 ++-- 1 file changed, 122 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c index ec8ad00..a76e70c 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c @@ -12,6 +12,8 @@ * GNU General Public License for more details. */ +#include +#include #include #include #include @@ -118,6 +120,13 @@ struct vop { const struct vop_data *data; + struct devfreq *devfreq; + struct devfreq_event_dev *devfreq_event_dev; + struct notifier_block dmc_nb; + int dmc_in_process; + int vop_switch_status; + wait_queue_head_t wait_dmc_queue; + wait_queue_head_t wait_vop_switch_queue; uint32_t *regsbak; void __iomem *regs; @@ -428,21 +437,59 @@ static void vop_dsp_hold_valid_irq_disable(struct vop *vop) spin_unlock_irqrestore(>irq_lock, flags); } +static int dmc_notify(struct notifier_block *nb, unsigned long event, + void *data) +{ + struct vop *vop = container_of(nb, struct vop, dmc_nb); + + if (event == DEVFREQ_PRECHANGE) { + /* +* check if vop in enable or disable process, +* if yes, wait until it finishes, use 200ms as +* timeout. +*/ + if (!wait_event_timeout(vop->wait_vop_switch_queue, + !vop->vop_switch_status, HZ / 5)) + dev_warn(vop->dev, +"Timeout waiting for vop swtich status\n"); + vop->dmc_in_process = 1; + } else if (event == DEVFREQ_POSTCHANGE) { + vop->dmc_in_process = 0; + wake_up(>wait_dmc_queue); + } + + return NOTIFY_OK; +} + static void vop_enable(struct drm_crtc *crtc) { struct vop *vop = to_vop(crtc); + int num_enabled_crtc = 0; int ret; + if (vop->is_enabled) + return; + + /* +* if in dmc scaling frequency process, wait until it finishes +* use 100ms as timeout time. +*/ + if (!wait_event_timeout(vop->wait_dmc_queue, + !vop->dmc_in_process, HZ / 5)) + dev_warn(vop->dev, +"Timeout waiting for dmc when vop enable\n"); + + vop->vop_switch_status = 1; ret = pm_runtime_get_sync(vop->dev); if (ret < 0) { dev_err(vop->dev, "failed to get pm runtime: %d\n", ret); - return; + goto err; } ret = clk_enable(vop->hclk); if (ret < 0) { dev_err(vop->dev, "failed to enable hclk - %d\n", ret); - return; + goto err; } ret = clk_enable(vop->dclk); @@ -456,7 +503,6 @@ static void vop_enable(struct drm_crtc *crtc) dev_err(vop->dev, "failed to enable aclk - %d\n", ret); goto err_disable_dclk; } - /* * Slave iommu shares power, irq and clock with vop. It was associated * automatically with this master device via common driver code. @@ -485,6 +531,21 @@ static void vop_enable(struct drm_crtc *crtc) drm_crtc_vblank_on(crtc); + vop->vop_switch_status = 0; + wake_up(&g
[PATCH v7 7/8] PM / devfreq: rockchip: add devfreq driver for rk3399 dmc
base on dfi result, we do ddr frequency scaling, register dmc driver to devfreq framework, and use simple-ondemand policy. Signed-off-by: Lin Huang Reviewed-by: Chanwoo Choi --- Changes in v7: - remove a blank line Changes in v6: - fix some nit suggest by Chanwoo Choi Changes in v5: - improve dmc driver suggest by Chanwoo Choi Changes in v4: - use arm_smccc_smc() function talk to bl31 - delete rockchip_dmc.c file and config - delete dmc_notify - adjust probe order Changes in v3: - operate dram setting through sip call - imporve set rate flow Changes in v2: - None Changes in v1: - move dfi controller to event - fix set voltage sequence when set rate fail - change Kconfig type from tristate to bool - move unuse EXPORT_SYMBOL_GPL() drivers/devfreq/Kconfig | 11 + drivers/devfreq/Makefile | 1 + drivers/devfreq/rk3399_dmc.c | 499 +++ 3 files changed, 511 insertions(+) create mode 100644 drivers/devfreq/rk3399_dmc.c diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig index a5be56e..e848121 100644 --- a/drivers/devfreq/Kconfig +++ b/drivers/devfreq/Kconfig @@ -100,6 +100,17 @@ config ARM_TEGRA_DEVFREQ It reads ACTMON counters of memory controllers and adjusts the operating frequencies and voltages with OPP support. +config ARM_RK3399_DMC_DEVFREQ + tristate "ARM RK3399 DMC DEVFREQ Driver" + depends on ARCH_ROCKCHIP + select DEVFREQ_EVENT_ROCKCHIP_DFI + select DEVFREQ_GOV_SIMPLE_ONDEMAND + select PM_OPP + help + This adds the DEVFREQ driver for the RK3399 DMC(Dynamic Memory Controller). + It sets the frequency for the memory controller and reads the usage counts + from hardware. + source "drivers/devfreq/event/Kconfig" endif # PM_DEVFREQ diff --git a/drivers/devfreq/Makefile b/drivers/devfreq/Makefile index 09f11d9..fbff40a 100644 --- a/drivers/devfreq/Makefile +++ b/drivers/devfreq/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_DEVFREQ_GOV_PASSIVE) += governor_passive.o # DEVFREQ Drivers obj-$(CONFIG_ARM_EXYNOS_BUS_DEVFREQ) += exynos-bus.o +obj-$(CONFIG_ARM_RK3399_DMC_DEVFREQ) += rk3399_dmc.o obj-$(CONFIG_ARM_TEGRA_DEVFREQ)+= tegra-devfreq.o # DEVFREQ Event Drivers diff --git a/drivers/devfreq/rk3399_dmc.c b/drivers/devfreq/rk3399_dmc.c new file mode 100644 index 000..b73a73c --- /dev/null +++ b/drivers/devfreq/rk3399_dmc.c @@ -0,0 +1,499 @@ +/* + * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd. + * Author: Lin Huang + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +struct dram_timing { + unsigned int ddr3_speed_bin; + unsigned int pd_idle; + unsigned int sr_idle; + unsigned int sr_mc_gate_idle; + unsigned int srpd_lite_idle; + unsigned int standby_idle; + unsigned int dram_dll_dis_freq; + unsigned int phy_dll_dis_freq; + unsigned int ddr3_odt_dis_freq; + unsigned int ddr3_drv; + unsigned int ddr3_odt; + unsigned int phy_ddr3_ca_drv; + unsigned int phy_ddr3_dq_drv; + unsigned int phy_ddr3_odt; + unsigned int lpddr3_odt_dis_freq; + unsigned int lpddr3_drv; + unsigned int lpddr3_odt; + unsigned int phy_lpddr3_ca_drv; + unsigned int phy_lpddr3_dq_drv; + unsigned int phy_lpddr3_odt; + unsigned int lpddr4_odt_dis_freq; + unsigned int lpddr4_drv; + unsigned int lpddr4_dq_odt; + unsigned int lpddr4_ca_odt; + unsigned int phy_lpddr4_ca_drv; + unsigned int phy_lpddr4_ck_cs_drv; + unsigned int phy_lpddr4_dq_drv; + unsigned int phy_lpddr4_odt; +}; + +struct rk3399_dmcfreq { + struct device *dev; + struct devfreq *devfreq; + struct devfreq_simple_ondemand_data ondemand_data; + struct clk *dmc_clk; + struct devfreq_event_dev *edev; + struct mutex lock; + struct dram_timing *timing; + + /* +* DDR Converser of Frequency (DCF) is used to implement DDR frequency +* conversion without the participation of CPU, we will implement and +* control it in arm trust firmware. +*/ + wait_queue_head_t wait_dcf_queue; + int irq; + int wait_dcf_flag; + struct regulator *vdd_center; + unsigned long rate, target_rate; + unsigned long volt, target_volt; + struct
[PATCH v7 6/8] Documentation: bindings: add dt documentation for rk3399 dmc
This patch adds the documentation for rockchip rk3399 dmc driver. Signed-off-by: Lin Huang --- Changes in v7: -None Changes in v6: -Add more detail in Documentation Changes in v5: -None Changes in v4: -None Changes in v3: -None Changes in v2: -None Changes in v1: -None .../devicetree/bindings/devfreq/rk3399_dmc.txt | 85 ++ 1 file changed, 85 insertions(+) create mode 100644 Documentation/devicetree/bindings/devfreq/rk3399_dmc.txt diff --git a/Documentation/devicetree/bindings/devfreq/rk3399_dmc.txt b/Documentation/devicetree/bindings/devfreq/rk3399_dmc.txt new file mode 100644 index 000..b787abb --- /dev/null +++ b/Documentation/devicetree/bindings/devfreq/rk3399_dmc.txt @@ -0,0 +1,85 @@ +* Rockchip rk3399 DMC(Dynamic Memory Controller) device + +Required properties: +- compatible: Must be "rockchip,rk3399-dmc". +- devfreq-events: Node to get DDR loading, Refer to + Documentation/devicetree/bindings/devfreq/rockchip-dfi.txt +- interrupts: The interrupt number to the CPU. The interrupt specifier format + depends on the interrupt controller. It should be DCF interrupts, + when DDR dvfs finish, it will happen. +- clocks: Phandles for clock specified in "clock-names" property +- clock-names : The name of clock used by the DFI, must be "pclk_ddr_mon"; +- operating-points-v2: Refer to Documentation/devicetree/bindings/power/opp.txt + for details. +- center-supply: DMC supply node. +- status: Marks the node enabled/disabled. + +Optional properties: +- ddr_timing: DDR timing need to pass to arm trust firmware +- upthreshold: The upthreshold to simpleondeamnd policy +- downdifferential: The downdifferential to simpleondeamnd policy + +Example: + + ddr_timing: ddr_timing { + compatible = "rockchip,ddr-timing"; + ddr3_speed_bin = <21>; + pd_idle = <0>; + sr_idle = <0>; + sr_mc_gate_idle = <0>; + srpd_lite_idle = <0>; + standby_idle = <0>; + dram_dll_dis_freq = <300>; + phy_dll_dis_freq = <125>; + + ddr3_odt_dis_freq = <333>; + ddr3_drv = ; + ddr3_odt = ; + phy_ddr3_ca_drv = ; + phy_ddr3_dq_drv = ; + phy_ddr3_odt = ; + + lpddr3_odt_dis_freq = <333>; + lpddr3_drv = ; + lpddr3_odt = ; + phy_lpddr3_ca_drv = ; + phy_lpddr3_dq_drv = ; + phy_lpddr3_odt = ; + + lpddr4_odt_dis_freq = <333>; + lpddr4_drv = ; + lpddr4_dq_odt = ; + lpddr4_ca_odt = ; + phy_lpddr4_ca_drv = ; + phy_lpddr4_ck_cs_drv = ; + phy_lpddr4_dq_drv = ; + phy_lpddr4_odt = ; + }; + + dmc_opp_table: dmc_opp_table { + compatible = "operating-points-v2"; + + opp00 { + opp-hz = /bits/ 64 <3>; + opp-microvolt = <90>; + }; + opp01 { + opp-hz = /bits/ 64 <66600>; + opp-microvolt = <90>; + }; + }; + + dmc: dmc { + compatible = "rockchip,rk3399-dmc"; + devfreq-events = <>; + interrupts = ; + clocks = < SCLK_DDRCLK>; + clock-names = "dmc_clk"; + ddr_timing = <_timing>; + operating-points-v2 = <_opp_table>; + center-supply = <_centerlogic>; + upthreshold = <15>; + downdifferential = <10>; + status = "disabled"; + }; + -- 2.6.6
[PATCH v7 5/8] PM / devfreq: event: support rockchip dfi controller
on rk3399 platform, there is dfi conroller can monitor ddr load, base on this result, we can do ddr freqency scaling. Signed-off-by: Lin Huang Acked-by: Chanwoo Choi --- Changes in v7: -access need to *4 to get right DDR loading Changes in v6: -None Changes in v5: -None Changes in v4: -None Changes in v3: -None Changes in v2: -None Changes in v1: -None drivers/devfreq/event/Kconfig| 7 + drivers/devfreq/event/Makefile | 1 + drivers/devfreq/event/rockchip-dfi.c | 256 +++ 3 files changed, 264 insertions(+) create mode 100644 drivers/devfreq/event/rockchip-dfi.c diff --git a/drivers/devfreq/event/Kconfig b/drivers/devfreq/event/Kconfig index eb6f74a..20d82c2 100644 --- a/drivers/devfreq/event/Kconfig +++ b/drivers/devfreq/event/Kconfig @@ -30,4 +30,11 @@ config DEVFREQ_EVENT_EXYNOS_PPMU (Platform Performance Monitoring Unit) counters to estimate the utilization of each module. +config DEVFREQ_EVENT_ROCKCHIP_DFI + tristate "ROCKCHIP DFI DEVFREQ event Driver" + depends on ARCH_ROCKCHIP + help + This add the devfreq-event driver for Rockchip SoC. It provides DFI + (DDR Monitor Module) driver to count ddr load. + endif # PM_DEVFREQ_EVENT diff --git a/drivers/devfreq/event/Makefile b/drivers/devfreq/event/Makefile index 3d6afd3..dda7090 100644 --- a/drivers/devfreq/event/Makefile +++ b/drivers/devfreq/event/Makefile @@ -2,3 +2,4 @@ obj-$(CONFIG_DEVFREQ_EVENT_EXYNOS_NOCP) += exynos-nocp.o obj-$(CONFIG_DEVFREQ_EVENT_EXYNOS_PPMU) += exynos-ppmu.o +obj-$(CONFIG_DEVFREQ_EVENT_ROCKCHIP_DFI) += rockchip-dfi.o diff --git a/drivers/devfreq/event/rockchip-dfi.c b/drivers/devfreq/event/rockchip-dfi.c new file mode 100644 index 000..43fcc5a --- /dev/null +++ b/drivers/devfreq/event/rockchip-dfi.c @@ -0,0 +1,256 @@ +/* + * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd + * Author: Lin Huang + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RK3399_DMC_NUM_CH 2 + +/* DDRMON_CTRL */ +#define DDRMON_CTRL0x04 +#define CLR_DDRMON_CTRL(0x1f << 0) +#define LPDDR4_EN (0x10001 << 4) +#define HARDWARE_EN(0x10001 << 3) +#define LPDDR3_EN (0x10001 << 2) +#define SOFTWARE_EN(0x10001 << 1) +#define SOFTWARE_DIS (0x1 << 1) +#define TIME_CNT_EN(0x10001 << 0) + +#define DDRMON_CH0_COUNT_NUM 0x28 +#define DDRMON_CH0_DFI_ACCESS_NUM 0x2c +#define DDRMON_CH1_COUNT_NUM 0x3c +#define DDRMON_CH1_DFI_ACCESS_NUM 0x40 + +/* pmu grf */ +#define PMUGRF_OS_REG2 0x308 +#define DDRTYPE_SHIFT 13 +#define DDRTYPE_MASK 7 + +enum { + DDR3 = 3, + LPDDR3 = 6, + LPDDR4 = 7, + UNUSED = 0xFF +}; + +struct dmc_usage { + u32 access; + u32 total; +}; + +/* + * The dfi controller can monitor DDR load. It has an upper and lower threshold + * for the operating points. Whenever the usage leaves these bounds an event is + * generated to indicate the DDR frequency should be changed. + */ +struct rockchip_dfi { + struct devfreq_event_dev *edev; + struct devfreq_event_desc *desc; + struct dmc_usage ch_usage[RK3399_DMC_NUM_CH]; + struct device *dev; + void __iomem *regs; + struct regmap *regmap_pmu; + struct clk *clk; +}; + +static void rockchip_dfi_start_hardware_counter(struct devfreq_event_dev *edev) +{ + struct rockchip_dfi *info = devfreq_event_get_drvdata(edev); + void __iomem *dfi_regs = info->regs; + u32 val; + u32 ddr_type; + + /* get ddr type */ + regmap_read(info->regmap_pmu, PMUGRF_OS_REG2, ); + ddr_type = (val >> DDRTYPE_SHIFT) & DDRTYPE_MASK; + + /* clear DDRMON_CTRL setting */ + writel_relaxed(CLR_DDRMON_CTRL, dfi_regs + DDRMON_CTRL); + + /* set ddr type to dfi */ + if (ddr_type == LPDDR3) + writel_relaxed(LPDDR3_EN, dfi_regs + DDRMON_CTRL); + else if (ddr_type == LPDDR4) + writel_relaxed(LPDDR4_EN, dfi_regs + DDRMON_CTRL); + + /* enable count, use software mode */ + writel_relaxed(SOFTWARE_EN, dfi_regs + DDRMON_CTRL); +} + +static void rockchip_dfi_stop_hardware_counter(struct devfreq_event_dev *edev) +{ + struct rockchip_dfi *info = devfreq_event_get_drvdata(edev); + void __iomem *dfi_regs = info->regs; + +
[PATCH v7 4/8] Documentation: bindings: add dt documentation for dfi controller
This patch adds the documentation for rockchip dfi devfreq-event driver. Signed-off-by: Lin Huang --- Changes in v7: -None Changes in v6: -None Changes in v5: -None Changes in v4: -None Changes in v3: -None Changes in v2: -None Changes in v1: -None .../bindings/devfreq/event/rockchip-dfi.txt | 20 1 file changed, 20 insertions(+) create mode 100644 Documentation/devicetree/bindings/devfreq/event/rockchip-dfi.txt diff --git a/Documentation/devicetree/bindings/devfreq/event/rockchip-dfi.txt b/Documentation/devicetree/bindings/devfreq/event/rockchip-dfi.txt new file mode 100644 index 000..bf42255 --- /dev/null +++ b/Documentation/devicetree/bindings/devfreq/event/rockchip-dfi.txt @@ -0,0 +1,20 @@ + +* Rockchip rk3399 DFI device + +Required properties: +- compatible: Must be "rockchip,rk3399-dfi". +- reg: physical base address of each DFI and length of memory mapped region +- rockchip,pmu: phandle to the syscon managing the "pmu general register files" +- clocks: phandles for clock specified in "clock-names" property +- clock-names : the name of clock used by the DFI, must be "pclk_ddr_mon"; + +Example: + dfi: dfi at 0xff63 { + reg = <0x00 0xff63 0x00 0x4000>; + compatible = "rockchip,rk3399-dfi"; + rockchip,pmu = <>; + clocks = < PCLK_DDR_MON>; + clock-names = "pclk_ddr_mon"; + status = "disabled"; + }; + -- 2.6.6
[PATCH v7 3/8] clk: rockchip: rk3399: add ddrc clock support
add ddrc clock setting, so we can do ddr frequency scaling on rk3399 platform in future. Signed-off-by: Lin Huang --- Changes in v7: - change SCLK_DDRC name from clk_ddrc to sclk_ddrc Changes in v6: - None Changes in v5: - fit for the ddr type Changes in v4: - None Changes in v3: - None Changes in v2: - remove clk_ddrc_dpll_src from critical clock list Changes in v1: - remove ddrc source CLK_IGNORE_UNUSED flag - move clk_ddrc and clk_ddrc_dpll_src to critical drivers/clk/rockchip/clk-rk3399.c | 19 +++ 1 file changed, 19 insertions(+) diff --git a/drivers/clk/rockchip/clk-rk3399.c b/drivers/clk/rockchip/clk-rk3399.c index e445cd6..134bd18 100644 --- a/drivers/clk/rockchip/clk-rk3399.c +++ b/drivers/clk/rockchip/clk-rk3399.c @@ -120,6 +120,10 @@ PNAME(mux_armclkb_p) = { "clk_core_b_lpll_src", "clk_core_b_bpll_src", "clk_core_b_dpll_src", "clk_core_b_gpll_src" }; +PNAME(mux_ddrclk_p)= { "clk_ddrc_lpll_src", + "clk_ddrc_bpll_src", + "clk_ddrc_dpll_src", + "clk_ddrc_gpll_src" }; PNAME(mux_aclk_cci_p) = { "cpll_aclk_cci_src", "gpll_aclk_cci_src", "npll_aclk_cci_src", @@ -1379,6 +1383,18 @@ static struct rockchip_clk_branch rk3399_clk_branches[] __initdata = { COMPOSITE_NOMUX(0, "clk_test", "clk_test_pre", CLK_IGNORE_UNUSED, RK3368_CLKSEL_CON(58), 0, 5, DFLAGS, RK3368_CLKGATE_CON(13), 11, GFLAGS), + + /* ddrc */ + GATE(0, "clk_ddrc_lpll_src", "lpll", 0, RK3399_CLKGATE_CON(3), +0, GFLAGS), + GATE(0, "clk_ddrc_bpll_src", "bpll", 0, RK3399_CLKGATE_CON(3), +1, GFLAGS), + GATE(0, "clk_ddrc_dpll_src", "dpll", 0, RK3399_CLKGATE_CON(3), +2, GFLAGS), + GATE(0, "clk_ddrc_gpll_src", "gpll", 0, RK3399_CLKGATE_CON(3), +3, GFLAGS), + COMPOSITE_DDRCLK(SCLK_DDRC, "sclk_ddrc", mux_ddrclk_p, 0, + RK3399_CLKSEL_CON(6), 4, 2, 0, 0, ROCKCHIP_DDRCLK_SIP), }; static struct rockchip_clk_branch rk3399_clk_pmu_branches[] __initdata = { @@ -1493,6 +1509,9 @@ static const char *const rk3399_cru_critical_clocks[] __initconst = { "gpll_aclk_perilp0_src", "gpll_aclk_perihp_src", "aclk_vio_noc", + + /* ddrc */ + "sclk_ddrc" }; static const char *const rk3399_pmucru_critical_clocks[] __initconst = { -- 2.6.6
[PATCH v7 2/8] clk: rockchip: rk3399: add SCLK_DDRCLK ID for ddrc
Signed-off-by: Lin Huang --- Changes in v7: -None Changes in v6: -None Changes in v5: -None Changes in v4: -None Changes in v3: -None Changes in v2: -None Changes in v1: -None include/dt-bindings/clock/rk3399-cru.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/dt-bindings/clock/rk3399-cru.h b/include/dt-bindings/clock/rk3399-cru.h index 50a44cf..ce5f3e9 100644 --- a/include/dt-bindings/clock/rk3399-cru.h +++ b/include/dt-bindings/clock/rk3399-cru.h @@ -131,6 +131,7 @@ #define SCLK_DPHY_RX0_CFG 165 #define SCLK_RMII_SRC 166 #define SCLK_PCIEPHY_REF100M 167 +#define SCLK_DDRC 168 #define DCLK_VOP0 180 #define DCLK_VOP1 181 -- 2.6.6
[PATCH v7 1/8] clk: rockchip: add new clock-type for the ddrclk
On new rockchip platform(rk3399 etc), there have dcf controller to do ddr frequency scaling, and this controller will implement in arm-trust-firmware. We add a special clock-type to handle that. Signed-off-by: Lin Huang --- Changes in v7: - add rockchip_ddrclk_sip_ops so we can distinguish other ddr clock operate - add ROCKCHIP_SIP_CONFIG_* in rockchip_sip.h give constants a specific name Changes in v6: - none Changes in v5: - delete unuse mux_flag - use div_flag to distinguish sip call and other operate Changes in v4: - use arm_smccc_smc() to set/read ddr rate Changes in v3: - use sip call to set/read ddr rate Changes in v2: - use GENMASK instead val_mask - use divider_recalc_rate() instead DIV_ROUND_UP_ULL - cleanup code Changes in v1: - none drivers/clk/rockchip/Makefile | 1 + drivers/clk/rockchip/clk-ddr.c | 157 drivers/clk/rockchip/clk.c | 9 +++ drivers/clk/rockchip/clk.h | 35 include/soc/rockchip/rockchip_sip.h | 27 +++ 5 files changed, 229 insertions(+) create mode 100644 drivers/clk/rockchip/clk-ddr.c create mode 100644 include/soc/rockchip/rockchip_sip.h diff --git a/drivers/clk/rockchip/Makefile b/drivers/clk/rockchip/Makefile index f47a2fa..b5f2c8e 100644 --- a/drivers/clk/rockchip/Makefile +++ b/drivers/clk/rockchip/Makefile @@ -8,6 +8,7 @@ obj-y += clk-pll.o obj-y += clk-cpu.o obj-y += clk-inverter.o obj-y += clk-mmc-phase.o +obj-y += clk-ddr.o obj-$(CONFIG_RESET_CONTROLLER) += softrst.o obj-y += clk-rk3036.o diff --git a/drivers/clk/rockchip/clk-ddr.c b/drivers/clk/rockchip/clk-ddr.c new file mode 100644 index 000..224e07e --- /dev/null +++ b/drivers/clk/rockchip/clk-ddr.c @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2016 Rockchip Electronics Co. Ltd. + * Author: Lin Huang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "clk.h" + +struct rockchip_ddrclk { + struct clk_hw hw; + void __iomem*reg_base; + int mux_offset; + int mux_shift; + int mux_width; + int div_shift; + int div_width; + int ddr_flag; + spinlock_t *lock; +}; + +#define to_rockchip_ddrclk_hw(hw) container_of(hw, struct rockchip_ddrclk, hw) + +static int rockchip_ddrclk_sip_set_rate(struct clk_hw *hw, unsigned long drate, + unsigned long prate) +{ + struct rockchip_ddrclk *ddrclk = to_rockchip_ddrclk_hw(hw); + unsigned long flags; + struct arm_smccc_res res; + + spin_lock_irqsave(ddrclk->lock, flags); + arm_smccc_smc(ROCKCHIP_SIP_DRAM_FREQ, drate, 0, + ROCKCHIP_SIP_CONFIG_DRAM_SET_RATE, + 0, 0, 0, 0, ); + spin_unlock_irqrestore(ddrclk->lock, flags); + + return res.a0; +} + +static unsigned long +rockchip_ddrclk_sip_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct arm_smccc_res res; + + arm_smccc_smc(ROCKCHIP_SIP_DRAM_FREQ, 0, 0, + ROCKCHIP_SIP_CONFIG_DRAM_GET_RATE, + 0, 0, 0, 0, ); + + return res.a0; +} + +static long rockchip_ddrclk_sip_round_rate(struct clk_hw *hw, + unsigned long rate, + unsigned long *prate) +{ + struct arm_smccc_res res; + + arm_smccc_smc(ROCKCHIP_SIP_DRAM_FREQ, rate, 0, + ROCKCHIP_SIP_CONFIG_DRAM_ROUND_RATE, + 0, 0, 0, 0, ); + + return res.a0; +} + +static u8 rockchip_ddrclk_get_parent(struct clk_hw *hw) +{ + struct rockchip_ddrclk *ddrclk = to_rockchip_ddrclk_hw(hw); + int num_parents = clk_hw_get_num_parents(hw); + u32 val; + + val = clk_readl(ddrclk->reg_base + + ddrclk->mux_offset) >> ddrclk->mux_shift; + val &= GENMASK(ddrclk->mux_width - 1, 0); + + if (val >= num_parents) + return -EINVAL; + + return val; +} + +static const struct clk_ops rockchip_ddrclk_sip_ops = { + .recalc_rate = rockchip_ddrclk_sip_recalc_rate, + .set_rate = rockchip_ddrclk_sip_set_rate, + .round_rate = rockchip_ddrclk_sip_round_rate, + .get_parent = rockchip_ddrclk_get_parent, +}; + +struct clk *rockchip_clk_re
[PATCH v7 0/8] rk3399 support ddr frequency scaling
rk3399 platform have dfi controller can monitor ddr load, and dcf controller to handle ddr register so we can get the right ddr frequency and make ddr controller happy work(which will implement in bl31). So we do ddr frequency scaling with following flow: kernelbl31 monitor ddr load | | get_target_rate | | pass rate to bl31 clk_set_rate(ddr) ->run dcf flow | | | | wait dcf interrupt<---trigger dcf interrupt | | return Lin Huang (8): clk: rockchip: add new clock-type for the ddrclk clk: rockchip: rk3399: add SCLK_DDRCLK ID for ddrc clk: rockchip: rk3399: add ddrc clock support Documentation: bindings: add dt documentation for dfi controller PM / devfreq: event: support rockchip dfi controller Documentation: bindings: add dt documentation for rk3399 dmc PM / devfreq: rockchip: add devfreq driver for rk3399 dmc drm/rockchip: Add dmc notifier in vop driver .../bindings/devfreq/event/rockchip-dfi.txt| 20 + .../devicetree/bindings/devfreq/rk3399_dmc.txt | 85 drivers/clk/rockchip/Makefile | 1 + drivers/clk/rockchip/clk-ddr.c | 157 +++ drivers/clk/rockchip/clk-rk3399.c | 19 + drivers/clk/rockchip/clk.c | 9 + drivers/clk/rockchip/clk.h | 35 ++ drivers/devfreq/Kconfig| 11 + drivers/devfreq/Makefile | 1 + drivers/devfreq/event/Kconfig | 7 + drivers/devfreq/event/Makefile | 1 + drivers/devfreq/event/rockchip-dfi.c | 256 +++ drivers/devfreq/rk3399_dmc.c | 499 + drivers/gpu/drm/rockchip/rockchip_drm_vop.c| 127 +- include/dt-bindings/clock/rk3399-cru.h | 1 + include/soc/rockchip/rockchip_sip.h| 27 ++ 16 files changed, 1251 insertions(+), 5 deletions(-) create mode 100644 Documentation/devicetree/bindings/devfreq/event/rockchip-dfi.txt create mode 100644 Documentation/devicetree/bindings/devfreq/rk3399_dmc.txt create mode 100644 drivers/clk/rockchip/clk-ddr.c create mode 100644 drivers/devfreq/event/rockchip-dfi.c create mode 100644 drivers/devfreq/rk3399_dmc.c create mode 100644 include/soc/rockchip/rockchip_sip.h -- 2.6.6
[PATCH v6 8/8] drm/rockchip: Add dmc notifier in vop driver
when in ddr frequency scaling process, vop can not do enable or disable operation, since dcf will base on vop vblank time to do frequency scaling and need to get vop irq if there have vop enabled. So need register to devfreq notifier, and we can get the dmc status. Also, when there have two vop enabled, we need to disable dmc, since dcf only base on one vop vblank time, so the other panel will flicker when do ddr frequency scaling. Signed-off-by: Lin Huang Reviewed-by: Chanwoo Choi --- Changes in v6: - fix a build error Changes in v5: - improve some nits Changes in v4: - register notifier to devfreq_register_notifier - use DEVFREQ_PRECHANGE and DEVFREQ_POSTCHANGE to get dmc status - when two vop enable, disable dmc - when two vop back to one vop, enable dmc Changes in v3: - when do vop eanble/disable, dmc will wait until it finish Changes in v2: - None Changes in v1: - use wait_event instead usleep drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 121 +++- 1 file changed, 119 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c index 31744fe..199529e 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c @@ -12,6 +12,8 @@ * GNU General Public License for more details. */ +#include +#include #include #include #include @@ -118,6 +120,13 @@ struct vop { const struct vop_data *data; + struct devfreq *devfreq; + struct devfreq_event_dev *devfreq_event_dev; + struct notifier_block dmc_nb; + int dmc_in_process; + int vop_switch_status; + wait_queue_head_t wait_dmc_queue; + wait_queue_head_t wait_vop_switch_queue; uint32_t *regsbak; void __iomem *regs; @@ -428,21 +437,56 @@ static void vop_dsp_hold_valid_irq_disable(struct vop *vop) spin_unlock_irqrestore(>irq_lock, flags); } +static int dmc_notify(struct notifier_block *nb, unsigned long event, + void *data) +{ + struct vop *vop = container_of(nb, struct vop, dmc_nb); + + if (event == DEVFREQ_PRECHANGE) { + /* +* check if vop in enable or disable process, +* if yes, wait until it finishes, use 200ms as +* timeout. +*/ + if (!wait_event_timeout(vop->wait_vop_switch_queue, + !vop->vop_switch_status, HZ / 5)) + dev_warn(vop->dev, +"Timeout waiting for vop swtich status\n"); + vop->dmc_in_process = 1; + } else if (event == DEVFREQ_POSTCHANGE) { + vop->dmc_in_process = 0; + wake_up(>wait_dmc_queue); + } + + return NOTIFY_OK; +} + static void vop_enable(struct drm_crtc *crtc) { struct vop *vop = to_vop(crtc); + int num_enabled_crtc = 0; int ret; + /* +* if in dmc scaling frequency process, wait until it finishes +* use 100ms as timeout time. +*/ + if (!wait_event_timeout(vop->wait_dmc_queue, + !vop->dmc_in_process, HZ / 5)) + dev_warn(vop->dev, +"Timeout waiting for dmc when vop enable\n"); + + vop->vop_switch_status = 1; ret = pm_runtime_get_sync(vop->dev); if (ret < 0) { dev_err(vop->dev, "failed to get pm runtime: %d\n", ret); - return; + goto err; } ret = clk_enable(vop->hclk); if (ret < 0) { dev_err(vop->dev, "failed to enable hclk - %d\n", ret); - return; + goto err; } ret = clk_enable(vop->dclk); @@ -485,6 +529,21 @@ static void vop_enable(struct drm_crtc *crtc) drm_crtc_vblank_on(crtc); + vop->vop_switch_status = 0; + wake_up(>wait_vop_switch_queue); + + /* check how many vop we use now */ + drm_for_each_crtc(crtc, vop->drm_dev) { + if (crtc->state->enable) + num_enabled_crtc++; + } + + /* if enable two vop, need to disable dmc */ + if ((num_enabled_crtc > 1) && vop->devfreq) { + if (vop->devfreq_event_dev) + devfreq_event_disable_edev(vop->devfreq_event_dev); + devfreq_suspend_device(vop->devfreq); + } return; err_disable_aclk: @@ -493,16 +552,32 @@ err_disable_dclk: clk_disable(vop->dclk); err_disable_hclk: clk_disable(vop->hclk); +err: + vop->vop_switch_status = 0; + wake_up(>wait_vop_switch_queue); + return; } static void vop_crtc_disable(struct drm_crtc *crtc) { struct vop *vop = to_vop(crtc); + int num
[PATCH v6 7/8] PM / devfreq: rockchip: add devfreq driver for rk3399 dmc
base on dfi result, we do ddr frequency scaling, register dmc driver to devfreq framework, and use simple-ondemand policy. Signed-off-by: Lin Huang Reviewed-by: Chanwoo Choi --- Changes in v6: - fix some nit suggest by Chanwoo Choi Changes in v5: - improve dmc driver suggest by Chanwoo Choi Changes in v4: - use arm_smccc_smc() function talk to bl31 - delete rockchip_dmc.c file and config - delete dmc_notify - adjust probe order Changes in v3: - operate dram setting through sip call - imporve set rate flow Changes in v2: - None Changes in v1: - move dfi controller to event - fix set voltage sequence when set rate fail - change Kconfig type from tristate to bool - move unuse EXPORT_SYMBOL_GPL() drivers/devfreq/Kconfig | 11 + drivers/devfreq/Makefile | 1 + drivers/devfreq/rk3399_dmc.c | 497 +++ 3 files changed, 509 insertions(+) create mode 100644 drivers/devfreq/rk3399_dmc.c diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig index a5be56e..e848121 100644 --- a/drivers/devfreq/Kconfig +++ b/drivers/devfreq/Kconfig @@ -100,6 +100,17 @@ config ARM_TEGRA_DEVFREQ It reads ACTMON counters of memory controllers and adjusts the operating frequencies and voltages with OPP support. +config ARM_RK3399_DMC_DEVFREQ + tristate "ARM RK3399 DMC DEVFREQ Driver" + depends on ARCH_ROCKCHIP + select DEVFREQ_EVENT_ROCKCHIP_DFI + select DEVFREQ_GOV_SIMPLE_ONDEMAND + select PM_OPP + help + This adds the DEVFREQ driver for the RK3399 DMC(Dynamic Memory Controller). + It sets the frequency for the memory controller and reads the usage counts + from hardware. + source "drivers/devfreq/event/Kconfig" endif # PM_DEVFREQ diff --git a/drivers/devfreq/Makefile b/drivers/devfreq/Makefile index 09f11d9..fbff40a 100644 --- a/drivers/devfreq/Makefile +++ b/drivers/devfreq/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_DEVFREQ_GOV_PASSIVE) += governor_passive.o # DEVFREQ Drivers obj-$(CONFIG_ARM_EXYNOS_BUS_DEVFREQ) += exynos-bus.o +obj-$(CONFIG_ARM_RK3399_DMC_DEVFREQ) += rk3399_dmc.o obj-$(CONFIG_ARM_TEGRA_DEVFREQ)+= tegra-devfreq.o # DEVFREQ Event Drivers diff --git a/drivers/devfreq/rk3399_dmc.c b/drivers/devfreq/rk3399_dmc.c new file mode 100644 index 000..5f98f5e --- /dev/null +++ b/drivers/devfreq/rk3399_dmc.c @@ -0,0 +1,497 @@ +/* + * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd. + * Author: Lin Huang + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +struct dram_timing { + unsigned int ddr3_speed_bin; + unsigned int pd_idle; + unsigned int sr_idle; + unsigned int sr_mc_gate_idle; + unsigned int srpd_lite_idle; + unsigned int standby_idle; + unsigned int dram_dll_dis_freq; + unsigned int phy_dll_dis_freq; + unsigned int ddr3_odt_dis_freq; + unsigned int ddr3_drv; + unsigned int ddr3_odt; + unsigned int phy_ddr3_ca_drv; + unsigned int phy_ddr3_dq_drv; + unsigned int phy_ddr3_odt; + unsigned int lpddr3_odt_dis_freq; + unsigned int lpddr3_drv; + unsigned int lpddr3_odt; + unsigned int phy_lpddr3_ca_drv; + unsigned int phy_lpddr3_dq_drv; + unsigned int phy_lpddr3_odt; + unsigned int lpddr4_odt_dis_freq; + unsigned int lpddr4_drv; + unsigned int lpddr4_dq_odt; + unsigned int lpddr4_ca_odt; + unsigned int phy_lpddr4_ca_drv; + unsigned int phy_lpddr4_ck_cs_drv; + unsigned int phy_lpddr4_dq_drv; + unsigned int phy_lpddr4_odt; +}; + +struct rk3399_dmcfreq { + struct device *dev; + struct devfreq *devfreq; + struct devfreq_simple_ondemand_data ondemand_data; + struct clk *dmc_clk; + struct devfreq_event_dev *edev; + struct mutex lock; + struct dram_timing *timing; + + /* +* DDR Converser of Frequency (DCF) is used to implement DDR frequency +* conversion without the participation of CPU, we will implement and +* control it in arm trust firmware. +*/ + wait_queue_head_t wait_dcf_queue; + int irq; + int wait_dcf_flag; + struct regulator *vdd_center; + unsigned long rate, target_rate; + unsigned long volt, target_volt; + struct dev_pm_opp *curr_opp; +}; + +static int r
[PATCH v6 6/8] Documentation: bindings: add dt documentation for rk3399 dmc
This patch adds the documentation for rockchip rk3399 dmc driver. Signed-off-by: Lin Huang --- Changes in v6: -Add more detail in Documentation Changes in v5: -None Changes in v4: -None Changes in v3: -None Changes in v2: -None Changes in v1: -None .../devicetree/bindings/devfreq/rk3399_dmc.txt | 84 ++ 1 file changed, 84 insertions(+) create mode 100644 Documentation/devicetree/bindings/devfreq/rk3399_dmc.txt diff --git a/Documentation/devicetree/bindings/devfreq/rk3399_dmc.txt b/Documentation/devicetree/bindings/devfreq/rk3399_dmc.txt new file mode 100644 index 000..e73067c --- /dev/null +++ b/Documentation/devicetree/bindings/devfreq/rk3399_dmc.txt @@ -0,0 +1,84 @@ +* Rockchip rk3399 DMC(Dynamic Memory Controller) device + +Required properties: +- compatible: Must be "rockchip,rk3399-dmc". +- devfreq-events: Node to get ddr loading, Refer to + Documentation/devicetree/bindings/devfreq/rockchip-dif.txt +- interrupts: The interrupt number to the cpu. The interrupt specifier format + depends on the interrupt controller. it should be dcf interrupts, + when ddr dvfs finish, it will happen. +- clocks: Phandles for clock specified in "clock-names" property +- clock-names : The name of clock used by the DFI, must be "pclk_ddr_mon"; +- operating-points-v2: Refer to Documentation/devicetree/bindings/power/opp.txt + for details. +- center-supply: Dmc supply node. +- status: Marks the node enabled/disabled. + +Optional properties: +- ddr_timing: ddr timing need to pass to arm trust firmware +- upthreshold: the upthreshold to simpleondeamnd policy +- downdifferential: The downdifferential to simpleondeamnd policy + +Example: + ddr_timing: ddr_timing { + compatible = "rockchip,ddr-timing"; + ddr3_speed_bin = <21>; + pd_idle = <0>; + sr_idle = <0>; + sr_mc_gate_idle = <0>; + srpd_lite_idle = <0>; + standby_idle = <0>; + dram_dll_dis_freq = <300>; + phy_dll_dis_freq = <125>; + + ddr3_odt_dis_freq = <333>; + ddr3_drv = ; + ddr3_odt = ; + phy_ddr3_ca_drv = ; + phy_ddr3_dq_drv = ; + phy_ddr3_odt = ; + + lpddr3_odt_dis_freq = <333>; + lpddr3_drv = ; + lpddr3_odt = ; + phy_lpddr3_ca_drv = ; + phy_lpddr3_dq_drv = ; + phy_lpddr3_odt = ; + + lpddr4_odt_dis_freq = <333>; + lpddr4_drv = ; + lpddr4_dq_odt = ; + lpddr4_ca_odt = ; + phy_lpddr4_ca_drv = ; + phy_lpddr4_ck_cs_drv = ; + phy_lpddr4_dq_drv = ; + phy_lpddr4_odt = ; + }; + + dmc_opp_table: dmc_opp_table { + compatible = "operating-points-v2"; + + opp00 { + opp-hz = /bits/ 64 <3>; + opp-microvolt = <90>; + }; + opp01 { + opp-hz = /bits/ 64 <66600>; + opp-microvolt = <90>; + }; + }; + + dmc: dmc { + compatible = "rockchip,rk3399-dmc"; + devfreq-events = <>; + interrupts = ; + clocks = < SCLK_DDRCLK>; + clock-names = "dmc_clk"; + ddr_timing = <_timing>; + operating-points-v2 = <_opp_table>; + center-supply = <_centerlogic>; + upthreshold = <15>; + downdifferential = <10>; + status = "disabled"; + }; + -- 2.6.6
[PATCH v6 5/8] PM / devfreq: event: support rockchip dfi controller
on rk3399 platform, there is dfi conroller can monitor ddr load, base on this result, we can do ddr freqency scaling. Signed-off-by: Lin Huang Acked-by: Chanwoo Choi --- Changes in v6: -None Changes in v5: -None Changes in v4: -None Changes in v3: -None Changes in v2: -None Changes in v1: -None drivers/devfreq/event/Kconfig| 7 + drivers/devfreq/event/Makefile | 1 + drivers/devfreq/event/rockchip-dfi.c | 253 +++ 3 files changed, 261 insertions(+) create mode 100644 drivers/devfreq/event/rockchip-dfi.c diff --git a/drivers/devfreq/event/Kconfig b/drivers/devfreq/event/Kconfig index eb6f74a..20d82c2 100644 --- a/drivers/devfreq/event/Kconfig +++ b/drivers/devfreq/event/Kconfig @@ -30,4 +30,11 @@ config DEVFREQ_EVENT_EXYNOS_PPMU (Platform Performance Monitoring Unit) counters to estimate the utilization of each module. +config DEVFREQ_EVENT_ROCKCHIP_DFI + tristate "ROCKCHIP DFI DEVFREQ event Driver" + depends on ARCH_ROCKCHIP + help + This add the devfreq-event driver for Rockchip SoC. It provides DFI + (DDR Monitor Module) driver to count ddr load. + endif # PM_DEVFREQ_EVENT diff --git a/drivers/devfreq/event/Makefile b/drivers/devfreq/event/Makefile index 3d6afd3..dda7090 100644 --- a/drivers/devfreq/event/Makefile +++ b/drivers/devfreq/event/Makefile @@ -2,3 +2,4 @@ obj-$(CONFIG_DEVFREQ_EVENT_EXYNOS_NOCP) += exynos-nocp.o obj-$(CONFIG_DEVFREQ_EVENT_EXYNOS_PPMU) += exynos-ppmu.o +obj-$(CONFIG_DEVFREQ_EVENT_ROCKCHIP_DFI) += rockchip-dfi.o diff --git a/drivers/devfreq/event/rockchip-dfi.c b/drivers/devfreq/event/rockchip-dfi.c new file mode 100644 index 000..3f12be7 --- /dev/null +++ b/drivers/devfreq/event/rockchip-dfi.c @@ -0,0 +1,253 @@ +/* + * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd + * Author: Lin Huang + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RK3399_DMC_NUM_CH 2 + +/* DDRMON_CTRL */ +#define DDRMON_CTRL0x04 +#define CLR_DDRMON_CTRL(0x1f << 0) +#define LPDDR4_EN (0x10001 << 4) +#define HARDWARE_EN(0x10001 << 3) +#define LPDDR3_EN (0x10001 << 2) +#define SOFTWARE_EN(0x10001 << 1) +#define TIME_CNT_EN(0x10001 << 0) + +#define DDRMON_CH0_COUNT_NUM 0x28 +#define DDRMON_CH0_DFI_ACCESS_NUM 0x2c +#define DDRMON_CH1_COUNT_NUM 0x3c +#define DDRMON_CH1_DFI_ACCESS_NUM 0x40 + +/* pmu grf */ +#define PMUGRF_OS_REG2 0x308 +#define DDRTYPE_SHIFT 13 +#define DDRTYPE_MASK 7 + +enum { + DDR3 = 3, + LPDDR3 = 6, + LPDDR4 = 7, + UNUSED = 0xFF +}; + +struct dmc_usage { + u32 access; + u32 total; +}; + +struct rockchip_dfi { + struct devfreq_event_dev *edev; + struct devfreq_event_desc *desc; + struct dmc_usage ch_usage[RK3399_DMC_NUM_CH]; + struct device *dev; + void __iomem *regs; + struct regmap *regmap_pmu; + struct clk *clk; +}; + +static void rockchip_dfi_start_hardware_counter(struct devfreq_event_dev *edev) +{ + struct rockchip_dfi *info = devfreq_event_get_drvdata(edev); + void __iomem *dfi_regs = info->regs; + u32 val; + u32 ddr_type; + + /* get ddr type */ + regmap_read(info->regmap_pmu, PMUGRF_OS_REG2, ); + ddr_type = (val >> DDRTYPE_SHIFT) & DDRTYPE_MASK; + + /* clear DDRMON_CTRL setting */ + writel_relaxed(CLR_DDRMON_CTRL, dfi_regs + DDRMON_CTRL); + + /* set ddr type to dfi */ + if (ddr_type == LPDDR3) + writel_relaxed(LPDDR3_EN, dfi_regs + DDRMON_CTRL); + else if (ddr_type == LPDDR4) + writel_relaxed(LPDDR4_EN, dfi_regs + DDRMON_CTRL); + + /* enable count, use software mode */ + writel_relaxed(SOFTWARE_EN, dfi_regs + DDRMON_CTRL); +} + +static void rockchip_dfi_stop_hardware_counter(struct devfreq_event_dev *edev) +{ + struct rockchip_dfi *info = devfreq_event_get_drvdata(edev); + void __iomem *dfi_regs = info->regs; + u32 val; + + val = readl_relaxed(dfi_regs + DDRMON_CTRL); + val &= ~SOFTWARE_EN; + writel_relaxed(val, dfi_regs + DDRMON_CTRL); +} + +static int rockchip_dfi_get_busier_ch(struct devfreq_event_dev *edev) +{ + struct rockchip_dfi *info = devfreq_event_get_drvdata(edev); + u32 tmp, max = 0; +
[PATCH v6 4/8] Documentation: bindings: add dt documentation for dfi controller
This patch adds the documentation for rockchip dfi devfreq-event driver. Signed-off-by: Lin Huang --- Changes in v6: -None Changes in v5: -None Changes in v4: -None Changes in v3: -None Changes in v2: -None Changes in v1: -None .../bindings/devfreq/event/rockchip-dfi.txt | 20 1 file changed, 20 insertions(+) create mode 100644 Documentation/devicetree/bindings/devfreq/event/rockchip-dfi.txt diff --git a/Documentation/devicetree/bindings/devfreq/event/rockchip-dfi.txt b/Documentation/devicetree/bindings/devfreq/event/rockchip-dfi.txt new file mode 100644 index 000..bf42255 --- /dev/null +++ b/Documentation/devicetree/bindings/devfreq/event/rockchip-dfi.txt @@ -0,0 +1,20 @@ + +* Rockchip rk3399 DFI device + +Required properties: +- compatible: Must be "rockchip,rk3399-dfi". +- reg: physical base address of each DFI and length of memory mapped region +- rockchip,pmu: phandle to the syscon managing the "pmu general register files" +- clocks: phandles for clock specified in "clock-names" property +- clock-names : the name of clock used by the DFI, must be "pclk_ddr_mon"; + +Example: + dfi: dfi at 0xff63 { + reg = <0x00 0xff63 0x00 0x4000>; + compatible = "rockchip,rk3399-dfi"; + rockchip,pmu = <>; + clocks = < PCLK_DDR_MON>; + clock-names = "pclk_ddr_mon"; + status = "disabled"; + }; + -- 2.6.6
[PATCH v6 3/8] clk: rockchip: rk3399: add ddrc clock support
add ddrc clock setting, so we can do ddr frequency scaling on rk3399 platform in future. Signed-off-by: Lin Huang --- Changes in v6: - None Changes in v5: - fit for the ddr type Changes in v4: - None Changes in v3: - None Changes in v2: - remove clk_ddrc_dpll_src from critical clock list Changes in v1: - remove ddrc source CLK_IGNORE_UNUSED flag - move clk_ddrc and clk_ddrc_dpll_src to critical drivers/clk/rockchip/clk-rk3399.c | 19 +++ 1 file changed, 19 insertions(+) diff --git a/drivers/clk/rockchip/clk-rk3399.c b/drivers/clk/rockchip/clk-rk3399.c index e445cd6..01d4945 100644 --- a/drivers/clk/rockchip/clk-rk3399.c +++ b/drivers/clk/rockchip/clk-rk3399.c @@ -120,6 +120,10 @@ PNAME(mux_armclkb_p) = { "clk_core_b_lpll_src", "clk_core_b_bpll_src", "clk_core_b_dpll_src", "clk_core_b_gpll_src" }; +PNAME(mux_ddrclk_p)= { "clk_ddrc_lpll_src", + "clk_ddrc_bpll_src", + "clk_ddrc_dpll_src", + "clk_ddrc_gpll_src" }; PNAME(mux_aclk_cci_p) = { "cpll_aclk_cci_src", "gpll_aclk_cci_src", "npll_aclk_cci_src", @@ -1379,6 +1383,18 @@ static struct rockchip_clk_branch rk3399_clk_branches[] __initdata = { COMPOSITE_NOMUX(0, "clk_test", "clk_test_pre", CLK_IGNORE_UNUSED, RK3368_CLKSEL_CON(58), 0, 5, DFLAGS, RK3368_CLKGATE_CON(13), 11, GFLAGS), + + /* ddrc */ + GATE(0, "clk_ddrc_lpll_src", "lpll", 0, RK3399_CLKGATE_CON(3), +0, GFLAGS), + GATE(0, "clk_ddrc_bpll_src", "bpll", 0, RK3399_CLKGATE_CON(3), +1, GFLAGS), + GATE(0, "clk_ddrc_dpll_src", "dpll", 0, RK3399_CLKGATE_CON(3), +2, GFLAGS), + GATE(0, "clk_ddrc_gpll_src", "gpll", 0, RK3399_CLKGATE_CON(3), +3, GFLAGS), + COMPOSITE_DDRCLK(SCLK_DDRC, "clk_ddrc", mux_ddrclk_p, 0, + RK3399_CLKSEL_CON(6), 4, 2, 0, 0, ROCKCHIP_DDRCLK_SIP), }; static struct rockchip_clk_branch rk3399_clk_pmu_branches[] __initdata = { @@ -1493,6 +1509,9 @@ static const char *const rk3399_cru_critical_clocks[] __initconst = { "gpll_aclk_perilp0_src", "gpll_aclk_perihp_src", "aclk_vio_noc", + + /* ddrc */ + "clk_ddrc" }; static const char *const rk3399_pmucru_critical_clocks[] __initconst = { -- 2.6.6
[PATCH v6 2/8] clk: rockchip: rk3399: add SCLK_DDRCLK ID for ddrc
Signed-off-by: Lin Huang --- Changes in v6: -None Changes in v5: -None Changes in v4: -None Changes in v3: -None Changes in v2: -None Changes in v1: -None include/dt-bindings/clock/rk3399-cru.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/dt-bindings/clock/rk3399-cru.h b/include/dt-bindings/clock/rk3399-cru.h index 50a44cf..ce5f3e9 100644 --- a/include/dt-bindings/clock/rk3399-cru.h +++ b/include/dt-bindings/clock/rk3399-cru.h @@ -131,6 +131,7 @@ #define SCLK_DPHY_RX0_CFG 165 #define SCLK_RMII_SRC 166 #define SCLK_PCIEPHY_REF100M 167 +#define SCLK_DDRC 168 #define DCLK_VOP0 180 #define DCLK_VOP1 181 -- 2.6.6
[PATCH v6 1/8] clk: rockchip: add new clock-type for the ddrclk
On new rockchip platform(rk3399 etc), there have dcf controller to do ddr frequency scaling, and this controller will implement in arm-trust-firmware. We add a special clock-type to handle that. Signed-off-by: Lin Huang --- Changes in v6: - none Changes in v5: - delete unuse mux_flag - use div_flag to distinguish sip call and other operate Changes in v4: - use arm_smccc_smc() to set/read ddr rate Changes in v3: - use sip call to set/read ddr rate Changes in v2: - use GENMASK instead val_mask - use divider_recalc_rate() instead DIV_ROUND_UP_ULL - cleanup code Changes in v1: - none drivers/clk/rockchip/Makefile | 1 + drivers/clk/rockchip/clk-ddr.c | 150 drivers/clk/rockchip/clk.c | 9 +++ drivers/clk/rockchip/clk.h | 33 include/soc/rockchip/rockchip_sip.h | 27 +++ 5 files changed, 220 insertions(+) create mode 100644 drivers/clk/rockchip/clk-ddr.c create mode 100644 include/soc/rockchip/rockchip_sip.h diff --git a/drivers/clk/rockchip/Makefile b/drivers/clk/rockchip/Makefile index f47a2fa..b5f2c8e 100644 --- a/drivers/clk/rockchip/Makefile +++ b/drivers/clk/rockchip/Makefile @@ -8,6 +8,7 @@ obj-y += clk-pll.o obj-y += clk-cpu.o obj-y += clk-inverter.o obj-y += clk-mmc-phase.o +obj-y += clk-ddr.o obj-$(CONFIG_RESET_CONTROLLER) += softrst.o obj-y += clk-rk3036.o diff --git a/drivers/clk/rockchip/clk-ddr.c b/drivers/clk/rockchip/clk-ddr.c new file mode 100644 index 000..7dbe8bff --- /dev/null +++ b/drivers/clk/rockchip/clk-ddr.c @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2016 Rockchip Electronics Co. Ltd. + * Author: Lin Huang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "clk.h" + +struct rockchip_ddrclk { + struct clk_hw hw; + void __iomem*reg_base; + int mux_offset; + int mux_shift; + int mux_width; + int div_shift; + int div_width; + int ddr_flag; + spinlock_t *lock; +}; + +#define to_rockchip_ddrclk_hw(hw) container_of(hw, struct rockchip_ddrclk, hw) + +static int rockchip_ddrclk_set_rate(struct clk_hw *hw, unsigned long drate, + unsigned long prate) +{ + struct rockchip_ddrclk *ddrclk = to_rockchip_ddrclk_hw(hw); + unsigned long flags; + struct arm_smccc_res res; + + spin_lock_irqsave(ddrclk->lock, flags); + if (ddrclk->ddr_flag == ROCKCHIP_DDRCLK_SIP) { + arm_smccc_smc(SIP_DRAM_FREQ, drate, 0, CONFIG_DRAM_SET_RATE, + 0, 0, 0, 0, ); + return res.a0; + } + spin_unlock_irqrestore(ddrclk->lock, flags); + + return 0; +} + +static unsigned long +rockchip_ddrclk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct rockchip_ddrclk *ddrclk = to_rockchip_ddrclk_hw(hw); + struct arm_smccc_res res; + + if (ddrclk->ddr_flag == ROCKCHIP_DDRCLK_SIP) { + arm_smccc_smc(SIP_DRAM_FREQ, 0, 0, CONFIG_DRAM_GET_RATE, + 0, 0, 0, 0, ); + return res.a0; + } + + return 0; +} + +static long clk_ddrclk_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + return rate; +} + +static u8 rockchip_ddrclk_get_parent(struct clk_hw *hw) +{ + struct rockchip_ddrclk *ddrclk = to_rockchip_ddrclk_hw(hw); + int num_parents = clk_hw_get_num_parents(hw); + u32 val; + + val = clk_readl(ddrclk->reg_base + + ddrclk->mux_offset) >> ddrclk->mux_shift; + val &= GENMASK(ddrclk->mux_width - 1, 0); + + if (val >= num_parents) + return -EINVAL; + + return val; +} + +static const struct clk_ops rockchip_ddrclk_ops = { + .recalc_rate = rockchip_ddrclk_recalc_rate, + .set_rate = rockchip_ddrclk_set_rate, + .round_rate = clk_ddrclk_round_rate, + .get_parent = rockchip_ddrclk_get_parent, +}; + +struct clk *rockchip_clk_register_ddrclk(const char *name, int flags, +const char *const *parent_names, +u8 num_parents, int mux_offset, +int mux_shift, int mux_width, +
[PATCH v6 0/8] rk3399 support ddr frequency scaling
rk3399 platform have dfi controller can monitor ddr load, and dcf controller to handle ddr register so we can get the right ddr frequency and make ddr controller happy work(which will implement in bl31). So we do ddr frequency scaling with following flow: kernelbl31 monitor ddr load | | get_target_rate | | pass rate to bl31 clk_set_rate(ddr) ->run dcf flow | | | | wait dcf interrupt<---trigger dcf interrupt | | return Lin Huang (8): clk: rockchip: add new clock-type for the ddrclk clk: rockchip: rk3399: add SCLK_DDRCLK ID for ddrc clk: rockchip: rk3399: add ddrc clock support Documentation: bindings: add dt documentation for dfi controller PM / devfreq: event: support rockchip dfi controller Documentation: bindings: add dt documentation for rk3399 dmc PM / devfreq: rockchip: add devfreq driver for rk3399 dmc drm/rockchip: Add dmc notifier in vop driver .../bindings/devfreq/event/rockchip-dfi.txt| 20 + .../devicetree/bindings/devfreq/rk3399_dmc.txt | 84 drivers/clk/rockchip/Makefile | 1 + drivers/clk/rockchip/clk-ddr.c | 150 +++ drivers/clk/rockchip/clk-rk3399.c | 19 + drivers/clk/rockchip/clk.c | 9 + drivers/clk/rockchip/clk.h | 33 ++ drivers/devfreq/Kconfig| 11 + drivers/devfreq/Makefile | 1 + drivers/devfreq/event/Kconfig | 7 + drivers/devfreq/event/Makefile | 1 + drivers/devfreq/event/rockchip-dfi.c | 253 +++ drivers/devfreq/rk3399_dmc.c | 497 + drivers/gpu/drm/rockchip/rockchip_drm_vop.c| 121 - include/dt-bindings/clock/rk3399-cru.h | 1 + include/soc/rockchip/rockchip_sip.h| 27 ++ 16 files changed, 1233 insertions(+), 2 deletions(-) create mode 100644 Documentation/devicetree/bindings/devfreq/event/rockchip-dfi.txt create mode 100644 Documentation/devicetree/bindings/devfreq/rk3399_dmc.txt create mode 100644 drivers/clk/rockchip/clk-ddr.c create mode 100644 drivers/devfreq/event/rockchip-dfi.c create mode 100644 drivers/devfreq/rk3399_dmc.c create mode 100644 include/soc/rockchip/rockchip_sip.h -- 2.6.6 *
[PATCH v5 8/8] drm/rockchip: Add dmc notifier in vop driver
when in ddr frequency scaling process, vop can not do enable or disable operation, since dcf will base on vop vblank time to do frequency scaling and need to get vop irq if there have vop enabled. So need register to devfreq notifier, and we can get the dmc status. Also, when there have two vop enabled, we need to disable dmc, since dcf only base on one vop vblank time, so the other panel will flicker when do ddr frequency scaling. Signed-off-by: Lin Huang --- Changes in v5: - improve some nits Changes in v4: - register notifier to devfreq_register_notifier - use DEVFREQ_PRECHANGE and DEVFREQ_POSTCHANGE to get dmc status - when two vop enable, disable dmc - when two vop back to one vop, enable dmc Changes in v3: - when do vop eanble/disable, dmc will wait until it finish Changes in v2: - None Changes in v1: - use wait_event instead usleep drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 128 +++- 1 file changed, 125 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c index 31744fe..7ce3890 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c @@ -12,6 +12,8 @@ * GNU General Public License for more details. */ +#include +#include #include #include #include @@ -118,6 +120,13 @@ struct vop { const struct vop_data *data; + struct devfreq *devfreq; + struct devfreq_event_dev *devfreq_event_dev; + struct notifier_block dmc_nb; + int dmc_in_process; + int vop_switch_status; + wait_queue_head_t wait_dmc_queue; + wait_queue_head_t wait_vop_switch_queue; uint32_t *regsbak; void __iomem *regs; @@ -428,21 +437,59 @@ static void vop_dsp_hold_valid_irq_disable(struct vop *vop) spin_unlock_irqrestore(>irq_lock, flags); } +static int dmc_notify(struct notifier_block *nb, unsigned long event, + void *data) +{ + struct vop *vop = container_of(nb, struct vop, dmc_nb); + + if (event == DEVFREQ_PRECHANGE) { + /* +* check if vop in enable or disable process, +* if yes, wait until it finishes, use 200ms as +* timeout. +*/ + if (!wait_event_timeout(vop->wait_vop_switch_queue, + !vop->vop_switch_status, HZ / 5)) + dev_warn(vop->dev, +"Timeout waiting for vop swtich status\n"); + vop->dmc_in_process = 1; + } else if (event == DEVFREQ_POSTCHANGE) { + vop->dmc_in_process = 0; + wake_up(>wait_dmc_queue); + } + + return NOTIFY_OK; +} + static void vop_enable(struct drm_crtc *crtc) { struct vop *vop = to_vop(crtc); + int num_enabled_crtc = 0; int ret; + if (vop->is_enabled) + return; + + /* +* if in dmc scaling frequency process, wait until it finishes +* use 100ms as timeout time. +*/ + if (!wait_event_timeout(vop->wait_dmc_queue, + !vop->dmc_in_process, HZ / 5)) + dev_warn(vop->dev, +"Timeout waiting for dmc when vop enable\n"); + + vop->vop_switch_status = 1; ret = pm_runtime_get_sync(vop->dev); if (ret < 0) { dev_err(vop->dev, "failed to get pm runtime: %d\n", ret); - return; + goto err; } ret = clk_enable(vop->hclk); if (ret < 0) { dev_err(vop->dev, "failed to enable hclk - %d\n", ret); - return; + goto err; } ret = clk_enable(vop->dclk); @@ -456,7 +503,6 @@ static void vop_enable(struct drm_crtc *crtc) dev_err(vop->dev, "failed to enable aclk - %d\n", ret); goto err_disable_dclk; } - /* * Slave iommu shares power, irq and clock with vop. It was associated * automatically with this master device via common driver code. @@ -485,6 +531,21 @@ static void vop_enable(struct drm_crtc *crtc) drm_crtc_vblank_on(crtc); + vop->vop_switch_status = 0; + wake_up(>wait_vop_switch_queue); + + /* check how many vop we use now */ + drm_for_each_crtc(crtc, vop->drm_dev) { + if (crtc->state->enable) + num_enabled_crtc++; + } + + /* if enable two vop, need to disable dmc */ + if ((num_enabled_crtc > 1) && vop->devfreq) { + if (vop->devfreq_event_dev) + devfreq_event_disable_edev(vop->devfreq_event_dev); + devfreq_suspend_device(vop->devfreq); + } return; err_disabl
[PATCH v5 7/8] PM / devfreq: rockchip: add devfreq driver for rk3399 dmc
base on dfi result, we do ddr frequency scaling, register dmc driver to devfreq framework, and use simple-ondemand policy. Signed-off-by: Lin Huang --- Changes in v5: - improve dmc driver suggest by Chanwoo Choi Changes in v4: - use arm_smccc_smc() function talk to bl31 - delete rockchip_dmc.c file and config - delete dmc_notify - adjust probe order Changes in v3: - operate dram setting through sip call - imporve set rate flow Changes in v2: - None Changes in v1: - move dfi controller to event - fix set voltage sequence when set rate fail - change Kconfig type from tristate to bool - move unuse EXPORT_SYMBOL_GPL() drivers/devfreq/Kconfig | 9 + drivers/devfreq/Makefile | 1 + drivers/devfreq/rk3399_dmc.c | 512 +++ 3 files changed, 522 insertions(+) create mode 100644 drivers/devfreq/rk3399_dmc.c diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig index a5be56e..749499d 100644 --- a/drivers/devfreq/Kconfig +++ b/drivers/devfreq/Kconfig @@ -100,6 +100,15 @@ config ARM_TEGRA_DEVFREQ It reads ACTMON counters of memory controllers and adjusts the operating frequencies and voltages with OPP support. +config ARM_RK3399_DMC_DEVFREQ + tristate "ARM RK3399 DMC DEVFREQ Driver" + select PM_OPP + select DEVFREQ_GOV_SIMPLE_ONDEMAND + help + This adds the DEVFREQ driver for the RK3399 dmc(Dynamic Memory Controller). + It sets the frequency for the memory controller and reads the usage counts + from hardware. + source "drivers/devfreq/event/Kconfig" endif # PM_DEVFREQ diff --git a/drivers/devfreq/Makefile b/drivers/devfreq/Makefile index 09f11d9..70d9549 100644 --- a/drivers/devfreq/Makefile +++ b/drivers/devfreq/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_DEVFREQ_GOV_PASSIVE) += governor_passive.o # DEVFREQ Drivers obj-$(CONFIG_ARM_EXYNOS_BUS_DEVFREQ) += exynos-bus.o obj-$(CONFIG_ARM_TEGRA_DEVFREQ)+= tegra-devfreq.o +obj-$(CONFIG_ARM_RK3399_DMC_DEVFREQ) += rk3399_dmc.o # DEVFREQ Event Drivers obj-$(CONFIG_PM_DEVFREQ_EVENT) += event/ diff --git a/drivers/devfreq/rk3399_dmc.c b/drivers/devfreq/rk3399_dmc.c new file mode 100644 index 000..c1157ba --- /dev/null +++ b/drivers/devfreq/rk3399_dmc.c @@ -0,0 +1,512 @@ +/* + * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd. + * Author: Lin Huang + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +struct dram_timing { + unsigned int ddr3_speed_bin; + unsigned int pd_idle; + unsigned int sr_idle; + unsigned int sr_mc_gate_idle; + unsigned int srpd_lite_idle; + unsigned int standby_idle; + unsigned int dram_dll_dis_freq; + unsigned int phy_dll_dis_freq; + unsigned int ddr3_odt_dis_freq; + unsigned int ddr3_drv; + unsigned int ddr3_odt; + unsigned int phy_ddr3_ca_drv; + unsigned int phy_ddr3_dq_drv; + unsigned int phy_ddr3_odt; + unsigned int lpddr3_odt_dis_freq; + unsigned int lpddr3_drv; + unsigned int lpddr3_odt; + unsigned int phy_lpddr3_ca_drv; + unsigned int phy_lpddr3_dq_drv; + unsigned int phy_lpddr3_odt; + unsigned int lpddr4_odt_dis_freq; + unsigned int lpddr4_drv; + unsigned int lpddr4_dq_odt; + unsigned int lpddr4_ca_odt; + unsigned int phy_lpddr4_ca_drv; + unsigned int phy_lpddr4_ck_cs_drv; + unsigned int phy_lpddr4_dq_drv; + unsigned int phy_lpddr4_odt; +}; + +struct rk3399_dmcfreq { + struct device *dev; + struct devfreq *devfreq; + struct devfreq_simple_ondemand_data ondemand_data; + struct clk *dmc_clk; + struct devfreq_event_dev *edev; + struct mutex lock; + struct dram_timing *timing; + + /* +* DDR Converser of Frequency (DCF) is used to implement DDR frequency +* conversion without the participation of CPU, we will implement and +* control it in arm trust firmware. +*/ + wait_queue_head_t wait_dcf_queue; + int irq; + int wait_dcf_flag; + struct regulator *vdd_center; + unsigned long rate, target_rate; + unsigned long volt, target_volt; + struct dev_pm_opp *curr_opp; +}; + +static int rk3399_dmcfreq_target(struct device *dev, unsigned long *freq, +u32 flag
[PATCH v5 6/8] Documentation: bindings: add dt documentation for rk3399 dmc
This patch adds the documentation for rockchip rk3399 dmc driver. Signed-off-by: Lin Huang --- Changes in v5: -None Changes in v4: -None Changes in v3: -None Changes in v2: -None Changes in v1: -None .../devicetree/bindings/devfreq/rk3399_dmc.txt | 35 ++ 1 file changed, 35 insertions(+) create mode 100644 Documentation/devicetree/bindings/devfreq/rk3399_dmc.txt diff --git a/Documentation/devicetree/bindings/devfreq/rk3399_dmc.txt b/Documentation/devicetree/bindings/devfreq/rk3399_dmc.txt new file mode 100644 index 000..90e9581 --- /dev/null +++ b/Documentation/devicetree/bindings/devfreq/rk3399_dmc.txt @@ -0,0 +1,35 @@ +* Rockchip rk3399 dmc device + +Required properties: +- compatible: Must be "rockchip,rk3399-dmc". +- devfreq-events: Node to get ddr loading, Refer to + Documentation/devicetree/bindings/devfreq/rockchip-dif.txt +- interrupts: The interrupt number to the cpu. The interrupt specifier format + depends on the interrupt controller. +- clocks: Phandles for clock specified in "clock-names" property +- clock-names : The name of clock used by the DFI, must be "pclk_ddr_mon"; +- operating-points-v2: Refer to Documentation/devicetree/bindings/power/opp.txt + for details. +- center-supply: Dmc supply node. +- status: Marks the node enabled/disabled. + +Optional properties: +- ddr_timing: ddr timing need to pass to arm trust firmware +- upthreshold: the upthreshold to simpleondeamnd policy +- downdifferential: The downdifferential to simpleondeamnd policy + +Example: + dmc: dmc { + compatible = "rockchip,rk3399-dmc"; + devfreq-events = <>; + interrupts = ; + clocks = < SCLK_DDRCLK>; + clock-names = "dmc_clk"; + ddr_timing = <_timing>; + operating-points-v2 = <_opp_table>; + center-supply = <_centerlogic>; + upthreshold = <15>; + downdifferential = <10>; + status = "disabled"; + }; + -- 1.9.1
[PATCH v5 5/8] PM / devfreq: event: support rockchip dfi controller
on rk3399 platform, there is dfi conroller can monitor ddr load, base on this result, we can do ddr freqency scaling. Signed-off-by: Lin Huang Acked-by: Chanwoo Choi --- Changes in v5: -None Changes in v4: -None Changes in v3: -None Changes in v2: -None Changes in v1: -None drivers/devfreq/event/Kconfig| 7 + drivers/devfreq/event/Makefile | 1 + drivers/devfreq/event/rockchip-dfi.c | 253 +++ 3 files changed, 261 insertions(+) create mode 100644 drivers/devfreq/event/rockchip-dfi.c diff --git a/drivers/devfreq/event/Kconfig b/drivers/devfreq/event/Kconfig index eb6f74a..20d82c2 100644 --- a/drivers/devfreq/event/Kconfig +++ b/drivers/devfreq/event/Kconfig @@ -30,4 +30,11 @@ config DEVFREQ_EVENT_EXYNOS_PPMU (Platform Performance Monitoring Unit) counters to estimate the utilization of each module. +config DEVFREQ_EVENT_ROCKCHIP_DFI + tristate "ROCKCHIP DFI DEVFREQ event Driver" + depends on ARCH_ROCKCHIP + help + This add the devfreq-event driver for Rockchip SoC. It provides DFI + (DDR Monitor Module) driver to count ddr load. + endif # PM_DEVFREQ_EVENT diff --git a/drivers/devfreq/event/Makefile b/drivers/devfreq/event/Makefile index 3d6afd3..dda7090 100644 --- a/drivers/devfreq/event/Makefile +++ b/drivers/devfreq/event/Makefile @@ -2,3 +2,4 @@ obj-$(CONFIG_DEVFREQ_EVENT_EXYNOS_NOCP) += exynos-nocp.o obj-$(CONFIG_DEVFREQ_EVENT_EXYNOS_PPMU) += exynos-ppmu.o +obj-$(CONFIG_DEVFREQ_EVENT_ROCKCHIP_DFI) += rockchip-dfi.o diff --git a/drivers/devfreq/event/rockchip-dfi.c b/drivers/devfreq/event/rockchip-dfi.c new file mode 100644 index 000..3f12be7 --- /dev/null +++ b/drivers/devfreq/event/rockchip-dfi.c @@ -0,0 +1,253 @@ +/* + * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd + * Author: Lin Huang + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RK3399_DMC_NUM_CH 2 + +/* DDRMON_CTRL */ +#define DDRMON_CTRL0x04 +#define CLR_DDRMON_CTRL(0x1f << 0) +#define LPDDR4_EN (0x10001 << 4) +#define HARDWARE_EN(0x10001 << 3) +#define LPDDR3_EN (0x10001 << 2) +#define SOFTWARE_EN(0x10001 << 1) +#define TIME_CNT_EN(0x10001 << 0) + +#define DDRMON_CH0_COUNT_NUM 0x28 +#define DDRMON_CH0_DFI_ACCESS_NUM 0x2c +#define DDRMON_CH1_COUNT_NUM 0x3c +#define DDRMON_CH1_DFI_ACCESS_NUM 0x40 + +/* pmu grf */ +#define PMUGRF_OS_REG2 0x308 +#define DDRTYPE_SHIFT 13 +#define DDRTYPE_MASK 7 + +enum { + DDR3 = 3, + LPDDR3 = 6, + LPDDR4 = 7, + UNUSED = 0xFF +}; + +struct dmc_usage { + u32 access; + u32 total; +}; + +struct rockchip_dfi { + struct devfreq_event_dev *edev; + struct devfreq_event_desc *desc; + struct dmc_usage ch_usage[RK3399_DMC_NUM_CH]; + struct device *dev; + void __iomem *regs; + struct regmap *regmap_pmu; + struct clk *clk; +}; + +static void rockchip_dfi_start_hardware_counter(struct devfreq_event_dev *edev) +{ + struct rockchip_dfi *info = devfreq_event_get_drvdata(edev); + void __iomem *dfi_regs = info->regs; + u32 val; + u32 ddr_type; + + /* get ddr type */ + regmap_read(info->regmap_pmu, PMUGRF_OS_REG2, ); + ddr_type = (val >> DDRTYPE_SHIFT) & DDRTYPE_MASK; + + /* clear DDRMON_CTRL setting */ + writel_relaxed(CLR_DDRMON_CTRL, dfi_regs + DDRMON_CTRL); + + /* set ddr type to dfi */ + if (ddr_type == LPDDR3) + writel_relaxed(LPDDR3_EN, dfi_regs + DDRMON_CTRL); + else if (ddr_type == LPDDR4) + writel_relaxed(LPDDR4_EN, dfi_regs + DDRMON_CTRL); + + /* enable count, use software mode */ + writel_relaxed(SOFTWARE_EN, dfi_regs + DDRMON_CTRL); +} + +static void rockchip_dfi_stop_hardware_counter(struct devfreq_event_dev *edev) +{ + struct rockchip_dfi *info = devfreq_event_get_drvdata(edev); + void __iomem *dfi_regs = info->regs; + u32 val; + + val = readl_relaxed(dfi_regs + DDRMON_CTRL); + val &= ~SOFTWARE_EN; + writel_relaxed(val, dfi_regs + DDRMON_CTRL); +} + +static int rockchip_dfi_get_busier_ch(struct devfreq_event_dev *edev) +{ + struct rockchip_dfi *info = devfreq_event_get_drvdata(edev); + u32 tmp, max = 0; +
[PATCH v5 4/8] Documentation: bindings: add dt documentation for dfi controller
This patch adds the documentation for rockchip dfi devfreq-event driver. Signed-off-by: Lin Huang --- Changes in v5: -None Changes in v4: -None Changes in v3: -None Changes in v2: -None Changes in v1: -None .../bindings/devfreq/event/rockchip-dfi.txt | 20 1 file changed, 20 insertions(+) create mode 100644 Documentation/devicetree/bindings/devfreq/event/rockchip-dfi.txt diff --git a/Documentation/devicetree/bindings/devfreq/event/rockchip-dfi.txt b/Documentation/devicetree/bindings/devfreq/event/rockchip-dfi.txt new file mode 100644 index 000..bf42255 --- /dev/null +++ b/Documentation/devicetree/bindings/devfreq/event/rockchip-dfi.txt @@ -0,0 +1,20 @@ + +* Rockchip rk3399 DFI device + +Required properties: +- compatible: Must be "rockchip,rk3399-dfi". +- reg: physical base address of each DFI and length of memory mapped region +- rockchip,pmu: phandle to the syscon managing the "pmu general register files" +- clocks: phandles for clock specified in "clock-names" property +- clock-names : the name of clock used by the DFI, must be "pclk_ddr_mon"; + +Example: + dfi: dfi at 0xff63 { + reg = <0x00 0xff63 0x00 0x4000>; + compatible = "rockchip,rk3399-dfi"; + rockchip,pmu = <>; + clocks = < PCLK_DDR_MON>; + clock-names = "pclk_ddr_mon"; + status = "disabled"; + }; + -- 1.9.1
[PATCH v5 3/8] clk: rockchip: rk3399: add ddrc clock support
add ddrc clock setting, so we can do ddr frequency scaling on rk3399 platform in future. Signed-off-by: Lin Huang --- Changes in v5: - fit for the ddr type Changes in v4: - None Changes in v3: - None Changes in v2: - remove clk_ddrc_dpll_src from critical clock list Changes in v1: - remove ddrc source CLK_IGNORE_UNUSED flag - move clk_ddrc and clk_ddrc_dpll_src to critical drivers/clk/rockchip/clk-rk3399.c | 19 +++ 1 file changed, 19 insertions(+) diff --git a/drivers/clk/rockchip/clk-rk3399.c b/drivers/clk/rockchip/clk-rk3399.c index c109d80..b962aeb 100644 --- a/drivers/clk/rockchip/clk-rk3399.c +++ b/drivers/clk/rockchip/clk-rk3399.c @@ -118,6 +118,10 @@ PNAME(mux_armclkb_p) = { "clk_core_b_lpll_src", "clk_core_b_bpll_src", "clk_core_b_dpll_src", "clk_core_b_gpll_src" }; +PNAME(mux_ddrclk_p)= { "clk_ddrc_lpll_src", + "clk_ddrc_bpll_src", + "clk_ddrc_dpll_src", + "clk_ddrc_gpll_src" }; PNAME(mux_aclk_cci_p) = { "cpll_aclk_cci_src", "gpll_aclk_cci_src", "npll_aclk_cci_src", @@ -1377,6 +1381,18 @@ static struct rockchip_clk_branch rk3399_clk_branches[] __initdata = { COMPOSITE_NOMUX(0, "clk_test", "clk_test_pre", CLK_IGNORE_UNUSED, RK3368_CLKSEL_CON(58), 0, 5, DFLAGS, RK3368_CLKGATE_CON(13), 11, GFLAGS), + + /* ddrc */ + GATE(0, "clk_ddrc_lpll_src", "lpll", 0, RK3399_CLKGATE_CON(3), +0, GFLAGS), + GATE(0, "clk_ddrc_bpll_src", "bpll", 0, RK3399_CLKGATE_CON(3), +1, GFLAGS), + GATE(0, "clk_ddrc_dpll_src", "dpll", 0, RK3399_CLKGATE_CON(3), +2, GFLAGS), + GATE(0, "clk_ddrc_gpll_src", "gpll", 0, RK3399_CLKGATE_CON(3), +3, GFLAGS), + COMPOSITE_DDRCLK(SCLK_DDRC, "clk_ddrc", mux_ddrclk_p, 0, + RK3399_CLKSEL_CON(6), 4, 2, 0, 0, ROCKCHIP_DDRCLK_SIP), }; static struct rockchip_clk_branch rk3399_clk_pmu_branches[] __initdata = { @@ -1487,6 +1503,9 @@ static const char *const rk3399_cru_critical_clocks[] __initconst = { "gpll_hclk_perilp1_src", "gpll_aclk_perilp0_src", "gpll_aclk_perihp_src", + + /* ddrc */ + "clk_ddrc" }; static const char *const rk3399_pmucru_critical_clocks[] __initconst = { -- 1.9.1
[PATCH v5 2/8] clk: rockchip: rk3399: add SCLK_DDRCLK ID for ddrc
Signed-off-by: Lin Huang --- Changes in v5: -None Changes in v4: -None Changes in v3: -None Changes in v2: -None Changes in v1: -None include/dt-bindings/clock/rk3399-cru.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/dt-bindings/clock/rk3399-cru.h b/include/dt-bindings/clock/rk3399-cru.h index 50a44cf..ce5f3e9 100644 --- a/include/dt-bindings/clock/rk3399-cru.h +++ b/include/dt-bindings/clock/rk3399-cru.h @@ -131,6 +131,7 @@ #define SCLK_DPHY_RX0_CFG 165 #define SCLK_RMII_SRC 166 #define SCLK_PCIEPHY_REF100M 167 +#define SCLK_DDRC 168 #define DCLK_VOP0 180 #define DCLK_VOP1 181 -- 1.9.1
[PATCH v5 1/8] clk: rockchip: add new clock-type for the ddrclk
On new rockchip platform(rk3399 etc), there have dcf controller to do ddr frequency scaling, and this controller will implement in arm-trust-firmware. We add a special clock-type to handle that. Signed-off-by: Lin Huang --- Changes in v5: - delete unuse mux_flag - use div_flag to distinguish sip call and other operate Changes in v4: - use arm_smccc_smc() to set/read ddr rate Changes in v3: - use sip call to set/read ddr rate Changes in v2: - use GENMASK instead val_mask - use divider_recalc_rate() instead DIV_ROUND_UP_ULL - cleanup code Changes in v1: - None drivers/clk/rockchip/Makefile | 1 + drivers/clk/rockchip/clk-ddr.c | 152 drivers/clk/rockchip/clk.c | 9 +++ drivers/clk/rockchip/clk.h | 33 include/soc/rockchip/rockchip_sip.h | 27 +++ 5 files changed, 222 insertions(+) create mode 100644 drivers/clk/rockchip/clk-ddr.c create mode 100644 include/soc/rockchip/rockchip_sip.h diff --git a/drivers/clk/rockchip/Makefile b/drivers/clk/rockchip/Makefile index f47a2fa..b5f2c8e 100644 --- a/drivers/clk/rockchip/Makefile +++ b/drivers/clk/rockchip/Makefile @@ -8,6 +8,7 @@ obj-y += clk-pll.o obj-y += clk-cpu.o obj-y += clk-inverter.o obj-y += clk-mmc-phase.o +obj-y += clk-ddr.o obj-$(CONFIG_RESET_CONTROLLER) += softrst.o obj-y += clk-rk3036.o diff --git a/drivers/clk/rockchip/clk-ddr.c b/drivers/clk/rockchip/clk-ddr.c new file mode 100644 index 000..756e1ad --- /dev/null +++ b/drivers/clk/rockchip/clk-ddr.c @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2016 Rockchip Electronics Co. Ltd. + * Author: Lin Huang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "clk.h" + +struct rockchip_ddrclk { + struct clk_hw hw; + void __iomem*reg_base; + int mux_offset; + int mux_shift; + int mux_width; + int div_shift; + int div_width; + int ddr_flag; + spinlock_t *lock; +}; + +#define to_rockchip_ddrclk_hw(hw) container_of(hw, struct rockchip_ddrclk, hw) + +static int rockchip_ddrclk_set_rate(struct clk_hw *hw, unsigned long drate, + unsigned long prate) +{ + struct rockchip_ddrclk *ddrclk = to_rockchip_ddrclk_hw(hw); + unsigned long flags; + struct arm_smccc_res res; + int ret; + + spin_lock_irqsave(ddrclk->lock, flags); + if (ddrclk->ddr_flag == ROCKCHIP_DDRCLK_SIP) { + arm_smccc_smc(SIP_DDR_FREQ, drate, 0, CONFIG_DRAM_SET_RATE, + 0, 0, 0, 0, ); + ret = res.a0; + } + spin_unlock_irqrestore(ddrclk->lock, flags); + + return ret; +} + +static unsigned long +rockchip_ddrclk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct rockchip_ddrclk *ddrclk = to_rockchip_ddrclk_hw(hw); + struct arm_smccc_res res; + int ret; + + if (ddrclk->ddr_flag == ROCKCHIP_DDRCLK_SIP) { + arm_smccc_smc(SIP_DDR_FREQ, 0, 0, CONFIG_DRAM_GET_RATE, + 0, 0, 0, 0, ); + ret = res.a0; + } + + return ret; +} + +static long clk_ddrclk_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + return rate; +} + +static u8 rockchip_ddrclk_get_parent(struct clk_hw *hw) +{ + struct rockchip_ddrclk *ddrclk = to_rockchip_ddrclk_hw(hw); + int num_parents = clk_hw_get_num_parents(hw); + u32 val; + + val = clk_readl(ddrclk->reg_base + + ddrclk->mux_offset) >> ddrclk->mux_shift; + val &= GENMASK(ddrclk->mux_width - 1, 0); + + if (val >= num_parents) + return -EINVAL; + + return val; +} + +static const struct clk_ops rockchip_ddrclk_ops = { + .recalc_rate = rockchip_ddrclk_recalc_rate, + .set_rate = rockchip_ddrclk_set_rate, + .round_rate = clk_ddrclk_round_rate, + .get_parent = rockchip_ddrclk_get_parent, +}; + +struct clk *rockchip_clk_register_ddrclk(const char *name, int flags, +const char *const *parent_names, +u8 num_parents, int mux_offset, +int mux_shift, int mux_widt
[PATCH v5 0/8] rk3399 support ddr frequency scaling
rk3399 platform have dfi controller can monitor ddr load, and dcf controller to handle ddr register so we can get the right ddr frequency and make ddr controller happy work(which will implement in bl31). So we do ddr frequency scaling with following flow: kernelbl31 monitor ddr load | | get_target_rate | | pass rate to bl31 clk_set_rate(ddr) ->run dcf flow | | | | wait dcf interrupt<---trigger dcf interrupt | | return Lin Huang (8): clk: rockchip: add new clock-type for the ddrclk clk: rockchip: rk3399: add SCLK_DDRCLK ID for ddrc clk: rockchip: rk3399: add ddrc clock support Documentation: bindings: add dt documentation for dfi controller PM / devfreq: event: support rockchip dfi controller Documentation: bindings: add dt documentation for rk3399 dmc PM / devfreq: rockchip: add devfreq driver for rk3399 dmc drm/rockchip: Add dmc notifier in vop driver .../bindings/devfreq/event/rockchip-dfi.txt| 20 + .../devicetree/bindings/devfreq/rk3399_dmc.txt | 35 ++ drivers/clk/rockchip/Makefile | 1 + drivers/clk/rockchip/clk-ddr.c | 152 ++ drivers/clk/rockchip/clk-rk3399.c | 19 + drivers/clk/rockchip/clk.c | 9 + drivers/clk/rockchip/clk.h | 33 ++ drivers/devfreq/Kconfig| 9 + drivers/devfreq/Makefile | 1 + drivers/devfreq/event/Kconfig | 7 + drivers/devfreq/event/Makefile | 1 + drivers/devfreq/event/rockchip-dfi.c | 253 ++ drivers/devfreq/rk3399_dmc.c | 512 + drivers/gpu/drm/rockchip/rockchip_drm_vop.c| 128 +- include/dt-bindings/clock/rk3399-cru.h | 1 + include/soc/rockchip/rockchip_sip.h| 27 ++ 16 files changed, 1205 insertions(+), 3 deletions(-) create mode 100644 Documentation/devicetree/bindings/devfreq/event/rockchip-dfi.txt create mode 100644 Documentation/devicetree/bindings/devfreq/rk3399_dmc.txt create mode 100644 drivers/clk/rockchip/clk-ddr.c create mode 100644 drivers/devfreq/event/rockchip-dfi.c create mode 100644 drivers/devfreq/rk3399_dmc.c create mode 100644 include/soc/rockchip/rockchip_sip.h -- 1.9.1
[PATCH v4 7/7] drm/rockchip: Add dmc notifier in vop driver
when in ddr frequency scaling process, vop can not do enable or disable operation, since dcf will base on vop vblank time to do frequency scaling and need to get vop irq if there have vop enabled. So need register to devfreq notifier, and we can get the dmc status. Also, when there have two vop enabled, we need to disable dmc, since dcf only base on one vop vblank time, so the other panel will flicker when do ddr frequency scaling. Signed-off-by: Lin Huang --- Changes in v4: - register notifier to devfreq_register_notifier - use DEVFREQ_PRECHANGE and DEVFREQ_POSTCHANGE to get dmc status - when two vop enable, disable dmc - when two vop back to one vop, enable dmc Changes in v3: - when do vop eanble/disable, dmc will wait until it finish Changes in v2: - None Changes in v1: - use wait_event instead usleep drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 124 ++-- 1 file changed, 119 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c index c179933..3b251d1 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c @@ -12,6 +12,8 @@ * GNU General Public License for more details. */ +#include +#include #include #include #include @@ -127,6 +129,13 @@ struct vop { const struct vop_data *data; + struct devfreq *devfreq; + struct devfreq_event_dev *devfreq_event_dev; + struct notifier_block dmc_nb; + int dmc_in_process; + int vop_switch_status; + wait_queue_head_t wait_dmc_queue; + wait_queue_head_t wait_vop_switch_queue; uint32_t *regsbak; void __iomem *regs; @@ -500,24 +509,60 @@ static void vop_line_flag_irq_disable(struct vop *vop) spin_unlock_irqrestore(>irq_lock, flags); } +static int dmc_notify(struct notifier_block *nb, unsigned long event, + void *data) +{ + struct vop *vop = container_of(nb, struct vop, dmc_nb); + + if (event == DEVFREQ_PRECHANGE) { + + /* +* check if vop in enable or disable process, +* if yes, wait until it finish, use 200ms as +* timeout. +*/ + if (!wait_event_timeout(vop->wait_vop_switch_queue, + !vop->vop_switch_status, HZ / 5)) + dev_warn(vop->dev, +"Timeout waiting for vop swtich status\n"); + vop->dmc_in_process = 1; + } else if (event == DEVFREQ_POSTCHANGE) { + vop->dmc_in_process = 0; + wake_up(>wait_dmc_queue); + } + + return NOTIFY_OK; +} + static void vop_enable(struct drm_crtc *crtc) { struct vop *vop = to_vop(crtc); + int num_enabled_crtc = 0; int ret; if (vop->is_enabled) return; + /* +* if in dmc scaling frequency process, wait until it finishes +* use 100ms as timeout time. +*/ + if (!wait_event_timeout(vop->wait_dmc_queue, + !vop->dmc_in_process, HZ / 5)) + dev_warn(vop->dev, +"Timeout waiting for dmc when vop enable\n"); + + vop->vop_switch_status = 1; ret = pm_runtime_get_sync(vop->dev); if (ret < 0) { dev_err(vop->dev, "failed to get pm runtime: %d\n", ret); - return; + goto err; } ret = clk_enable(vop->hclk); if (ret < 0) { dev_err(vop->dev, "failed to enable hclk - %d\n", ret); - return; + goto err; } ret = clk_enable(vop->dclk); @@ -531,7 +576,6 @@ static void vop_enable(struct drm_crtc *crtc) dev_err(vop->dev, "failed to enable aclk - %d\n", ret); goto err_disable_dclk; } - /* * Slave iommu shares power, irq and clock with vop. It was associated * automatically with this master device via common driver code. @@ -560,6 +604,21 @@ static void vop_enable(struct drm_crtc *crtc) drm_crtc_vblank_on(crtc); + vop->vop_switch_status = 0; + wake_up(>wait_vop_switch_queue); + + /* check how many vop we use now */ + drm_for_each_crtc(crtc, vop->drm_dev) { + if (crtc->state->enable) + num_enabled_crtc++; + } + + /* if enable two vop, need to disable dmc */ + if ((num_enabled_crtc > 1) && vop->devfreq) { + if (vop->devfreq_event_dev) + devfreq_event_disable_edev(vop->devfreq_event_dev); + devfreq_suspend_device(vop->devfreq); + } return; err_disable_aclk: @@ -568,17 +627,33 @@ err_disabl
[PATCH v4 6/7] PM / devfreq: rockchip: add devfreq driver for rk3399 dmc
base on dfi result, we do ddr frequency scaling, register dmc driver to devfreq framework, and use simple-ondemand policy. Signed-off-by: Lin Huang --- Changes in v4: - use arm_smccc_smc() function talk to bl31 - delete rockchip_dmc.c file and config - delete dmc_notify - adjust probe order Changes in v3: - operate dram setting through sip call - imporve set rate flow Changes in v2: - None Changes in v1: - move dfi controller to event - fix set voltage sequence when set rate fail - change Kconfig type from tristate to bool - move unuse EXPORT_SYMBOL_GPL() drivers/devfreq/Kconfig | 1 + drivers/devfreq/Makefile | 1 + drivers/devfreq/rockchip/Kconfig | 8 + drivers/devfreq/rockchip/Makefile | 1 + drivers/devfreq/rockchip/rk3399_dmc.c | 473 ++ 5 files changed, 484 insertions(+) create mode 100644 drivers/devfreq/rockchip/Kconfig create mode 100644 drivers/devfreq/rockchip/Makefile create mode 100644 drivers/devfreq/rockchip/rk3399_dmc.c diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig index 64281bb..acb2a57 100644 --- a/drivers/devfreq/Kconfig +++ b/drivers/devfreq/Kconfig @@ -99,5 +99,6 @@ config ARM_TEGRA_DEVFREQ operating frequencies and voltages with OPP support. source "drivers/devfreq/event/Kconfig" +source "drivers/devfreq/rockchip/Kconfig" endif # PM_DEVFREQ diff --git a/drivers/devfreq/Makefile b/drivers/devfreq/Makefile index 5134f9e..d844e23 100644 --- a/drivers/devfreq/Makefile +++ b/drivers/devfreq/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_DEVFREQ_GOV_USERSPACE) += governor_userspace.o obj-$(CONFIG_ARM_EXYNOS4_BUS_DEVFREQ) += exynos/ obj-$(CONFIG_ARM_EXYNOS5_BUS_DEVFREQ) += exynos/ obj-$(CONFIG_ARM_TEGRA_DEVFREQ)+= tegra-devfreq.o +obj-$(CONFIG_ARCH_ROCKCHIP)+= rockchip/ # DEVFREQ Event Drivers obj-$(CONFIG_PM_DEVFREQ_EVENT) += event/ diff --git a/drivers/devfreq/rockchip/Kconfig b/drivers/devfreq/rockchip/Kconfig new file mode 100644 index 000..d8f9e66 --- /dev/null +++ b/drivers/devfreq/rockchip/Kconfig @@ -0,0 +1,8 @@ +config ARM_RK3399_DMC_DEVFREQ + tristate "ARM RK3399 DMC DEVFREQ Driver" + select PM_OPP + select DEVFREQ_GOV_SIMPLE_ONDEMAND + help + This adds the DEVFREQ driver for the RK3399 dmc. It sets the frequency + for the memory controller and reads the usage counts from hardware. + diff --git a/drivers/devfreq/rockchip/Makefile b/drivers/devfreq/rockchip/Makefile new file mode 100644 index 000..c62c105 --- /dev/null +++ b/drivers/devfreq/rockchip/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_ARM_RK3399_DMC_DEVFREQ) += rk3399_dmc.o diff --git a/drivers/devfreq/rockchip/rk3399_dmc.c b/drivers/devfreq/rockchip/rk3399_dmc.c new file mode 100644 index 000..527aa11 --- /dev/null +++ b/drivers/devfreq/rockchip/rk3399_dmc.c @@ -0,0 +1,473 @@ +/* + * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd + * Author: Lin Huang + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +struct dram_timing { + unsigned int ddr3_speed_bin; + unsigned int pd_idle; + unsigned int sr_idle; + unsigned int sr_mc_gate_idle; + unsigned int srpd_lite_idle; + unsigned int standby_idle; + unsigned int dram_dll_dis_freq; + unsigned int phy_dll_dis_freq; + unsigned int ddr3_odt_dis_freq; + unsigned int ddr3_drv; + unsigned int ddr3_odt; + unsigned int phy_ddr3_ca_drv; + unsigned int phy_ddr3_dq_drv; + unsigned int phy_ddr3_odt; + unsigned int lpddr3_odt_dis_freq; + unsigned int lpddr3_drv; + unsigned int lpddr3_odt; + unsigned int phy_lpddr3_ca_drv; + unsigned int phy_lpddr3_dq_drv; + unsigned int phy_lpddr3_odt; + unsigned int lpddr4_odt_dis_freq; + unsigned int lpddr4_drv; + unsigned int lpddr4_dq_odt; + unsigned int lpddr4_ca_odt; + unsigned int phy_lpddr4_ca_drv; + unsigned int phy_lpddr4_ck_cs_drv; + unsigned int phy_lpddr4_dq_drv; + unsigned int phy_lpddr4_odt; +}; + +struct rk3399_dmcfreq { + struct device *dev; + struct devfreq *devfreq; + struct devfreq_simple_ondemand_data ondemand_data; + struct clk *dmc_clk; + struct devfreq_event_dev *edev; + struct mutex lock; +
[PATCH v4 5/7] PM / devfreq: event: support rockchip dfi controller
on rk3399 platform, there is dfi conroller can monitor ddr load, base on this result, we can do ddr freqency scaling. Signed-off-by: Lin Huang Acked-by: Chanwoo Choi --- Changes in v4: - None Changes in v3: - None Changes in v2: - use clk_disable_unprepare and clk_enable_prepare - remove clk_enable_prepare in probe - remove rockchip_dfi_remove function Changes in v1: - None drivers/devfreq/event/Kconfig| 7 + drivers/devfreq/event/Makefile | 1 + drivers/devfreq/event/rockchip-dfi.c | 253 +++ 3 files changed, 261 insertions(+) create mode 100644 drivers/devfreq/event/rockchip-dfi.c diff --git a/drivers/devfreq/event/Kconfig b/drivers/devfreq/event/Kconfig index a11720a..ff9279f 100644 --- a/drivers/devfreq/event/Kconfig +++ b/drivers/devfreq/event/Kconfig @@ -22,4 +22,11 @@ config DEVFREQ_EVENT_EXYNOS_PPMU (Platform Performance Monitoring Unit) counters to estimate the utilization of each module. +config DEVFREQ_EVENT_ROCKCHIP_DFI + tristate "ROCKCHIP DFI DEVFREQ event Driver" + depends on ARCH_ROCKCHIP + help + This add the devfreq-event driver for Rockchip SoC. It provides DFI + (DDR Monitor Module) driver to count ddr load. + endif # PM_DEVFREQ_EVENT diff --git a/drivers/devfreq/event/Makefile b/drivers/devfreq/event/Makefile index be146ea..e3f88fc 100644 --- a/drivers/devfreq/event/Makefile +++ b/drivers/devfreq/event/Makefile @@ -1,2 +1,3 @@ # Exynos DEVFREQ Event Drivers obj-$(CONFIG_DEVFREQ_EVENT_EXYNOS_PPMU) += exynos-ppmu.o +obj-$(CONFIG_DEVFREQ_EVENT_ROCKCHIP_DFI) += rockchip-dfi.o diff --git a/drivers/devfreq/event/rockchip-dfi.c b/drivers/devfreq/event/rockchip-dfi.c new file mode 100644 index 000..96a0307 --- /dev/null +++ b/drivers/devfreq/event/rockchip-dfi.c @@ -0,0 +1,253 @@ +/* + * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd + * Author: Lin Huang + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RK3399_DMC_NUM_CH 2 + +/* DDRMON_CTRL */ +#define DDRMON_CTRL0x04 +#define CLR_DDRMON_CTRL(0x1f << 0) +#define LPDDR4_EN (0x10001 << 4) +#define HARDWARE_EN(0x10001 << 3) +#define LPDDR3_EN (0x10001 << 2) +#define SOFTWARE_EN(0x10001 << 1) +#define TIME_CNT_EN(0x10001 << 0) + +#define DDRMON_CH0_COUNT_NUM 0x28 +#define DDRMON_CH0_DFI_ACCESS_NUM 0x2c +#define DDRMON_CH1_COUNT_NUM 0x3c +#define DDRMON_CH1_DFI_ACCESS_NUM 0x40 + +/* pmu grf */ +#define PMUGRF_OS_REG2 0x308 +#define DDRTYPE_SHIFT 13 +#define DDRTYPE_MASK 7 + +enum { + DDR3 = 3, + LPDDR3 = 6, + LPDDR4 = 7, + UNUSED = 0xFF +}; + +struct dmc_usage { + u32 access; + u32 total; +}; + +struct rockchip_dfi { + struct devfreq_event_dev *edev; + struct devfreq_event_desc *desc; + struct dmc_usage ch_usage[RK3399_DMC_NUM_CH]; + struct device *dev; + void __iomem *regs; + struct regmap *regmap_pmu; + struct clk *clk; +}; + +static void rockchip_dfi_start_hardware_counter(struct devfreq_event_dev *edev) +{ + struct rockchip_dfi *info = devfreq_event_get_drvdata(edev); + void __iomem *dfi_regs = info->regs; + u32 val; + u32 ddr_type; + + /* get ddr type */ + regmap_read(info->regmap_pmu, PMUGRF_OS_REG2, ); + ddr_type = (val >> DDRTYPE_SHIFT) & DDRTYPE_MASK; + + /* clear DDRMON_CTRL setting */ + writel_relaxed(CLR_DDRMON_CTRL, dfi_regs + DDRMON_CTRL); + + /* set ddr type to dfi */ + if (ddr_type == LPDDR3) + writel_relaxed(LPDDR3_EN, dfi_regs + DDRMON_CTRL); + else if (ddr_type == LPDDR4) + writel_relaxed(LPDDR4_EN, dfi_regs + DDRMON_CTRL); + + /* enable count, use software mode */ + writel_relaxed(SOFTWARE_EN, dfi_regs + DDRMON_CTRL); +} + +static void rockchip_dfi_stop_hardware_counter(struct devfreq_event_dev *edev) +{ + struct rockchip_dfi *info = devfreq_event_get_drvdata(edev); + void __iomem *dfi_regs = info->regs; + u32 val; + + val = readl_relaxed(dfi_regs + DDRMON_CTRL); + val &= ~SOFTWARE_EN; + writel_relaxed(val, dfi_regs + DDRMON_CTRL); +} + +static int rockchip_dfi_get_busier_ch(struct devfreq_event_dev *edev) +{ + struct rockchip_dfi *info = devfreq_event_get_
[PATCH v4 4/7] clk: rockchip: rk3399: add ddrc clock support
add ddrc clock setting, so we can do ddr frequency scaling on rk3399 platform in future. Signed-off-by: Lin Huang --- Changes in v4: - None Changes in v3: - None Changes in v2: - remove clk_ddrc_dpll_src from critical clock list Changes in v1: - remove ddrc source CLK_IGNORE_UNUSED flag - move clk_ddrc and clk_ddrc_dpll_src to critical drivers/clk/rockchip/clk-rk3399.c | 19 +++ 1 file changed, 19 insertions(+) diff --git a/drivers/clk/rockchip/clk-rk3399.c b/drivers/clk/rockchip/clk-rk3399.c index d4a1cf0..b7b42d9 100644 --- a/drivers/clk/rockchip/clk-rk3399.c +++ b/drivers/clk/rockchip/clk-rk3399.c @@ -118,6 +118,10 @@ PNAME(mux_armclkb_p) = { "clk_core_b_lpll_src", "clk_core_b_bpll_src", "clk_core_b_dpll_src", "clk_core_b_gpll_src" }; +PNAME(mux_ddrclk_p)= { "clk_ddrc_lpll_src", + "clk_ddrc_bpll_src", + "clk_ddrc_dpll_src", + "clk_ddrc_gpll_src" }; PNAME(mux_aclk_cci_p) = { "cpll_aclk_cci_src", "gpll_aclk_cci_src", "npll_aclk_cci_src", @@ -1377,6 +1381,18 @@ static struct rockchip_clk_branch rk3399_clk_branches[] __initdata = { COMPOSITE_NOMUX(0, "clk_test", "clk_test_pre", CLK_IGNORE_UNUSED, RK3368_CLKSEL_CON(58), 0, 5, DFLAGS, RK3368_CLKGATE_CON(13), 11, GFLAGS), + + /* ddrc */ + GATE(0, "clk_ddrc_lpll_src", "lpll", 0, RK3399_CLKGATE_CON(3), +0, GFLAGS), + GATE(0, "clk_ddrc_bpll_src", "bpll", 0, RK3399_CLKGATE_CON(3), +1, GFLAGS), + GATE(0, "clk_ddrc_dpll_src", "dpll", 0, RK3399_CLKGATE_CON(3), +2, GFLAGS), + GATE(0, "clk_ddrc_gpll_src", "gpll", 0, RK3399_CLKGATE_CON(3), +3, GFLAGS), + COMPOSITE_DDRC(SCLK_DDRCLK, "clk_ddrc", mux_ddrclk_p, 0, + RK3399_CLKSEL_CON(6), 4, 2, MFLAGS, 0, 3, DFLAGS), }; static struct rockchip_clk_branch rk3399_clk_pmu_branches[] __initdata = { @@ -1487,6 +1503,9 @@ static const char *const rk3399_cru_critical_clocks[] __initconst = { "gpll_hclk_perilp1_src", "gpll_aclk_perilp0_src", "gpll_aclk_perihp_src", + + /* ddrc */ + "clk_ddrc" }; static const char *const rk3399_pmucru_critical_clocks[] __initconst = { -- 1.9.1
[PATCH v4 3/7] clk: rockchip: rk3399: add SCLK_DDRCLK ID for ddrc
Signed-off-by: Lin Huang --- Changes in v4: -None Changes in v3: -None Changes in v2: - None Changes in v1: - None include/dt-bindings/clock/rk3399-cru.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/dt-bindings/clock/rk3399-cru.h b/include/dt-bindings/clock/rk3399-cru.h index 50a44cf..8a0f0442 100644 --- a/include/dt-bindings/clock/rk3399-cru.h +++ b/include/dt-bindings/clock/rk3399-cru.h @@ -131,6 +131,7 @@ #define SCLK_DPHY_RX0_CFG 165 #define SCLK_RMII_SRC 166 #define SCLK_PCIEPHY_REF100M 167 +#define SCLK_DDRCLK168 #define DCLK_VOP0 180 #define DCLK_VOP1 181 -- 1.9.1
[PATCH v4 2/7] clk: rockchip: add new clock-type for the ddrclk
On new rockchip platform(rk3399 etc), there have dcf controller to do ddr frequency scaling, and this controller will implement in arm-trust-firmware. We add a special clock-type to handle that. Signed-off-by: Lin Huang --- Changes in v4: - use arm_smccc_smc() to set/read ddr rate Changes in v3: - use sip call to set/read ddr rate Changes in v2: - use GENMASK instead val_mask - use divider_recalc_rate() instead DIV_ROUND_UP_ULL - cleanup code Changes in v1: - None drivers/clk/rockchip/Makefile | 1 + drivers/clk/rockchip/clk-ddr.c | 146 drivers/clk/rockchip/clk.c | 9 +++ drivers/clk/rockchip/clk.h | 27 +++ include/soc/rockchip/rockchip_sip.h | 27 +++ 5 files changed, 210 insertions(+) create mode 100644 drivers/clk/rockchip/clk-ddr.c create mode 100644 include/soc/rockchip/rockchip_sip.h diff --git a/drivers/clk/rockchip/Makefile b/drivers/clk/rockchip/Makefile index f47a2fa..b5f2c8e 100644 --- a/drivers/clk/rockchip/Makefile +++ b/drivers/clk/rockchip/Makefile @@ -8,6 +8,7 @@ obj-y += clk-pll.o obj-y += clk-cpu.o obj-y += clk-inverter.o obj-y += clk-mmc-phase.o +obj-y += clk-ddr.o obj-$(CONFIG_RESET_CONTROLLER) += softrst.o obj-y += clk-rk3036.o diff --git a/drivers/clk/rockchip/clk-ddr.c b/drivers/clk/rockchip/clk-ddr.c new file mode 100644 index 000..dd657c6 --- /dev/null +++ b/drivers/clk/rockchip/clk-ddr.c @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2016 Rockchip Electronics Co. Ltd. + * Author: Lin Huang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "clk.h" + +struct rockchip_ddrclk { + struct clk_hw hw; + void __iomem*reg_base; + int mux_offset; + int mux_shift; + int mux_width; + int mux_flag; + int div_shift; + int div_width; + int div_flag; + spinlock_t *lock; +}; + +#define to_rockchip_ddrclk_hw(hw) container_of(hw, struct rockchip_ddrclk, hw) + +static int rockchip_ddrclk_set_rate(struct clk_hw *hw, unsigned long drate, + unsigned long prate) +{ + struct rockchip_ddrclk *ddrclk = to_rockchip_ddrclk_hw(hw); + unsigned long flags; + struct arm_smccc_res res; + + spin_lock_irqsave(ddrclk->lock, flags); + arm_smccc_smc(SIP_DDR_FREQ, drate, 0, CONFIG_DRAM_SET_RATE, + 0, 0, 0, 0, ); + spin_unlock_irqrestore(ddrclk->lock, flags); + + return res.a0; +} + +static unsigned long +rockchip_ddrclk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct arm_smccc_res res; + + arm_smccc_smc(SIP_DDR_FREQ, 0, 0, CONFIG_DRAM_GET_RATE, + 0, 0, 0, 0, ); + + return res.a0; +} + +static long clk_ddrclk_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + return rate; +} + +static u8 rockchip_ddrclk_get_parent(struct clk_hw *hw) +{ + struct rockchip_ddrclk *ddrclk = to_rockchip_ddrclk_hw(hw); + int num_parents = clk_hw_get_num_parents(hw); + u32 val; + + val = clk_readl(ddrclk->reg_base + + ddrclk->mux_offset) >> ddrclk->mux_shift; + val &= GENMASK(ddrclk->mux_width - 1, 0); + + if (val >= num_parents) + return -EINVAL; + + return val; +} + +static const struct clk_ops rockchip_ddrclk_ops = { + .recalc_rate = rockchip_ddrclk_recalc_rate, + .set_rate = rockchip_ddrclk_set_rate, + .round_rate = clk_ddrclk_round_rate, + .get_parent = rockchip_ddrclk_get_parent, +}; + +struct clk *rockchip_clk_register_ddrclk(const char *name, int flags, +const char *const *parent_names, +u8 num_parents, int mux_offset, +int mux_shift, int mux_width, +int mux_flag, int div_shift, +int div_width, int div_flag, +void __iomem *reg_base, +spinlock_t *lock) +{ + struct rockchip_ddrclk *ddrclk; + struct clk_init_data init; + struct clk *clk; + + ddr
[PATCH v4 1/7] clk: rockchip: add clock flag parameter when register pll
From: Heiko Stübner <he...@sntech.de> add clock flag parameter so we can pass specific clock flag (like CLK_GET_RATE_NOCACHE etc..)to pll driver. Signed-off-by: Heiko Stübner Signed-off-by: Lin Huang --- Changes in v4: - None Changes in v3: - None Changes in v2: - None Changes in v1: - None drivers/clk/rockchip/clk-pll.c | 4 ++-- drivers/clk/rockchip/clk.c | 2 +- drivers/clk/rockchip/clk.h | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/clk/rockchip/clk-pll.c b/drivers/clk/rockchip/clk-pll.c index 8ac73bc..d824c36 100644 --- a/drivers/clk/rockchip/clk-pll.c +++ b/drivers/clk/rockchip/clk-pll.c @@ -864,7 +864,7 @@ struct clk *rockchip_clk_register_pll(struct rockchip_clk_provider *ctx, u8 num_parents, int con_offset, int grf_lock_offset, int lock_shift, int mode_offset, int mode_shift, struct rockchip_pll_rate_table *rate_table, - u8 clk_pll_flags) + unsigned long flags, u8 clk_pll_flags) { const char *pll_parents[3]; struct clk_init_data init; @@ -919,7 +919,7 @@ struct clk *rockchip_clk_register_pll(struct rockchip_clk_provider *ctx, init.name = pll_name; /* keep all plls untouched for now */ - init.flags = CLK_IGNORE_UNUSED; + init.flags = flags | CLK_IGNORE_UNUSED; init.parent_names = _names[0]; init.num_parents = 1; diff --git a/drivers/clk/rockchip/clk.c b/drivers/clk/rockchip/clk.c index f0a8be1..9a046f1 100644 --- a/drivers/clk/rockchip/clk.c +++ b/drivers/clk/rockchip/clk.c @@ -390,7 +390,7 @@ void __init rockchip_clk_register_plls(struct rockchip_clk_provider *ctx, list->con_offset, grf_lock_offset, list->lock_shift, list->mode_offset, list->mode_shift, list->rate_table, - list->pll_flags); + list->flags, list->pll_flags); if (IS_ERR(clk)) { pr_err("%s: failed to register clock %s\n", __func__, list->name); diff --git a/drivers/clk/rockchip/clk.h b/drivers/clk/rockchip/clk.h index 1abb7d0..bac775d 100644 --- a/drivers/clk/rockchip/clk.h +++ b/drivers/clk/rockchip/clk.h @@ -238,7 +238,7 @@ struct clk *rockchip_clk_register_pll(struct rockchip_clk_provider *ctx, u8 num_parents, int con_offset, int grf_lock_offset, int lock_shift, int mode_offset, int mode_shift, struct rockchip_pll_rate_table *rate_table, - u8 clk_pll_flags); + unsigned long flags, u8 clk_pll_flags); struct rockchip_cpuclk_clksel { int reg; -- 1.9.1
[PATCH v4 0/7] rk3399 support ddr frequency scaling
rk3399 platform have dfi controller can monitor ddr load, and dcf controller to handle ddr register so we can get the right ddr frequency and make ddr controller happy work(which will implement in bl31). So we do ddr frequency scaling with following flow: kernelbl31 monitor ddr load | | get_target_rate | | pass rate to bl31 clk_set_rate(ddr) ->run dcf flow | | | | wait dcf interrupt<---trigger dcf interrupt | | return Lin Huang (6): clk: rockchip: add new clock-type for the ddrclk clk: rockchip: rk3399: add SCLK_DDRCLK ID for ddrc clk: rockchip: rk3399: add ddrc clock support PM / devfreq: event: support rockchip dfi controller PM / devfreq: rockchip: add devfreq driver for rk3399 dmc drm/rockchip: Add dmc notifier in vop driver Heiko Stübner (1): clk: rockchip: add clock flag parameter when register pll Lin Huang (6): clk: rockchip: add new clock-type for the ddrclk clk: rockchip: rk3399: add SCLK_DDRCLK ID for ddrc clk: rockchip: rk3399: add ddrc clock support PM / devfreq: event: support rockchip dfi controller PM / devfreq: rockchip: add devfreq driver for rk3399 dmc drm/rockchip: Add dmc notifier in vop driver drivers/clk/rockchip/Makefile | 1 + drivers/clk/rockchip/clk-ddr.c | 146 + drivers/clk/rockchip/clk-pll.c | 4 +- drivers/clk/rockchip/clk-rk3399.c | 19 ++ drivers/clk/rockchip/clk.c | 11 +- drivers/clk/rockchip/clk.h | 29 +- drivers/devfreq/Kconfig | 1 + drivers/devfreq/Makefile| 1 + drivers/devfreq/event/Kconfig | 7 + drivers/devfreq/event/Makefile | 1 + drivers/devfreq/event/rockchip-dfi.c| 253 +++ drivers/devfreq/rockchip/Kconfig| 8 + drivers/devfreq/rockchip/Makefile | 1 + drivers/devfreq/rockchip/rk3399_dmc.c | 473 drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 124 +++- include/dt-bindings/clock/rk3399-cru.h | 1 + include/soc/rockchip/rockchip_sip.h | 27 ++ 17 files changed, 1098 insertions(+), 9 deletions(-) create mode 100644 drivers/clk/rockchip/clk-ddr.c create mode 100644 drivers/devfreq/event/rockchip-dfi.c create mode 100644 drivers/devfreq/rockchip/Kconfig create mode 100644 drivers/devfreq/rockchip/Makefile create mode 100644 drivers/devfreq/rockchip/rk3399_dmc.c create mode 100644 include/soc/rockchip/rockchip_sip.h -- 1.9.1