diff options
Diffstat (limited to 'kernel/drivers/gpu/drm/msm/dsi')
-rw-r--r-- | kernel/drivers/gpu/drm/msm/dsi/dsi.c | 99 | ||||
-rw-r--r-- | kernel/drivers/gpu/drm/msm/dsi/dsi.h | 98 | ||||
-rw-r--r-- | kernel/drivers/gpu/drm/msm/dsi/dsi.xml.h | 582 | ||||
-rw-r--r-- | kernel/drivers/gpu/drm/msm/dsi/dsi_cfg.c | 92 | ||||
-rw-r--r-- | kernel/drivers/gpu/drm/msm/dsi/dsi_cfg.h | 44 | ||||
-rw-r--r-- | kernel/drivers/gpu/drm/msm/dsi/dsi_host.c | 388 | ||||
-rw-r--r-- | kernel/drivers/gpu/drm/msm/dsi/dsi_manager.c | 289 | ||||
-rw-r--r-- | kernel/drivers/gpu/drm/msm/dsi/dsi_phy.c | 352 | ||||
-rw-r--r-- | kernel/drivers/gpu/drm/msm/dsi/mmss_cc.xml.h | 26 | ||||
-rw-r--r-- | kernel/drivers/gpu/drm/msm/dsi/phy/dsi_phy.c | 452 | ||||
-rw-r--r-- | kernel/drivers/gpu/drm/msm/dsi/phy/dsi_phy.h | 89 | ||||
-rw-r--r-- | kernel/drivers/gpu/drm/msm/dsi/phy/dsi_phy_20nm.c | 150 | ||||
-rw-r--r-- | kernel/drivers/gpu/drm/msm/dsi/phy/dsi_phy_28nm.c | 164 | ||||
-rw-r--r-- | kernel/drivers/gpu/drm/msm/dsi/pll/dsi_pll.c | 170 | ||||
-rw-r--r-- | kernel/drivers/gpu/drm/msm/dsi/pll/dsi_pll.h | 98 | ||||
-rw-r--r-- | kernel/drivers/gpu/drm/msm/dsi/pll/dsi_pll_28nm.c | 647 | ||||
-rw-r--r-- | kernel/drivers/gpu/drm/msm/dsi/sfpb.xml.h | 39 |
17 files changed, 3022 insertions, 757 deletions
diff --git a/kernel/drivers/gpu/drm/msm/dsi/dsi.c b/kernel/drivers/gpu/drm/msm/dsi/dsi.c index ad50b8022..6edcd6f57 100644 --- a/kernel/drivers/gpu/drm/msm/dsi/dsi.c +++ b/kernel/drivers/gpu/drm/msm/dsi/dsi.c @@ -15,20 +15,55 @@ struct drm_encoder *msm_dsi_get_encoder(struct msm_dsi *msm_dsi) { - if (!msm_dsi || !msm_dsi->panel) + if (!msm_dsi || !msm_dsi_device_connected(msm_dsi)) return NULL; - return (msm_dsi->panel_flags & MIPI_DSI_MODE_VIDEO) ? + return (msm_dsi->device_flags & MIPI_DSI_MODE_VIDEO) ? msm_dsi->encoders[MSM_DSI_VIDEO_ENCODER_ID] : msm_dsi->encoders[MSM_DSI_CMD_ENCODER_ID]; } +static int dsi_get_phy(struct msm_dsi *msm_dsi) +{ + struct platform_device *pdev = msm_dsi->pdev; + struct platform_device *phy_pdev; + struct device_node *phy_node; + + phy_node = of_parse_phandle(pdev->dev.of_node, "qcom,dsi-phy", 0); + if (!phy_node) { + dev_err(&pdev->dev, "cannot find phy device\n"); + return -ENXIO; + } + + phy_pdev = of_find_device_by_node(phy_node); + if (phy_pdev) + msm_dsi->phy = platform_get_drvdata(phy_pdev); + + of_node_put(phy_node); + + if (!phy_pdev || !msm_dsi->phy) { + dev_err(&pdev->dev, "%s: phy driver is not ready\n", __func__); + return -EPROBE_DEFER; + } + + msm_dsi->phy_dev = get_device(&phy_pdev->dev); + + return 0; +} + static void dsi_destroy(struct msm_dsi *msm_dsi) { if (!msm_dsi) return; msm_dsi_manager_unregister(msm_dsi); + + if (msm_dsi->phy_dev) { + put_device(msm_dsi->phy_dev); + msm_dsi->phy = NULL; + msm_dsi->phy_dev = NULL; + } + if (msm_dsi->host) { msm_dsi_host_destroy(msm_dsi->host); msm_dsi->host = NULL; @@ -39,20 +74,15 @@ static void dsi_destroy(struct msm_dsi *msm_dsi) static struct msm_dsi *dsi_init(struct platform_device *pdev) { - struct msm_dsi *msm_dsi = NULL; + struct msm_dsi *msm_dsi; int ret; - if (!pdev) { - dev_err(&pdev->dev, "no dsi device\n"); - ret = -ENXIO; - goto fail; - } + if (!pdev) + return ERR_PTR(-ENXIO); msm_dsi = devm_kzalloc(&pdev->dev, sizeof(*msm_dsi), GFP_KERNEL); - if (!msm_dsi) { - ret = -ENOMEM; - goto fail; - } + if (!msm_dsi) + return ERR_PTR(-ENOMEM); DBG("dsi probed=%p", msm_dsi); msm_dsi->pdev = pdev; @@ -61,19 +91,22 @@ static struct msm_dsi *dsi_init(struct platform_device *pdev) /* Init dsi host */ ret = msm_dsi_host_init(msm_dsi); if (ret) - goto fail; + goto destroy_dsi; + + /* GET dsi PHY */ + ret = dsi_get_phy(msm_dsi); + if (ret) + goto destroy_dsi; /* Register to dsi manager */ ret = msm_dsi_manager_register(msm_dsi); if (ret) - goto fail; + goto destroy_dsi; return msm_dsi; -fail: - if (msm_dsi) - dsi_destroy(msm_dsi); - +destroy_dsi: + dsi_destroy(msm_dsi); return ERR_PTR(ret); } @@ -142,12 +175,14 @@ static struct platform_driver dsi_driver = { void __init msm_dsi_register(void) { DBG(""); + msm_dsi_phy_driver_register(); platform_driver_register(&dsi_driver); } void __exit msm_dsi_unregister(void) { DBG(""); + msm_dsi_phy_driver_unregister(); platform_driver_unregister(&dsi_driver); } @@ -155,6 +190,7 @@ int msm_dsi_modeset_init(struct msm_dsi *msm_dsi, struct drm_device *dev, struct drm_encoder *encoders[MSM_DSI_ENCODER_NUM]) { struct msm_drm_private *priv = dev->dev_private; + struct drm_bridge *ext_bridge; int ret, i; if (WARN_ON(!encoders[MSM_DSI_VIDEO_ENCODER_ID] || @@ -182,10 +218,25 @@ int msm_dsi_modeset_init(struct msm_dsi *msm_dsi, struct drm_device *dev, msm_dsi->encoders[i] = encoders[i]; } - msm_dsi->connector = msm_dsi_manager_connector_init(msm_dsi->id); + /* + * check if the dsi encoder output is connected to a panel or an + * external bridge. We create a connector only if we're connected to a + * drm_panel device. When we're connected to an external bridge, we + * assume that the drm_bridge driver will create the connector itself. + */ + ext_bridge = msm_dsi_host_get_bridge(msm_dsi->host); + + if (ext_bridge) + msm_dsi->connector = + msm_dsi_manager_ext_bridge_init(msm_dsi->id); + else + msm_dsi->connector = + msm_dsi_manager_connector_init(msm_dsi->id); + if (IS_ERR(msm_dsi->connector)) { ret = PTR_ERR(msm_dsi->connector); - dev_err(dev->dev, "failed to create dsi connector: %d\n", ret); + dev_err(dev->dev, + "failed to create dsi connector: %d\n", ret); msm_dsi->connector = NULL; goto fail; } @@ -201,10 +252,12 @@ fail: msm_dsi_manager_bridge_destroy(msm_dsi->bridge); msm_dsi->bridge = NULL; } - if (msm_dsi->connector) { + + /* don't destroy connector if we didn't make it */ + if (msm_dsi->connector && !msm_dsi->external_bridge) msm_dsi->connector->funcs->destroy(msm_dsi->connector); - msm_dsi->connector = NULL; - } + + msm_dsi->connector = NULL; } return ret; diff --git a/kernel/drivers/gpu/drm/msm/dsi/dsi.h b/kernel/drivers/gpu/drm/msm/dsi/dsi.h index 10f54d4e3..5f5a3732c 100644 --- a/kernel/drivers/gpu/drm/msm/dsi/dsi.h +++ b/kernel/drivers/gpu/drm/msm/dsi/dsi.h @@ -14,6 +14,7 @@ #ifndef __DSI_CONNECTOR_H__ #define __DSI_CONNECTOR_H__ +#include <linux/of_platform.h> #include <linux/platform_device.h> #include "drm_crtc.h" @@ -26,29 +27,50 @@ #define DSI_1 1 #define DSI_MAX 2 -#define DSI_CLOCK_MASTER DSI_0 -#define DSI_CLOCK_SLAVE DSI_1 +enum msm_dsi_phy_type { + MSM_DSI_PHY_28NM_HPM, + MSM_DSI_PHY_28NM_LP, + MSM_DSI_PHY_20NM, + MSM_DSI_PHY_MAX +}; -#define DSI_LEFT DSI_0 -#define DSI_RIGHT DSI_1 +#define DSI_DEV_REGULATOR_MAX 8 -/* According to the current drm framework sequence, take the encoder of - * DSI_1 as master encoder - */ -#define DSI_ENCODER_MASTER DSI_1 -#define DSI_ENCODER_SLAVE DSI_0 +/* Regulators for DSI devices */ +struct dsi_reg_entry { + char name[32]; + int min_voltage; + int max_voltage; + int enable_load; + int disable_load; +}; + +struct dsi_reg_config { + int num; + struct dsi_reg_entry regs[DSI_DEV_REGULATOR_MAX]; +}; struct msm_dsi { struct drm_device *dev; struct platform_device *pdev; + /* connector managed by us when we're connected to a drm_panel */ struct drm_connector *connector; + /* internal dsi bridge attached to MDP interface */ struct drm_bridge *bridge; struct mipi_dsi_host *host; struct msm_dsi_phy *phy; + + /* + * panel/external_bridge connected to dsi bridge output, only one of the + * two can be valid at a time + */ struct drm_panel *panel; - unsigned long panel_flags; + struct drm_bridge *external_bridge; + unsigned long device_flags; + + struct device *phy_dev; bool phy_enabled; /* the encoders we are hooked to (outside of dsi block) */ @@ -61,6 +83,7 @@ struct msm_dsi { struct drm_bridge *msm_dsi_manager_bridge_init(u8 id); void msm_dsi_manager_bridge_destroy(struct drm_bridge *bridge); struct drm_connector *msm_dsi_manager_connector_init(u8 id); +struct drm_connector *msm_dsi_manager_ext_bridge_init(u8 id); int msm_dsi_manager_phy_enable(int id, const unsigned long bit_rate, const unsigned long esc_rate, u32 *clk_pre, u32 *clk_post); @@ -71,8 +94,45 @@ int msm_dsi_manager_register(struct msm_dsi *msm_dsi); void msm_dsi_manager_unregister(struct msm_dsi *msm_dsi); /* msm dsi */ +static inline bool msm_dsi_device_connected(struct msm_dsi *msm_dsi) +{ + return msm_dsi->panel || msm_dsi->external_bridge; +} + struct drm_encoder *msm_dsi_get_encoder(struct msm_dsi *msm_dsi); +/* dsi pll */ +struct msm_dsi_pll; +#ifdef CONFIG_DRM_MSM_DSI_PLL +struct msm_dsi_pll *msm_dsi_pll_init(struct platform_device *pdev, + enum msm_dsi_phy_type type, int dsi_id); +void msm_dsi_pll_destroy(struct msm_dsi_pll *pll); +int msm_dsi_pll_get_clk_provider(struct msm_dsi_pll *pll, + struct clk **byte_clk_provider, struct clk **pixel_clk_provider); +void msm_dsi_pll_save_state(struct msm_dsi_pll *pll); +int msm_dsi_pll_restore_state(struct msm_dsi_pll *pll); +#else +static inline struct msm_dsi_pll *msm_dsi_pll_init(struct platform_device *pdev, + enum msm_dsi_phy_type type, int id) { + return ERR_PTR(-ENODEV); +} +static inline void msm_dsi_pll_destroy(struct msm_dsi_pll *pll) +{ +} +static inline int msm_dsi_pll_get_clk_provider(struct msm_dsi_pll *pll, + struct clk **byte_clk_provider, struct clk **pixel_clk_provider) +{ + return -ENODEV; +} +static inline void msm_dsi_pll_save_state(struct msm_dsi_pll *pll) +{ +} +static inline int msm_dsi_pll_restore_state(struct msm_dsi_pll *pll) +{ + return 0; +} +#endif + /* dsi host */ int msm_dsi_host_xfer_prepare(struct mipi_dsi_host *host, const struct mipi_dsi_msg *msg); @@ -92,8 +152,11 @@ int msm_dsi_host_set_display_mode(struct mipi_dsi_host *host, struct drm_display_mode *mode); struct drm_panel *msm_dsi_host_get_panel(struct mipi_dsi_host *host, unsigned long *panel_flags); +struct drm_bridge *msm_dsi_host_get_bridge(struct mipi_dsi_host *host); int msm_dsi_host_register(struct mipi_dsi_host *host, bool check_defer); void msm_dsi_host_unregister(struct mipi_dsi_host *host); +int msm_dsi_host_set_src_pll(struct mipi_dsi_host *host, + struct msm_dsi_pll *src_pll); void msm_dsi_host_destroy(struct mipi_dsi_host *host); int msm_dsi_host_modeset_init(struct mipi_dsi_host *host, struct drm_device *dev); @@ -101,17 +164,14 @@ int msm_dsi_host_init(struct msm_dsi *msm_dsi); /* dsi phy */ struct msm_dsi_phy; -enum msm_dsi_phy_type { - MSM_DSI_PHY_UNKNOWN, - MSM_DSI_PHY_28NM, - MSM_DSI_PHY_MAX -}; -struct msm_dsi_phy *msm_dsi_phy_init(struct platform_device *pdev, - enum msm_dsi_phy_type type, int id); -int msm_dsi_phy_enable(struct msm_dsi_phy *phy, bool is_dual_panel, +void msm_dsi_phy_driver_register(void); +void msm_dsi_phy_driver_unregister(void); +int msm_dsi_phy_enable(struct msm_dsi_phy *phy, int src_pll_id, const unsigned long bit_rate, const unsigned long esc_rate); -int msm_dsi_phy_disable(struct msm_dsi_phy *phy); +void msm_dsi_phy_disable(struct msm_dsi_phy *phy); void msm_dsi_phy_get_clk_pre_post(struct msm_dsi_phy *phy, u32 *clk_pre, u32 *clk_post); +struct msm_dsi_pll *msm_dsi_phy_get_pll(struct msm_dsi_phy *phy); + #endif /* __DSI_CONNECTOR_H__ */ diff --git a/kernel/drivers/gpu/drm/msm/dsi/dsi.xml.h b/kernel/drivers/gpu/drm/msm/dsi/dsi.xml.h index 1dcfae265..b2b5f3dd1 100644 --- a/kernel/drivers/gpu/drm/msm/dsi/dsi.xml.h +++ b/kernel/drivers/gpu/drm/msm/dsi/dsi.xml.h @@ -8,8 +8,17 @@ http://github.com/freedreno/envytools/ git clone https://github.com/freedreno/envytools.git The rules-ng-ng source files this header was generated from are: -- /usr2/hali/local/envytools/envytools/rnndb/dsi/dsi.xml ( 18681 bytes, from 2015-03-04 23:08:31) -- /usr2/hali/local/envytools/envytools/rnndb/freedreno_copyright.xml ( 1453 bytes, from 2015-01-28 21:43:22) +- /home/robclark/src/freedreno/envytools/rnndb/msm.xml ( 676 bytes, from 2015-05-20 20:03:14) +- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1453 bytes, from 2015-05-20 20:03:07) +- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp4.xml ( 20915 bytes, from 2015-05-20 20:03:14) +- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp_common.xml ( 2849 bytes, from 2015-09-18 12:07:28) +- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp5.xml ( 37194 bytes, from 2015-09-18 12:07:28) +- /home/robclark/src/freedreno/envytools/rnndb/dsi/dsi.xml ( 27887 bytes, from 2015-10-22 16:34:52) +- /home/robclark/src/freedreno/envytools/rnndb/dsi/sfpb.xml ( 602 bytes, from 2015-10-22 16:35:02) +- /home/robclark/src/freedreno/envytools/rnndb/dsi/mmss_cc.xml ( 1686 bytes, from 2015-05-20 20:03:14) +- /home/robclark/src/freedreno/envytools/rnndb/hdmi/qfprom.xml ( 600 bytes, from 2015-05-20 20:03:07) +- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml ( 29154 bytes, from 2015-08-10 21:25:43) +- /home/robclark/src/freedreno/envytools/rnndb/edp/edp.xml ( 10416 bytes, from 2015-05-20 20:03:14) Copyright (C) 2013-2015 by the following authors: - Rob Clark <robdclark@gmail.com> (robclark) @@ -373,6 +382,11 @@ static inline uint32_t DSI_TRIG_CTRL_STREAM(uint32_t val) #define REG_DSI_TRIG_DMA 0x0000008c #define REG_DSI_DLN0_PHY_ERR 0x000000b0 +#define DSI_DLN0_PHY_ERR_DLN0_ERR_ESC 0x00000001 +#define DSI_DLN0_PHY_ERR_DLN0_ERR_SYNC_ESC 0x00000010 +#define DSI_DLN0_PHY_ERR_DLN0_ERR_CONTROL 0x00000100 +#define DSI_DLN0_PHY_ERR_DLN0_ERR_CONTENTION_LP0 0x00001000 +#define DSI_DLN0_PHY_ERR_DLN0_ERR_CONTENTION_LP1 0x00010000 #define REG_DSI_TIMEOUT_STATUS 0x000000bc @@ -394,6 +408,9 @@ static inline uint32_t DSI_CLKOUT_TIMING_CTRL_T_CLK_POST(uint32_t val) #define DSI_EOT_PACKET_CTRL_TX_EOT_APPEND 0x00000001 #define DSI_EOT_PACKET_CTRL_RX_EOT_IGNORE 0x00000010 +#define REG_DSI_LANE_CTRL 0x000000a8 +#define DSI_LANE_CTRL_CLKLN_HS_FORCE_REQUEST 0x10000000 + #define REG_DSI_LANE_SWAP_CTRL 0x000000ac #define DSI_LANE_SWAP_CTRL_DLN_SWAP_SEL__MASK 0x00000007 #define DSI_LANE_SWAP_CTRL_DLN_SWAP_SEL__SHIFT 0 @@ -423,6 +440,9 @@ static inline uint32_t DSI_LANE_SWAP_CTRL_DLN_SWAP_SEL(enum dsi_lane_swap val) #define REG_DSI_PHY_RESET 0x00000128 #define DSI_PHY_RESET_RESET 0x00000001 +#define REG_DSI_T_CLK_PRE_EXTEND 0x0000017c +#define DSI_T_CLK_PRE_EXTEND_INC_BY_2_BYTECLK 0x00000001 + #define REG_DSI_RDBK_DATA_CTRL 0x000001d0 #define DSI_RDBK_DATA_CTRL_COUNT__MASK 0x00ff0000 #define DSI_RDBK_DATA_CTRL_COUNT__SHIFT 16 @@ -547,114 +567,234 @@ static inline uint32_t DSI_VERSION_MAJOR(uint32_t val) #define REG_DSI_8x60_PHY_CAL_STATUS 0x000000fc #define DSI_8x60_PHY_CAL_STATUS_CAL_BUSY 0x10000000 -static inline uint32_t REG_DSI_8960_LN(uint32_t i0) { return 0x00000300 + 0x40*i0; } +static inline uint32_t REG_DSI_28nm_8960_PHY_LN(uint32_t i0) { return 0x00000000 + 0x40*i0; } + +static inline uint32_t REG_DSI_28nm_8960_PHY_LN_CFG_0(uint32_t i0) { return 0x00000000 + 0x40*i0; } + +static inline uint32_t REG_DSI_28nm_8960_PHY_LN_CFG_1(uint32_t i0) { return 0x00000004 + 0x40*i0; } + +static inline uint32_t REG_DSI_28nm_8960_PHY_LN_CFG_2(uint32_t i0) { return 0x00000008 + 0x40*i0; } + +static inline uint32_t REG_DSI_28nm_8960_PHY_LN_TEST_DATAPATH(uint32_t i0) { return 0x0000000c + 0x40*i0; } + +static inline uint32_t REG_DSI_28nm_8960_PHY_LN_TEST_STR_0(uint32_t i0) { return 0x00000014 + 0x40*i0; } + +static inline uint32_t REG_DSI_28nm_8960_PHY_LN_TEST_STR_1(uint32_t i0) { return 0x00000018 + 0x40*i0; } + +#define REG_DSI_28nm_8960_PHY_LNCK_CFG_0 0x00000100 + +#define REG_DSI_28nm_8960_PHY_LNCK_CFG_1 0x00000104 + +#define REG_DSI_28nm_8960_PHY_LNCK_CFG_2 0x00000108 + +#define REG_DSI_28nm_8960_PHY_LNCK_TEST_DATAPATH 0x0000010c + +#define REG_DSI_28nm_8960_PHY_LNCK_TEST_STR0 0x00000114 + +#define REG_DSI_28nm_8960_PHY_LNCK_TEST_STR1 0x00000118 + +#define REG_DSI_28nm_8960_PHY_TIMING_CTRL_0 0x00000140 +#define DSI_28nm_8960_PHY_TIMING_CTRL_0_CLK_ZERO__MASK 0x000000ff +#define DSI_28nm_8960_PHY_TIMING_CTRL_0_CLK_ZERO__SHIFT 0 +static inline uint32_t DSI_28nm_8960_PHY_TIMING_CTRL_0_CLK_ZERO(uint32_t val) +{ + return ((val) << DSI_28nm_8960_PHY_TIMING_CTRL_0_CLK_ZERO__SHIFT) & DSI_28nm_8960_PHY_TIMING_CTRL_0_CLK_ZERO__MASK; +} + +#define REG_DSI_28nm_8960_PHY_TIMING_CTRL_1 0x00000144 +#define DSI_28nm_8960_PHY_TIMING_CTRL_1_CLK_TRAIL__MASK 0x000000ff +#define DSI_28nm_8960_PHY_TIMING_CTRL_1_CLK_TRAIL__SHIFT 0 +static inline uint32_t DSI_28nm_8960_PHY_TIMING_CTRL_1_CLK_TRAIL(uint32_t val) +{ + return ((val) << DSI_28nm_8960_PHY_TIMING_CTRL_1_CLK_TRAIL__SHIFT) & DSI_28nm_8960_PHY_TIMING_CTRL_1_CLK_TRAIL__MASK; +} + +#define REG_DSI_28nm_8960_PHY_TIMING_CTRL_2 0x00000148 +#define DSI_28nm_8960_PHY_TIMING_CTRL_2_CLK_PREPARE__MASK 0x000000ff +#define DSI_28nm_8960_PHY_TIMING_CTRL_2_CLK_PREPARE__SHIFT 0 +static inline uint32_t DSI_28nm_8960_PHY_TIMING_CTRL_2_CLK_PREPARE(uint32_t val) +{ + return ((val) << DSI_28nm_8960_PHY_TIMING_CTRL_2_CLK_PREPARE__SHIFT) & DSI_28nm_8960_PHY_TIMING_CTRL_2_CLK_PREPARE__MASK; +} + +#define REG_DSI_28nm_8960_PHY_TIMING_CTRL_3 0x0000014c -static inline uint32_t REG_DSI_8960_LN_CFG_0(uint32_t i0) { return 0x00000300 + 0x40*i0; } +#define REG_DSI_28nm_8960_PHY_TIMING_CTRL_4 0x00000150 +#define DSI_28nm_8960_PHY_TIMING_CTRL_4_HS_EXIT__MASK 0x000000ff +#define DSI_28nm_8960_PHY_TIMING_CTRL_4_HS_EXIT__SHIFT 0 +static inline uint32_t DSI_28nm_8960_PHY_TIMING_CTRL_4_HS_EXIT(uint32_t val) +{ + return ((val) << DSI_28nm_8960_PHY_TIMING_CTRL_4_HS_EXIT__SHIFT) & DSI_28nm_8960_PHY_TIMING_CTRL_4_HS_EXIT__MASK; +} + +#define REG_DSI_28nm_8960_PHY_TIMING_CTRL_5 0x00000154 +#define DSI_28nm_8960_PHY_TIMING_CTRL_5_HS_ZERO__MASK 0x000000ff +#define DSI_28nm_8960_PHY_TIMING_CTRL_5_HS_ZERO__SHIFT 0 +static inline uint32_t DSI_28nm_8960_PHY_TIMING_CTRL_5_HS_ZERO(uint32_t val) +{ + return ((val) << DSI_28nm_8960_PHY_TIMING_CTRL_5_HS_ZERO__SHIFT) & DSI_28nm_8960_PHY_TIMING_CTRL_5_HS_ZERO__MASK; +} + +#define REG_DSI_28nm_8960_PHY_TIMING_CTRL_6 0x00000158 +#define DSI_28nm_8960_PHY_TIMING_CTRL_6_HS_PREPARE__MASK 0x000000ff +#define DSI_28nm_8960_PHY_TIMING_CTRL_6_HS_PREPARE__SHIFT 0 +static inline uint32_t DSI_28nm_8960_PHY_TIMING_CTRL_6_HS_PREPARE(uint32_t val) +{ + return ((val) << DSI_28nm_8960_PHY_TIMING_CTRL_6_HS_PREPARE__SHIFT) & DSI_28nm_8960_PHY_TIMING_CTRL_6_HS_PREPARE__MASK; +} + +#define REG_DSI_28nm_8960_PHY_TIMING_CTRL_7 0x0000015c +#define DSI_28nm_8960_PHY_TIMING_CTRL_7_HS_TRAIL__MASK 0x000000ff +#define DSI_28nm_8960_PHY_TIMING_CTRL_7_HS_TRAIL__SHIFT 0 +static inline uint32_t DSI_28nm_8960_PHY_TIMING_CTRL_7_HS_TRAIL(uint32_t val) +{ + return ((val) << DSI_28nm_8960_PHY_TIMING_CTRL_7_HS_TRAIL__SHIFT) & DSI_28nm_8960_PHY_TIMING_CTRL_7_HS_TRAIL__MASK; +} + +#define REG_DSI_28nm_8960_PHY_TIMING_CTRL_8 0x00000160 +#define DSI_28nm_8960_PHY_TIMING_CTRL_8_HS_RQST__MASK 0x000000ff +#define DSI_28nm_8960_PHY_TIMING_CTRL_8_HS_RQST__SHIFT 0 +static inline uint32_t DSI_28nm_8960_PHY_TIMING_CTRL_8_HS_RQST(uint32_t val) +{ + return ((val) << DSI_28nm_8960_PHY_TIMING_CTRL_8_HS_RQST__SHIFT) & DSI_28nm_8960_PHY_TIMING_CTRL_8_HS_RQST__MASK; +} + +#define REG_DSI_28nm_8960_PHY_TIMING_CTRL_9 0x00000164 +#define DSI_28nm_8960_PHY_TIMING_CTRL_9_TA_GO__MASK 0x00000007 +#define DSI_28nm_8960_PHY_TIMING_CTRL_9_TA_GO__SHIFT 0 +static inline uint32_t DSI_28nm_8960_PHY_TIMING_CTRL_9_TA_GO(uint32_t val) +{ + return ((val) << DSI_28nm_8960_PHY_TIMING_CTRL_9_TA_GO__SHIFT) & DSI_28nm_8960_PHY_TIMING_CTRL_9_TA_GO__MASK; +} +#define DSI_28nm_8960_PHY_TIMING_CTRL_9_TA_SURE__MASK 0x00000070 +#define DSI_28nm_8960_PHY_TIMING_CTRL_9_TA_SURE__SHIFT 4 +static inline uint32_t DSI_28nm_8960_PHY_TIMING_CTRL_9_TA_SURE(uint32_t val) +{ + return ((val) << DSI_28nm_8960_PHY_TIMING_CTRL_9_TA_SURE__SHIFT) & DSI_28nm_8960_PHY_TIMING_CTRL_9_TA_SURE__MASK; +} + +#define REG_DSI_28nm_8960_PHY_TIMING_CTRL_10 0x00000168 +#define DSI_28nm_8960_PHY_TIMING_CTRL_10_TA_GET__MASK 0x00000007 +#define DSI_28nm_8960_PHY_TIMING_CTRL_10_TA_GET__SHIFT 0 +static inline uint32_t DSI_28nm_8960_PHY_TIMING_CTRL_10_TA_GET(uint32_t val) +{ + return ((val) << DSI_28nm_8960_PHY_TIMING_CTRL_10_TA_GET__SHIFT) & DSI_28nm_8960_PHY_TIMING_CTRL_10_TA_GET__MASK; +} -static inline uint32_t REG_DSI_8960_LN_CFG_1(uint32_t i0) { return 0x00000304 + 0x40*i0; } +#define REG_DSI_28nm_8960_PHY_TIMING_CTRL_11 0x0000016c +#define DSI_28nm_8960_PHY_TIMING_CTRL_11_TRIG3_CMD__MASK 0x000000ff +#define DSI_28nm_8960_PHY_TIMING_CTRL_11_TRIG3_CMD__SHIFT 0 +static inline uint32_t DSI_28nm_8960_PHY_TIMING_CTRL_11_TRIG3_CMD(uint32_t val) +{ + return ((val) << DSI_28nm_8960_PHY_TIMING_CTRL_11_TRIG3_CMD__SHIFT) & DSI_28nm_8960_PHY_TIMING_CTRL_11_TRIG3_CMD__MASK; +} -static inline uint32_t REG_DSI_8960_LN_CFG_2(uint32_t i0) { return 0x00000308 + 0x40*i0; } +#define REG_DSI_28nm_8960_PHY_CTRL_0 0x00000170 -static inline uint32_t REG_DSI_8960_LN_TEST_DATAPATH(uint32_t i0) { return 0x0000030c + 0x40*i0; } +#define REG_DSI_28nm_8960_PHY_CTRL_1 0x00000174 -static inline uint32_t REG_DSI_8960_LN_TEST_STR_0(uint32_t i0) { return 0x00000314 + 0x40*i0; } +#define REG_DSI_28nm_8960_PHY_CTRL_2 0x00000178 -static inline uint32_t REG_DSI_8960_LN_TEST_STR_1(uint32_t i0) { return 0x00000318 + 0x40*i0; } +#define REG_DSI_28nm_8960_PHY_CTRL_3 0x0000017c -#define REG_DSI_8960_PHY_LNCK_CFG_0 0x00000400 +#define REG_DSI_28nm_8960_PHY_STRENGTH_0 0x00000180 -#define REG_DSI_8960_PHY_LNCK_CFG_1 0x00000404 +#define REG_DSI_28nm_8960_PHY_STRENGTH_1 0x00000184 -#define REG_DSI_8960_PHY_LNCK_CFG_2 0x00000408 +#define REG_DSI_28nm_8960_PHY_STRENGTH_2 0x00000188 -#define REG_DSI_8960_PHY_LNCK_TEST_DATAPATH 0x0000040c +#define REG_DSI_28nm_8960_PHY_BIST_CTRL_0 0x0000018c -#define REG_DSI_8960_PHY_LNCK_TEST_STR0 0x00000414 +#define REG_DSI_28nm_8960_PHY_BIST_CTRL_1 0x00000190 -#define REG_DSI_8960_PHY_LNCK_TEST_STR1 0x00000418 +#define REG_DSI_28nm_8960_PHY_BIST_CTRL_2 0x00000194 -#define REG_DSI_8960_PHY_TIMING_CTRL_0 0x00000440 +#define REG_DSI_28nm_8960_PHY_BIST_CTRL_3 0x00000198 -#define REG_DSI_8960_PHY_TIMING_CTRL_1 0x00000444 +#define REG_DSI_28nm_8960_PHY_BIST_CTRL_4 0x0000019c -#define REG_DSI_8960_PHY_TIMING_CTRL_2 0x00000448 +#define REG_DSI_28nm_8960_PHY_LDO_CTRL 0x000001b0 -#define REG_DSI_8960_PHY_TIMING_CTRL_3 0x0000044c +#define REG_DSI_28nm_8960_PHY_MISC_REGULATOR_CTRL_0 0x00000000 -#define REG_DSI_8960_PHY_TIMING_CTRL_4 0x00000450 +#define REG_DSI_28nm_8960_PHY_MISC_REGULATOR_CTRL_1 0x00000004 -#define REG_DSI_8960_PHY_TIMING_CTRL_5 0x00000454 +#define REG_DSI_28nm_8960_PHY_MISC_REGULATOR_CTRL_2 0x00000008 -#define REG_DSI_8960_PHY_TIMING_CTRL_6 0x00000458 +#define REG_DSI_28nm_8960_PHY_MISC_REGULATOR_CTRL_3 0x0000000c -#define REG_DSI_8960_PHY_TIMING_CTRL_7 0x0000045c +#define REG_DSI_28nm_8960_PHY_MISC_REGULATOR_CTRL_4 0x00000010 -#define REG_DSI_8960_PHY_TIMING_CTRL_8 0x00000460 +#define REG_DSI_28nm_8960_PHY_MISC_REGULATOR_CTRL_5 0x00000014 -#define REG_DSI_8960_PHY_TIMING_CTRL_9 0x00000464 +#define REG_DSI_28nm_8960_PHY_MISC_REGULATOR_CAL_PWR_CFG 0x00000018 -#define REG_DSI_8960_PHY_TIMING_CTRL_10 0x00000468 +#define REG_DSI_28nm_8960_PHY_MISC_CAL_HW_TRIGGER 0x00000028 -#define REG_DSI_8960_PHY_TIMING_CTRL_11 0x0000046c +#define REG_DSI_28nm_8960_PHY_MISC_CAL_SW_CFG_0 0x0000002c -#define REG_DSI_8960_PHY_CTRL_0 0x00000470 +#define REG_DSI_28nm_8960_PHY_MISC_CAL_SW_CFG_1 0x00000030 -#define REG_DSI_8960_PHY_CTRL_1 0x00000474 +#define REG_DSI_28nm_8960_PHY_MISC_CAL_SW_CFG_2 0x00000034 -#define REG_DSI_8960_PHY_CTRL_2 0x00000478 +#define REG_DSI_28nm_8960_PHY_MISC_CAL_HW_CFG_0 0x00000038 -#define REG_DSI_8960_PHY_CTRL_3 0x0000047c +#define REG_DSI_28nm_8960_PHY_MISC_CAL_HW_CFG_1 0x0000003c -#define REG_DSI_8960_PHY_STRENGTH_0 0x00000480 +#define REG_DSI_28nm_8960_PHY_MISC_CAL_HW_CFG_2 0x00000040 -#define REG_DSI_8960_PHY_STRENGTH_1 0x00000484 +#define REG_DSI_28nm_8960_PHY_MISC_CAL_HW_CFG_3 0x00000044 -#define REG_DSI_8960_PHY_STRENGTH_2 0x00000488 +#define REG_DSI_28nm_8960_PHY_MISC_CAL_HW_CFG_4 0x00000048 -#define REG_DSI_8960_PHY_BIST_CTRL_0 0x0000048c +#define REG_DSI_28nm_8960_PHY_MISC_CAL_STATUS 0x00000050 +#define DSI_28nm_8960_PHY_MISC_CAL_STATUS_CAL_BUSY 0x00000010 -#define REG_DSI_8960_PHY_BIST_CTRL_1 0x00000490 +#define REG_DSI_28nm_8960_PHY_PLL_CTRL_0 0x00000000 +#define DSI_28nm_8960_PHY_PLL_CTRL_0_ENABLE 0x00000001 -#define REG_DSI_8960_PHY_BIST_CTRL_2 0x00000494 +#define REG_DSI_28nm_8960_PHY_PLL_CTRL_1 0x00000004 -#define REG_DSI_8960_PHY_BIST_CTRL_3 0x00000498 +#define REG_DSI_28nm_8960_PHY_PLL_CTRL_2 0x00000008 -#define REG_DSI_8960_PHY_BIST_CTRL_4 0x0000049c +#define REG_DSI_28nm_8960_PHY_PLL_CTRL_3 0x0000000c -#define REG_DSI_8960_PHY_LDO_CTRL 0x000004b0 +#define REG_DSI_28nm_8960_PHY_PLL_CTRL_4 0x00000010 -#define REG_DSI_8960_PHY_REGULATOR_CTRL_0 0x00000500 +#define REG_DSI_28nm_8960_PHY_PLL_CTRL_5 0x00000014 -#define REG_DSI_8960_PHY_REGULATOR_CTRL_1 0x00000504 +#define REG_DSI_28nm_8960_PHY_PLL_CTRL_6 0x00000018 -#define REG_DSI_8960_PHY_REGULATOR_CTRL_2 0x00000508 +#define REG_DSI_28nm_8960_PHY_PLL_CTRL_7 0x0000001c -#define REG_DSI_8960_PHY_REGULATOR_CTRL_3 0x0000050c +#define REG_DSI_28nm_8960_PHY_PLL_CTRL_8 0x00000020 -#define REG_DSI_8960_PHY_REGULATOR_CTRL_4 0x00000510 +#define REG_DSI_28nm_8960_PHY_PLL_CTRL_9 0x00000024 -#define REG_DSI_8960_PHY_REGULATOR_CAL_PWR_CFG 0x00000518 +#define REG_DSI_28nm_8960_PHY_PLL_CTRL_10 0x00000028 -#define REG_DSI_8960_PHY_CAL_HW_TRIGGER 0x00000528 +#define REG_DSI_28nm_8960_PHY_PLL_CTRL_11 0x0000002c -#define REG_DSI_8960_PHY_CAL_SW_CFG_0 0x0000052c +#define REG_DSI_28nm_8960_PHY_PLL_CTRL_12 0x00000030 -#define REG_DSI_8960_PHY_CAL_SW_CFG_1 0x00000530 +#define REG_DSI_28nm_8960_PHY_PLL_CTRL_13 0x00000034 -#define REG_DSI_8960_PHY_CAL_SW_CFG_2 0x00000534 +#define REG_DSI_28nm_8960_PHY_PLL_CTRL_14 0x00000038 -#define REG_DSI_8960_PHY_CAL_HW_CFG_0 0x00000538 +#define REG_DSI_28nm_8960_PHY_PLL_CTRL_15 0x0000003c -#define REG_DSI_8960_PHY_CAL_HW_CFG_1 0x0000053c +#define REG_DSI_28nm_8960_PHY_PLL_CTRL_16 0x00000040 -#define REG_DSI_8960_PHY_CAL_HW_CFG_2 0x00000540 +#define REG_DSI_28nm_8960_PHY_PLL_CTRL_17 0x00000044 -#define REG_DSI_8960_PHY_CAL_HW_CFG_3 0x00000544 +#define REG_DSI_28nm_8960_PHY_PLL_CTRL_18 0x00000048 -#define REG_DSI_8960_PHY_CAL_HW_CFG_4 0x00000548 +#define REG_DSI_28nm_8960_PHY_PLL_CTRL_19 0x0000004c -#define REG_DSI_8960_PHY_CAL_STATUS 0x00000550 -#define DSI_8960_PHY_CAL_STATUS_CAL_BUSY 0x00000010 +#define REG_DSI_28nm_8960_PHY_PLL_CTRL_20 0x00000050 + +#define REG_DSI_28nm_8960_PHY_PLL_RDY 0x00000080 +#define DSI_28nm_8960_PHY_PLL_RDY_PLL_RDY 0x00000001 static inline uint32_t REG_DSI_28nm_PHY_LN(uint32_t i0) { return 0x00000000 + 0x40*i0; } @@ -818,6 +958,7 @@ static inline uint32_t DSI_28nm_PHY_TIMING_CTRL_11_TRIG3_CMD(uint32_t val) #define REG_DSI_28nm_PHY_BIST_CTRL_5 0x000001c8 #define REG_DSI_28nm_PHY_GLBL_TEST_CTRL 0x000001d4 +#define DSI_28nm_PHY_GLBL_TEST_CTRL_BITCLK_HS_SEL 0x00000001 #define REG_DSI_28nm_PHY_LDO_CNTRL 0x000001dc @@ -835,5 +976,332 @@ static inline uint32_t DSI_28nm_PHY_TIMING_CTRL_11_TRIG3_CMD(uint32_t val) #define REG_DSI_28nm_PHY_REGULATOR_CAL_PWR_CFG 0x00000018 +#define REG_DSI_28nm_PHY_PLL_REFCLK_CFG 0x00000000 +#define DSI_28nm_PHY_PLL_REFCLK_CFG_DBLR 0x00000001 + +#define REG_DSI_28nm_PHY_PLL_POSTDIV1_CFG 0x00000004 + +#define REG_DSI_28nm_PHY_PLL_CHGPUMP_CFG 0x00000008 + +#define REG_DSI_28nm_PHY_PLL_VCOLPF_CFG 0x0000000c + +#define REG_DSI_28nm_PHY_PLL_VREG_CFG 0x00000010 +#define DSI_28nm_PHY_PLL_VREG_CFG_POSTDIV1_BYPASS_B 0x00000002 + +#define REG_DSI_28nm_PHY_PLL_PWRGEN_CFG 0x00000014 + +#define REG_DSI_28nm_PHY_PLL_DMUX_CFG 0x00000018 + +#define REG_DSI_28nm_PHY_PLL_AMUX_CFG 0x0000001c + +#define REG_DSI_28nm_PHY_PLL_GLB_CFG 0x00000020 +#define DSI_28nm_PHY_PLL_GLB_CFG_PLL_PWRDN_B 0x00000001 +#define DSI_28nm_PHY_PLL_GLB_CFG_PLL_LDO_PWRDN_B 0x00000002 +#define DSI_28nm_PHY_PLL_GLB_CFG_PLL_PWRGEN_PWRDN_B 0x00000004 +#define DSI_28nm_PHY_PLL_GLB_CFG_PLL_ENABLE 0x00000008 + +#define REG_DSI_28nm_PHY_PLL_POSTDIV2_CFG 0x00000024 + +#define REG_DSI_28nm_PHY_PLL_POSTDIV3_CFG 0x00000028 + +#define REG_DSI_28nm_PHY_PLL_LPFR_CFG 0x0000002c + +#define REG_DSI_28nm_PHY_PLL_LPFC1_CFG 0x00000030 + +#define REG_DSI_28nm_PHY_PLL_LPFC2_CFG 0x00000034 + +#define REG_DSI_28nm_PHY_PLL_SDM_CFG0 0x00000038 +#define DSI_28nm_PHY_PLL_SDM_CFG0_BYP_DIV__MASK 0x0000003f +#define DSI_28nm_PHY_PLL_SDM_CFG0_BYP_DIV__SHIFT 0 +static inline uint32_t DSI_28nm_PHY_PLL_SDM_CFG0_BYP_DIV(uint32_t val) +{ + return ((val) << DSI_28nm_PHY_PLL_SDM_CFG0_BYP_DIV__SHIFT) & DSI_28nm_PHY_PLL_SDM_CFG0_BYP_DIV__MASK; +} +#define DSI_28nm_PHY_PLL_SDM_CFG0_BYP 0x00000040 + +#define REG_DSI_28nm_PHY_PLL_SDM_CFG1 0x0000003c +#define DSI_28nm_PHY_PLL_SDM_CFG1_DC_OFFSET__MASK 0x0000003f +#define DSI_28nm_PHY_PLL_SDM_CFG1_DC_OFFSET__SHIFT 0 +static inline uint32_t DSI_28nm_PHY_PLL_SDM_CFG1_DC_OFFSET(uint32_t val) +{ + return ((val) << DSI_28nm_PHY_PLL_SDM_CFG1_DC_OFFSET__SHIFT) & DSI_28nm_PHY_PLL_SDM_CFG1_DC_OFFSET__MASK; +} +#define DSI_28nm_PHY_PLL_SDM_CFG1_DITHER_EN__MASK 0x00000040 +#define DSI_28nm_PHY_PLL_SDM_CFG1_DITHER_EN__SHIFT 6 +static inline uint32_t DSI_28nm_PHY_PLL_SDM_CFG1_DITHER_EN(uint32_t val) +{ + return ((val) << DSI_28nm_PHY_PLL_SDM_CFG1_DITHER_EN__SHIFT) & DSI_28nm_PHY_PLL_SDM_CFG1_DITHER_EN__MASK; +} + +#define REG_DSI_28nm_PHY_PLL_SDM_CFG2 0x00000040 +#define DSI_28nm_PHY_PLL_SDM_CFG2_FREQ_SEED_7_0__MASK 0x000000ff +#define DSI_28nm_PHY_PLL_SDM_CFG2_FREQ_SEED_7_0__SHIFT 0 +static inline uint32_t DSI_28nm_PHY_PLL_SDM_CFG2_FREQ_SEED_7_0(uint32_t val) +{ + return ((val) << DSI_28nm_PHY_PLL_SDM_CFG2_FREQ_SEED_7_0__SHIFT) & DSI_28nm_PHY_PLL_SDM_CFG2_FREQ_SEED_7_0__MASK; +} + +#define REG_DSI_28nm_PHY_PLL_SDM_CFG3 0x00000044 +#define DSI_28nm_PHY_PLL_SDM_CFG3_FREQ_SEED_15_8__MASK 0x000000ff +#define DSI_28nm_PHY_PLL_SDM_CFG3_FREQ_SEED_15_8__SHIFT 0 +static inline uint32_t DSI_28nm_PHY_PLL_SDM_CFG3_FREQ_SEED_15_8(uint32_t val) +{ + return ((val) << DSI_28nm_PHY_PLL_SDM_CFG3_FREQ_SEED_15_8__SHIFT) & DSI_28nm_PHY_PLL_SDM_CFG3_FREQ_SEED_15_8__MASK; +} + +#define REG_DSI_28nm_PHY_PLL_SDM_CFG4 0x00000048 + +#define REG_DSI_28nm_PHY_PLL_SSC_CFG0 0x0000004c + +#define REG_DSI_28nm_PHY_PLL_SSC_CFG1 0x00000050 + +#define REG_DSI_28nm_PHY_PLL_SSC_CFG2 0x00000054 + +#define REG_DSI_28nm_PHY_PLL_SSC_CFG3 0x00000058 + +#define REG_DSI_28nm_PHY_PLL_LKDET_CFG0 0x0000005c + +#define REG_DSI_28nm_PHY_PLL_LKDET_CFG1 0x00000060 + +#define REG_DSI_28nm_PHY_PLL_LKDET_CFG2 0x00000064 + +#define REG_DSI_28nm_PHY_PLL_TEST_CFG 0x00000068 +#define DSI_28nm_PHY_PLL_TEST_CFG_PLL_SW_RESET 0x00000001 + +#define REG_DSI_28nm_PHY_PLL_CAL_CFG0 0x0000006c + +#define REG_DSI_28nm_PHY_PLL_CAL_CFG1 0x00000070 + +#define REG_DSI_28nm_PHY_PLL_CAL_CFG2 0x00000074 + +#define REG_DSI_28nm_PHY_PLL_CAL_CFG3 0x00000078 + +#define REG_DSI_28nm_PHY_PLL_CAL_CFG4 0x0000007c + +#define REG_DSI_28nm_PHY_PLL_CAL_CFG5 0x00000080 + +#define REG_DSI_28nm_PHY_PLL_CAL_CFG6 0x00000084 + +#define REG_DSI_28nm_PHY_PLL_CAL_CFG7 0x00000088 + +#define REG_DSI_28nm_PHY_PLL_CAL_CFG8 0x0000008c + +#define REG_DSI_28nm_PHY_PLL_CAL_CFG9 0x00000090 + +#define REG_DSI_28nm_PHY_PLL_CAL_CFG10 0x00000094 + +#define REG_DSI_28nm_PHY_PLL_CAL_CFG11 0x00000098 + +#define REG_DSI_28nm_PHY_PLL_EFUSE_CFG 0x0000009c + +#define REG_DSI_28nm_PHY_PLL_DEBUG_BUS_SEL 0x000000a0 + +#define REG_DSI_28nm_PHY_PLL_CTRL_42 0x000000a4 + +#define REG_DSI_28nm_PHY_PLL_CTRL_43 0x000000a8 + +#define REG_DSI_28nm_PHY_PLL_CTRL_44 0x000000ac + +#define REG_DSI_28nm_PHY_PLL_CTRL_45 0x000000b0 + +#define REG_DSI_28nm_PHY_PLL_CTRL_46 0x000000b4 + +#define REG_DSI_28nm_PHY_PLL_CTRL_47 0x000000b8 + +#define REG_DSI_28nm_PHY_PLL_CTRL_48 0x000000bc + +#define REG_DSI_28nm_PHY_PLL_STATUS 0x000000c0 +#define DSI_28nm_PHY_PLL_STATUS_PLL_RDY 0x00000001 + +#define REG_DSI_28nm_PHY_PLL_DEBUG_BUS0 0x000000c4 + +#define REG_DSI_28nm_PHY_PLL_DEBUG_BUS1 0x000000c8 + +#define REG_DSI_28nm_PHY_PLL_DEBUG_BUS2 0x000000cc + +#define REG_DSI_28nm_PHY_PLL_DEBUG_BUS3 0x000000d0 + +#define REG_DSI_28nm_PHY_PLL_CTRL_54 0x000000d4 + +static inline uint32_t REG_DSI_20nm_PHY_LN(uint32_t i0) { return 0x00000000 + 0x40*i0; } + +static inline uint32_t REG_DSI_20nm_PHY_LN_CFG_0(uint32_t i0) { return 0x00000000 + 0x40*i0; } + +static inline uint32_t REG_DSI_20nm_PHY_LN_CFG_1(uint32_t i0) { return 0x00000004 + 0x40*i0; } + +static inline uint32_t REG_DSI_20nm_PHY_LN_CFG_2(uint32_t i0) { return 0x00000008 + 0x40*i0; } + +static inline uint32_t REG_DSI_20nm_PHY_LN_CFG_3(uint32_t i0) { return 0x0000000c + 0x40*i0; } + +static inline uint32_t REG_DSI_20nm_PHY_LN_CFG_4(uint32_t i0) { return 0x00000010 + 0x40*i0; } + +static inline uint32_t REG_DSI_20nm_PHY_LN_TEST_DATAPATH(uint32_t i0) { return 0x00000014 + 0x40*i0; } + +static inline uint32_t REG_DSI_20nm_PHY_LN_DEBUG_SEL(uint32_t i0) { return 0x00000018 + 0x40*i0; } + +static inline uint32_t REG_DSI_20nm_PHY_LN_TEST_STR_0(uint32_t i0) { return 0x0000001c + 0x40*i0; } + +static inline uint32_t REG_DSI_20nm_PHY_LN_TEST_STR_1(uint32_t i0) { return 0x00000020 + 0x40*i0; } + +#define REG_DSI_20nm_PHY_LNCK_CFG_0 0x00000100 + +#define REG_DSI_20nm_PHY_LNCK_CFG_1 0x00000104 + +#define REG_DSI_20nm_PHY_LNCK_CFG_2 0x00000108 + +#define REG_DSI_20nm_PHY_LNCK_CFG_3 0x0000010c + +#define REG_DSI_20nm_PHY_LNCK_CFG_4 0x00000110 + +#define REG_DSI_20nm_PHY_LNCK_TEST_DATAPATH 0x00000114 + +#define REG_DSI_20nm_PHY_LNCK_DEBUG_SEL 0x00000118 + +#define REG_DSI_20nm_PHY_LNCK_TEST_STR0 0x0000011c + +#define REG_DSI_20nm_PHY_LNCK_TEST_STR1 0x00000120 + +#define REG_DSI_20nm_PHY_TIMING_CTRL_0 0x00000140 +#define DSI_20nm_PHY_TIMING_CTRL_0_CLK_ZERO__MASK 0x000000ff +#define DSI_20nm_PHY_TIMING_CTRL_0_CLK_ZERO__SHIFT 0 +static inline uint32_t DSI_20nm_PHY_TIMING_CTRL_0_CLK_ZERO(uint32_t val) +{ + return ((val) << DSI_20nm_PHY_TIMING_CTRL_0_CLK_ZERO__SHIFT) & DSI_20nm_PHY_TIMING_CTRL_0_CLK_ZERO__MASK; +} + +#define REG_DSI_20nm_PHY_TIMING_CTRL_1 0x00000144 +#define DSI_20nm_PHY_TIMING_CTRL_1_CLK_TRAIL__MASK 0x000000ff +#define DSI_20nm_PHY_TIMING_CTRL_1_CLK_TRAIL__SHIFT 0 +static inline uint32_t DSI_20nm_PHY_TIMING_CTRL_1_CLK_TRAIL(uint32_t val) +{ + return ((val) << DSI_20nm_PHY_TIMING_CTRL_1_CLK_TRAIL__SHIFT) & DSI_20nm_PHY_TIMING_CTRL_1_CLK_TRAIL__MASK; +} + +#define REG_DSI_20nm_PHY_TIMING_CTRL_2 0x00000148 +#define DSI_20nm_PHY_TIMING_CTRL_2_CLK_PREPARE__MASK 0x000000ff +#define DSI_20nm_PHY_TIMING_CTRL_2_CLK_PREPARE__SHIFT 0 +static inline uint32_t DSI_20nm_PHY_TIMING_CTRL_2_CLK_PREPARE(uint32_t val) +{ + return ((val) << DSI_20nm_PHY_TIMING_CTRL_2_CLK_PREPARE__SHIFT) & DSI_20nm_PHY_TIMING_CTRL_2_CLK_PREPARE__MASK; +} + +#define REG_DSI_20nm_PHY_TIMING_CTRL_3 0x0000014c +#define DSI_20nm_PHY_TIMING_CTRL_3_CLK_ZERO_8 0x00000001 + +#define REG_DSI_20nm_PHY_TIMING_CTRL_4 0x00000150 +#define DSI_20nm_PHY_TIMING_CTRL_4_HS_EXIT__MASK 0x000000ff +#define DSI_20nm_PHY_TIMING_CTRL_4_HS_EXIT__SHIFT 0 +static inline uint32_t DSI_20nm_PHY_TIMING_CTRL_4_HS_EXIT(uint32_t val) +{ + return ((val) << DSI_20nm_PHY_TIMING_CTRL_4_HS_EXIT__SHIFT) & DSI_20nm_PHY_TIMING_CTRL_4_HS_EXIT__MASK; +} + +#define REG_DSI_20nm_PHY_TIMING_CTRL_5 0x00000154 +#define DSI_20nm_PHY_TIMING_CTRL_5_HS_ZERO__MASK 0x000000ff +#define DSI_20nm_PHY_TIMING_CTRL_5_HS_ZERO__SHIFT 0 +static inline uint32_t DSI_20nm_PHY_TIMING_CTRL_5_HS_ZERO(uint32_t val) +{ + return ((val) << DSI_20nm_PHY_TIMING_CTRL_5_HS_ZERO__SHIFT) & DSI_20nm_PHY_TIMING_CTRL_5_HS_ZERO__MASK; +} + +#define REG_DSI_20nm_PHY_TIMING_CTRL_6 0x00000158 +#define DSI_20nm_PHY_TIMING_CTRL_6_HS_PREPARE__MASK 0x000000ff +#define DSI_20nm_PHY_TIMING_CTRL_6_HS_PREPARE__SHIFT 0 +static inline uint32_t DSI_20nm_PHY_TIMING_CTRL_6_HS_PREPARE(uint32_t val) +{ + return ((val) << DSI_20nm_PHY_TIMING_CTRL_6_HS_PREPARE__SHIFT) & DSI_20nm_PHY_TIMING_CTRL_6_HS_PREPARE__MASK; +} + +#define REG_DSI_20nm_PHY_TIMING_CTRL_7 0x0000015c +#define DSI_20nm_PHY_TIMING_CTRL_7_HS_TRAIL__MASK 0x000000ff +#define DSI_20nm_PHY_TIMING_CTRL_7_HS_TRAIL__SHIFT 0 +static inline uint32_t DSI_20nm_PHY_TIMING_CTRL_7_HS_TRAIL(uint32_t val) +{ + return ((val) << DSI_20nm_PHY_TIMING_CTRL_7_HS_TRAIL__SHIFT) & DSI_20nm_PHY_TIMING_CTRL_7_HS_TRAIL__MASK; +} + +#define REG_DSI_20nm_PHY_TIMING_CTRL_8 0x00000160 +#define DSI_20nm_PHY_TIMING_CTRL_8_HS_RQST__MASK 0x000000ff +#define DSI_20nm_PHY_TIMING_CTRL_8_HS_RQST__SHIFT 0 +static inline uint32_t DSI_20nm_PHY_TIMING_CTRL_8_HS_RQST(uint32_t val) +{ + return ((val) << DSI_20nm_PHY_TIMING_CTRL_8_HS_RQST__SHIFT) & DSI_20nm_PHY_TIMING_CTRL_8_HS_RQST__MASK; +} + +#define REG_DSI_20nm_PHY_TIMING_CTRL_9 0x00000164 +#define DSI_20nm_PHY_TIMING_CTRL_9_TA_GO__MASK 0x00000007 +#define DSI_20nm_PHY_TIMING_CTRL_9_TA_GO__SHIFT 0 +static inline uint32_t DSI_20nm_PHY_TIMING_CTRL_9_TA_GO(uint32_t val) +{ + return ((val) << DSI_20nm_PHY_TIMING_CTRL_9_TA_GO__SHIFT) & DSI_20nm_PHY_TIMING_CTRL_9_TA_GO__MASK; +} +#define DSI_20nm_PHY_TIMING_CTRL_9_TA_SURE__MASK 0x00000070 +#define DSI_20nm_PHY_TIMING_CTRL_9_TA_SURE__SHIFT 4 +static inline uint32_t DSI_20nm_PHY_TIMING_CTRL_9_TA_SURE(uint32_t val) +{ + return ((val) << DSI_20nm_PHY_TIMING_CTRL_9_TA_SURE__SHIFT) & DSI_20nm_PHY_TIMING_CTRL_9_TA_SURE__MASK; +} + +#define REG_DSI_20nm_PHY_TIMING_CTRL_10 0x00000168 +#define DSI_20nm_PHY_TIMING_CTRL_10_TA_GET__MASK 0x00000007 +#define DSI_20nm_PHY_TIMING_CTRL_10_TA_GET__SHIFT 0 +static inline uint32_t DSI_20nm_PHY_TIMING_CTRL_10_TA_GET(uint32_t val) +{ + return ((val) << DSI_20nm_PHY_TIMING_CTRL_10_TA_GET__SHIFT) & DSI_20nm_PHY_TIMING_CTRL_10_TA_GET__MASK; +} + +#define REG_DSI_20nm_PHY_TIMING_CTRL_11 0x0000016c +#define DSI_20nm_PHY_TIMING_CTRL_11_TRIG3_CMD__MASK 0x000000ff +#define DSI_20nm_PHY_TIMING_CTRL_11_TRIG3_CMD__SHIFT 0 +static inline uint32_t DSI_20nm_PHY_TIMING_CTRL_11_TRIG3_CMD(uint32_t val) +{ + return ((val) << DSI_20nm_PHY_TIMING_CTRL_11_TRIG3_CMD__SHIFT) & DSI_20nm_PHY_TIMING_CTRL_11_TRIG3_CMD__MASK; +} + +#define REG_DSI_20nm_PHY_CTRL_0 0x00000170 + +#define REG_DSI_20nm_PHY_CTRL_1 0x00000174 + +#define REG_DSI_20nm_PHY_CTRL_2 0x00000178 + +#define REG_DSI_20nm_PHY_CTRL_3 0x0000017c + +#define REG_DSI_20nm_PHY_CTRL_4 0x00000180 + +#define REG_DSI_20nm_PHY_STRENGTH_0 0x00000184 + +#define REG_DSI_20nm_PHY_STRENGTH_1 0x00000188 + +#define REG_DSI_20nm_PHY_BIST_CTRL_0 0x000001b4 + +#define REG_DSI_20nm_PHY_BIST_CTRL_1 0x000001b8 + +#define REG_DSI_20nm_PHY_BIST_CTRL_2 0x000001bc + +#define REG_DSI_20nm_PHY_BIST_CTRL_3 0x000001c0 + +#define REG_DSI_20nm_PHY_BIST_CTRL_4 0x000001c4 + +#define REG_DSI_20nm_PHY_BIST_CTRL_5 0x000001c8 + +#define REG_DSI_20nm_PHY_GLBL_TEST_CTRL 0x000001d4 +#define DSI_20nm_PHY_GLBL_TEST_CTRL_BITCLK_HS_SEL 0x00000001 + +#define REG_DSI_20nm_PHY_LDO_CNTRL 0x000001dc + +#define REG_DSI_20nm_PHY_REGULATOR_CTRL_0 0x00000000 + +#define REG_DSI_20nm_PHY_REGULATOR_CTRL_1 0x00000004 + +#define REG_DSI_20nm_PHY_REGULATOR_CTRL_2 0x00000008 + +#define REG_DSI_20nm_PHY_REGULATOR_CTRL_3 0x0000000c + +#define REG_DSI_20nm_PHY_REGULATOR_CTRL_4 0x00000010 + +#define REG_DSI_20nm_PHY_REGULATOR_CTRL_5 0x00000014 + +#define REG_DSI_20nm_PHY_REGULATOR_CAL_PWR_CFG 0x00000018 + #endif /* DSI_XML */ diff --git a/kernel/drivers/gpu/drm/msm/dsi/dsi_cfg.c b/kernel/drivers/gpu/drm/msm/dsi/dsi_cfg.c new file mode 100644 index 000000000..5872d5e59 --- /dev/null +++ b/kernel/drivers/gpu/drm/msm/dsi/dsi_cfg.c @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2015, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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 "dsi_cfg.h" + +/* DSI v2 has not been supported by now */ +static const struct msm_dsi_config dsi_v2_cfg = { + .io_offset = 0, +}; + +static const struct msm_dsi_config msm8974_apq8084_dsi_cfg = { + .io_offset = DSI_6G_REG_SHIFT, + .reg_cfg = { + .num = 4, + .regs = { + {"gdsc", -1, -1, -1, -1}, + {"vdd", 3000000, 3000000, 150000, 100}, + {"vdda", 1200000, 1200000, 100000, 100}, + {"vddio", 1800000, 1800000, 100000, 100}, + }, + }, +}; + +static const struct msm_dsi_config msm8916_dsi_cfg = { + .io_offset = DSI_6G_REG_SHIFT, + .reg_cfg = { + .num = 4, + .regs = { + {"gdsc", -1, -1, -1, -1}, + {"vdd", 2850000, 2850000, 100000, 100}, + {"vdda", 1200000, 1200000, 100000, 100}, + {"vddio", 1800000, 1800000, 100000, 100}, + }, + }, +}; + +static const struct msm_dsi_config msm8994_dsi_cfg = { + .io_offset = DSI_6G_REG_SHIFT, + .reg_cfg = { + .num = 7, + .regs = { + {"gdsc", -1, -1, -1, -1}, + {"vdda", 1250000, 1250000, 100000, 100}, + {"vddio", 1800000, 1800000, 100000, 100}, + {"vcca", 1000000, 1000000, 10000, 100}, + {"vdd", 1800000, 1800000, 100000, 100}, + {"lab_reg", -1, -1, -1, -1}, + {"ibb_reg", -1, -1, -1, -1}, + }, + } +}; + +static const struct msm_dsi_cfg_handler dsi_cfg_handlers[] = { + {MSM_DSI_VER_MAJOR_V2, U32_MAX, &dsi_v2_cfg}, + {MSM_DSI_VER_MAJOR_6G, MSM_DSI_6G_VER_MINOR_V1_0, + &msm8974_apq8084_dsi_cfg}, + {MSM_DSI_VER_MAJOR_6G, MSM_DSI_6G_VER_MINOR_V1_1, + &msm8974_apq8084_dsi_cfg}, + {MSM_DSI_VER_MAJOR_6G, MSM_DSI_6G_VER_MINOR_V1_1_1, + &msm8974_apq8084_dsi_cfg}, + {MSM_DSI_VER_MAJOR_6G, MSM_DSI_6G_VER_MINOR_V1_2, + &msm8974_apq8084_dsi_cfg}, + {MSM_DSI_VER_MAJOR_6G, MSM_DSI_6G_VER_MINOR_V1_3, &msm8994_dsi_cfg}, + {MSM_DSI_VER_MAJOR_6G, MSM_DSI_6G_VER_MINOR_V1_3_1, &msm8916_dsi_cfg}, +}; + +const struct msm_dsi_cfg_handler *msm_dsi_cfg_get(u32 major, u32 minor) +{ + const struct msm_dsi_cfg_handler *cfg_hnd = NULL; + int i; + + for (i = ARRAY_SIZE(dsi_cfg_handlers) - 1; i >= 0; i--) { + if ((dsi_cfg_handlers[i].major == major) && + (dsi_cfg_handlers[i].minor == minor)) { + cfg_hnd = &dsi_cfg_handlers[i]; + break; + } + } + + return cfg_hnd; +} + diff --git a/kernel/drivers/gpu/drm/msm/dsi/dsi_cfg.h b/kernel/drivers/gpu/drm/msm/dsi/dsi_cfg.h new file mode 100644 index 000000000..4cf887240 --- /dev/null +++ b/kernel/drivers/gpu/drm/msm/dsi/dsi_cfg.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2015, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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. + */ + +#ifndef __MSM_DSI_CFG_H__ +#define __MSM_DSI_CFG_H__ + +#include "dsi.h" + +#define MSM_DSI_VER_MAJOR_V2 0x02 +#define MSM_DSI_VER_MAJOR_6G 0x03 +#define MSM_DSI_6G_VER_MINOR_V1_0 0x10000000 +#define MSM_DSI_6G_VER_MINOR_V1_1 0x10010000 +#define MSM_DSI_6G_VER_MINOR_V1_1_1 0x10010001 +#define MSM_DSI_6G_VER_MINOR_V1_2 0x10020000 +#define MSM_DSI_6G_VER_MINOR_V1_3 0x10030000 +#define MSM_DSI_6G_VER_MINOR_V1_3_1 0x10030001 + +#define DSI_6G_REG_SHIFT 4 + +struct msm_dsi_config { + u32 io_offset; + struct dsi_reg_config reg_cfg; +}; + +struct msm_dsi_cfg_handler { + u32 major; + u32 minor; + const struct msm_dsi_config *cfg; +}; + +const struct msm_dsi_cfg_handler *msm_dsi_cfg_get(u32 major, u32 minor); + +#endif /* __MSM_DSI_CFG_H__ */ + diff --git a/kernel/drivers/gpu/drm/msm/dsi/dsi_host.c b/kernel/drivers/gpu/drm/msm/dsi/dsi_host.c index 649d20d29..4c49868ef 100644 --- a/kernel/drivers/gpu/drm/msm/dsi/dsi_host.c +++ b/kernel/drivers/gpu/drm/msm/dsi/dsi_host.c @@ -15,127 +15,20 @@ #include <linux/delay.h> #include <linux/err.h> #include <linux/gpio.h> +#include <linux/gpio/consumer.h> #include <linux/interrupt.h> #include <linux/of_device.h> #include <linux/of_gpio.h> #include <linux/of_irq.h> +#include <linux/pinctrl/consumer.h> +#include <linux/of_graph.h> #include <linux/regulator/consumer.h> #include <linux/spinlock.h> #include <video/mipi_display.h> #include "dsi.h" #include "dsi.xml.h" - -#define MSM_DSI_VER_MAJOR_V2 0x02 -#define MSM_DSI_VER_MAJOR_6G 0x03 -#define MSM_DSI_6G_VER_MINOR_V1_0 0x10000000 -#define MSM_DSI_6G_VER_MINOR_V1_1 0x10010000 -#define MSM_DSI_6G_VER_MINOR_V1_1_1 0x10010001 -#define MSM_DSI_6G_VER_MINOR_V1_2 0x10020000 -#define MSM_DSI_6G_VER_MINOR_V1_3_1 0x10030001 - -#define DSI_6G_REG_SHIFT 4 - -#define DSI_REGULATOR_MAX 8 -struct dsi_reg_entry { - char name[32]; - int min_voltage; - int max_voltage; - int enable_load; - int disable_load; -}; - -struct dsi_reg_config { - int num; - struct dsi_reg_entry regs[DSI_REGULATOR_MAX]; -}; - -struct dsi_config { - u32 major; - u32 minor; - u32 io_offset; - enum msm_dsi_phy_type phy_type; - struct dsi_reg_config reg_cfg; -}; - -static const struct dsi_config dsi_cfgs[] = { - {MSM_DSI_VER_MAJOR_V2, 0, 0, MSM_DSI_PHY_UNKNOWN}, - { /* 8974 v1 */ - .major = MSM_DSI_VER_MAJOR_6G, - .minor = MSM_DSI_6G_VER_MINOR_V1_0, - .io_offset = DSI_6G_REG_SHIFT, - .phy_type = MSM_DSI_PHY_28NM, - .reg_cfg = { - .num = 4, - .regs = { - {"gdsc", -1, -1, -1, -1}, - {"vdd", 3000000, 3000000, 150000, 100}, - {"vdda", 1200000, 1200000, 100000, 100}, - {"vddio", 1800000, 1800000, 100000, 100}, - }, - }, - }, - { /* 8974 v2 */ - .major = MSM_DSI_VER_MAJOR_6G, - .minor = MSM_DSI_6G_VER_MINOR_V1_1, - .io_offset = DSI_6G_REG_SHIFT, - .phy_type = MSM_DSI_PHY_28NM, - .reg_cfg = { - .num = 4, - .regs = { - {"gdsc", -1, -1, -1, -1}, - {"vdd", 3000000, 3000000, 150000, 100}, - {"vdda", 1200000, 1200000, 100000, 100}, - {"vddio", 1800000, 1800000, 100000, 100}, - }, - }, - }, - { /* 8974 v3 */ - .major = MSM_DSI_VER_MAJOR_6G, - .minor = MSM_DSI_6G_VER_MINOR_V1_1_1, - .io_offset = DSI_6G_REG_SHIFT, - .phy_type = MSM_DSI_PHY_28NM, - .reg_cfg = { - .num = 4, - .regs = { - {"gdsc", -1, -1, -1, -1}, - {"vdd", 3000000, 3000000, 150000, 100}, - {"vdda", 1200000, 1200000, 100000, 100}, - {"vddio", 1800000, 1800000, 100000, 100}, - }, - }, - }, - { /* 8084 */ - .major = MSM_DSI_VER_MAJOR_6G, - .minor = MSM_DSI_6G_VER_MINOR_V1_2, - .io_offset = DSI_6G_REG_SHIFT, - .phy_type = MSM_DSI_PHY_28NM, - .reg_cfg = { - .num = 4, - .regs = { - {"gdsc", -1, -1, -1, -1}, - {"vdd", 3000000, 3000000, 150000, 100}, - {"vdda", 1200000, 1200000, 100000, 100}, - {"vddio", 1800000, 1800000, 100000, 100}, - }, - }, - }, - { /* 8916 */ - .major = MSM_DSI_VER_MAJOR_6G, - .minor = MSM_DSI_6G_VER_MINOR_V1_3_1, - .io_offset = DSI_6G_REG_SHIFT, - .phy_type = MSM_DSI_PHY_28NM, - .reg_cfg = { - .num = 4, - .regs = { - {"gdsc", -1, -1, -1, -1}, - {"vdd", 2850000, 2850000, 100000, 100}, - {"vdda", 1200000, 1200000, 100000, 100}, - {"vddio", 1800000, 1800000, 100000, 100}, - }, - }, - }, -}; +#include "dsi_cfg.h" static int dsi_get_version(const void __iomem *base, u32 *major, u32 *minor) { @@ -197,7 +90,7 @@ struct msm_dsi_host { int id; void __iomem *ctrl_base; - struct regulator_bulk_data supplies[DSI_REGULATOR_MAX]; + struct regulator_bulk_data supplies[DSI_DEV_REGULATOR_MAX]; struct clk *mdp_core_clk; struct clk *ahb_clk; struct clk *axi_clk; @@ -205,12 +98,15 @@ struct msm_dsi_host { struct clk *byte_clk; struct clk *esc_clk; struct clk *pixel_clk; + struct clk *byte_clk_src; + struct clk *pixel_clk_src; + u32 byte_clk_rate; struct gpio_desc *disp_en_gpio; struct gpio_desc *te_gpio; - const struct dsi_config *cfg; + const struct msm_dsi_cfg_handler *cfg_hnd; struct completion dma_comp; struct completion video_comp; @@ -228,8 +124,8 @@ struct msm_dsi_host { struct drm_display_mode *mode; - /* Panel info */ - struct device_node *panel_node; + /* connected device info */ + struct device_node *device_node; unsigned int channel; unsigned int lanes; enum mipi_dsi_pixel_format format; @@ -255,61 +151,58 @@ static u32 dsi_get_bpp(const enum mipi_dsi_pixel_format fmt) static inline u32 dsi_read(struct msm_dsi_host *msm_host, u32 reg) { - return msm_readl(msm_host->ctrl_base + msm_host->cfg->io_offset + reg); + return msm_readl(msm_host->ctrl_base + reg); } static inline void dsi_write(struct msm_dsi_host *msm_host, u32 reg, u32 data) { - msm_writel(data, msm_host->ctrl_base + msm_host->cfg->io_offset + reg); + msm_writel(data, msm_host->ctrl_base + reg); } static int dsi_host_regulator_enable(struct msm_dsi_host *msm_host); static void dsi_host_regulator_disable(struct msm_dsi_host *msm_host); -static const struct dsi_config *dsi_get_config(struct msm_dsi_host *msm_host) +static const struct msm_dsi_cfg_handler *dsi_get_config( + struct msm_dsi_host *msm_host) { - const struct dsi_config *cfg; + const struct msm_dsi_cfg_handler *cfg_hnd = NULL; struct regulator *gdsc_reg; - int i, ret; + int ret; u32 major = 0, minor = 0; gdsc_reg = regulator_get(&msm_host->pdev->dev, "gdsc"); - if (IS_ERR_OR_NULL(gdsc_reg)) { + if (IS_ERR(gdsc_reg)) { pr_err("%s: cannot get gdsc\n", __func__); - goto fail; + goto exit; } ret = regulator_enable(gdsc_reg); if (ret) { pr_err("%s: unable to enable gdsc\n", __func__); - regulator_put(gdsc_reg); - goto fail; + goto put_gdsc; } ret = clk_prepare_enable(msm_host->ahb_clk); if (ret) { pr_err("%s: unable to enable ahb_clk\n", __func__); - regulator_disable(gdsc_reg); - regulator_put(gdsc_reg); - goto fail; + goto disable_gdsc; } ret = dsi_get_version(msm_host->ctrl_base, &major, &minor); - - clk_disable_unprepare(msm_host->ahb_clk); - regulator_disable(gdsc_reg); - regulator_put(gdsc_reg); if (ret) { pr_err("%s: Invalid version\n", __func__); - goto fail; + goto disable_clks; } - for (i = 0; i < ARRAY_SIZE(dsi_cfgs); i++) { - cfg = dsi_cfgs + i; - if ((cfg->major == major) && (cfg->minor == minor)) - return cfg; - } - pr_err("%s: Version %x:%x not support\n", __func__, major, minor); + cfg_hnd = msm_dsi_cfg_get(major, minor); -fail: - return NULL; + DBG("%s: Version %x:%x\n", __func__, major, minor); + +disable_clks: + clk_disable_unprepare(msm_host->ahb_clk); +disable_gdsc: + regulator_disable(gdsc_reg); +put_gdsc: + regulator_put(gdsc_reg); +exit: + return cfg_hnd; } static inline struct msm_dsi_host *to_msm_dsi_host(struct mipi_dsi_host *host) @@ -320,8 +213,8 @@ static inline struct msm_dsi_host *to_msm_dsi_host(struct mipi_dsi_host *host) static void dsi_host_regulator_disable(struct msm_dsi_host *msm_host) { struct regulator_bulk_data *s = msm_host->supplies; - const struct dsi_reg_entry *regs = msm_host->cfg->reg_cfg.regs; - int num = msm_host->cfg->reg_cfg.num; + const struct dsi_reg_entry *regs = msm_host->cfg_hnd->cfg->reg_cfg.regs; + int num = msm_host->cfg_hnd->cfg->reg_cfg.num; int i; DBG(""); @@ -336,8 +229,8 @@ static void dsi_host_regulator_disable(struct msm_dsi_host *msm_host) static int dsi_host_regulator_enable(struct msm_dsi_host *msm_host) { struct regulator_bulk_data *s = msm_host->supplies; - const struct dsi_reg_entry *regs = msm_host->cfg->reg_cfg.regs; - int num = msm_host->cfg->reg_cfg.num; + const struct dsi_reg_entry *regs = msm_host->cfg_hnd->cfg->reg_cfg.regs; + int num = msm_host->cfg_hnd->cfg->reg_cfg.num; int ret, i; DBG(""); @@ -370,8 +263,8 @@ fail: static int dsi_regulator_init(struct msm_dsi_host *msm_host) { struct regulator_bulk_data *s = msm_host->supplies; - const struct dsi_reg_entry *regs = msm_host->cfg->reg_cfg.regs; - int num = msm_host->cfg->reg_cfg.num; + const struct dsi_reg_entry *regs = msm_host->cfg_hnd->cfg->reg_cfg.regs; + int num = msm_host->cfg_hnd->cfg->reg_cfg.num; int i, ret; for (i = 0; i < num; i++) @@ -385,7 +278,7 @@ static int dsi_regulator_init(struct msm_dsi_host *msm_host) } for (i = 0; i < num; i++) { - if ((regs[i].min_voltage >= 0) && (regs[i].max_voltage >= 0)) { + if (regulator_can_change_voltage(s[i].consumer)) { ret = regulator_set_voltage(s[i].consumer, regs[i].min_voltage, regs[i].max_voltage); if (ret < 0) { @@ -463,6 +356,22 @@ static int dsi_clk_init(struct msm_dsi_host *msm_host) goto exit; } + msm_host->byte_clk_src = devm_clk_get(dev, "byte_clk_src"); + if (IS_ERR(msm_host->byte_clk_src)) { + ret = PTR_ERR(msm_host->byte_clk_src); + pr_err("%s: can't find byte_clk_src. ret=%d\n", __func__, ret); + msm_host->byte_clk_src = NULL; + goto exit; + } + + msm_host->pixel_clk_src = devm_clk_get(dev, "pixel_clk_src"); + if (IS_ERR(msm_host->pixel_clk_src)) { + ret = PTR_ERR(msm_host->pixel_clk_src); + pr_err("%s: can't find pixel_clk_src. ret=%d\n", __func__, ret); + msm_host->pixel_clk_src = NULL; + goto exit; + } + exit: return ret; } @@ -697,6 +606,7 @@ static void dsi_ctrl_config(struct msm_dsi_host *msm_host, bool enable, { u32 flags = msm_host->mode_flags; enum mipi_dsi_pixel_format mipi_fmt = msm_host->format; + const struct msm_dsi_cfg_handler *cfg_hnd = msm_host->cfg_hnd; u32 data = 0; if (!enable) { @@ -750,8 +660,8 @@ static void dsi_ctrl_config(struct msm_dsi_host *msm_host, bool enable, data |= DSI_TRIG_CTRL_MDP_TRIGGER(TRIGGER_NONE); data |= DSI_TRIG_CTRL_DMA_TRIGGER(TRIGGER_SW); data |= DSI_TRIG_CTRL_STREAM(msm_host->channel); - if ((msm_host->cfg->major == MSM_DSI_VER_MAJOR_6G) && - (msm_host->cfg->minor >= MSM_DSI_6G_VER_MINOR_V1_2)) + if ((cfg_hnd->major == MSM_DSI_VER_MAJOR_6G) && + (cfg_hnd->minor >= MSM_DSI_6G_VER_MINOR_V1_2)) data |= DSI_TRIG_CTRL_BLOCK_DMA_WITHIN_FRAME; dsi_write(msm_host, REG_DSI_TRIG_CTRL, data); @@ -787,6 +697,11 @@ static void dsi_ctrl_config(struct msm_dsi_host *msm_host, bool enable, dsi_write(msm_host, REG_DSI_LANE_SWAP_CTRL, DSI_LANE_SWAP_CTRL_DLN_SWAP_SEL(LANE_SWAP_0123)); } + + if (!(flags & MIPI_DSI_CLOCK_NON_CONTINUOUS)) + dsi_write(msm_host, REG_DSI_LANE_CTRL, + DSI_LANE_CTRL_CLKLN_HS_FORCE_REQUEST); + data |= DSI_CTRL_ENABLE; dsi_write(msm_host, REG_DSI_CTRL, data); @@ -1252,7 +1167,11 @@ static void dsi_dln0_phy_err(struct msm_dsi_host *msm_host) status = dsi_read(msm_host, REG_DSI_DLN0_PHY_ERR); - if (status) { + if (status & (DSI_DLN0_PHY_ERR_DLN0_ERR_ESC | + DSI_DLN0_PHY_ERR_DLN0_ERR_SYNC_ESC | + DSI_DLN0_PHY_ERR_DLN0_ERR_CONTROL | + DSI_DLN0_PHY_ERR_DLN0_ERR_CONTENTION_LP0 | + DSI_DLN0_PHY_ERR_DLN0_ERR_CONTENTION_LP1)) { dsi_write(msm_host, REG_DSI_DLN0_PHY_ERR, status); msm_host->err_work_state |= DSI_ERR_STATE_DLN0_PHY; } @@ -1345,36 +1264,20 @@ static irqreturn_t dsi_host_irq(int irq, void *ptr) static int dsi_host_init_panel_gpios(struct msm_dsi_host *msm_host, struct device *panel_device) { - int ret; - - msm_host->disp_en_gpio = devm_gpiod_get(panel_device, - "disp-enable"); + msm_host->disp_en_gpio = devm_gpiod_get_optional(panel_device, + "disp-enable", + GPIOD_OUT_LOW); if (IS_ERR(msm_host->disp_en_gpio)) { DBG("cannot get disp-enable-gpios %ld", PTR_ERR(msm_host->disp_en_gpio)); - msm_host->disp_en_gpio = NULL; - } - if (msm_host->disp_en_gpio) { - ret = gpiod_direction_output(msm_host->disp_en_gpio, 0); - if (ret) { - pr_err("cannot set dir to disp-en-gpios %d\n", ret); - return ret; - } + return PTR_ERR(msm_host->disp_en_gpio); } - msm_host->te_gpio = devm_gpiod_get(panel_device, "disp-te"); + msm_host->te_gpio = devm_gpiod_get_optional(panel_device, "disp-te", + GPIOD_IN); if (IS_ERR(msm_host->te_gpio)) { DBG("cannot get disp-te-gpios %ld", PTR_ERR(msm_host->te_gpio)); - msm_host->te_gpio = NULL; - } - - if (msm_host->te_gpio) { - ret = gpiod_direction_input(msm_host->te_gpio); - if (ret) { - pr_err("%s: cannot set dir to disp-te-gpios, %d\n", - __func__, ret); - return ret; - } + return PTR_ERR(msm_host->te_gpio); } return 0; @@ -1391,7 +1294,7 @@ static int dsi_host_attach(struct mipi_dsi_host *host, msm_host->format = dsi->format; msm_host->mode_flags = dsi->mode_flags; - msm_host->panel_node = dsi->dev.of_node; + WARN_ON(dsi->dev.of_node != msm_host->device_node); /* Some gpios defined in panel DT need to be controlled by host */ ret = dsi_host_init_panel_gpios(msm_host, &dsi->dev); @@ -1410,7 +1313,7 @@ static int dsi_host_detach(struct mipi_dsi_host *host, { struct msm_dsi_host *msm_host = to_msm_dsi_host(host); - msm_host->panel_node = NULL; + msm_host->device_node = NULL; DBG("id=%d", msm_host->id); if (msm_host->dev) @@ -1441,6 +1344,48 @@ static struct mipi_dsi_host_ops dsi_host_ops = { .transfer = dsi_host_transfer, }; +static int dsi_host_parse_dt(struct msm_dsi_host *msm_host) +{ + struct device *dev = &msm_host->pdev->dev; + struct device_node *np = dev->of_node; + struct device_node *endpoint, *device_node; + int ret; + + ret = of_property_read_u32(np, "qcom,dsi-host-index", &msm_host->id); + if (ret) { + dev_err(dev, "%s: host index not specified, ret=%d\n", + __func__, ret); + return ret; + } + + /* + * Get the first endpoint node. In our case, dsi has one output port + * to which the panel is connected. Don't return an error if a port + * isn't defined. It's possible that there is nothing connected to + * the dsi output. + */ + endpoint = of_graph_get_next_endpoint(np, NULL); + if (!endpoint) { + dev_dbg(dev, "%s: no endpoint\n", __func__); + return 0; + } + + /* Get panel node from the output port's endpoint data */ + device_node = of_graph_get_remote_port_parent(endpoint); + if (!device_node) { + dev_err(dev, "%s: no valid device\n", __func__); + of_node_put(endpoint); + return -ENODEV; + } + + of_node_put(endpoint); + of_node_put(device_node); + + msm_host->device_node = device_node; + + return 0; +} + int msm_dsi_host_init(struct msm_dsi *msm_dsi) { struct msm_dsi_host *msm_host = NULL; @@ -1455,15 +1400,13 @@ int msm_dsi_host_init(struct msm_dsi *msm_dsi) goto fail; } - ret = of_property_read_u32(pdev->dev.of_node, - "qcom,dsi-host-index", &msm_host->id); + msm_host->pdev = pdev; + + ret = dsi_host_parse_dt(msm_host); if (ret) { - dev_err(&pdev->dev, - "%s: host index not specified, ret=%d\n", - __func__, ret); + pr_err("%s: failed to parse dt\n", __func__); goto fail; } - msm_host->pdev = pdev; ret = dsi_clk_init(msm_host); if (ret) { @@ -1478,13 +1421,16 @@ int msm_dsi_host_init(struct msm_dsi *msm_dsi) goto fail; } - msm_host->cfg = dsi_get_config(msm_host); - if (!msm_host->cfg) { + msm_host->cfg_hnd = dsi_get_config(msm_host); + if (!msm_host->cfg_hnd) { ret = -EINVAL; pr_err("%s: get config failed\n", __func__); goto fail; } + /* fixup base address by io offset */ + msm_host->ctrl_base += msm_host->cfg_hnd->cfg->io_offset; + ret = dsi_regulator_init(msm_host); if (ret) { pr_err("%s: regulator init failed\n", __func__); @@ -1508,13 +1454,6 @@ int msm_dsi_host_init(struct msm_dsi *msm_dsi) msm_host->workqueue = alloc_ordered_workqueue("dsi_drm_work", 0); INIT_WORK(&msm_host->err_work, dsi_err_worker); - msm_dsi->phy = msm_dsi_phy_init(pdev, msm_host->cfg->phy_type, - msm_host->id); - if (!msm_dsi->phy) { - ret = -EINVAL; - pr_err("%s: phy init failed\n", __func__); - goto fail; - } msm_dsi->host = &msm_host->base; msm_dsi->id = msm_host->id; @@ -1578,7 +1517,6 @@ int msm_dsi_host_modeset_init(struct mipi_dsi_host *host, int msm_dsi_host_register(struct mipi_dsi_host *host, bool check_defer) { struct msm_dsi_host *msm_host = to_msm_dsi_host(host); - struct device_node *node; int ret; /* Register mipi dsi host */ @@ -1596,14 +1534,13 @@ int msm_dsi_host_register(struct mipi_dsi_host *host, bool check_defer) * It makes sure panel is connected when fbcon detects * connector status and gets the proper display mode to * create framebuffer. + * Don't try to defer if there is nothing connected to the dsi + * output */ - if (check_defer) { - node = of_get_child_by_name(msm_host->pdev->dev.of_node, - "panel"); - if (node) { - if (!of_drm_find_panel(node)) + if (check_defer && msm_host->device_node) { + if (!of_drm_find_panel(msm_host->device_node)) + if (!of_drm_find_bridge(msm_host->device_node)) return -EPROBE_DEFER; - } } } @@ -1682,6 +1619,7 @@ int msm_dsi_host_cmd_rx(struct mipi_dsi_host *host, const struct mipi_dsi_msg *msg) { struct msm_dsi_host *msm_host = to_msm_dsi_host(host); + const struct msm_dsi_cfg_handler *cfg_hnd = msm_host->cfg_hnd; int data_byte, rx_byte, dlen, end; int short_response, diff, pkt_size, ret = 0; char cmd; @@ -1723,8 +1661,8 @@ int msm_dsi_host_cmd_rx(struct mipi_dsi_host *host, return -EINVAL; } - if ((msm_host->cfg->major == MSM_DSI_VER_MAJOR_6G) && - (msm_host->cfg->minor >= MSM_DSI_6G_VER_MINOR_V1_1)) { + if ((cfg_hnd->major == MSM_DSI_VER_MAJOR_6G) && + (cfg_hnd->minor >= MSM_DSI_6G_VER_MINOR_V1_1)) { /* Clear the RDBK_DATA registers */ dsi_write(msm_host, REG_DSI_RDBK_DATA_CTRL, DSI_RDBK_DATA_CTRL_CLR); @@ -1824,6 +1762,39 @@ void msm_dsi_host_cmd_xfer_commit(struct mipi_dsi_host *host, u32 iova, u32 len) wmb(); } +int msm_dsi_host_set_src_pll(struct mipi_dsi_host *host, + struct msm_dsi_pll *src_pll) +{ + struct msm_dsi_host *msm_host = to_msm_dsi_host(host); + struct clk *byte_clk_provider, *pixel_clk_provider; + int ret; + + ret = msm_dsi_pll_get_clk_provider(src_pll, + &byte_clk_provider, &pixel_clk_provider); + if (ret) { + pr_info("%s: can't get provider from pll, don't set parent\n", + __func__); + return 0; + } + + ret = clk_set_parent(msm_host->byte_clk_src, byte_clk_provider); + if (ret) { + pr_err("%s: can't set parent to byte_clk_src. ret=%d\n", + __func__, ret); + goto exit; + } + + ret = clk_set_parent(msm_host->pixel_clk_src, pixel_clk_provider); + if (ret) { + pr_err("%s: can't set parent to pixel_clk_src. ret=%d\n", + __func__, ret); + goto exit; + } + +exit: + return ret; +} + int msm_dsi_host_enable(struct mipi_dsi_host *host) { struct msm_dsi_host *msm_host = to_msm_dsi_host(host); @@ -1905,6 +1876,13 @@ int msm_dsi_host_power_on(struct mipi_dsi_host *host) goto fail_disable_reg; } + ret = pinctrl_pm_select_default_state(&msm_host->pdev->dev); + if (ret) { + pr_err("%s: failed to set pinctrl default state, %d\n", + __func__, ret); + goto fail_disable_clk; + } + dsi_timing_setup(msm_host); dsi_sw_reset(msm_host); dsi_ctrl_config(msm_host, true, clk_pre, clk_post); @@ -1917,6 +1895,8 @@ int msm_dsi_host_power_on(struct mipi_dsi_host *host) return 0; +fail_disable_clk: + dsi_clk_ctrl(msm_host, 0); fail_disable_reg: dsi_host_regulator_disable(msm_host); unlock_ret: @@ -1939,6 +1919,8 @@ int msm_dsi_host_power_off(struct mipi_dsi_host *host) if (msm_host->disp_en_gpio) gpiod_set_value(msm_host->disp_en_gpio, 0); + pinctrl_pm_select_sleep_state(&msm_host->pdev->dev); + msm_dsi_manager_phy_disable(msm_host->id); dsi_clk_ctrl(msm_host, 0); @@ -1979,10 +1961,16 @@ struct drm_panel *msm_dsi_host_get_panel(struct mipi_dsi_host *host, struct msm_dsi_host *msm_host = to_msm_dsi_host(host); struct drm_panel *panel; - panel = of_drm_find_panel(msm_host->panel_node); + panel = of_drm_find_panel(msm_host->device_node); if (panel_flags) *panel_flags = msm_host->mode_flags; return panel; } +struct drm_bridge *msm_dsi_host_get_bridge(struct mipi_dsi_host *host) +{ + struct msm_dsi_host *msm_host = to_msm_dsi_host(host); + + return of_drm_find_bridge(msm_host->device_node); +} diff --git a/kernel/drivers/gpu/drm/msm/dsi/dsi_manager.c b/kernel/drivers/gpu/drm/msm/dsi/dsi_manager.c index 0a40f3c64..0455ff750 100644 --- a/kernel/drivers/gpu/drm/msm/dsi/dsi_manager.c +++ b/kernel/drivers/gpu/drm/msm/dsi/dsi_manager.c @@ -14,19 +14,31 @@ #include "msm_kms.h" #include "dsi.h" +#define DSI_CLOCK_MASTER DSI_0 +#define DSI_CLOCK_SLAVE DSI_1 + +#define DSI_LEFT DSI_0 +#define DSI_RIGHT DSI_1 + +/* According to the current drm framework sequence, take the encoder of + * DSI_1 as master encoder + */ +#define DSI_ENCODER_MASTER DSI_1 +#define DSI_ENCODER_SLAVE DSI_0 + struct msm_dsi_manager { struct msm_dsi *dsi[DSI_MAX]; - bool is_dual_panel; + bool is_dual_dsi; bool is_sync_needed; - int master_panel_id; + int master_dsi_link_id; }; static struct msm_dsi_manager msm_dsim_glb; -#define IS_DUAL_PANEL() (msm_dsim_glb.is_dual_panel) +#define IS_DUAL_DSI() (msm_dsim_glb.is_dual_dsi) #define IS_SYNC_NEEDED() (msm_dsim_glb.is_sync_needed) -#define IS_MASTER_PANEL(id) (msm_dsim_glb.master_panel_id == id) +#define IS_MASTER_DSI_LINK(id) (msm_dsim_glb.master_dsi_link_id == id) static inline struct msm_dsi *dsi_mgr_get_dsi(int id) { @@ -38,28 +50,75 @@ static inline struct msm_dsi *dsi_mgr_get_other_dsi(int id) return msm_dsim_glb.dsi[(id + 1) % DSI_MAX]; } -static int dsi_mgr_parse_dual_panel(struct device_node *np, int id) +static int dsi_mgr_parse_dual_dsi(struct device_node *np, int id) { struct msm_dsi_manager *msm_dsim = &msm_dsim_glb; - /* We assume 2 dsi nodes have the same information of dual-panel and + /* We assume 2 dsi nodes have the same information of dual-dsi and * sync-mode, and only one node specifies master in case of dual mode. */ - if (!msm_dsim->is_dual_panel) - msm_dsim->is_dual_panel = of_property_read_bool( - np, "qcom,dual-panel-mode"); + if (!msm_dsim->is_dual_dsi) + msm_dsim->is_dual_dsi = of_property_read_bool( + np, "qcom,dual-dsi-mode"); - if (msm_dsim->is_dual_panel) { - if (of_property_read_bool(np, "qcom,master-panel")) - msm_dsim->master_panel_id = id; + if (msm_dsim->is_dual_dsi) { + if (of_property_read_bool(np, "qcom,master-dsi")) + msm_dsim->master_dsi_link_id = id; if (!msm_dsim->is_sync_needed) msm_dsim->is_sync_needed = of_property_read_bool( - np, "qcom,sync-dual-panel"); + np, "qcom,sync-dual-dsi"); } return 0; } +static int dsi_mgr_host_register(int id) +{ + struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id); + struct msm_dsi *other_dsi = dsi_mgr_get_other_dsi(id); + struct msm_dsi *clk_master_dsi = dsi_mgr_get_dsi(DSI_CLOCK_MASTER); + struct msm_dsi_pll *src_pll; + int ret; + + if (!IS_DUAL_DSI()) { + ret = msm_dsi_host_register(msm_dsi->host, true); + if (ret) + return ret; + + src_pll = msm_dsi_phy_get_pll(msm_dsi->phy); + ret = msm_dsi_host_set_src_pll(msm_dsi->host, src_pll); + } else if (!other_dsi) { + ret = 0; + } else { + struct msm_dsi *mdsi = IS_MASTER_DSI_LINK(id) ? + msm_dsi : other_dsi; + struct msm_dsi *sdsi = IS_MASTER_DSI_LINK(id) ? + other_dsi : msm_dsi; + /* Register slave host first, so that slave DSI device + * has a chance to probe, and do not block the master + * DSI device's probe. + * Also, do not check defer for the slave host, + * because only master DSI device adds the panel to global + * panel list. The panel's device is the master DSI device. + */ + ret = msm_dsi_host_register(sdsi->host, false); + if (ret) + return ret; + ret = msm_dsi_host_register(mdsi->host, true); + if (ret) + return ret; + + /* PLL0 is to drive both 2 DSI link clocks in Dual DSI mode. */ + src_pll = msm_dsi_phy_get_pll(clk_master_dsi->phy); + ret = msm_dsi_host_set_src_pll(msm_dsi->host, src_pll); + if (ret) + return ret; + ret = msm_dsi_host_set_src_pll(other_dsi->host, src_pll); + } + + return ret; +} + struct dsi_connector { struct drm_connector base; int id; @@ -97,28 +156,28 @@ static enum drm_connector_status dsi_mgr_connector_detect( DBG("id=%d", id); if (!msm_dsi->panel) { msm_dsi->panel = msm_dsi_host_get_panel(msm_dsi->host, - &msm_dsi->panel_flags); + &msm_dsi->device_flags); /* There is only 1 panel in the global panel list - * for dual panel mode. Therefore slave dsi should get + * for dual DSI mode. Therefore slave dsi should get * the drm_panel instance from master dsi, and * keep using the panel flags got from the current DSI link. */ - if (!msm_dsi->panel && IS_DUAL_PANEL() && - !IS_MASTER_PANEL(id) && other_dsi) + if (!msm_dsi->panel && IS_DUAL_DSI() && + !IS_MASTER_DSI_LINK(id) && other_dsi) msm_dsi->panel = msm_dsi_host_get_panel( other_dsi->host, NULL); - if (msm_dsi->panel && IS_DUAL_PANEL()) + if (msm_dsi->panel && IS_DUAL_DSI()) drm_object_attach_property(&connector->base, connector->dev->mode_config.tile_property, 0); - /* Set split display info to kms once dual panel is connected - * to both hosts + /* Set split display info to kms once dual DSI panel is + * connected to both hosts. */ - if (msm_dsi->panel && IS_DUAL_PANEL() && + if (msm_dsi->panel && IS_DUAL_DSI() && other_dsi && other_dsi->panel) { - bool cmd_mode = !(msm_dsi->panel_flags & + bool cmd_mode = !(msm_dsi->device_flags & MIPI_DSI_MODE_VIDEO); struct drm_encoder *encoder = msm_dsi_get_encoder( dsi_mgr_get_dsi(DSI_ENCODER_MASTER)); @@ -129,7 +188,7 @@ static enum drm_connector_status dsi_mgr_connector_detect( kms->funcs->set_split_display(kms, encoder, slave_enc, cmd_mode); else - pr_err("mdp does not support dual panel\n"); + pr_err("mdp does not support dual DSI\n"); } } @@ -226,7 +285,7 @@ static int dsi_mgr_connector_get_modes(struct drm_connector *connector) if (!num) return 0; - if (IS_DUAL_PANEL()) { + if (IS_DUAL_DSI()) { /* report half resolution to user */ dsi_dual_connector_fix_modes(connector); ret = dsi_dual_connector_tile_init(connector, id); @@ -281,11 +340,12 @@ static void dsi_mgr_bridge_pre_enable(struct drm_bridge *bridge) struct msm_dsi *msm_dsi1 = dsi_mgr_get_dsi(DSI_1); struct mipi_dsi_host *host = msm_dsi->host; struct drm_panel *panel = msm_dsi->panel; - bool is_dual_panel = IS_DUAL_PANEL(); + bool is_dual_dsi = IS_DUAL_DSI(); int ret; DBG("id=%d", id); - if (!panel || (is_dual_panel && (DSI_1 == id))) + if (!msm_dsi_device_connected(msm_dsi) || + (is_dual_dsi && (DSI_1 == id))) return; ret = msm_dsi_host_power_on(host); @@ -294,7 +354,7 @@ static void dsi_mgr_bridge_pre_enable(struct drm_bridge *bridge) goto host_on_fail; } - if (is_dual_panel && msm_dsi1) { + if (is_dual_dsi && msm_dsi1) { ret = msm_dsi_host_power_on(msm_dsi1->host); if (ret) { pr_err("%s: power on host1 failed, %d\n", @@ -306,10 +366,13 @@ static void dsi_mgr_bridge_pre_enable(struct drm_bridge *bridge) /* Always call panel functions once, because even for dual panels, * there is only one drm_panel instance. */ - ret = drm_panel_prepare(panel); - if (ret) { - pr_err("%s: prepare panel %d failed, %d\n", __func__, id, ret); - goto panel_prep_fail; + if (panel) { + ret = drm_panel_prepare(panel); + if (ret) { + pr_err("%s: prepare panel %d failed, %d\n", __func__, + id, ret); + goto panel_prep_fail; + } } ret = msm_dsi_host_enable(host); @@ -318,7 +381,7 @@ static void dsi_mgr_bridge_pre_enable(struct drm_bridge *bridge) goto host_en_fail; } - if (is_dual_panel && msm_dsi1) { + if (is_dual_dsi && msm_dsi1) { ret = msm_dsi_host_enable(msm_dsi1->host); if (ret) { pr_err("%s: enable host1 failed, %d\n", __func__, ret); @@ -326,23 +389,27 @@ static void dsi_mgr_bridge_pre_enable(struct drm_bridge *bridge) } } - ret = drm_panel_enable(panel); - if (ret) { - pr_err("%s: enable panel %d failed, %d\n", __func__, id, ret); - goto panel_en_fail; + if (panel) { + ret = drm_panel_enable(panel); + if (ret) { + pr_err("%s: enable panel %d failed, %d\n", __func__, id, + ret); + goto panel_en_fail; + } } return; panel_en_fail: - if (is_dual_panel && msm_dsi1) + if (is_dual_dsi && msm_dsi1) msm_dsi_host_disable(msm_dsi1->host); host1_en_fail: msm_dsi_host_disable(host); host_en_fail: - drm_panel_unprepare(panel); + if (panel) + drm_panel_unprepare(panel); panel_prep_fail: - if (is_dual_panel && msm_dsi1) + if (is_dual_dsi && msm_dsi1) msm_dsi_host_power_off(msm_dsi1->host); host1_on_fail: msm_dsi_host_power_off(host); @@ -367,37 +434,44 @@ static void dsi_mgr_bridge_post_disable(struct drm_bridge *bridge) struct msm_dsi *msm_dsi1 = dsi_mgr_get_dsi(DSI_1); struct mipi_dsi_host *host = msm_dsi->host; struct drm_panel *panel = msm_dsi->panel; - bool is_dual_panel = IS_DUAL_PANEL(); + bool is_dual_dsi = IS_DUAL_DSI(); int ret; DBG("id=%d", id); - if (!panel || (is_dual_panel && (DSI_1 == id))) + if (!msm_dsi_device_connected(msm_dsi) || + (is_dual_dsi && (DSI_1 == id))) return; - ret = drm_panel_disable(panel); - if (ret) - pr_err("%s: Panel %d OFF failed, %d\n", __func__, id, ret); + if (panel) { + ret = drm_panel_disable(panel); + if (ret) + pr_err("%s: Panel %d OFF failed, %d\n", __func__, id, + ret); + } ret = msm_dsi_host_disable(host); if (ret) pr_err("%s: host %d disable failed, %d\n", __func__, id, ret); - if (is_dual_panel && msm_dsi1) { + if (is_dual_dsi && msm_dsi1) { ret = msm_dsi_host_disable(msm_dsi1->host); if (ret) pr_err("%s: host1 disable failed, %d\n", __func__, ret); } - ret = drm_panel_unprepare(panel); - if (ret) - pr_err("%s: Panel %d unprepare failed,%d\n", __func__, id, ret); + if (panel) { + ret = drm_panel_unprepare(panel); + if (ret) + pr_err("%s: Panel %d unprepare failed,%d\n", __func__, + id, ret); + } ret = msm_dsi_host_power_off(host); if (ret) pr_err("%s: host %d power off failed,%d\n", __func__, id, ret); - if (is_dual_panel && msm_dsi1) { + if (is_dual_dsi && msm_dsi1) { ret = msm_dsi_host_power_off(msm_dsi1->host); if (ret) pr_err("%s: host1 power off failed, %d\n", @@ -413,7 +487,7 @@ static void dsi_mgr_bridge_mode_set(struct drm_bridge *bridge, struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id); struct msm_dsi *other_dsi = dsi_mgr_get_other_dsi(id); struct mipi_dsi_host *host = msm_dsi->host; - bool is_dual_panel = IS_DUAL_PANEL(); + bool is_dual_dsi = IS_DUAL_DSI(); DBG("set mode: %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x", mode->base.id, mode->name, @@ -424,11 +498,11 @@ static void dsi_mgr_bridge_mode_set(struct drm_bridge *bridge, mode->vsync_end, mode->vtotal, mode->type, mode->flags); - if (is_dual_panel && (DSI_1 == id)) + if (is_dual_dsi && (DSI_1 == id)) return; msm_dsi_host_set_display_mode(host, adjusted_mode); - if (is_dual_panel && other_dsi) + if (is_dual_dsi && other_dsi) msm_dsi_host_set_display_mode(other_dsi->host, adjusted_mode); } @@ -456,7 +530,7 @@ static const struct drm_bridge_funcs dsi_mgr_bridge_funcs = { .mode_set = dsi_mgr_bridge_mode_set, }; -/* initialize connector */ +/* initialize connector when we're connected to a drm_panel */ struct drm_connector *msm_dsi_manager_connector_init(u8 id) { struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id); @@ -541,6 +615,53 @@ fail: return ERR_PTR(ret); } +struct drm_connector *msm_dsi_manager_ext_bridge_init(u8 id) +{ + struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id); + struct drm_device *dev = msm_dsi->dev; + struct drm_encoder *encoder; + struct drm_bridge *int_bridge, *ext_bridge; + struct drm_connector *connector; + struct list_head *connector_list; + + int_bridge = msm_dsi->bridge; + ext_bridge = msm_dsi->external_bridge = + msm_dsi_host_get_bridge(msm_dsi->host); + + /* + * HACK: we may not know the external DSI bridge device's mode + * flags here. We'll get to know them only when the device + * attaches to the dsi host. For now, assume the bridge supports + * DSI video mode + */ + encoder = msm_dsi->encoders[MSM_DSI_VIDEO_ENCODER_ID]; + + /* link the internal dsi bridge to the external bridge */ + int_bridge->next = ext_bridge; + /* set the external bridge's encoder as dsi's encoder */ + ext_bridge->encoder = encoder; + + drm_bridge_attach(dev, ext_bridge); + + /* + * we need the drm_connector created by the external bridge + * driver (or someone else) to feed it to our driver's + * priv->connector[] list, mainly for msm_fbdev_init() + */ + connector_list = &dev->mode_config.connector_list; + + list_for_each_entry(connector, connector_list, head) { + int i; + + for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { + if (connector->encoder_ids[i] == encoder->base.id) + return connector; + } + } + + return ERR_PTR(-ENODEV); +} + void msm_dsi_manager_bridge_destroy(struct drm_bridge *bridge) { } @@ -551,12 +672,29 @@ int msm_dsi_manager_phy_enable(int id, { struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id); struct msm_dsi_phy *phy = msm_dsi->phy; + int src_pll_id = IS_DUAL_DSI() ? DSI_CLOCK_MASTER : id; + struct msm_dsi_pll *pll = msm_dsi_phy_get_pll(msm_dsi->phy); int ret; - ret = msm_dsi_phy_enable(phy, IS_DUAL_PANEL(), bit_rate, esc_rate); + ret = msm_dsi_phy_enable(phy, src_pll_id, bit_rate, esc_rate); if (ret) return ret; + /* + * Reset DSI PHY silently changes its PLL registers to reset status, + * which will confuse clock driver and result in wrong output rate of + * link clocks. Restore PLL status if its PLL is being used as clock + * source. + */ + if (!IS_DUAL_DSI() || (id == DSI_CLOCK_MASTER)) { + ret = msm_dsi_pll_restore_state(pll); + if (ret) { + pr_err("%s: failed to restore pll state\n", __func__); + msm_dsi_phy_disable(phy); + return ret; + } + } + msm_dsi->phy_enabled = true; msm_dsi_phy_get_clk_pre_post(phy, clk_pre, clk_post); @@ -569,13 +707,18 @@ void msm_dsi_manager_phy_disable(int id) struct msm_dsi *mdsi = dsi_mgr_get_dsi(DSI_CLOCK_MASTER); struct msm_dsi *sdsi = dsi_mgr_get_dsi(DSI_CLOCK_SLAVE); struct msm_dsi_phy *phy = msm_dsi->phy; + struct msm_dsi_pll *pll = msm_dsi_phy_get_pll(msm_dsi->phy); + + /* Save PLL status if it is a clock source */ + if (!IS_DUAL_DSI() || (id == DSI_CLOCK_MASTER)) + msm_dsi_pll_save_state(pll); /* disable DSI phy * In dual-dsi configuration, the phy should be disabled for the * first controller only when the second controller is disabled. */ msm_dsi->phy_enabled = false; - if (IS_DUAL_PANEL() && mdsi && sdsi) { + if (IS_DUAL_DSI() && mdsi && sdsi) { if (!mdsi->phy_enabled && !sdsi->phy_enabled) { msm_dsi_phy_disable(sdsi->phy); msm_dsi_phy_disable(mdsi->phy); @@ -652,7 +795,6 @@ int msm_dsi_manager_register(struct msm_dsi *msm_dsi) { struct msm_dsi_manager *msm_dsim = &msm_dsim_glb; int id = msm_dsi->id; - struct msm_dsi *other_dsi = dsi_mgr_get_other_dsi(id); int ret; if (id > DSI_MAX) { @@ -667,34 +809,23 @@ int msm_dsi_manager_register(struct msm_dsi *msm_dsi) msm_dsim->dsi[id] = msm_dsi; - ret = dsi_mgr_parse_dual_panel(msm_dsi->pdev->dev.of_node, id); + ret = dsi_mgr_parse_dual_dsi(msm_dsi->pdev->dev.of_node, id); if (ret) { - pr_err("%s: failed to parse dual panel info\n", __func__); - return ret; + pr_err("%s: failed to parse dual DSI info\n", __func__); + goto fail; } - if (!IS_DUAL_PANEL()) { - ret = msm_dsi_host_register(msm_dsi->host, true); - } else if (!other_dsi) { - return 0; - } else { - struct msm_dsi *mdsi = IS_MASTER_PANEL(id) ? - msm_dsi : other_dsi; - struct msm_dsi *sdsi = IS_MASTER_PANEL(id) ? - other_dsi : msm_dsi; - /* Register slave host first, so that slave DSI device - * has a chance to probe, and do not block the master - * DSI device's probe. - * Also, do not check defer for the slave host, - * because only master DSI device adds the panel to global - * panel list. The panel's device is the master DSI device. - */ - ret = msm_dsi_host_register(sdsi->host, false); - if (ret) - return ret; - ret = msm_dsi_host_register(mdsi->host, true); + ret = dsi_mgr_host_register(id); + if (ret) { + pr_err("%s: failed to register mipi dsi host for DSI %d\n", + __func__, id); + goto fail; } + return 0; + +fail: + msm_dsim->dsi[id] = NULL; return ret; } diff --git a/kernel/drivers/gpu/drm/msm/dsi/dsi_phy.c b/kernel/drivers/gpu/drm/msm/dsi/dsi_phy.c deleted file mode 100644 index f0cea8927..000000000 --- a/kernel/drivers/gpu/drm/msm/dsi/dsi_phy.c +++ /dev/null @@ -1,352 +0,0 @@ -/* - * Copyright (c) 2015, The Linux Foundation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * 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 "dsi.h" -#include "dsi.xml.h" - -#define dsi_phy_read(offset) msm_readl((offset)) -#define dsi_phy_write(offset, data) msm_writel((data), (offset)) - -struct dsi_dphy_timing { - u32 clk_pre; - u32 clk_post; - u32 clk_zero; - u32 clk_trail; - u32 clk_prepare; - u32 hs_exit; - u32 hs_zero; - u32 hs_prepare; - u32 hs_trail; - u32 hs_rqst; - u32 ta_go; - u32 ta_sure; - u32 ta_get; -}; - -struct msm_dsi_phy { - void __iomem *base; - void __iomem *reg_base; - int id; - struct dsi_dphy_timing timing; - int (*enable)(struct msm_dsi_phy *phy, bool is_dual_panel, - const unsigned long bit_rate, const unsigned long esc_rate); - int (*disable)(struct msm_dsi_phy *phy); -}; - -#define S_DIV_ROUND_UP(n, d) \ - (((n) >= 0) ? (((n) + (d) - 1) / (d)) : (((n) - (d) + 1) / (d))) - -static inline s32 linear_inter(s32 tmax, s32 tmin, s32 percent, - s32 min_result, bool even) -{ - s32 v; - v = (tmax - tmin) * percent; - v = S_DIV_ROUND_UP(v, 100) + tmin; - if (even && (v & 0x1)) - return max_t(s32, min_result, v - 1); - else - return max_t(s32, min_result, v); -} - -static void dsi_dphy_timing_calc_clk_zero(struct dsi_dphy_timing *timing, - s32 ui, s32 coeff, s32 pcnt) -{ - s32 tmax, tmin, clk_z; - s32 temp; - - /* reset */ - temp = 300 * coeff - ((timing->clk_prepare >> 1) + 1) * 2 * ui; - tmin = S_DIV_ROUND_UP(temp, ui) - 2; - if (tmin > 255) { - tmax = 511; - clk_z = linear_inter(2 * tmin, tmin, pcnt, 0, true); - } else { - tmax = 255; - clk_z = linear_inter(tmax, tmin, pcnt, 0, true); - } - - /* adjust */ - temp = (timing->hs_rqst + timing->clk_prepare + clk_z) & 0x7; - timing->clk_zero = clk_z + 8 - temp; -} - -static int dsi_dphy_timing_calc(struct dsi_dphy_timing *timing, - const unsigned long bit_rate, const unsigned long esc_rate) -{ - s32 ui, lpx; - s32 tmax, tmin; - s32 pcnt0 = 10; - s32 pcnt1 = (bit_rate > 1200000000) ? 15 : 10; - s32 pcnt2 = 10; - s32 pcnt3 = (bit_rate > 180000000) ? 10 : 40; - s32 coeff = 1000; /* Precision, should avoid overflow */ - s32 temp; - - if (!bit_rate || !esc_rate) - return -EINVAL; - - ui = mult_frac(NSEC_PER_MSEC, coeff, bit_rate / 1000); - lpx = mult_frac(NSEC_PER_MSEC, coeff, esc_rate / 1000); - - tmax = S_DIV_ROUND_UP(95 * coeff, ui) - 2; - tmin = S_DIV_ROUND_UP(38 * coeff, ui) - 2; - timing->clk_prepare = linear_inter(tmax, tmin, pcnt0, 0, true); - - temp = lpx / ui; - if (temp & 0x1) - timing->hs_rqst = temp; - else - timing->hs_rqst = max_t(s32, 0, temp - 2); - - /* Calculate clk_zero after clk_prepare and hs_rqst */ - dsi_dphy_timing_calc_clk_zero(timing, ui, coeff, pcnt2); - - temp = 105 * coeff + 12 * ui - 20 * coeff; - tmax = S_DIV_ROUND_UP(temp, ui) - 2; - tmin = S_DIV_ROUND_UP(60 * coeff, ui) - 2; - timing->clk_trail = linear_inter(tmax, tmin, pcnt3, 0, true); - - temp = 85 * coeff + 6 * ui; - tmax = S_DIV_ROUND_UP(temp, ui) - 2; - temp = 40 * coeff + 4 * ui; - tmin = S_DIV_ROUND_UP(temp, ui) - 2; - timing->hs_prepare = linear_inter(tmax, tmin, pcnt1, 0, true); - - tmax = 255; - temp = ((timing->hs_prepare >> 1) + 1) * 2 * ui + 2 * ui; - temp = 145 * coeff + 10 * ui - temp; - tmin = S_DIV_ROUND_UP(temp, ui) - 2; - timing->hs_zero = linear_inter(tmax, tmin, pcnt2, 24, true); - - temp = 105 * coeff + 12 * ui - 20 * coeff; - tmax = S_DIV_ROUND_UP(temp, ui) - 2; - temp = 60 * coeff + 4 * ui; - tmin = DIV_ROUND_UP(temp, ui) - 2; - timing->hs_trail = linear_inter(tmax, tmin, pcnt3, 0, true); - - tmax = 255; - tmin = S_DIV_ROUND_UP(100 * coeff, ui) - 2; - timing->hs_exit = linear_inter(tmax, tmin, pcnt2, 0, true); - - tmax = 63; - temp = ((timing->hs_exit >> 1) + 1) * 2 * ui; - temp = 60 * coeff + 52 * ui - 24 * ui - temp; - tmin = S_DIV_ROUND_UP(temp, 8 * ui) - 1; - timing->clk_post = linear_inter(tmax, tmin, pcnt2, 0, false); - - tmax = 63; - temp = ((timing->clk_prepare >> 1) + 1) * 2 * ui; - temp += ((timing->clk_zero >> 1) + 1) * 2 * ui; - temp += 8 * ui + lpx; - tmin = S_DIV_ROUND_UP(temp, 8 * ui) - 1; - if (tmin > tmax) { - temp = linear_inter(2 * tmax, tmin, pcnt2, 0, false) >> 1; - timing->clk_pre = temp >> 1; - temp = (2 * tmax - tmin) * pcnt2; - } else { - timing->clk_pre = linear_inter(tmax, tmin, pcnt2, 0, false); - } - - timing->ta_go = 3; - timing->ta_sure = 0; - timing->ta_get = 4; - - DBG("PHY timings: %d, %d, %d, %d, %d, %d, %d, %d, %d, %d", - timing->clk_pre, timing->clk_post, timing->clk_zero, - timing->clk_trail, timing->clk_prepare, timing->hs_exit, - timing->hs_zero, timing->hs_prepare, timing->hs_trail, - timing->hs_rqst); - - return 0; -} - -static void dsi_28nm_phy_regulator_ctrl(struct msm_dsi_phy *phy, bool enable) -{ - void __iomem *base = phy->reg_base; - - if (!enable) { - dsi_phy_write(base + REG_DSI_28nm_PHY_REGULATOR_CAL_PWR_CFG, 0); - return; - } - - dsi_phy_write(base + REG_DSI_28nm_PHY_REGULATOR_CTRL_0, 0x0); - dsi_phy_write(base + REG_DSI_28nm_PHY_REGULATOR_CAL_PWR_CFG, 1); - dsi_phy_write(base + REG_DSI_28nm_PHY_REGULATOR_CTRL_5, 0); - dsi_phy_write(base + REG_DSI_28nm_PHY_REGULATOR_CTRL_3, 0); - dsi_phy_write(base + REG_DSI_28nm_PHY_REGULATOR_CTRL_2, 0x3); - dsi_phy_write(base + REG_DSI_28nm_PHY_REGULATOR_CTRL_1, 0x9); - dsi_phy_write(base + REG_DSI_28nm_PHY_REGULATOR_CTRL_0, 0x7); - dsi_phy_write(base + REG_DSI_28nm_PHY_REGULATOR_CTRL_4, 0x20); -} - -static int dsi_28nm_phy_enable(struct msm_dsi_phy *phy, bool is_dual_panel, - const unsigned long bit_rate, const unsigned long esc_rate) -{ - struct dsi_dphy_timing *timing = &phy->timing; - int i; - void __iomem *base = phy->base; - - DBG(""); - - if (dsi_dphy_timing_calc(timing, bit_rate, esc_rate)) { - pr_err("%s: D-PHY timing calculation failed\n", __func__); - return -EINVAL; - } - - dsi_phy_write(base + REG_DSI_28nm_PHY_STRENGTH_0, 0xff); - - dsi_28nm_phy_regulator_ctrl(phy, true); - - dsi_phy_write(base + REG_DSI_28nm_PHY_LDO_CNTRL, 0x00); - - dsi_phy_write(base + REG_DSI_28nm_PHY_TIMING_CTRL_0, - DSI_28nm_PHY_TIMING_CTRL_0_CLK_ZERO(timing->clk_zero)); - dsi_phy_write(base + REG_DSI_28nm_PHY_TIMING_CTRL_1, - DSI_28nm_PHY_TIMING_CTRL_1_CLK_TRAIL(timing->clk_trail)); - dsi_phy_write(base + REG_DSI_28nm_PHY_TIMING_CTRL_2, - DSI_28nm_PHY_TIMING_CTRL_2_CLK_PREPARE(timing->clk_prepare)); - if (timing->clk_zero & BIT(8)) - dsi_phy_write(base + REG_DSI_28nm_PHY_TIMING_CTRL_3, - DSI_28nm_PHY_TIMING_CTRL_3_CLK_ZERO_8); - dsi_phy_write(base + REG_DSI_28nm_PHY_TIMING_CTRL_4, - DSI_28nm_PHY_TIMING_CTRL_4_HS_EXIT(timing->hs_exit)); - dsi_phy_write(base + REG_DSI_28nm_PHY_TIMING_CTRL_5, - DSI_28nm_PHY_TIMING_CTRL_5_HS_ZERO(timing->hs_zero)); - dsi_phy_write(base + REG_DSI_28nm_PHY_TIMING_CTRL_6, - DSI_28nm_PHY_TIMING_CTRL_6_HS_PREPARE(timing->hs_prepare)); - dsi_phy_write(base + REG_DSI_28nm_PHY_TIMING_CTRL_7, - DSI_28nm_PHY_TIMING_CTRL_7_HS_TRAIL(timing->hs_trail)); - dsi_phy_write(base + REG_DSI_28nm_PHY_TIMING_CTRL_8, - DSI_28nm_PHY_TIMING_CTRL_8_HS_RQST(timing->hs_rqst)); - dsi_phy_write(base + REG_DSI_28nm_PHY_TIMING_CTRL_9, - DSI_28nm_PHY_TIMING_CTRL_9_TA_GO(timing->ta_go) | - DSI_28nm_PHY_TIMING_CTRL_9_TA_SURE(timing->ta_sure)); - dsi_phy_write(base + REG_DSI_28nm_PHY_TIMING_CTRL_10, - DSI_28nm_PHY_TIMING_CTRL_10_TA_GET(timing->ta_get)); - dsi_phy_write(base + REG_DSI_28nm_PHY_TIMING_CTRL_11, - DSI_28nm_PHY_TIMING_CTRL_11_TRIG3_CMD(0)); - - dsi_phy_write(base + REG_DSI_28nm_PHY_CTRL_1, 0x00); - dsi_phy_write(base + REG_DSI_28nm_PHY_CTRL_0, 0x5f); - - dsi_phy_write(base + REG_DSI_28nm_PHY_STRENGTH_1, 0x6); - - for (i = 0; i < 4; i++) { - dsi_phy_write(base + REG_DSI_28nm_PHY_LN_CFG_0(i), 0); - dsi_phy_write(base + REG_DSI_28nm_PHY_LN_CFG_1(i), 0); - dsi_phy_write(base + REG_DSI_28nm_PHY_LN_CFG_2(i), 0); - dsi_phy_write(base + REG_DSI_28nm_PHY_LN_CFG_3(i), 0); - dsi_phy_write(base + REG_DSI_28nm_PHY_LN_TEST_DATAPATH(i), 0); - dsi_phy_write(base + REG_DSI_28nm_PHY_LN_DEBUG_SEL(i), 0); - dsi_phy_write(base + REG_DSI_28nm_PHY_LN_TEST_STR_0(i), 0x1); - dsi_phy_write(base + REG_DSI_28nm_PHY_LN_TEST_STR_1(i), 0x97); - } - dsi_phy_write(base + REG_DSI_28nm_PHY_LN_CFG_4(0), 0); - dsi_phy_write(base + REG_DSI_28nm_PHY_LN_CFG_4(1), 0x5); - dsi_phy_write(base + REG_DSI_28nm_PHY_LN_CFG_4(2), 0xa); - dsi_phy_write(base + REG_DSI_28nm_PHY_LN_CFG_4(3), 0xf); - - dsi_phy_write(base + REG_DSI_28nm_PHY_LNCK_CFG_1, 0xc0); - dsi_phy_write(base + REG_DSI_28nm_PHY_LNCK_TEST_STR0, 0x1); - dsi_phy_write(base + REG_DSI_28nm_PHY_LNCK_TEST_STR1, 0xbb); - - dsi_phy_write(base + REG_DSI_28nm_PHY_CTRL_0, 0x5f); - - if (is_dual_panel && (phy->id != DSI_CLOCK_MASTER)) - dsi_phy_write(base + REG_DSI_28nm_PHY_GLBL_TEST_CTRL, 0x00); - else - dsi_phy_write(base + REG_DSI_28nm_PHY_GLBL_TEST_CTRL, 0x01); - - return 0; -} - -static int dsi_28nm_phy_disable(struct msm_dsi_phy *phy) -{ - dsi_phy_write(phy->base + REG_DSI_28nm_PHY_CTRL_0, 0); - dsi_28nm_phy_regulator_ctrl(phy, false); - - /* - * Wait for the registers writes to complete in order to - * ensure that the phy is completely disabled - */ - wmb(); - - return 0; -} - -#define dsi_phy_func_init(name) \ - do { \ - phy->enable = dsi_##name##_phy_enable; \ - phy->disable = dsi_##name##_phy_disable; \ - } while (0) - -struct msm_dsi_phy *msm_dsi_phy_init(struct platform_device *pdev, - enum msm_dsi_phy_type type, int id) -{ - struct msm_dsi_phy *phy; - - phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL); - if (!phy) - return NULL; - - phy->base = msm_ioremap(pdev, "dsi_phy", "DSI_PHY"); - if (IS_ERR_OR_NULL(phy->base)) { - pr_err("%s: failed to map phy base\n", __func__); - return NULL; - } - phy->reg_base = msm_ioremap(pdev, "dsi_phy_regulator", "DSI_PHY_REG"); - if (IS_ERR_OR_NULL(phy->reg_base)) { - pr_err("%s: failed to map phy regulator base\n", __func__); - return NULL; - } - - switch (type) { - case MSM_DSI_PHY_28NM: - dsi_phy_func_init(28nm); - break; - default: - pr_err("%s: unsupported type, %d\n", __func__, type); - return NULL; - } - - phy->id = id; - - return phy; -} - -int msm_dsi_phy_enable(struct msm_dsi_phy *phy, bool is_dual_panel, - const unsigned long bit_rate, const unsigned long esc_rate) -{ - if (!phy || !phy->enable) - return -EINVAL; - return phy->enable(phy, is_dual_panel, bit_rate, esc_rate); -} - -int msm_dsi_phy_disable(struct msm_dsi_phy *phy) -{ - if (!phy || !phy->disable) - return -EINVAL; - return phy->disable(phy); -} - -void msm_dsi_phy_get_clk_pre_post(struct msm_dsi_phy *phy, - u32 *clk_pre, u32 *clk_post) -{ - if (!phy) - return; - if (clk_pre) - *clk_pre = phy->timing.clk_pre; - if (clk_post) - *clk_post = phy->timing.clk_post; -} - diff --git a/kernel/drivers/gpu/drm/msm/dsi/mmss_cc.xml.h b/kernel/drivers/gpu/drm/msm/dsi/mmss_cc.xml.h index 695f99d4b..80ec65e47 100644 --- a/kernel/drivers/gpu/drm/msm/dsi/mmss_cc.xml.h +++ b/kernel/drivers/gpu/drm/msm/dsi/mmss_cc.xml.h @@ -8,19 +8,19 @@ http://github.com/freedreno/envytools/ git clone https://github.com/freedreno/envytools.git The rules-ng-ng source files this header was generated from are: -- /home/robclark/src/freedreno/envytools/rnndb/msm.xml ( 676 bytes, from 2014-12-05 15:34:49) -- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1453 bytes, from 2013-03-31 16:51:27) -- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp4.xml ( 20908 bytes, from 2014-12-08 16:13:00) -- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp_common.xml ( 2357 bytes, from 2014-12-08 16:13:00) -- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp5.xml ( 27208 bytes, from 2015-01-13 23:56:11) -- /home/robclark/src/freedreno/envytools/rnndb/dsi/dsi.xml ( 11712 bytes, from 2013-08-17 17:13:43) -- /home/robclark/src/freedreno/envytools/rnndb/dsi/sfpb.xml ( 344 bytes, from 2013-08-11 19:26:32) -- /home/robclark/src/freedreno/envytools/rnndb/dsi/mmss_cc.xml ( 1686 bytes, from 2014-10-31 16:48:57) -- /home/robclark/src/freedreno/envytools/rnndb/hdmi/qfprom.xml ( 600 bytes, from 2013-07-05 19:21:12) -- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml ( 26848 bytes, from 2015-01-13 23:55:57) -- /home/robclark/src/freedreno/envytools/rnndb/edp/edp.xml ( 8253 bytes, from 2014-12-08 16:13:00) - -Copyright (C) 2013-2014 by the following authors: +- /home/robclark/src/freedreno/envytools/rnndb/msm.xml ( 676 bytes, from 2015-05-20 20:03:14) +- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1453 bytes, from 2015-05-20 20:03:07) +- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp4.xml ( 20915 bytes, from 2015-05-20 20:03:14) +- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp_common.xml ( 2849 bytes, from 2015-09-18 12:07:28) +- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp5.xml ( 37194 bytes, from 2015-09-18 12:07:28) +- /home/robclark/src/freedreno/envytools/rnndb/dsi/dsi.xml ( 27887 bytes, from 2015-10-22 16:34:52) +- /home/robclark/src/freedreno/envytools/rnndb/dsi/sfpb.xml ( 602 bytes, from 2015-10-22 16:35:02) +- /home/robclark/src/freedreno/envytools/rnndb/dsi/mmss_cc.xml ( 1686 bytes, from 2015-05-20 20:03:14) +- /home/robclark/src/freedreno/envytools/rnndb/hdmi/qfprom.xml ( 600 bytes, from 2015-05-20 20:03:07) +- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml ( 29154 bytes, from 2015-08-10 21:25:43) +- /home/robclark/src/freedreno/envytools/rnndb/edp/edp.xml ( 10416 bytes, from 2015-05-20 20:03:14) + +Copyright (C) 2013-2015 by the following authors: - Rob Clark <robdclark@gmail.com> (robclark) Permission is hereby granted, free of charge, to any person obtaining diff --git a/kernel/drivers/gpu/drm/msm/dsi/phy/dsi_phy.c b/kernel/drivers/gpu/drm/msm/dsi/phy/dsi_phy.c new file mode 100644 index 000000000..f1f955f57 --- /dev/null +++ b/kernel/drivers/gpu/drm/msm/dsi/phy/dsi_phy.c @@ -0,0 +1,452 @@ +/* + * Copyright (c) 2015, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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 <linux/platform_device.h> + +#include "dsi_phy.h" + +#define S_DIV_ROUND_UP(n, d) \ + (((n) >= 0) ? (((n) + (d) - 1) / (d)) : (((n) - (d) + 1) / (d))) + +static inline s32 linear_inter(s32 tmax, s32 tmin, s32 percent, + s32 min_result, bool even) +{ + s32 v; + + v = (tmax - tmin) * percent; + v = S_DIV_ROUND_UP(v, 100) + tmin; + if (even && (v & 0x1)) + return max_t(s32, min_result, v - 1); + else + return max_t(s32, min_result, v); +} + +static void dsi_dphy_timing_calc_clk_zero(struct msm_dsi_dphy_timing *timing, + s32 ui, s32 coeff, s32 pcnt) +{ + s32 tmax, tmin, clk_z; + s32 temp; + + /* reset */ + temp = 300 * coeff - ((timing->clk_prepare >> 1) + 1) * 2 * ui; + tmin = S_DIV_ROUND_UP(temp, ui) - 2; + if (tmin > 255) { + tmax = 511; + clk_z = linear_inter(2 * tmin, tmin, pcnt, 0, true); + } else { + tmax = 255; + clk_z = linear_inter(tmax, tmin, pcnt, 0, true); + } + + /* adjust */ + temp = (timing->hs_rqst + timing->clk_prepare + clk_z) & 0x7; + timing->clk_zero = clk_z + 8 - temp; +} + +int msm_dsi_dphy_timing_calc(struct msm_dsi_dphy_timing *timing, + const unsigned long bit_rate, const unsigned long esc_rate) +{ + s32 ui, lpx; + s32 tmax, tmin; + s32 pcnt0 = 10; + s32 pcnt1 = (bit_rate > 1200000000) ? 15 : 10; + s32 pcnt2 = 10; + s32 pcnt3 = (bit_rate > 180000000) ? 10 : 40; + s32 coeff = 1000; /* Precision, should avoid overflow */ + s32 temp; + + if (!bit_rate || !esc_rate) + return -EINVAL; + + ui = mult_frac(NSEC_PER_MSEC, coeff, bit_rate / 1000); + lpx = mult_frac(NSEC_PER_MSEC, coeff, esc_rate / 1000); + + tmax = S_DIV_ROUND_UP(95 * coeff, ui) - 2; + tmin = S_DIV_ROUND_UP(38 * coeff, ui) - 2; + timing->clk_prepare = linear_inter(tmax, tmin, pcnt0, 0, true); + + temp = lpx / ui; + if (temp & 0x1) + timing->hs_rqst = temp; + else + timing->hs_rqst = max_t(s32, 0, temp - 2); + + /* Calculate clk_zero after clk_prepare and hs_rqst */ + dsi_dphy_timing_calc_clk_zero(timing, ui, coeff, pcnt2); + + temp = 105 * coeff + 12 * ui - 20 * coeff; + tmax = S_DIV_ROUND_UP(temp, ui) - 2; + tmin = S_DIV_ROUND_UP(60 * coeff, ui) - 2; + timing->clk_trail = linear_inter(tmax, tmin, pcnt3, 0, true); + + temp = 85 * coeff + 6 * ui; + tmax = S_DIV_ROUND_UP(temp, ui) - 2; + temp = 40 * coeff + 4 * ui; + tmin = S_DIV_ROUND_UP(temp, ui) - 2; + timing->hs_prepare = linear_inter(tmax, tmin, pcnt1, 0, true); + + tmax = 255; + temp = ((timing->hs_prepare >> 1) + 1) * 2 * ui + 2 * ui; + temp = 145 * coeff + 10 * ui - temp; + tmin = S_DIV_ROUND_UP(temp, ui) - 2; + timing->hs_zero = linear_inter(tmax, tmin, pcnt2, 24, true); + + temp = 105 * coeff + 12 * ui - 20 * coeff; + tmax = S_DIV_ROUND_UP(temp, ui) - 2; + temp = 60 * coeff + 4 * ui; + tmin = DIV_ROUND_UP(temp, ui) - 2; + timing->hs_trail = linear_inter(tmax, tmin, pcnt3, 0, true); + + tmax = 255; + tmin = S_DIV_ROUND_UP(100 * coeff, ui) - 2; + timing->hs_exit = linear_inter(tmax, tmin, pcnt2, 0, true); + + tmax = 63; + temp = ((timing->hs_exit >> 1) + 1) * 2 * ui; + temp = 60 * coeff + 52 * ui - 24 * ui - temp; + tmin = S_DIV_ROUND_UP(temp, 8 * ui) - 1; + timing->clk_post = linear_inter(tmax, tmin, pcnt2, 0, false); + + tmax = 63; + temp = ((timing->clk_prepare >> 1) + 1) * 2 * ui; + temp += ((timing->clk_zero >> 1) + 1) * 2 * ui; + temp += 8 * ui + lpx; + tmin = S_DIV_ROUND_UP(temp, 8 * ui) - 1; + if (tmin > tmax) { + temp = linear_inter(2 * tmax, tmin, pcnt2, 0, false); + timing->clk_pre = temp >> 1; + } else { + timing->clk_pre = linear_inter(tmax, tmin, pcnt2, 0, false); + } + + timing->ta_go = 3; + timing->ta_sure = 0; + timing->ta_get = 4; + + DBG("PHY timings: %d, %d, %d, %d, %d, %d, %d, %d, %d, %d", + timing->clk_pre, timing->clk_post, timing->clk_zero, + timing->clk_trail, timing->clk_prepare, timing->hs_exit, + timing->hs_zero, timing->hs_prepare, timing->hs_trail, + timing->hs_rqst); + + return 0; +} + +void msm_dsi_phy_set_src_pll(struct msm_dsi_phy *phy, int pll_id, u32 reg, + u32 bit_mask) +{ + int phy_id = phy->id; + u32 val; + + if ((phy_id >= DSI_MAX) || (pll_id >= DSI_MAX)) + return; + + val = dsi_phy_read(phy->base + reg); + + if (phy->cfg->src_pll_truthtable[phy_id][pll_id]) + dsi_phy_write(phy->base + reg, val | bit_mask); + else + dsi_phy_write(phy->base + reg, val & (~bit_mask)); +} + +static int dsi_phy_regulator_init(struct msm_dsi_phy *phy) +{ + struct regulator_bulk_data *s = phy->supplies; + const struct dsi_reg_entry *regs = phy->cfg->reg_cfg.regs; + struct device *dev = &phy->pdev->dev; + int num = phy->cfg->reg_cfg.num; + int i, ret; + + for (i = 0; i < num; i++) + s[i].supply = regs[i].name; + + ret = devm_regulator_bulk_get(dev, num, s); + if (ret < 0) { + dev_err(dev, "%s: failed to init regulator, ret=%d\n", + __func__, ret); + return ret; + } + + for (i = 0; i < num; i++) { + if (regulator_can_change_voltage(s[i].consumer)) { + ret = regulator_set_voltage(s[i].consumer, + regs[i].min_voltage, regs[i].max_voltage); + if (ret < 0) { + dev_err(dev, + "regulator %d set voltage failed, %d\n", + i, ret); + return ret; + } + } + } + + return 0; +} + +static void dsi_phy_regulator_disable(struct msm_dsi_phy *phy) +{ + struct regulator_bulk_data *s = phy->supplies; + const struct dsi_reg_entry *regs = phy->cfg->reg_cfg.regs; + int num = phy->cfg->reg_cfg.num; + int i; + + DBG(""); + for (i = num - 1; i >= 0; i--) + if (regs[i].disable_load >= 0) + regulator_set_load(s[i].consumer, regs[i].disable_load); + + regulator_bulk_disable(num, s); +} + +static int dsi_phy_regulator_enable(struct msm_dsi_phy *phy) +{ + struct regulator_bulk_data *s = phy->supplies; + const struct dsi_reg_entry *regs = phy->cfg->reg_cfg.regs; + struct device *dev = &phy->pdev->dev; + int num = phy->cfg->reg_cfg.num; + int ret, i; + + DBG(""); + for (i = 0; i < num; i++) { + if (regs[i].enable_load >= 0) { + ret = regulator_set_load(s[i].consumer, + regs[i].enable_load); + if (ret < 0) { + dev_err(dev, + "regulator %d set op mode failed, %d\n", + i, ret); + goto fail; + } + } + } + + ret = regulator_bulk_enable(num, s); + if (ret < 0) { + dev_err(dev, "regulator enable failed, %d\n", ret); + goto fail; + } + + return 0; + +fail: + for (i--; i >= 0; i--) + regulator_set_load(s[i].consumer, regs[i].disable_load); + return ret; +} + +static int dsi_phy_enable_resource(struct msm_dsi_phy *phy) +{ + struct device *dev = &phy->pdev->dev; + int ret; + + pm_runtime_get_sync(dev); + + ret = clk_prepare_enable(phy->ahb_clk); + if (ret) { + dev_err(dev, "%s: can't enable ahb clk, %d\n", __func__, ret); + pm_runtime_put_sync(dev); + } + + return ret; +} + +static void dsi_phy_disable_resource(struct msm_dsi_phy *phy) +{ + clk_disable_unprepare(phy->ahb_clk); + pm_runtime_put_sync(&phy->pdev->dev); +} + +static const struct of_device_id dsi_phy_dt_match[] = { +#ifdef CONFIG_DRM_MSM_DSI_28NM_PHY + { .compatible = "qcom,dsi-phy-28nm-hpm", + .data = &dsi_phy_28nm_hpm_cfgs }, + { .compatible = "qcom,dsi-phy-28nm-lp", + .data = &dsi_phy_28nm_lp_cfgs }, +#endif +#ifdef CONFIG_DRM_MSM_DSI_20NM_PHY + { .compatible = "qcom,dsi-phy-20nm", + .data = &dsi_phy_20nm_cfgs }, +#endif + {} +}; + +static int dsi_phy_driver_probe(struct platform_device *pdev) +{ + struct msm_dsi_phy *phy; + struct device *dev = &pdev->dev; + const struct of_device_id *match; + int ret; + + phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); + if (!phy) + return -ENOMEM; + + match = of_match_node(dsi_phy_dt_match, dev->of_node); + if (!match) + return -ENODEV; + + phy->cfg = match->data; + phy->pdev = pdev; + + ret = of_property_read_u32(dev->of_node, + "qcom,dsi-phy-index", &phy->id); + if (ret) { + dev_err(dev, "%s: PHY index not specified, %d\n", + __func__, ret); + goto fail; + } + + phy->regulator_ldo_mode = of_property_read_bool(dev->of_node, + "qcom,dsi-phy-regulator-ldo-mode"); + + phy->base = msm_ioremap(pdev, "dsi_phy", "DSI_PHY"); + if (IS_ERR(phy->base)) { + dev_err(dev, "%s: failed to map phy base\n", __func__); + ret = -ENOMEM; + goto fail; + } + + phy->reg_base = msm_ioremap(pdev, "dsi_phy_regulator", + "DSI_PHY_REG"); + if (IS_ERR(phy->reg_base)) { + dev_err(dev, "%s: failed to map phy regulator base\n", + __func__); + ret = -ENOMEM; + goto fail; + } + + ret = dsi_phy_regulator_init(phy); + if (ret) { + dev_err(dev, "%s: failed to init regulator\n", __func__); + goto fail; + } + + phy->ahb_clk = devm_clk_get(dev, "iface_clk"); + if (IS_ERR(phy->ahb_clk)) { + dev_err(dev, "%s: Unable to get ahb clk\n", __func__); + ret = PTR_ERR(phy->ahb_clk); + goto fail; + } + + /* PLL init will call into clk_register which requires + * register access, so we need to enable power and ahb clock. + */ + ret = dsi_phy_enable_resource(phy); + if (ret) + goto fail; + + phy->pll = msm_dsi_pll_init(pdev, phy->cfg->type, phy->id); + if (!phy->pll) + dev_info(dev, + "%s: pll init failed, need separate pll clk driver\n", + __func__); + + dsi_phy_disable_resource(phy); + + platform_set_drvdata(pdev, phy); + + return 0; + +fail: + return ret; +} + +static int dsi_phy_driver_remove(struct platform_device *pdev) +{ + struct msm_dsi_phy *phy = platform_get_drvdata(pdev); + + if (phy && phy->pll) { + msm_dsi_pll_destroy(phy->pll); + phy->pll = NULL; + } + + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static struct platform_driver dsi_phy_platform_driver = { + .probe = dsi_phy_driver_probe, + .remove = dsi_phy_driver_remove, + .driver = { + .name = "msm_dsi_phy", + .of_match_table = dsi_phy_dt_match, + }, +}; + +void __init msm_dsi_phy_driver_register(void) +{ + platform_driver_register(&dsi_phy_platform_driver); +} + +void __exit msm_dsi_phy_driver_unregister(void) +{ + platform_driver_unregister(&dsi_phy_platform_driver); +} + +int msm_dsi_phy_enable(struct msm_dsi_phy *phy, int src_pll_id, + const unsigned long bit_rate, const unsigned long esc_rate) +{ + struct device *dev = &phy->pdev->dev; + int ret; + + if (!phy || !phy->cfg->ops.enable) + return -EINVAL; + + ret = dsi_phy_regulator_enable(phy); + if (ret) { + dev_err(dev, "%s: regulator enable failed, %d\n", + __func__, ret); + return ret; + } + + ret = phy->cfg->ops.enable(phy, src_pll_id, bit_rate, esc_rate); + if (ret) { + dev_err(dev, "%s: phy enable failed, %d\n", __func__, ret); + dsi_phy_regulator_disable(phy); + return ret; + } + + return 0; +} + +void msm_dsi_phy_disable(struct msm_dsi_phy *phy) +{ + if (!phy || !phy->cfg->ops.disable) + return; + + phy->cfg->ops.disable(phy); + + dsi_phy_regulator_disable(phy); +} + +void msm_dsi_phy_get_clk_pre_post(struct msm_dsi_phy *phy, + u32 *clk_pre, u32 *clk_post) +{ + if (!phy) + return; + + if (clk_pre) + *clk_pre = phy->timing.clk_pre; + if (clk_post) + *clk_post = phy->timing.clk_post; +} + +struct msm_dsi_pll *msm_dsi_phy_get_pll(struct msm_dsi_phy *phy) +{ + if (!phy) + return NULL; + + return phy->pll; +} + diff --git a/kernel/drivers/gpu/drm/msm/dsi/phy/dsi_phy.h b/kernel/drivers/gpu/drm/msm/dsi/phy/dsi_phy.h new file mode 100644 index 000000000..0456b2532 --- /dev/null +++ b/kernel/drivers/gpu/drm/msm/dsi/phy/dsi_phy.h @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2015, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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. + */ + +#ifndef __DSI_PHY_H__ +#define __DSI_PHY_H__ + +#include <linux/regulator/consumer.h> + +#include "dsi.h" + +#define dsi_phy_read(offset) msm_readl((offset)) +#define dsi_phy_write(offset, data) msm_writel((data), (offset)) + +struct msm_dsi_phy_ops { + int (*enable)(struct msm_dsi_phy *phy, int src_pll_id, + const unsigned long bit_rate, const unsigned long esc_rate); + void (*disable)(struct msm_dsi_phy *phy); +}; + +struct msm_dsi_phy_cfg { + enum msm_dsi_phy_type type; + struct dsi_reg_config reg_cfg; + struct msm_dsi_phy_ops ops; + + /* + * Each cell {phy_id, pll_id} of the truth table indicates + * if the source PLL selection bit should be set for each PHY. + * Fill default H/W values in illegal cells, eg. cell {0, 1}. + */ + bool src_pll_truthtable[DSI_MAX][DSI_MAX]; +}; + +extern const struct msm_dsi_phy_cfg dsi_phy_28nm_hpm_cfgs; +extern const struct msm_dsi_phy_cfg dsi_phy_28nm_lp_cfgs; +extern const struct msm_dsi_phy_cfg dsi_phy_20nm_cfgs; + +struct msm_dsi_dphy_timing { + u32 clk_pre; + u32 clk_post; + u32 clk_zero; + u32 clk_trail; + u32 clk_prepare; + u32 hs_exit; + u32 hs_zero; + u32 hs_prepare; + u32 hs_trail; + u32 hs_rqst; + u32 ta_go; + u32 ta_sure; + u32 ta_get; +}; + +struct msm_dsi_phy { + struct platform_device *pdev; + void __iomem *base; + void __iomem *reg_base; + int id; + + struct clk *ahb_clk; + struct regulator_bulk_data supplies[DSI_DEV_REGULATOR_MAX]; + + struct msm_dsi_dphy_timing timing; + const struct msm_dsi_phy_cfg *cfg; + + bool regulator_ldo_mode; + + struct msm_dsi_pll *pll; +}; + +/* + * PHY internal functions + */ +int msm_dsi_dphy_timing_calc(struct msm_dsi_dphy_timing *timing, + const unsigned long bit_rate, const unsigned long esc_rate); +void msm_dsi_phy_set_src_pll(struct msm_dsi_phy *phy, int pll_id, u32 reg, + u32 bit_mask); + +#endif /* __DSI_PHY_H__ */ + diff --git a/kernel/drivers/gpu/drm/msm/dsi/phy/dsi_phy_20nm.c b/kernel/drivers/gpu/drm/msm/dsi/phy/dsi_phy_20nm.c new file mode 100644 index 000000000..2e9ba118d --- /dev/null +++ b/kernel/drivers/gpu/drm/msm/dsi/phy/dsi_phy_20nm.c @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2015, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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 "dsi_phy.h" +#include "dsi.xml.h" + +static void dsi_20nm_dphy_set_timing(struct msm_dsi_phy *phy, + struct msm_dsi_dphy_timing *timing) +{ + void __iomem *base = phy->base; + + dsi_phy_write(base + REG_DSI_20nm_PHY_TIMING_CTRL_0, + DSI_20nm_PHY_TIMING_CTRL_0_CLK_ZERO(timing->clk_zero)); + dsi_phy_write(base + REG_DSI_20nm_PHY_TIMING_CTRL_1, + DSI_20nm_PHY_TIMING_CTRL_1_CLK_TRAIL(timing->clk_trail)); + dsi_phy_write(base + REG_DSI_20nm_PHY_TIMING_CTRL_2, + DSI_20nm_PHY_TIMING_CTRL_2_CLK_PREPARE(timing->clk_prepare)); + if (timing->clk_zero & BIT(8)) + dsi_phy_write(base + REG_DSI_20nm_PHY_TIMING_CTRL_3, + DSI_20nm_PHY_TIMING_CTRL_3_CLK_ZERO_8); + dsi_phy_write(base + REG_DSI_20nm_PHY_TIMING_CTRL_4, + DSI_20nm_PHY_TIMING_CTRL_4_HS_EXIT(timing->hs_exit)); + dsi_phy_write(base + REG_DSI_20nm_PHY_TIMING_CTRL_5, + DSI_20nm_PHY_TIMING_CTRL_5_HS_ZERO(timing->hs_zero)); + dsi_phy_write(base + REG_DSI_20nm_PHY_TIMING_CTRL_6, + DSI_20nm_PHY_TIMING_CTRL_6_HS_PREPARE(timing->hs_prepare)); + dsi_phy_write(base + REG_DSI_20nm_PHY_TIMING_CTRL_7, + DSI_20nm_PHY_TIMING_CTRL_7_HS_TRAIL(timing->hs_trail)); + dsi_phy_write(base + REG_DSI_20nm_PHY_TIMING_CTRL_8, + DSI_20nm_PHY_TIMING_CTRL_8_HS_RQST(timing->hs_rqst)); + dsi_phy_write(base + REG_DSI_20nm_PHY_TIMING_CTRL_9, + DSI_20nm_PHY_TIMING_CTRL_9_TA_GO(timing->ta_go) | + DSI_20nm_PHY_TIMING_CTRL_9_TA_SURE(timing->ta_sure)); + dsi_phy_write(base + REG_DSI_20nm_PHY_TIMING_CTRL_10, + DSI_20nm_PHY_TIMING_CTRL_10_TA_GET(timing->ta_get)); + dsi_phy_write(base + REG_DSI_20nm_PHY_TIMING_CTRL_11, + DSI_20nm_PHY_TIMING_CTRL_11_TRIG3_CMD(0)); +} + +static void dsi_20nm_phy_regulator_ctrl(struct msm_dsi_phy *phy, bool enable) +{ + void __iomem *base = phy->reg_base; + + if (!enable) { + dsi_phy_write(base + REG_DSI_20nm_PHY_REGULATOR_CAL_PWR_CFG, 0); + return; + } + + if (phy->regulator_ldo_mode) { + dsi_phy_write(phy->base + REG_DSI_20nm_PHY_LDO_CNTRL, 0x1d); + return; + } + + /* non LDO mode */ + dsi_phy_write(base + REG_DSI_20nm_PHY_REGULATOR_CTRL_1, 0x03); + dsi_phy_write(base + REG_DSI_20nm_PHY_REGULATOR_CTRL_2, 0x03); + dsi_phy_write(base + REG_DSI_20nm_PHY_REGULATOR_CTRL_3, 0x00); + dsi_phy_write(base + REG_DSI_20nm_PHY_REGULATOR_CTRL_4, 0x20); + dsi_phy_write(base + REG_DSI_20nm_PHY_REGULATOR_CAL_PWR_CFG, 0x01); + dsi_phy_write(phy->base + REG_DSI_20nm_PHY_LDO_CNTRL, 0x00); + dsi_phy_write(base + REG_DSI_20nm_PHY_REGULATOR_CTRL_0, 0x03); +} + +static int dsi_20nm_phy_enable(struct msm_dsi_phy *phy, int src_pll_id, + const unsigned long bit_rate, const unsigned long esc_rate) +{ + struct msm_dsi_dphy_timing *timing = &phy->timing; + int i; + void __iomem *base = phy->base; + u32 cfg_4[4] = {0x20, 0x40, 0x20, 0x00}; + + DBG(""); + + if (msm_dsi_dphy_timing_calc(timing, bit_rate, esc_rate)) { + dev_err(&phy->pdev->dev, + "%s: D-PHY timing calculation failed\n", __func__); + return -EINVAL; + } + + dsi_20nm_phy_regulator_ctrl(phy, true); + + dsi_phy_write(base + REG_DSI_20nm_PHY_STRENGTH_0, 0xff); + + msm_dsi_phy_set_src_pll(phy, src_pll_id, + REG_DSI_20nm_PHY_GLBL_TEST_CTRL, + DSI_20nm_PHY_GLBL_TEST_CTRL_BITCLK_HS_SEL); + + for (i = 0; i < 4; i++) { + dsi_phy_write(base + REG_DSI_20nm_PHY_LN_CFG_3(i), + (i >> 1) * 0x40); + dsi_phy_write(base + REG_DSI_20nm_PHY_LN_TEST_STR_0(i), 0x01); + dsi_phy_write(base + REG_DSI_20nm_PHY_LN_TEST_STR_1(i), 0x46); + dsi_phy_write(base + REG_DSI_20nm_PHY_LN_CFG_0(i), 0x02); + dsi_phy_write(base + REG_DSI_20nm_PHY_LN_CFG_1(i), 0xa0); + dsi_phy_write(base + REG_DSI_20nm_PHY_LN_CFG_4(i), cfg_4[i]); + } + + dsi_phy_write(base + REG_DSI_20nm_PHY_LNCK_CFG_3, 0x80); + dsi_phy_write(base + REG_DSI_20nm_PHY_LNCK_TEST_STR0, 0x01); + dsi_phy_write(base + REG_DSI_20nm_PHY_LNCK_TEST_STR1, 0x46); + dsi_phy_write(base + REG_DSI_20nm_PHY_LNCK_CFG_0, 0x00); + dsi_phy_write(base + REG_DSI_20nm_PHY_LNCK_CFG_1, 0xa0); + dsi_phy_write(base + REG_DSI_20nm_PHY_LNCK_CFG_2, 0x00); + dsi_phy_write(base + REG_DSI_20nm_PHY_LNCK_CFG_4, 0x00); + + dsi_20nm_dphy_set_timing(phy, timing); + + dsi_phy_write(base + REG_DSI_20nm_PHY_CTRL_1, 0x00); + + dsi_phy_write(base + REG_DSI_20nm_PHY_STRENGTH_1, 0x06); + + /* make sure everything is written before enable */ + wmb(); + dsi_phy_write(base + REG_DSI_20nm_PHY_CTRL_0, 0x7f); + + return 0; +} + +static void dsi_20nm_phy_disable(struct msm_dsi_phy *phy) +{ + dsi_phy_write(phy->base + REG_DSI_20nm_PHY_CTRL_0, 0); + dsi_20nm_phy_regulator_ctrl(phy, false); +} + +const struct msm_dsi_phy_cfg dsi_phy_20nm_cfgs = { + .type = MSM_DSI_PHY_20NM, + .src_pll_truthtable = { {false, true}, {false, true} }, + .reg_cfg = { + .num = 2, + .regs = { + {"vddio", 1800000, 1800000, 100000, 100}, + {"vcca", 1000000, 1000000, 10000, 100}, + }, + }, + .ops = { + .enable = dsi_20nm_phy_enable, + .disable = dsi_20nm_phy_disable, + } +}; + diff --git a/kernel/drivers/gpu/drm/msm/dsi/phy/dsi_phy_28nm.c b/kernel/drivers/gpu/drm/msm/dsi/phy/dsi_phy_28nm.c new file mode 100644 index 000000000..edf74110c --- /dev/null +++ b/kernel/drivers/gpu/drm/msm/dsi/phy/dsi_phy_28nm.c @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2015, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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 "dsi_phy.h" +#include "dsi.xml.h" + +static void dsi_28nm_dphy_set_timing(struct msm_dsi_phy *phy, + struct msm_dsi_dphy_timing *timing) +{ + void __iomem *base = phy->base; + + dsi_phy_write(base + REG_DSI_28nm_PHY_TIMING_CTRL_0, + DSI_28nm_PHY_TIMING_CTRL_0_CLK_ZERO(timing->clk_zero)); + dsi_phy_write(base + REG_DSI_28nm_PHY_TIMING_CTRL_1, + DSI_28nm_PHY_TIMING_CTRL_1_CLK_TRAIL(timing->clk_trail)); + dsi_phy_write(base + REG_DSI_28nm_PHY_TIMING_CTRL_2, + DSI_28nm_PHY_TIMING_CTRL_2_CLK_PREPARE(timing->clk_prepare)); + if (timing->clk_zero & BIT(8)) + dsi_phy_write(base + REG_DSI_28nm_PHY_TIMING_CTRL_3, + DSI_28nm_PHY_TIMING_CTRL_3_CLK_ZERO_8); + dsi_phy_write(base + REG_DSI_28nm_PHY_TIMING_CTRL_4, + DSI_28nm_PHY_TIMING_CTRL_4_HS_EXIT(timing->hs_exit)); + dsi_phy_write(base + REG_DSI_28nm_PHY_TIMING_CTRL_5, + DSI_28nm_PHY_TIMING_CTRL_5_HS_ZERO(timing->hs_zero)); + dsi_phy_write(base + REG_DSI_28nm_PHY_TIMING_CTRL_6, + DSI_28nm_PHY_TIMING_CTRL_6_HS_PREPARE(timing->hs_prepare)); + dsi_phy_write(base + REG_DSI_28nm_PHY_TIMING_CTRL_7, + DSI_28nm_PHY_TIMING_CTRL_7_HS_TRAIL(timing->hs_trail)); + dsi_phy_write(base + REG_DSI_28nm_PHY_TIMING_CTRL_8, + DSI_28nm_PHY_TIMING_CTRL_8_HS_RQST(timing->hs_rqst)); + dsi_phy_write(base + REG_DSI_28nm_PHY_TIMING_CTRL_9, + DSI_28nm_PHY_TIMING_CTRL_9_TA_GO(timing->ta_go) | + DSI_28nm_PHY_TIMING_CTRL_9_TA_SURE(timing->ta_sure)); + dsi_phy_write(base + REG_DSI_28nm_PHY_TIMING_CTRL_10, + DSI_28nm_PHY_TIMING_CTRL_10_TA_GET(timing->ta_get)); + dsi_phy_write(base + REG_DSI_28nm_PHY_TIMING_CTRL_11, + DSI_28nm_PHY_TIMING_CTRL_11_TRIG3_CMD(0)); +} + +static void dsi_28nm_phy_regulator_ctrl(struct msm_dsi_phy *phy, bool enable) +{ + void __iomem *base = phy->reg_base; + + if (!enable) { + dsi_phy_write(base + REG_DSI_28nm_PHY_REGULATOR_CAL_PWR_CFG, 0); + return; + } + + dsi_phy_write(base + REG_DSI_28nm_PHY_REGULATOR_CTRL_0, 0x0); + dsi_phy_write(base + REG_DSI_28nm_PHY_REGULATOR_CAL_PWR_CFG, 1); + dsi_phy_write(base + REG_DSI_28nm_PHY_REGULATOR_CTRL_5, 0); + dsi_phy_write(base + REG_DSI_28nm_PHY_REGULATOR_CTRL_3, 0); + dsi_phy_write(base + REG_DSI_28nm_PHY_REGULATOR_CTRL_2, 0x3); + dsi_phy_write(base + REG_DSI_28nm_PHY_REGULATOR_CTRL_1, 0x9); + dsi_phy_write(base + REG_DSI_28nm_PHY_REGULATOR_CTRL_0, 0x7); + dsi_phy_write(base + REG_DSI_28nm_PHY_REGULATOR_CTRL_4, 0x20); +} + +static int dsi_28nm_phy_enable(struct msm_dsi_phy *phy, int src_pll_id, + const unsigned long bit_rate, const unsigned long esc_rate) +{ + struct msm_dsi_dphy_timing *timing = &phy->timing; + int i; + void __iomem *base = phy->base; + + DBG(""); + + if (msm_dsi_dphy_timing_calc(timing, bit_rate, esc_rate)) { + dev_err(&phy->pdev->dev, + "%s: D-PHY timing calculation failed\n", __func__); + return -EINVAL; + } + + dsi_phy_write(base + REG_DSI_28nm_PHY_STRENGTH_0, 0xff); + + dsi_28nm_phy_regulator_ctrl(phy, true); + + dsi_phy_write(base + REG_DSI_28nm_PHY_LDO_CNTRL, 0x00); + + dsi_28nm_dphy_set_timing(phy, timing); + + dsi_phy_write(base + REG_DSI_28nm_PHY_CTRL_1, 0x00); + dsi_phy_write(base + REG_DSI_28nm_PHY_CTRL_0, 0x5f); + + dsi_phy_write(base + REG_DSI_28nm_PHY_STRENGTH_1, 0x6); + + for (i = 0; i < 4; i++) { + dsi_phy_write(base + REG_DSI_28nm_PHY_LN_CFG_0(i), 0); + dsi_phy_write(base + REG_DSI_28nm_PHY_LN_CFG_1(i), 0); + dsi_phy_write(base + REG_DSI_28nm_PHY_LN_CFG_2(i), 0); + dsi_phy_write(base + REG_DSI_28nm_PHY_LN_CFG_3(i), 0); + dsi_phy_write(base + REG_DSI_28nm_PHY_LN_CFG_4(i), 0); + dsi_phy_write(base + REG_DSI_28nm_PHY_LN_TEST_DATAPATH(i), 0); + dsi_phy_write(base + REG_DSI_28nm_PHY_LN_DEBUG_SEL(i), 0); + dsi_phy_write(base + REG_DSI_28nm_PHY_LN_TEST_STR_0(i), 0x1); + dsi_phy_write(base + REG_DSI_28nm_PHY_LN_TEST_STR_1(i), 0x97); + } + + dsi_phy_write(base + REG_DSI_28nm_PHY_LNCK_CFG_4, 0); + dsi_phy_write(base + REG_DSI_28nm_PHY_LNCK_CFG_1, 0xc0); + dsi_phy_write(base + REG_DSI_28nm_PHY_LNCK_TEST_STR0, 0x1); + dsi_phy_write(base + REG_DSI_28nm_PHY_LNCK_TEST_STR1, 0xbb); + + dsi_phy_write(base + REG_DSI_28nm_PHY_CTRL_0, 0x5f); + + msm_dsi_phy_set_src_pll(phy, src_pll_id, + REG_DSI_28nm_PHY_GLBL_TEST_CTRL, + DSI_28nm_PHY_GLBL_TEST_CTRL_BITCLK_HS_SEL); + + return 0; +} + +static void dsi_28nm_phy_disable(struct msm_dsi_phy *phy) +{ + dsi_phy_write(phy->base + REG_DSI_28nm_PHY_CTRL_0, 0); + dsi_28nm_phy_regulator_ctrl(phy, false); + + /* + * Wait for the registers writes to complete in order to + * ensure that the phy is completely disabled + */ + wmb(); +} + +const struct msm_dsi_phy_cfg dsi_phy_28nm_hpm_cfgs = { + .type = MSM_DSI_PHY_28NM_HPM, + .src_pll_truthtable = { {true, true}, {false, true} }, + .reg_cfg = { + .num = 1, + .regs = { + {"vddio", 1800000, 1800000, 100000, 100}, + }, + }, + .ops = { + .enable = dsi_28nm_phy_enable, + .disable = dsi_28nm_phy_disable, + }, +}; + +const struct msm_dsi_phy_cfg dsi_phy_28nm_lp_cfgs = { + .type = MSM_DSI_PHY_28NM_LP, + .src_pll_truthtable = { {true, true}, {true, true} }, + .reg_cfg = { + .num = 1, + .regs = { + {"vddio", 1800000, 1800000, 100000, 100}, + }, + }, + .ops = { + .enable = dsi_28nm_phy_enable, + .disable = dsi_28nm_phy_disable, + }, +}; + diff --git a/kernel/drivers/gpu/drm/msm/dsi/pll/dsi_pll.c b/kernel/drivers/gpu/drm/msm/dsi/pll/dsi_pll.c new file mode 100644 index 000000000..5104fc9f9 --- /dev/null +++ b/kernel/drivers/gpu/drm/msm/dsi/pll/dsi_pll.c @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2012-2015, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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 "dsi_pll.h" + +static int dsi_pll_enable(struct msm_dsi_pll *pll) +{ + int i, ret = 0; + + /* + * Certain PLLs do not allow VCO rate update when it is on. + * Keep track of their status to turn on/off after set rate success. + */ + if (unlikely(pll->pll_on)) + return 0; + + /* Try all enable sequences until one succeeds */ + for (i = 0; i < pll->en_seq_cnt; i++) { + ret = pll->enable_seqs[i](pll); + DBG("DSI PLL %s after sequence #%d", + ret ? "unlocked" : "locked", i + 1); + if (!ret) + break; + } + + if (ret) { + DRM_ERROR("DSI PLL failed to lock\n"); + return ret; + } + + pll->pll_on = true; + + return 0; +} + +static void dsi_pll_disable(struct msm_dsi_pll *pll) +{ + if (unlikely(!pll->pll_on)) + return; + + pll->disable_seq(pll); + + pll->pll_on = false; +} + +/* + * DSI PLL Helper functions + */ +long msm_dsi_pll_helper_clk_round_rate(struct clk_hw *hw, + unsigned long rate, unsigned long *parent_rate) +{ + struct msm_dsi_pll *pll = hw_clk_to_pll(hw); + + if (rate < pll->min_rate) + return pll->min_rate; + else if (rate > pll->max_rate) + return pll->max_rate; + else + return rate; +} + +int msm_dsi_pll_helper_clk_prepare(struct clk_hw *hw) +{ + struct msm_dsi_pll *pll = hw_clk_to_pll(hw); + + return dsi_pll_enable(pll); +} + +void msm_dsi_pll_helper_clk_unprepare(struct clk_hw *hw) +{ + struct msm_dsi_pll *pll = hw_clk_to_pll(hw); + + dsi_pll_disable(pll); +} + +void msm_dsi_pll_helper_unregister_clks(struct platform_device *pdev, + struct clk **clks, u32 num_clks) +{ + of_clk_del_provider(pdev->dev.of_node); + + if (!num_clks || !clks) + return; + + do { + clk_unregister(clks[--num_clks]); + clks[num_clks] = NULL; + } while (num_clks); +} + +/* + * DSI PLL API + */ +int msm_dsi_pll_get_clk_provider(struct msm_dsi_pll *pll, + struct clk **byte_clk_provider, struct clk **pixel_clk_provider) +{ + if (pll->get_provider) + return pll->get_provider(pll, + byte_clk_provider, + pixel_clk_provider); + + return -EINVAL; +} + +void msm_dsi_pll_destroy(struct msm_dsi_pll *pll) +{ + if (pll->destroy) + pll->destroy(pll); +} + +void msm_dsi_pll_save_state(struct msm_dsi_pll *pll) +{ + if (pll->save_state) { + pll->save_state(pll); + pll->state_saved = true; + } +} + +int msm_dsi_pll_restore_state(struct msm_dsi_pll *pll) +{ + int ret; + + if (pll->restore_state && pll->state_saved) { + ret = pll->restore_state(pll); + if (ret) + return ret; + + pll->state_saved = false; + } + + return 0; +} + +struct msm_dsi_pll *msm_dsi_pll_init(struct platform_device *pdev, + enum msm_dsi_phy_type type, int id) +{ + struct device *dev = &pdev->dev; + struct msm_dsi_pll *pll; + + switch (type) { + case MSM_DSI_PHY_28NM_HPM: + case MSM_DSI_PHY_28NM_LP: + pll = msm_dsi_pll_28nm_init(pdev, type, id); + break; + default: + pll = ERR_PTR(-ENXIO); + break; + } + + if (IS_ERR(pll)) { + dev_err(dev, "%s: failed to init DSI PLL\n", __func__); + return NULL; + } + + pll->type = type; + + DBG("DSI:%d PLL registered", id); + + return pll; +} + diff --git a/kernel/drivers/gpu/drm/msm/dsi/pll/dsi_pll.h b/kernel/drivers/gpu/drm/msm/dsi/pll/dsi_pll.h new file mode 100644 index 000000000..063caa2c5 --- /dev/null +++ b/kernel/drivers/gpu/drm/msm/dsi/pll/dsi_pll.h @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2012-2015, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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. + */ + +#ifndef __DSI_PLL_H__ +#define __DSI_PLL_H__ + +#include <linux/clk.h> +#include <linux/clk-provider.h> + +#include "dsi.h" + +#define NUM_DSI_CLOCKS_MAX 6 +#define MAX_DSI_PLL_EN_SEQS 10 + +struct msm_dsi_pll { + enum msm_dsi_phy_type type; + + struct clk_hw clk_hw; + bool pll_on; + bool state_saved; + + unsigned long min_rate; + unsigned long max_rate; + u32 en_seq_cnt; + + int (*enable_seqs[MAX_DSI_PLL_EN_SEQS])(struct msm_dsi_pll *pll); + void (*disable_seq)(struct msm_dsi_pll *pll); + int (*get_provider)(struct msm_dsi_pll *pll, + struct clk **byte_clk_provider, + struct clk **pixel_clk_provider); + void (*destroy)(struct msm_dsi_pll *pll); + void (*save_state)(struct msm_dsi_pll *pll); + int (*restore_state)(struct msm_dsi_pll *pll); +}; + +#define hw_clk_to_pll(x) container_of(x, struct msm_dsi_pll, clk_hw) + +static inline void pll_write(void __iomem *reg, u32 data) +{ + msm_writel(data, reg); +} + +static inline u32 pll_read(const void __iomem *reg) +{ + return msm_readl(reg); +} + +static inline void pll_write_udelay(void __iomem *reg, u32 data, u32 delay_us) +{ + pll_write(reg, data); + udelay(delay_us); +} + +static inline void pll_write_ndelay(void __iomem *reg, u32 data, u32 delay_ns) +{ + pll_write((reg), data); + ndelay(delay_ns); +} + +/* + * DSI PLL Helper functions + */ + +/* clock callbacks */ +long msm_dsi_pll_helper_clk_round_rate(struct clk_hw *hw, + unsigned long rate, unsigned long *parent_rate); +int msm_dsi_pll_helper_clk_prepare(struct clk_hw *hw); +void msm_dsi_pll_helper_clk_unprepare(struct clk_hw *hw); +/* misc */ +void msm_dsi_pll_helper_unregister_clks(struct platform_device *pdev, + struct clk **clks, u32 num_clks); + +/* + * Initialization for Each PLL Type + */ +#ifdef CONFIG_DRM_MSM_DSI_28NM_PHY +struct msm_dsi_pll *msm_dsi_pll_28nm_init(struct platform_device *pdev, + enum msm_dsi_phy_type type, int id); +#else +static inline struct msm_dsi_pll *msm_dsi_pll_28nm_init( + struct platform_device *pdev, enum msm_dsi_phy_type type, int id) +{ + return ERR_PTR(-ENODEV); +} +#endif + +#endif /* __DSI_PLL_H__ */ + diff --git a/kernel/drivers/gpu/drm/msm/dsi/pll/dsi_pll_28nm.c b/kernel/drivers/gpu/drm/msm/dsi/pll/dsi_pll_28nm.c new file mode 100644 index 000000000..598fdaff0 --- /dev/null +++ b/kernel/drivers/gpu/drm/msm/dsi/pll/dsi_pll_28nm.c @@ -0,0 +1,647 @@ +/* + * Copyright (c) 2012-2015, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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 <linux/clk.h> +#include <linux/clk-provider.h> + +#include "dsi_pll.h" +#include "dsi.xml.h" + +/* + * DSI PLL 28nm - clock diagram (eg: DSI0): + * + * dsi0analog_postdiv_clk + * | dsi0indirect_path_div2_clk + * | | + * +------+ | +----+ | |\ dsi0byte_mux + * dsi0vco_clk --o--| DIV1 |--o--| /2 |--o--| \ | + * | +------+ +----+ | m| | +----+ + * | | u|--o--| /4 |-- dsi0pllbyte + * | | x| +----+ + * o--------------------------| / + * | |/ + * | +------+ + * o----------| DIV3 |------------------------- dsi0pll + * +------+ + */ + +#define POLL_MAX_READS 10 +#define POLL_TIMEOUT_US 50 + +#define NUM_PROVIDED_CLKS 2 + +#define VCO_REF_CLK_RATE 19200000 +#define VCO_MIN_RATE 350000000 +#define VCO_MAX_RATE 750000000 + +#define DSI_BYTE_PLL_CLK 0 +#define DSI_PIXEL_PLL_CLK 1 + +#define LPFR_LUT_SIZE 10 +struct lpfr_cfg { + unsigned long vco_rate; + u32 resistance; +}; + +/* Loop filter resistance: */ +static const struct lpfr_cfg lpfr_lut[LPFR_LUT_SIZE] = { + { 479500000, 8 }, + { 480000000, 11 }, + { 575500000, 8 }, + { 576000000, 12 }, + { 610500000, 8 }, + { 659500000, 9 }, + { 671500000, 10 }, + { 672000000, 14 }, + { 708500000, 10 }, + { 750000000, 11 }, +}; + +struct pll_28nm_cached_state { + unsigned long vco_rate; + u8 postdiv3; + u8 postdiv1; + u8 byte_mux; +}; + +struct dsi_pll_28nm { + struct msm_dsi_pll base; + + int id; + struct platform_device *pdev; + void __iomem *mmio; + + int vco_delay; + + /* private clocks: */ + struct clk *clks[NUM_DSI_CLOCKS_MAX]; + u32 num_clks; + + /* clock-provider: */ + struct clk *provided_clks[NUM_PROVIDED_CLKS]; + struct clk_onecell_data clk_data; + + struct pll_28nm_cached_state cached_state; +}; + +#define to_pll_28nm(x) container_of(x, struct dsi_pll_28nm, base) + +static bool pll_28nm_poll_for_ready(struct dsi_pll_28nm *pll_28nm, + u32 nb_tries, u32 timeout_us) +{ + bool pll_locked = false; + u32 val; + + while (nb_tries--) { + val = pll_read(pll_28nm->mmio + REG_DSI_28nm_PHY_PLL_STATUS); + pll_locked = !!(val & DSI_28nm_PHY_PLL_STATUS_PLL_RDY); + + if (pll_locked) + break; + + udelay(timeout_us); + } + DBG("DSI PLL is %slocked", pll_locked ? "" : "*not* "); + + return pll_locked; +} + +static void pll_28nm_software_reset(struct dsi_pll_28nm *pll_28nm) +{ + void __iomem *base = pll_28nm->mmio; + + /* + * Add HW recommended delays after toggling the software + * reset bit off and back on. + */ + pll_write_udelay(base + REG_DSI_28nm_PHY_PLL_TEST_CFG, + DSI_28nm_PHY_PLL_TEST_CFG_PLL_SW_RESET, 1); + pll_write_udelay(base + REG_DSI_28nm_PHY_PLL_TEST_CFG, 0x00, 1); +} + +/* + * Clock Callbacks + */ +static int dsi_pll_28nm_clk_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct msm_dsi_pll *pll = hw_clk_to_pll(hw); + struct dsi_pll_28nm *pll_28nm = to_pll_28nm(pll); + struct device *dev = &pll_28nm->pdev->dev; + void __iomem *base = pll_28nm->mmio; + unsigned long div_fbx1000, gen_vco_clk; + u32 refclk_cfg, frac_n_mode, frac_n_value; + u32 sdm_cfg0, sdm_cfg1, sdm_cfg2, sdm_cfg3; + u32 cal_cfg10, cal_cfg11; + u32 rem; + int i; + + VERB("rate=%lu, parent's=%lu", rate, parent_rate); + + /* Force postdiv2 to be div-4 */ + pll_write(base + REG_DSI_28nm_PHY_PLL_POSTDIV2_CFG, 3); + + /* Configure the Loop filter resistance */ + for (i = 0; i < LPFR_LUT_SIZE; i++) + if (rate <= lpfr_lut[i].vco_rate) + break; + if (i == LPFR_LUT_SIZE) { + dev_err(dev, "unable to get loop filter resistance. vco=%lu\n", + rate); + return -EINVAL; + } + pll_write(base + REG_DSI_28nm_PHY_PLL_LPFR_CFG, lpfr_lut[i].resistance); + + /* Loop filter capacitance values : c1 and c2 */ + pll_write(base + REG_DSI_28nm_PHY_PLL_LPFC1_CFG, 0x70); + pll_write(base + REG_DSI_28nm_PHY_PLL_LPFC2_CFG, 0x15); + + rem = rate % VCO_REF_CLK_RATE; + if (rem) { + refclk_cfg = DSI_28nm_PHY_PLL_REFCLK_CFG_DBLR; + frac_n_mode = 1; + div_fbx1000 = rate / (VCO_REF_CLK_RATE / 500); + gen_vco_clk = div_fbx1000 * (VCO_REF_CLK_RATE / 500); + } else { + refclk_cfg = 0x0; + frac_n_mode = 0; + div_fbx1000 = rate / (VCO_REF_CLK_RATE / 1000); + gen_vco_clk = div_fbx1000 * (VCO_REF_CLK_RATE / 1000); + } + + DBG("refclk_cfg = %d", refclk_cfg); + + rem = div_fbx1000 % 1000; + frac_n_value = (rem << 16) / 1000; + + DBG("div_fb = %lu", div_fbx1000); + DBG("frac_n_value = %d", frac_n_value); + + DBG("Generated VCO Clock: %lu", gen_vco_clk); + rem = 0; + sdm_cfg1 = pll_read(base + REG_DSI_28nm_PHY_PLL_SDM_CFG1); + sdm_cfg1 &= ~DSI_28nm_PHY_PLL_SDM_CFG1_DC_OFFSET__MASK; + if (frac_n_mode) { + sdm_cfg0 = 0x0; + sdm_cfg0 |= DSI_28nm_PHY_PLL_SDM_CFG0_BYP_DIV(0); + sdm_cfg1 |= DSI_28nm_PHY_PLL_SDM_CFG1_DC_OFFSET( + (u32)(((div_fbx1000 / 1000) & 0x3f) - 1)); + sdm_cfg3 = frac_n_value >> 8; + sdm_cfg2 = frac_n_value & 0xff; + } else { + sdm_cfg0 = DSI_28nm_PHY_PLL_SDM_CFG0_BYP; + sdm_cfg0 |= DSI_28nm_PHY_PLL_SDM_CFG0_BYP_DIV( + (u32)(((div_fbx1000 / 1000) & 0x3f) - 1)); + sdm_cfg1 |= DSI_28nm_PHY_PLL_SDM_CFG1_DC_OFFSET(0); + sdm_cfg2 = 0; + sdm_cfg3 = 0; + } + + DBG("sdm_cfg0=%d", sdm_cfg0); + DBG("sdm_cfg1=%d", sdm_cfg1); + DBG("sdm_cfg2=%d", sdm_cfg2); + DBG("sdm_cfg3=%d", sdm_cfg3); + + cal_cfg11 = (u32)(gen_vco_clk / (256 * 1000000)); + cal_cfg10 = (u32)((gen_vco_clk % (256 * 1000000)) / 1000000); + DBG("cal_cfg10=%d, cal_cfg11=%d", cal_cfg10, cal_cfg11); + + pll_write(base + REG_DSI_28nm_PHY_PLL_CHGPUMP_CFG, 0x02); + pll_write(base + REG_DSI_28nm_PHY_PLL_CAL_CFG3, 0x2b); + pll_write(base + REG_DSI_28nm_PHY_PLL_CAL_CFG4, 0x06); + pll_write(base + REG_DSI_28nm_PHY_PLL_LKDET_CFG2, 0x0d); + + pll_write(base + REG_DSI_28nm_PHY_PLL_SDM_CFG1, sdm_cfg1); + pll_write(base + REG_DSI_28nm_PHY_PLL_SDM_CFG2, + DSI_28nm_PHY_PLL_SDM_CFG2_FREQ_SEED_7_0(sdm_cfg2)); + pll_write(base + REG_DSI_28nm_PHY_PLL_SDM_CFG3, + DSI_28nm_PHY_PLL_SDM_CFG3_FREQ_SEED_15_8(sdm_cfg3)); + pll_write(base + REG_DSI_28nm_PHY_PLL_SDM_CFG4, 0x00); + + /* Add hardware recommended delay for correct PLL configuration */ + if (pll_28nm->vco_delay) + udelay(pll_28nm->vco_delay); + + pll_write(base + REG_DSI_28nm_PHY_PLL_REFCLK_CFG, refclk_cfg); + pll_write(base + REG_DSI_28nm_PHY_PLL_PWRGEN_CFG, 0x00); + pll_write(base + REG_DSI_28nm_PHY_PLL_VCOLPF_CFG, 0x31); + pll_write(base + REG_DSI_28nm_PHY_PLL_SDM_CFG0, sdm_cfg0); + pll_write(base + REG_DSI_28nm_PHY_PLL_CAL_CFG0, 0x12); + pll_write(base + REG_DSI_28nm_PHY_PLL_CAL_CFG6, 0x30); + pll_write(base + REG_DSI_28nm_PHY_PLL_CAL_CFG7, 0x00); + pll_write(base + REG_DSI_28nm_PHY_PLL_CAL_CFG8, 0x60); + pll_write(base + REG_DSI_28nm_PHY_PLL_CAL_CFG9, 0x00); + pll_write(base + REG_DSI_28nm_PHY_PLL_CAL_CFG10, cal_cfg10 & 0xff); + pll_write(base + REG_DSI_28nm_PHY_PLL_CAL_CFG11, cal_cfg11 & 0xff); + pll_write(base + REG_DSI_28nm_PHY_PLL_EFUSE_CFG, 0x20); + + return 0; +} + +static int dsi_pll_28nm_clk_is_enabled(struct clk_hw *hw) +{ + struct msm_dsi_pll *pll = hw_clk_to_pll(hw); + struct dsi_pll_28nm *pll_28nm = to_pll_28nm(pll); + + return pll_28nm_poll_for_ready(pll_28nm, POLL_MAX_READS, + POLL_TIMEOUT_US); +} + +static unsigned long dsi_pll_28nm_clk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct msm_dsi_pll *pll = hw_clk_to_pll(hw); + struct dsi_pll_28nm *pll_28nm = to_pll_28nm(pll); + void __iomem *base = pll_28nm->mmio; + u32 sdm0, doubler, sdm_byp_div; + u32 sdm_dc_off, sdm_freq_seed, sdm2, sdm3; + u32 ref_clk = VCO_REF_CLK_RATE; + unsigned long vco_rate; + + VERB("parent_rate=%lu", parent_rate); + + /* Check to see if the ref clk doubler is enabled */ + doubler = pll_read(base + REG_DSI_28nm_PHY_PLL_REFCLK_CFG) & + DSI_28nm_PHY_PLL_REFCLK_CFG_DBLR; + ref_clk += (doubler * VCO_REF_CLK_RATE); + + /* see if it is integer mode or sdm mode */ + sdm0 = pll_read(base + REG_DSI_28nm_PHY_PLL_SDM_CFG0); + if (sdm0 & DSI_28nm_PHY_PLL_SDM_CFG0_BYP) { + /* integer mode */ + sdm_byp_div = FIELD( + pll_read(base + REG_DSI_28nm_PHY_PLL_SDM_CFG0), + DSI_28nm_PHY_PLL_SDM_CFG0_BYP_DIV) + 1; + vco_rate = ref_clk * sdm_byp_div; + } else { + /* sdm mode */ + sdm_dc_off = FIELD( + pll_read(base + REG_DSI_28nm_PHY_PLL_SDM_CFG1), + DSI_28nm_PHY_PLL_SDM_CFG1_DC_OFFSET); + DBG("sdm_dc_off = %d", sdm_dc_off); + sdm2 = FIELD(pll_read(base + REG_DSI_28nm_PHY_PLL_SDM_CFG2), + DSI_28nm_PHY_PLL_SDM_CFG2_FREQ_SEED_7_0); + sdm3 = FIELD(pll_read(base + REG_DSI_28nm_PHY_PLL_SDM_CFG3), + DSI_28nm_PHY_PLL_SDM_CFG3_FREQ_SEED_15_8); + sdm_freq_seed = (sdm3 << 8) | sdm2; + DBG("sdm_freq_seed = %d", sdm_freq_seed); + + vco_rate = (ref_clk * (sdm_dc_off + 1)) + + mult_frac(ref_clk, sdm_freq_seed, BIT(16)); + DBG("vco rate = %lu", vco_rate); + } + + DBG("returning vco rate = %lu", vco_rate); + + return vco_rate; +} + +static const struct clk_ops clk_ops_dsi_pll_28nm_vco = { + .round_rate = msm_dsi_pll_helper_clk_round_rate, + .set_rate = dsi_pll_28nm_clk_set_rate, + .recalc_rate = dsi_pll_28nm_clk_recalc_rate, + .prepare = msm_dsi_pll_helper_clk_prepare, + .unprepare = msm_dsi_pll_helper_clk_unprepare, + .is_enabled = dsi_pll_28nm_clk_is_enabled, +}; + +/* + * PLL Callbacks + */ +static int dsi_pll_28nm_enable_seq_hpm(struct msm_dsi_pll *pll) +{ + struct dsi_pll_28nm *pll_28nm = to_pll_28nm(pll); + struct device *dev = &pll_28nm->pdev->dev; + void __iomem *base = pll_28nm->mmio; + u32 max_reads = 5, timeout_us = 100; + bool locked; + u32 val; + int i; + + DBG("id=%d", pll_28nm->id); + + pll_28nm_software_reset(pll_28nm); + + /* + * PLL power up sequence. + * Add necessary delays recommended by hardware. + */ + val = DSI_28nm_PHY_PLL_GLB_CFG_PLL_PWRDN_B; + pll_write_udelay(base + REG_DSI_28nm_PHY_PLL_GLB_CFG, val, 1); + + val |= DSI_28nm_PHY_PLL_GLB_CFG_PLL_PWRGEN_PWRDN_B; + pll_write_udelay(base + REG_DSI_28nm_PHY_PLL_GLB_CFG, val, 200); + + val |= DSI_28nm_PHY_PLL_GLB_CFG_PLL_LDO_PWRDN_B; + pll_write_udelay(base + REG_DSI_28nm_PHY_PLL_GLB_CFG, val, 500); + + val |= DSI_28nm_PHY_PLL_GLB_CFG_PLL_ENABLE; + pll_write_udelay(base + REG_DSI_28nm_PHY_PLL_GLB_CFG, val, 600); + + for (i = 0; i < 2; i++) { + /* DSI Uniphy lock detect setting */ + pll_write_udelay(base + REG_DSI_28nm_PHY_PLL_LKDET_CFG2, + 0x0c, 100); + pll_write(base + REG_DSI_28nm_PHY_PLL_LKDET_CFG2, 0x0d); + + /* poll for PLL ready status */ + locked = pll_28nm_poll_for_ready(pll_28nm, + max_reads, timeout_us); + if (locked) + break; + + pll_28nm_software_reset(pll_28nm); + + /* + * PLL power up sequence. + * Add necessary delays recommended by hardware. + */ + val = DSI_28nm_PHY_PLL_GLB_CFG_PLL_PWRDN_B; + pll_write_udelay(base + REG_DSI_28nm_PHY_PLL_GLB_CFG, val, 1); + + val |= DSI_28nm_PHY_PLL_GLB_CFG_PLL_PWRGEN_PWRDN_B; + pll_write_udelay(base + REG_DSI_28nm_PHY_PLL_GLB_CFG, val, 200); + + val |= DSI_28nm_PHY_PLL_GLB_CFG_PLL_LDO_PWRDN_B; + pll_write_udelay(base + REG_DSI_28nm_PHY_PLL_GLB_CFG, val, 250); + + val &= ~DSI_28nm_PHY_PLL_GLB_CFG_PLL_LDO_PWRDN_B; + pll_write_udelay(base + REG_DSI_28nm_PHY_PLL_GLB_CFG, val, 200); + + val |= DSI_28nm_PHY_PLL_GLB_CFG_PLL_LDO_PWRDN_B; + pll_write_udelay(base + REG_DSI_28nm_PHY_PLL_GLB_CFG, val, 500); + + val |= DSI_28nm_PHY_PLL_GLB_CFG_PLL_ENABLE; + pll_write_udelay(base + REG_DSI_28nm_PHY_PLL_GLB_CFG, val, 600); + } + + if (unlikely(!locked)) + dev_err(dev, "DSI PLL lock failed\n"); + else + DBG("DSI PLL Lock success"); + + return locked ? 0 : -EINVAL; +} + +static int dsi_pll_28nm_enable_seq_lp(struct msm_dsi_pll *pll) +{ + struct dsi_pll_28nm *pll_28nm = to_pll_28nm(pll); + struct device *dev = &pll_28nm->pdev->dev; + void __iomem *base = pll_28nm->mmio; + bool locked; + u32 max_reads = 10, timeout_us = 50; + u32 val; + + DBG("id=%d", pll_28nm->id); + + pll_28nm_software_reset(pll_28nm); + + /* + * PLL power up sequence. + * Add necessary delays recommended by hardware. + */ + pll_write_ndelay(base + REG_DSI_28nm_PHY_PLL_CAL_CFG1, 0x34, 500); + + val = DSI_28nm_PHY_PLL_GLB_CFG_PLL_PWRDN_B; + pll_write_ndelay(base + REG_DSI_28nm_PHY_PLL_GLB_CFG, val, 500); + + val |= DSI_28nm_PHY_PLL_GLB_CFG_PLL_PWRGEN_PWRDN_B; + pll_write_ndelay(base + REG_DSI_28nm_PHY_PLL_GLB_CFG, val, 500); + + val |= DSI_28nm_PHY_PLL_GLB_CFG_PLL_LDO_PWRDN_B | + DSI_28nm_PHY_PLL_GLB_CFG_PLL_ENABLE; + pll_write_ndelay(base + REG_DSI_28nm_PHY_PLL_GLB_CFG, val, 500); + + /* DSI PLL toggle lock detect setting */ + pll_write_ndelay(base + REG_DSI_28nm_PHY_PLL_LKDET_CFG2, 0x04, 500); + pll_write_udelay(base + REG_DSI_28nm_PHY_PLL_LKDET_CFG2, 0x05, 512); + + locked = pll_28nm_poll_for_ready(pll_28nm, max_reads, timeout_us); + + if (unlikely(!locked)) + dev_err(dev, "DSI PLL lock failed\n"); + else + DBG("DSI PLL lock success"); + + return locked ? 0 : -EINVAL; +} + +static void dsi_pll_28nm_disable_seq(struct msm_dsi_pll *pll) +{ + struct dsi_pll_28nm *pll_28nm = to_pll_28nm(pll); + + DBG("id=%d", pll_28nm->id); + pll_write(pll_28nm->mmio + REG_DSI_28nm_PHY_PLL_GLB_CFG, 0x00); +} + +static void dsi_pll_28nm_save_state(struct msm_dsi_pll *pll) +{ + struct dsi_pll_28nm *pll_28nm = to_pll_28nm(pll); + struct pll_28nm_cached_state *cached_state = &pll_28nm->cached_state; + void __iomem *base = pll_28nm->mmio; + + cached_state->postdiv3 = + pll_read(base + REG_DSI_28nm_PHY_PLL_POSTDIV3_CFG); + cached_state->postdiv1 = + pll_read(base + REG_DSI_28nm_PHY_PLL_POSTDIV1_CFG); + cached_state->byte_mux = pll_read(base + REG_DSI_28nm_PHY_PLL_VREG_CFG); + cached_state->vco_rate = clk_hw_get_rate(&pll->clk_hw); +} + +static int dsi_pll_28nm_restore_state(struct msm_dsi_pll *pll) +{ + struct dsi_pll_28nm *pll_28nm = to_pll_28nm(pll); + struct pll_28nm_cached_state *cached_state = &pll_28nm->cached_state; + void __iomem *base = pll_28nm->mmio; + int ret; + + ret = dsi_pll_28nm_clk_set_rate(&pll->clk_hw, + cached_state->vco_rate, 0); + if (ret) { + dev_err(&pll_28nm->pdev->dev, + "restore vco rate failed. ret=%d\n", ret); + return ret; + } + + pll_write(base + REG_DSI_28nm_PHY_PLL_POSTDIV3_CFG, + cached_state->postdiv3); + pll_write(base + REG_DSI_28nm_PHY_PLL_POSTDIV1_CFG, + cached_state->postdiv1); + pll_write(base + REG_DSI_28nm_PHY_PLL_VREG_CFG, + cached_state->byte_mux); + + return 0; +} + +static int dsi_pll_28nm_get_provider(struct msm_dsi_pll *pll, + struct clk **byte_clk_provider, + struct clk **pixel_clk_provider) +{ + struct dsi_pll_28nm *pll_28nm = to_pll_28nm(pll); + + if (byte_clk_provider) + *byte_clk_provider = pll_28nm->provided_clks[DSI_BYTE_PLL_CLK]; + if (pixel_clk_provider) + *pixel_clk_provider = + pll_28nm->provided_clks[DSI_PIXEL_PLL_CLK]; + + return 0; +} + +static void dsi_pll_28nm_destroy(struct msm_dsi_pll *pll) +{ + struct dsi_pll_28nm *pll_28nm = to_pll_28nm(pll); + int i; + + msm_dsi_pll_helper_unregister_clks(pll_28nm->pdev, + pll_28nm->clks, pll_28nm->num_clks); + + for (i = 0; i < NUM_PROVIDED_CLKS; i++) + pll_28nm->provided_clks[i] = NULL; + + pll_28nm->num_clks = 0; + pll_28nm->clk_data.clks = NULL; + pll_28nm->clk_data.clk_num = 0; +} + +static int pll_28nm_register(struct dsi_pll_28nm *pll_28nm) +{ + char clk_name[32], parent1[32], parent2[32], vco_name[32]; + struct clk_init_data vco_init = { + .parent_names = (const char *[]){ "xo" }, + .num_parents = 1, + .name = vco_name, + .ops = &clk_ops_dsi_pll_28nm_vco, + }; + struct device *dev = &pll_28nm->pdev->dev; + struct clk **clks = pll_28nm->clks; + struct clk **provided_clks = pll_28nm->provided_clks; + int num = 0; + int ret; + + DBG("%d", pll_28nm->id); + + snprintf(vco_name, 32, "dsi%dvco_clk", pll_28nm->id); + pll_28nm->base.clk_hw.init = &vco_init; + clks[num++] = clk_register(dev, &pll_28nm->base.clk_hw); + + snprintf(clk_name, 32, "dsi%danalog_postdiv_clk", pll_28nm->id); + snprintf(parent1, 32, "dsi%dvco_clk", pll_28nm->id); + clks[num++] = clk_register_divider(dev, clk_name, + parent1, CLK_SET_RATE_PARENT, + pll_28nm->mmio + + REG_DSI_28nm_PHY_PLL_POSTDIV1_CFG, + 0, 4, 0, NULL); + + snprintf(clk_name, 32, "dsi%dindirect_path_div2_clk", pll_28nm->id); + snprintf(parent1, 32, "dsi%danalog_postdiv_clk", pll_28nm->id); + clks[num++] = clk_register_fixed_factor(dev, clk_name, + parent1, CLK_SET_RATE_PARENT, + 1, 2); + + snprintf(clk_name, 32, "dsi%dpll", pll_28nm->id); + snprintf(parent1, 32, "dsi%dvco_clk", pll_28nm->id); + clks[num++] = provided_clks[DSI_PIXEL_PLL_CLK] = + clk_register_divider(dev, clk_name, + parent1, 0, pll_28nm->mmio + + REG_DSI_28nm_PHY_PLL_POSTDIV3_CFG, + 0, 8, 0, NULL); + + snprintf(clk_name, 32, "dsi%dbyte_mux", pll_28nm->id); + snprintf(parent1, 32, "dsi%dvco_clk", pll_28nm->id); + snprintf(parent2, 32, "dsi%dindirect_path_div2_clk", pll_28nm->id); + clks[num++] = clk_register_mux(dev, clk_name, + (const char *[]){ + parent1, parent2 + }, 2, CLK_SET_RATE_PARENT, pll_28nm->mmio + + REG_DSI_28nm_PHY_PLL_VREG_CFG, 1, 1, 0, NULL); + + snprintf(clk_name, 32, "dsi%dpllbyte", pll_28nm->id); + snprintf(parent1, 32, "dsi%dbyte_mux", pll_28nm->id); + clks[num++] = provided_clks[DSI_BYTE_PLL_CLK] = + clk_register_fixed_factor(dev, clk_name, + parent1, CLK_SET_RATE_PARENT, 1, 4); + + pll_28nm->num_clks = num; + + pll_28nm->clk_data.clk_num = NUM_PROVIDED_CLKS; + pll_28nm->clk_data.clks = provided_clks; + + ret = of_clk_add_provider(dev->of_node, + of_clk_src_onecell_get, &pll_28nm->clk_data); + if (ret) { + dev_err(dev, "failed to register clk provider: %d\n", ret); + return ret; + } + + return 0; +} + +struct msm_dsi_pll *msm_dsi_pll_28nm_init(struct platform_device *pdev, + enum msm_dsi_phy_type type, int id) +{ + struct dsi_pll_28nm *pll_28nm; + struct msm_dsi_pll *pll; + int ret; + + if (!pdev) + return ERR_PTR(-ENODEV); + + pll_28nm = devm_kzalloc(&pdev->dev, sizeof(*pll_28nm), GFP_KERNEL); + if (!pll_28nm) + return ERR_PTR(-ENOMEM); + + pll_28nm->pdev = pdev; + pll_28nm->id = id; + + pll_28nm->mmio = msm_ioremap(pdev, "dsi_pll", "DSI_PLL"); + if (IS_ERR_OR_NULL(pll_28nm->mmio)) { + dev_err(&pdev->dev, "%s: failed to map pll base\n", __func__); + return ERR_PTR(-ENOMEM); + } + + pll = &pll_28nm->base; + pll->min_rate = VCO_MIN_RATE; + pll->max_rate = VCO_MAX_RATE; + pll->get_provider = dsi_pll_28nm_get_provider; + pll->destroy = dsi_pll_28nm_destroy; + pll->disable_seq = dsi_pll_28nm_disable_seq; + pll->save_state = dsi_pll_28nm_save_state; + pll->restore_state = dsi_pll_28nm_restore_state; + + if (type == MSM_DSI_PHY_28NM_HPM) { + pll_28nm->vco_delay = 1; + + pll->en_seq_cnt = 3; + pll->enable_seqs[0] = dsi_pll_28nm_enable_seq_hpm; + pll->enable_seqs[1] = dsi_pll_28nm_enable_seq_hpm; + pll->enable_seqs[2] = dsi_pll_28nm_enable_seq_hpm; + } else if (type == MSM_DSI_PHY_28NM_LP) { + pll_28nm->vco_delay = 1000; + + pll->en_seq_cnt = 1; + pll->enable_seqs[0] = dsi_pll_28nm_enable_seq_lp; + } else { + dev_err(&pdev->dev, "phy type (%d) is not 28nm\n", type); + return ERR_PTR(-EINVAL); + } + + ret = pll_28nm_register(pll_28nm); + if (ret) { + dev_err(&pdev->dev, "failed to register PLL: %d\n", ret); + return ERR_PTR(ret); + } + + return pll; +} + diff --git a/kernel/drivers/gpu/drm/msm/dsi/sfpb.xml.h b/kernel/drivers/gpu/drm/msm/dsi/sfpb.xml.h index 50ff9851d..7d7662e69 100644 --- a/kernel/drivers/gpu/drm/msm/dsi/sfpb.xml.h +++ b/kernel/drivers/gpu/drm/msm/dsi/sfpb.xml.h @@ -8,19 +8,19 @@ http://github.com/freedreno/envytools/ git clone https://github.com/freedreno/envytools.git The rules-ng-ng source files this header was generated from are: -- /home/robclark/src/freedreno/envytools/rnndb/msm.xml ( 676 bytes, from 2014-12-05 15:34:49) -- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1453 bytes, from 2013-03-31 16:51:27) -- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp4.xml ( 20908 bytes, from 2014-12-08 16:13:00) -- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp_common.xml ( 2357 bytes, from 2014-12-08 16:13:00) -- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp5.xml ( 27208 bytes, from 2015-01-13 23:56:11) -- /home/robclark/src/freedreno/envytools/rnndb/dsi/dsi.xml ( 11712 bytes, from 2013-08-17 17:13:43) -- /home/robclark/src/freedreno/envytools/rnndb/dsi/sfpb.xml ( 344 bytes, from 2013-08-11 19:26:32) -- /home/robclark/src/freedreno/envytools/rnndb/dsi/mmss_cc.xml ( 1686 bytes, from 2014-10-31 16:48:57) -- /home/robclark/src/freedreno/envytools/rnndb/hdmi/qfprom.xml ( 600 bytes, from 2013-07-05 19:21:12) -- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml ( 26848 bytes, from 2015-01-13 23:55:57) -- /home/robclark/src/freedreno/envytools/rnndb/edp/edp.xml ( 8253 bytes, from 2014-12-08 16:13:00) - -Copyright (C) 2013 by the following authors: +- /home/robclark/src/freedreno/envytools/rnndb/msm.xml ( 676 bytes, from 2015-05-20 20:03:14) +- /home/robclark/src/freedreno/envytools/rnndb/freedreno_copyright.xml ( 1453 bytes, from 2015-05-20 20:03:07) +- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp4.xml ( 20915 bytes, from 2015-05-20 20:03:14) +- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp_common.xml ( 2849 bytes, from 2015-09-18 12:07:28) +- /home/robclark/src/freedreno/envytools/rnndb/mdp/mdp5.xml ( 37194 bytes, from 2015-09-18 12:07:28) +- /home/robclark/src/freedreno/envytools/rnndb/dsi/dsi.xml ( 27887 bytes, from 2015-10-22 16:34:52) +- /home/robclark/src/freedreno/envytools/rnndb/dsi/sfpb.xml ( 602 bytes, from 2015-10-22 16:35:02) +- /home/robclark/src/freedreno/envytools/rnndb/dsi/mmss_cc.xml ( 1686 bytes, from 2015-05-20 20:03:14) +- /home/robclark/src/freedreno/envytools/rnndb/hdmi/qfprom.xml ( 600 bytes, from 2015-05-20 20:03:07) +- /home/robclark/src/freedreno/envytools/rnndb/hdmi/hdmi.xml ( 29154 bytes, from 2015-08-10 21:25:43) +- /home/robclark/src/freedreno/envytools/rnndb/edp/edp.xml ( 10416 bytes, from 2015-05-20 20:03:14) + +Copyright (C) 2013-2015 by the following authors: - Rob Clark <robdclark@gmail.com> (robclark) Permission is hereby granted, free of charge, to any person obtaining @@ -45,7 +45,18 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#define REG_SFPB_CFG 0x00000058 +enum sfpb_ahb_arb_master_port_en { + SFPB_MASTER_PORT_ENABLE = 3, + SFPB_MASTER_PORT_DISABLE = 0, +}; + +#define REG_SFPB_GPREG 0x00000058 +#define SFPB_GPREG_MASTER_PORT_EN__MASK 0x00001800 +#define SFPB_GPREG_MASTER_PORT_EN__SHIFT 11 +static inline uint32_t SFPB_GPREG_MASTER_PORT_EN(enum sfpb_ahb_arb_master_port_en val) +{ + return ((val) << SFPB_GPREG_MASTER_PORT_EN__SHIFT) & SFPB_GPREG_MASTER_PORT_EN__MASK; +} #endif /* SFPB_XML */ |